Vorlesung vom 03.07.2006

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