Vorlesung Thomas Jansen 03.07.2006 zur Implementierung von

Werbung
zur Implementierung von Kompressionsalgorithmen
Suffix-Bäume als Implementierung des Wörterbuchs
Effiziente Algorithmen und Komplexitätstheorie
Suffix-Baum = kompaktierter Suffix-Trie
Suffix-Trie = Trie aller Suffixe
Trie = Baum, Kanten mit Buchstaben beschriftet
∀ Knoten ∀ ausgehende Kanten: Beschriftungen verschieden,
mit Knoten w Konkatenation der
Kantenbeschriftungen w assoziiert
Vorlesung Thomas Jansen
03.07.2006
Suffix-Tries im Worst Case quadratische Größe,
darum Suffix-Bäume
1
Suffix-Bäume
2
Begriffe für den Algorithmus von Ukkonen
I
Kanten mit Zeichenketten ∈ Σ+ beschriftet
I
I
(Trie: Beschriftungen verschieden)
Anfangsbuchstaben verschieden)
X = x1 x2 · · · xn ∈ Σn = Zeichenkette, für die Suffix-Baum
konstruiert wird
I
I
für Speicherplatz: statt Zeichenkette xi · · · xj nur Indizes (i, j)
an Kanten schreiben
I
Größe ≤ 2n
Ti = Suffix-Trie für x1 x2 · · · xi
Tei = Suffix-Baum für x1 x2 · · · xi
Phase i = Konstruktion von Tei aus Tg
i−1
I
Ziel Konstruktion in linearer Zeit auf linearem Platz
I
(Baum:
I
I
I
Endknoten = sl , so dass ∀sj mit j < l xi -Kante angehängt
werden muss
dafür (schon bei Suffix-Tries) Suffix-Links
I
aktiver Knoten = sk , so dass ∀sj mit j < k sj Blatt ist
I
Suffix-Link(aW ) = Zeiger auf W
I
I
für Algorithmus künstliche Wurzel ⊥ mit Suffix-Link(ε)=⊥
Änderungen nur zwischen Endknoten und aktivem Knoten
erforderlich
Knoten in Ti explizit, wenn auch in Tei vorhanden, sonst
I
effiziente Konstruktion von Suffix-Bäumen
Ukkonen (1995): On-line construction of suffix trees
implizit
3
4
Referenzen
Vorbereitung des Algorithmus von Ukkonnen
Lemma 217
I
(xj xj+1 · · · xk−1 , (k, p)) Referenz von xj xj+1 · · · xp , wenn
xj xj+1 · · · xk−1 ∈ Tei , p ≤ i
I
Referenzen nicht eindeutig
I
Referenz heißt kanonisch, wenn p − k minimal
e
Sei X = x1 x2 · · · xn ∈ Σn , Tg
i−1 Suffix-Baum für x1 · · · xi−1 , Ti
Suffix-Baum für x1 · · · xi für i ∈ {1, . . . , n}.
Ist (s, (k, i − 1)) eine Referenz in Tg
i−1 auf den Endknoten, dann
e
ist (s, (k, i)) eine Referenz in Ti auf den aktiven Knoten.
I
kanonische Referenzen nicht eindeutig
hilft aktiven Knoten für nächste Phase zu finden
I
(xj xj+1 · · · xk−1 , (k, ∞)) offene Referenz von xj xj+1 · · · xn ,
wenn xj xj+1 · · · xk−1 ∈ Tei
Lemma 218
I
Sei X = x1 x2 · · · xn ∈ Σn , sei aW ∈ X mit a ∈ Σ, sei aW innerer
Knoten im Suffix-Baum T für X .
W ist innerer Knoten in T .
Kanten zu Blättern mit offenen Referenzen beschriftet
brauchen keine Änderungen
garantiert Existenz der Suffix-Links
6
5
Der Algorithmus von Ukkonen
Zentrale Prozedur Update
Eingabe Referenz (s, (k, p)) des aktiven Knotens, Phase i
Ausgabe Endknoten in Tg
i−1
1. w := ε
2. (s, k) := Kanonisiere(s, (k, p))
3. (fertig, r ) := TesteUndTeile(s, (k, p), xi )
4. So lange nicht fertig
5.
Erzeuge neuen Knoten m, Kante (r , m) mit Beschriftung (i, ∞)
6.
Falls w 6= ε
7.
suflink(w ) := r
8.
w := r
9.
(s, k) := Kanonisiere(suflink(s), (k, p))
10.
(fertig, r ) := TesteUndTeile(s, (k, p), xi )
11. Falls w 6= ε
12.
suflink(w ) := s
13. Ausgabe (s, k)
Σn )
KonstruiereSuffixBaum(X
= x1 · · · xn ∈
S
1. T := {⊥, ε, v }, {(ε, v )} ∪
{(⊥, ε)}
x∈Σ
2.
3.
4.
5.
6.
sufLink(ε) := ⊥
s := ε
k := 2
Für i ∈ {2, 3, . . . , n}
(s, k) := Update(s, (k, i − 1), i)
Beobachtungen
f1 konstruiert
I initial T
I
(s, (k, i − 1)) ist immer aktiver Knoten in Tg
i−1
I
Korrektheit und Laufzeit hängt ganz von Update ab
7
8
Beispiel Algrithmus von Ukkonen
BBAA
BA
A
A
B
B
BAA
⊥
ε
A
A, B
A
B
A
AA
{⊥, ε, v}, {(ε, v)} ∪
KonstruiereSuffixBaum(X = ABABBAA, n = 7)
:=
10
S {(⊥, ε)}
Laufzeit durch Kanonisiere
x∈Σ
Laufzeit des Algorithmus von Ukkonen
suflink(ε) := ⊥
s := ε
k := 2
Für i ∈ {2, 3, . . . , n}
–(s, k) := Update(s, (k, i − 1), i )
9
BAA
ABBAA1. T
2.
3.
4.
5.
6.
s 0.
BBAA
AB
ABBAA
ABABBAA
···xp |+1 ···w|w |
A
w|x
k =6 i =7
11.
···xp |
n=7 s =
w1 w2 ···w|x
Σ = {A, B}, X = ABABBAA
fertig
Eingabe Referenz (s, (k, p)) des aktiven Knotens, neues Zeichen x
Ausgabe falsch“ und Knoten, an den xi angehängt werden muss
”
oder wahr“
”
1. Falls |xk xk+1 · · · xp | = 0
2.
Falls x-Kante aus s existiert
3.
Dann Ausgabe (wahr, s). STOP
4.
Sonst Ausgabe (falsch, s). STOP
5. Sonst
w
6.
Sei e Kante s → s 0 mit w1 = xk .
7.
Falls x = w|xk xk+1 ···xp |+1
8.
Dann Ausgabe (wahr, s). STOP
9.
Sonst
w
10.
Teile e = s → s 0 wie folgt aus:
s
−→k
m k −→
Ausgabe (falsch, m)
BABBAA
Hilfsprozedur TesteUndTeile
!
Beobachtung jeder Aufruf von Kanonisiere
von TesteUndTeile-Aufruf gefolgt
Wir wollen zeigen lineare Laufzeit bei geeigneter Implementierung
also Kanonisiere-Aufrufe müssen nicht gezählt werden
klar Σ ist konstant
Beobachtungen
I
Laufzeit proportional zur Anzahl betrachteter Knoten
I
es genügt, Knoten in Kanonisiere und TesteUndTeile zu zählen
I
k wird nirgendwo gesenkt
Beobachtung in Kanonisiere für jeden weiteren Knoten
wird k erhöht
Erinnerung k sinkt nie und steigt nicht über n + 1
also Rechenzeit in Kanonisiere
= O(Rechenzeit in TesteUndTeile + n)
11
12
Laufzeit durch TesteUndTeile
Zur Implementierung von Suffix-Bäumen
unterscheide Aufrufe mit Ergebnis wahr und falsch
unsere Überlegungen setzen voraus
wahr-Aufrufe lassen Update enden
beim Verlassen von Update endet Phase i
also Aufwand durch wahr-Aufrufe von TesteUndTeile = O(n)
I
Platz je Knoten O(1)
I
Zeit je Zugriff auf xi -Kind O(1)
Wie realisiert man das?
Implementierung mit Arrays
Beobachtung in falsch-Aufruf von TesteUndTeile
wächst Suffix-Baum
I
Platz je Knoten O(|Σ|) = O(1)
I
Zugriff je xi -Kind O(1)
Erinnerung Suffix-Baum hat Größe ≤ 2n
+ schnell
also Aufwand durch falsch-Aufrufe von TesteUndTeile = O(n)
− O(|Σ|) je Knoten sehr groß
also insgesamt Rechnzeit und Speicherplatz O(n)
14
13
Nicht-triviale Implementierung von Suffix-Bäumen
Nicht-triviale Implementierung von Suffix-Bäumen (2)
Idee Verwalte Kinder aller Knoten in einem Array, Größe O(n)
Implementierung mit linearen Listen
I
Platz je Knoten O(|Kinder|) = O(1)
I
Zugriff je xi -Kind O(|Kinder|) = O(1)
Zugriff auf xi -Kind des Knotens v
durch Hashfunktion h : V × Σ → N0
klar |V | · |Σ| = Θ(n · |Σ|) Hashtabelle
also Hashing-typische Probleme mit Kollisionen
+ wesentlich platzeffizienter
− langsam
Anmerkung mit vernünftiger Hashing-Implementierung
Zeit O(1) bei Platz O(n) mit W’keit dicht bei 1 machbar
Implementierung mit balancierten Bäumen (z. B. AVL-Bäume)
I
Platz je Knoten O(|Kinder|) = O(1)
I
Zugriff je xi -Kind O(log |Kinder|) = O(1)
Anmerkung in Suffix-Bäumen meist große Verzweigungsgrade
nur nah bei der Wurzel
+ wesentlich platzeffizienter
− etwas langsamer als mit Arrays
naheliegend hybride Implementierung mit
Arrays wurzelnah, sonst nicht-triviale Implementierung
15
16
Herunterladen