D2. Suche in Strings
1Marcel L¨ uthi
Universit¨at Basel
22. Mai 2019
1Folien angelehnt an Vorlesungsfolien von Sedgewick & Wayne https://algs4.cs.princeton.edu/lectures/52Tries-2x2.pdf
Einf¨ uhrung
Erinnerung: Symboltabellen
Abstraktion f¨ ur Schl¨ ussel/Werte Paar Grundlegende Operationen
Speichere Schl¨ ussel mit dazugeh¨ orendem Wert.
Suche zu Schl¨ ussel geh¨ orenden Wert.
Schl¨ ussel und Wert l¨ oschen.
Typische Beispiele
DNS - Suche IP-Adresse zu Domainnamen
Telefonbuch - Suche Telefonnummer zu Person / Adresse
W¨ orterbuch - Suche ¨ Ubersetzungen f¨ ur Wort
Ubersicht ¨
Worst-case Average-case Implementation suchen einf¨ugen suchen (hit) einf¨ugen Rot-Schwarz B¨aume 2 log2(N) 2 log2(N) 1 log2(N) 1 log2(N)
Hashtabellen N N 1 1
Frage: Geht es noch schneller?
Antwort: Ja, wenn wir nicht ganzen String vergleichen m¨ussen.
Ubersicht ¨
Worst-case Average-case Implementation suchen einf¨ugen suchen (hit) einf¨ugen Rot-Schwarz B¨aume 2 log2(N) 2 log2(N) 1 log2(N) 1 log2(N)
Hashtabellen N N 1 1
Frage: Geht es noch schneller?
Antwort: Ja, wenn wir nicht ganzen String vergleichen m¨ussen.
Symboltabelle f¨ ur Strings
c l a s s S t r i n g S T [ V a l u e ]:
def S t r i n g S T ()
def put ( key : String , v a l u e : V a l u e ) - > N o n e def get ( key : S t r i n g ) - > V a l u e
def d e l e t e ( key : S t r i n g ) - > N o n e def k e y s () - > I t e r a t o r [ S t r i n g ]
Normale Symboltabellen Operationen, aber mit fixem Typ String
als Schl¨ ussel
Symboltabelle f¨ ur Strings
c l a s s S t r i n g S T [ V a l u e ]:
def S t r i n g S T ()
def put ( key : String , v a l u e : V a l u e ) - > N o n e def get ( key : S t r i n g ) - > V a l u e
def d e l e t e ( key : S t r i n g ) - > N o n e def k e y s () - > I t e r a t o r [ S t r i n g ]
def k e y s W i t h P r e f i x ( s : S t r i n g ) - > I t e r a t o r [ S t r i n g ] def k e y s T h a t M a t c h ( s : S t r i n g ) - > I t e r a t o r [ S t r i n g ] def l o n g e s t P r e f i x O f ( s : S t r i n g ) - > S t r i n g
Mittels Tries lassen sich viele n¨ utzliche, zeichenbasierte
Suchoperationen definieren.
Tries
Tries
Trie Von Retrieval.
Ausgesprochen wie try
Zeichen (nicht Schl¨ ussel werden in Knoten gespeichert)
Jeder Knoten hat R Knoten (also einen pro m¨ oglichem Zeichen)
Quelle: Sedgewick & Wayne, Algorithmen, Abbildung 5.19
Erinnerung: Alphabet
Abstraktion Alphabet macht Code unabh¨ angig von Alphabet.
c l a s s A l p h a b e t :
def _ _ i n i t _ _ ( s : L i s t [ c h a r ]) def t o C h a r ( i n d e x : Int ) - > c h a r def t o I n d e x ( c : C h a r ) - > int def c o n t a i n s ( c : C h a r ) - > b o o l e a n def R () - > int # R a d i x
Name Radix (R) Bits (log2(R)) Zeichen
BINARY 2 1 0 1
DNA 4 2 A C G T
ASCII 128 7 ASCII Characters
EXTENDED ASCII 256 8 EXTENDED ASCII
UNICODE 1’114’112 21 UNICODE
Repr¨ asentation der Knoten
Quelle: Sedgewick & Wayne, Algorithmen, Abbildung 5.21
c l a s s N o d e : v a l u e = N o n e
c h i l d r e n = [ N o n e ] * R # R : R a d i x v o n A l p h a b e t
Suche in Trie
Dem Zeichen entsprechenden Link folgen
Erfolgreiche Suche:
Endet an Knoten mit definiertem Wert Erfolglose Suche:
Endet an Knoten mit undefiniertem Wert (null)
Quelle: Sedgewick & Wayne, Algorithmen, Abbildung 5.20
Suche in Tries
def get ( node , key , d ):
if ( n o d e == N o n e ):
r e t u r n N o n e if d == len( key ):
r e t u r n n o d e
c = a l p h a b e t . t o I n d e x ( key [ d ])
r e t u r n get ( n o d e . c h i l d r e n [ c ] , key , d + 1)
Einf¨ ugen in Trie
Dem Zeichen entsprechenden Link folgen
Erfolgreiche Suche:
Wert neu setzten Erfolglose Suche:
Neuen Knoten erzeugen.
Quelle: Sedgewick & Wayne, Algorithmen, Abbildung 5.22
Einf¨ ugen in Trie
def put ( node , key , value , d ):
if n o d e == N o n e :
n o d e = N o d e ( a l p h a b e t . r a d i x ()) if d == len( key ):
n o d e . v a l u e = v a l u e r e t u r n n o d e
c = a l p h a b e t . t o I n d e x ( key [ d ])
n o d e . c h i l d r e n [ c ] = put ( n o d e . c h i l d r e n [ c ] , key , value , d + 1) r e t u r n n o d e
L¨ oschen von Schl¨ usseln
Schl¨ ussel finden und Knoten l¨ oschen.
Rekursiv alle Knoten mit nur null-Werten und null-links l¨ oschen
Quelle: Sedgewick & Wayne, Algorithmen, Abbildung 5.26
L¨ oschen von Schl¨ usseln
def d e l e t e ( node , key , d ):
if n o d e == N o n e : r e t u r n N o n e if d == len( key ):
n o d e . v a l u e = N o n e e l s e:
c = a l p h a b e t . t o I n d e x ( key [ d ])
n o d e . c h i l d r e n [ c ] = d e l e t e ( n o d e . c h i l d r e n [ c ] , key , d + 1) if n o d e . v a l u e != N o n e :
r e t u r n n o d e
n o n N u l l C h i l d r e n = [ c for c in n o d e . c h i l d r e n if c != N o n e ] if len( n o n N u l l C h i l d r e n ) > 0:
r e t u r n n o d e e l s e:
r e t u r n N o n e
Implementation und Beispielanwendung
Jupyter Notebook: Tries.ipynb
Analyse: Form des Tries
Theorem
Die verkettete Struktur (Form) eines Trie ist nicht abh¨ angig von
der Schl¨ usselreihenfolge beim L¨ oschen/Einf¨ ugen: F¨ ur jede
gegebene Menge von Schl¨ usseln gibt es einen eindeutigen Trie.
Analyse: Einf¨ ugen
Theorem
Die Anzahl der Arrayzugriffe beim Suchen in einem Trie oder beim Einf¨ ugen eines Schl¨ ussels in einen Trie ist h¨ ochstens 1 plus der L¨ ange des Schl¨ ussels.
Theorem
Die durchschnittliche Anzahl der untersuchten Knoten bei einer
erfolglosen Suche in einem Trie, der aus N Zufallsschl¨ usseln ¨ uber
einem Alphabet der Gr¨ osse R erstellt wird, betr¨ agt ∼ log
R(N).
Analyse: Einf¨ ugen
Theorem
Die Anzahl der Arrayzugriffe beim Suchen in einem Trie oder beim Einf¨ ugen eines Schl¨ ussels in einen Trie ist h¨ ochstens 1 plus der L¨ ange des Schl¨ ussels.
Theorem
Die durchschnittliche Anzahl der untersuchten Knoten bei einer
erfolglosen Suche in einem Trie, der aus N Zufallsschl¨ usseln ¨ uber
einem Alphabet der Gr¨ osse R erstellt wird, betr¨ agt ∼ log
R(N).
Take-Home Message
Auch in riesigen Datenmengen k¨ onnen wir mit wenigen Vergleichen
jeden Wert finden.
Speichereffiziente Variante: Tern¨ are Suchtries
Ein Problem mit Tries
In jedem Knoten wird ein Array der Gr¨ osse R gespeichert Beispiel Unicode (utf-16): R = 2
16= 65536.
L¨ osung:
Speichere Zeichen c sowie Wert im Knoten Jeder Knoten hat 3 Kinder:
Kleiner c (linker Teilbaum) Gleich c (mittlerer Teilbaum) Gr¨ osser c (rechter Teilbaum)
Bentley, Jon L., and Robert Sedgewick. ”Fast algorithms for
sorting and searching strings.”1997.
Speichereffiziente Variante: Tern¨ are Suchtries
Beispiel:
Quelle: Sedgewick & Wayne, Algorithmen, Abbildung 5.28
Bei ¨ Ubereinstimmung wird Suche mit n¨ achstem Zeichen im mittleren Teilbaum weitergef¨ uhrt.
Suche ist erfolgreich, wenn Wert in Endknoten der Suche
gespeichert ist.
Tries versus Hashing
Hashing:
Muss ganzen Schl¨ ussel anschauen
Etwa gleiche Kosten f¨ ur erfolgreiche und erfolglose Suche Performance h¨ angt von Hashfuktion ab
Keine ordnungsbasierten Operationen Tries:
Nur f¨ ur Strings geeignet
Macht nur so viele Vergleiche wie gerade ben¨ otigt werden
Erfolglose Suche ben¨ otigt nur ein paar Zeichenvergleichen
Flexible zeichenbasierte Operationen werden unterst¨ utzt
Zeichenbasierte Operationen
Zeichenbasierte Operationen
Schl¨ ussel Wert
by 4
sea 6
sells 1
she 0
shells 3
shore 7
the 5
Pr¨ afix matching Pr¨ afix: sh: Schl¨ ussel, she, shells, shore
Wildcard matching .he: Schl¨ ussel, she, the L¨ angstes Pr¨ afix Anfrage: shellsort
Schl¨ ussel: shells
Pr¨ afix¨ ubereinstimmung
Beispielanwendung: Autocomplete
Zeichenbasierte Operationen
c l a s s S t r i n g S T : def S t r i n g S T ()
def put ( key : String , v a l u e : V a l u e ) - > N o n e def get ( key : S t r i n g ) - > V a l u e
def d e l e t e ( key : S t r i n g ) - > N o n e def k e y s () - > I t e r a t o r [ S t r i n g ]
def k e y s W i t h P r e f i x ( s : S t r i n g ) - > I t e r a t o r [ S t r i n g ] def k e y s T h a t M a t c h ( s : S t r i n g ) - > I t e r a t o r [ S t r i n g ] def l o n g e s t P r e f i x O f ( s : S t r i n g ) - > S t r i n g
Warmup: Alle Schl¨ ussel zur¨ uckgeben
Traversieren des Baumes
Bei jedem Knoten mit Wert 6= null, Schl¨ ussel merken
Quelle: Sedgewick & Wayne, Algorithmen, Abbildung 5.23
Pr¨ afix¨ ubereinstimmung
Subtrie mit Pr¨ afix finden
Alle Schl¨ ussel von diesem Subtrie zur¨ uckgeben
Quelle: Sedgewick & Wayne, Algorithmen, Abbildung 5.24
Pr¨ afix¨ ubereinstimmung
def k e y s ( n o d e ):
r e t u r n k e y s W i t h P r e f i x ( node , " " ) def k e y s W i t h P r e f i x ( node , r e f i x ):
q u e u e = []
c o l l e c t ( get ( node , prefix , 0) , prefix , q u e u e ) r e t u r n q u e u e
Pr¨ afix¨ ubereinstimmung
def k e y s ( n o d e ):
r e t u r n k e y s W i t h P r e f i x ( node , " " ) def k e y s W i t h P r e f i x ( node , r e f i x ):
q u e u e = []
c o l l e c t ( get ( node , prefix , 0) , prefix , q u e u e ) r e t u r n q u e u e
def c o l l e c t ( node , prefix , q u e u e ):
if n o d e == N o n e : r e t u r n
if n o d e . v a l u e != N o n e : q u e u e . a p p e n d ( p r e f i x )
l a b e l e d C h i l d r e n = [( a l p h a b e t . t o C h a r ( p ) , c h i l d )
for ( p , c h i l d ) in e n u m e r a t e( n o d e . c h i l d r e n ) if c h i l d != N o n e ]
for ( char , c h i l d ) in l a b e l e d C h i l d r e n : c o l l e c t ( child , p r e f i x + char , q u e u e )
L¨ angstes Pr¨ afix
Beispielanwendung: Routing, LZW-Kompression
”128”
”128.112”
”128.112.055”
”128.112.055.15”
”128.112.136”
”128.112.155.11”
”128.112.155.13”
”128.222”
”128.222.136”
Anfrage:
longestPrefixOf(”128.112.136.11”) = ”128.112.136”
longestPrefixOf(”128.112.100.16”) = ”128.112”
longestPrefixOf(”128.166.123.45”) = ”128”
L¨ angstes Pr¨ afix
Nach Anfragestring suchen
L¨ angster Schl¨ ussel auf dem Weg dahin speichern.
Quelle: Sedgewick & Wayne, Algorithmen, Abbildung 5.25
L¨ angstes Pr¨ afix
def l o n g e s t P r e f i x O f ( node , s ):
l = s e a r c h ( node , s , 0 , 0) r e t u r n s [0: l ]
def s e a r c h ( node , s , d , l e n g t h ):
if n o d e == N o n e : r e t u r n l e n g t h if n o d e . v a l u e != N o n e :
l e n g t h = d if d == len( s ):
r e t u r n l e n g t h
c = a l p h a b e t . t o I n d e x ( s [ d ])
r e t u r n s e a r c h ( n o d e . c h i l d r e n [ c ] , s , d +1 , l e n g t h )
Quiz: Wildcard matching
Wie implementieren wir Wildcard matching?
Quelle: Sedgewick & Wayne, Algorithmen, Abbildung 5.28