Informatik I 5. Kapitel Hashverfahren Rainer Schrader Zentrum für Angewandte Informatik Köln 3. Juni 2008 1 / 86 2 / 86 Hashverfahren Hashverfahren Gliederung • zur Suche haben wir bisher lediglich Vergleichsoperationen auf den • Adressberechnung durch Hashing Schlüsseln zugelassen • wir werden jetzt arithmetische Operationen auf den Schlüsseln • Hashfunktionen erlauben, • Kollisionsbehandlung • um daraus die mögliche Position des Datums zu berechnen. • Anwendung von Hashfunktionen 3 / 86 4 / 86 Hashverfahren Hashverfahren Anwendungen: Szenario: Symboltabellen für Compiler • wir müssen eine Menge von Schlüssen verwalten • Bezeichner dürfen 80 Zeichen lang sein • das erste Zeichen muss ein Buchstabe sein • auf der Schlüsselmenge müssen die Operationen Einfügen, Suchen und Löschen unterstützt werden • die folgenden Zeichen können auch Sonderzeichen sein • die Schlüssel entstammen einer (im allgemeinen sehr großen) Menge • zulässige Bezeichner: 26 · 3779 • davon kommen in einem Programm nur k 26 · 3779 vor von potentiellen Schlüsseln 5 / 86 6 / 86 Hashverfahren Hashverfahren gesucht: Datenstruktur, • die Suchen, Einfügen, Entfernen effizient unterstützt Zugangskontrolle über Personalausweise • Personalausweise tragen eine 10-stellige Identifikationsnummer 10 • dies ergibt 10 • die Größe soll nur von der Anzahl der verwendeten Schlüssel abhängen potentielle Nummern • „effizient” soll bedeuten: möglichst konstante Zeit pro Operation • für den Zugang (z.B. zu einem Industriebtrieb) kommen davon nur sehr • wir könnten dazu verwenden wenige in Frage • ein Array in der Größe der Anzahl der potentiellen Schlüssel ; Platzverschwendung analog bei der Verwaltung von Passwörtern • eine ungeordnete verkettete Liste ; schlechte Laufzeit 7 / 86 8 / 86 Hashverfahren Hashverfahren Idee: „verallgemeinerte Felder“ • wir verwenden ein Feld T (m) (Hashtabelle) • m ist die Größe der Hashtabelle • dem Schlüssel k wird eine Adresse h(k ) in T zugeordnet • aber zwei Schlüssel können auf dieselbe Adresse abgebildet werden • im letzten Kapitel: • lineare geordnete Listen • Suchen mittels Schlüsselvergleichen ; immer noch zu aufwendig (Kollisionen) • jetzt: Kollisionen: • Adressen werden durch eine arithmetische Berechnung ermittelt • müssen behandelt werden, • sollen möglichst selten auftreten, • jeder Schlüssel k wird über eine Adresse h(k ) angesprochen • sollen möglichst effizient aufgelöst werden. 9 / 86 10 / 86 Hashverfahren Hashverfahren Der einfachste Fall: direkte Adressierung • alle Schlüssel sind verschieden. • U = {0, 1, . . . , m − 1}. Szenario • die Schlüssel entstammen einer (im allgemeinen großen) Menge U (Universum) Implementierung der Operationen: • die zu speichernden Schlüssel K bilden eine Teilmenge von U • üblicherweise ist |K | |U | • verwende Feld T [0, . . . , m − 1] (Adresstabelle) • T (k ) zeigt auf einen Datensatz mit Schlüssel k • die Größe m der Hashtabelle wird vorab festgelegt, • (oft ist m ≤ |K |) • suche(T,k): gib T (k ) zurück • ebenso die Hashfunktion h : U → {0, . . . m − 1} • füge_ein(T,x): T (key (x )) zeigt auf x • lösche(T,x): T (key (x )) = nil • alle diese Operationen benötigen im worst-case O(1) Zeit. 11 / 86 12 / 86 Hashverfahren Hashverfahren Illustration: (m = 10) 0 10 U (alle möglichen Schlüssel) 6 1 10 2 3 K (aktulle Schlüssel) 10 2 5 8 5 8 3 0 9 3 • Θ(|K |) Speicherplatz 4 • Suche im Durchschnitt in Θ(1) Zeit 5 • Methode: Benutzung einer Hashfunktion 7 h : U → {0, 1, 2, . . . , m − 1} 8 9 10 7 • Ziele: 2 6 10 10 • im Allgemeinen gilt jedoch: |K | |U | 1 4 14 / 86 13 / 86 Hashverfahren Hashtabellen Illustration: 0 U 1 Probleme: h(k1 ) (1) Wie soll man die Hashfunktion wählen? h(k4 ) K k3 k1 (2) Wie geht man mit Kollisionen um? h(k2 ) = h(k5 ) k4 h(k3 ) k2 k5 m−1 15 / 86 16 / 86 Hashverfahren Hashverfahren • was zeichnet eine gute Hashfunktion aus? Gliederung Ziel: • Adressberechnung durch Hashing Die Hashadressen sollen gleichverteilt in {0, 1, . . . , m − 1} sein. • Hashfunktionen • die Verteilung der Adressen hängt ab von der Verteilung der Schlüssel. • sei p(k ) die Wahrscheinlichkeit, dass Schlüssel k ∈ U vorkommt. • Kollisionsbehandlung • Anwendung von Hashfunktionen Gleichverteilungsziel: X p(k ) = k : h(k )=j 1 m für j = 0, 1, . . . , m − 1. 17 / 86 18 / 86 Hashverfahren Hashverfahren • Generalannahme: die Schlüssel sind nichtnegative ganze Zahlen Beispiel: • Schlüssel sind reelle Zahlen, unabhängig gleichverteilt im Intervall [0, 1) • dann erfüllt • es ist immer möglich, Schlüssel so zu interpretieren, z.B. • Charakterstrings: jedem Zeichen entspricht im ASCII-Code eine Zahl im Bereich [0, . . . , 127] • interpretiere String als Zahl zur Basis 128, z.B. h(k ) = bkmc das Gleichverteilungsziel • p = ˆ 112 • Problem: im Allgemeinen kennen wir p(k ) nicht. • t = ˆ 116 • ⇒ pt = ˆ 112 × 128 + 116 = 14452 19 / 86 20 / 86 Hashverfahren Hashverfahren Divisionsmethode Gliederung h(k ) = k mod m • Adressberechnung durch Hashing Eigenschaften • Hashfunktionen (1) h(k ) kann schnell berechnet werden, (2) die richtige Wahl von m (Tabellengröße) ist sehr wichtig: • Divisionsmethode • Multiplikationsmethode Beispiele • universelles Hashing • ist m gerade, dann gilt: h(k ) gerade ⇐⇒ k gerade • Kollisionsbehandlung • kann z.B. dann ungünstig sein, wenn: • Anwendung von Hashfunktionen • die Parität von k das Geschlecht kodiert • und K sehr viele „weibliche” Schlüssel enthält 22 / 86 21 / 86 Hashverfahren Hashverfahren zu (2): Man sollte vermeiden: • Potenzen der Basis des Zahlensystems, in dem die Schlüssel dargestellt sind: allgemein gilt: • m = 2i : alle bis auf die letzten i Binärziffern werden ignoriert • m = 10i : analog bei Dezimalzahlen • sei A ein Alphabet mit den Buchstaben 0, . . . , 2p − 1 • sei k eine Zeichenkette über A, k 0 eine Permutation von k • dann ist: k ≡ k 0 mod 2p . • r i : analog bei r -adischen Zahlen • allgemeiner sollte man vermeiden: m = r i ± j für kleines j : Übungsaufgabe: warum? • z.B. m = 27 − 1 = 127: • pt = (112 · 128 + 116) mod 127 = 14452 mod 127 = 101 • tp = (116 · 128 + 112) mod 127 = 14960 mod 127 = 101 • dasselbe passiert, wenn in einer längeren Zeichenkette zwei Buchstaben vertauscht werden 23 / 86 24 / 86 Hashverfahren Hashverfahren Gliederung • Gute Wahl: Primzahl m, die kein r i ± j , j klein, teilt • Adressberechnung durch Hashing • praktisch bewährt • Hashfunktionen • Divisionsmethode • Multiplikationsmethode Beispiel: • die Hashtabelle soll ca. 1000 Einträge aufnehmen, • universelles Hashing • die Schlüssel sind Zeichenketten, interpretiert als 2-adische Zahlen • Kollisionsbehandlung • gute Wahl: m = 701, da 29 = 512 und 210 = 1024. • Anwendung von Hashfunktionen 25 / 86 26 / 86 Hashverfahren Hashverfahren zu (2): Irrationale Zahlen sind eine gute Wahl. Multiplikationsmethode • zur Erinnerung: Sei 0 < A < 1. Setze: √ 1+ 5 Φ= = 1.6180339887 . . . 2 h(k ) = bm(k · A mod 1)c = bm (k · A − bk · Ac)c | {z } ∈[0,1) √ 1− 5 Φ̂ = = −0.6180339887 . . . 2 • beste Wahl für A nach Knuth [1973]: Eigenschaften A = Φ−1 = (1) die Wahl von m ist unkritisch 2 √ = −Φ̂ = 1+ 5 √ 5−1 = 0.6180339887 . . . 2 • Φ−1 ist bekannt als der goldene Schnitt • (manchmal wird auch Φ als der goldene Schnitt bezeichnet). (2) wir erhalten eine gleichmäßige Verteilung für U = {1, 2, . . . , n} bei einer guten Wahl von A 27 / 86 28 / 86 Hashverfahren Hashverfahren goldener Schnitt: goldener Schnitt: • 2 Strecken der Längen a und b mit a ≥ b stehen im Verhältnis des • goldenes Rechteck: Rechteck mit Seitenlängen im goldenen Schnitt goldenen Schnitts, wenn gilt: a a+b = . b a • Φ= a b = a+b a =1+ b a =1+ a−b b Schnitts teilt: Ψ = 360° − 360° = 137, 5 . . . ° Φ • Fibonacci-Zahlen: 1 Φ • daraus ergibt sich das Verhältnis Φ = • auch gilt: • goldener Winkel Ψ: Winkel, der 360° im Verhältnis des goldenen √ 1+ 5 2 • = 1.6180339887 Fn+1 Fn Fn+1 Fn = Fn +Fn−1 Fn =1+ Fn−1 Fn konvergiert gegen Φ. = Φ (stetige Teilung) 29 / 86 30 / 86 Hashverfahren Hashverfahren zu (2): • irrationale Zahlen sind eine gute Wahl • die Schlüssel 1, 2, . . . werden ziemlich gleichmäßig verteilt: Veranschaulichung: j ` ´k h(k ) = m k Φ−1 − bk Φ−1 c Satz für m = 10 und k = 1, . . . , 10. Sei ξ eine irrationale Zahl. Plaziere in das Intervall [0, 1] die Punkte ξ − bξc, 2ξ − b2ξc, . . . , nξ − bnξc. h(1) 6 (i) Die entstehenden n + 1 Teilintervalle von [0, 1] haben höchstens drei verschiedene Längen. h(2) 2 h(3) 8 h(4) 4 h(5) 0 h(6) 7 h(7) 3 h(8) 9 h(9) 5 h(10) 1 • es treten jeweils höchstens 3 Intervalllängen auf • die jeweils nächste Zahl fällt in ein längstes Intervall (ii) Der nächste Punkt (n + 1)ξ − b(n + 1)ξc fällt in eines der größten Intervalle. 31 / 86 32 / 86 Hashverfahren Hashverfahren zu (1): Eine gute Wahl ist m = 2i . Dann kann h(k ) effizient berechnet werden: w Bits Beispiel: (Dezimalrechnung) k = 123456, m = 10000, A = Φ−1 k bA · 2w c h(k ) = b10000 · (123456 · 0.61803 . . . mod 1)c = b10000 · (76300.0041151 . . . mod 1)c r1 = b10000 · 0.0041151 . . .c r0 = b41.151 . . .c h(k) = 41 i Bits • sei w die Wortlänge • dann ist A = s/2w , wobei 0 < s < 2w • k wird mit der w -bit-Zahl s = A · 2w multipliziert • Ergebnis: 2w -bit-Zahl: r1 · 2w + r0 • Hashwert: die oberen i bits von r0 . 33 / 86 34 / 86 Hashverfahren Hashverfahren Beispiel: i = 5, w = 8, A = 0.101, k = 101111 Beispiel: i = 5, w = 8, A = 0.101, k = 101111 k * A = 101111 * 0.101 ————– 101.111 10111.1 ————– 11101.011 - 11101 ————– 0.011 * 2ˆ5 = 1100 = h(k ) 00101111 * 10100000 —————————————– 10111100000 1011110000000 —————————————– 1110101100000 h(k ) 35 / 86 1 Überlauf ignorieren 36 / 86 Hashverfahren Hashverfahren • empirisch schneidet die Multiplikationsmethode gut ab Gliederung • Adressberechnung durch Hashing • trotzdem kann jede fest gewählte Hashfunktion „hereingelegt” werden: • Hashfunktionen • gib eine Folge von Schlüsseln ki an, die alle auf denselben Wert abgebildet werden, d.h. • Divisionsmethode • Multiplikationsmethode h(ki ) = j für festes j ∈ {0, 1, . . . , m − 1} • universelles Hashing • Kollisionsbehandlung • Abhilfe: zufällige Auswahl der Hashfunktion aus einer endlichen • Anwendung von Hashfunktionen Menge H von möglichen Hashfunktionen. 37 / 86 38 / 86 Hashverfahren Hashverfahren Sei H eine Familie von Hash-Funktionen. Schräges Beispiel • für x ∈ U wähle h(u) in {0, 1, . . . , m − 1} • wähle dabei zufällig, gleichverteilt und unabhängig von anderen y ∈ U • dann ist die Wahrscheinlichkeit einer Kollision m1 : H heißt universell, wenn für je zwei verschiedene Schlüssel x , y ∈ U gilt: |{h ∈ H | h(x ) = h(y )}| 1 ≤ . |H| m • denn alle m 2 Paare anschaulich: • und m davon führen zu einer Kollision • nur der m-te Teil von H führt zu einer Kollision von x und y . • die Wahrscheinlichkeit, dass x und y bei zufälliger Wahl von h ∈ H kollidieren, ist höchstens “ ” h(x ), h(y ) treten gleichwahrscheinlich auf • aber: wir hätten keine Chance, die Daten wiederzufinden 1 . m 39 / 86 40 / 86 Hashverfahren Hashverfahren Wir definieren eine Funktion, die Kollisionen anzeigt: • δ(x , y , h) = • wir wählen h ∈ H zufällig 1 0 falls h(x ) = h(y ) und x 6= y sonst • mittels h bilden wir eine Folge von Schlüsseln nacheinander auf die y ∈Y δ(x , y , h) für Y ⊆ U • wenn x eingefügt wird, sei eine Menge S von Schlüsseln bereits • δ(x , Y , h) = P • δ(x , y , H) = P h∈H Hashadressen 0, 1, . . . , m − 1 ab eingefügt δ(x , y , h) • wir bewerten den Aufwand zum Einfügen von x über die Anzahl der • d.h., H ist universell, wenn für je zwei beliebige x , y ∈ U mit Elemente von S, mit denen x kollidiert (Begründung folgt später) x 6= y gilt: δ(x , y , H) ≤ |H| m • der zu erwartende Aufwand zum Einfügen von x ist dann E [δ(x , S, h)] • wir schätzen E [δ(x , S, h)] ab: 42 / 86 41 / 86 Hashverfahren E [δ(x , S, h)] = Hashverfahren 1 X δ(x , S, h) |H| E [δ(x , S, h)] = h∈H = |S| m 1 XX δ(x , y , h) |H| h∈H y ∈S Damit gilt (im Erwartungswert): 1 XX δ(x , y , h) = |H| • sei H eine universelle Klasse von Hashfunktionen, y ∈S h∈H = • werde h ∈ H zufällig gewählt, 1 X δ(x , y , H) |H| • sei S eine beliebige, noch so „bösartig“ gewählte Folge von Schlüsseln, y ∈S 1 X |H| ≤ |H| m • dann wird S so gleichmäßig wie nur möglich in der Hashtabelle verteilt. (H universell) y ∈S = |S| m Bleibt, eine universelle Familie von Hashfunktionen anzugeben. 43 / 86 44 / 86 Hashverfahren Hashverfahren eine universelle Klasse H Satz ˆ ˜ H = {ha,b : ha,b (x ) = (ax + b) mod p mod m eine universelle Klasse von Hashfunktionen. • sei p eine Primzahl und • U = {0, . . . , p − 1} = Zp das zugrunde liegende Universum • sei Z∗p = {1, . . . , p − 1} Beweis: • sei ˆ ˜ H = {ha,b : ha,b (x ) = (ax + b) mod p mod m mit a ∈ Z∗p , b Wir müssen zeigen: |{h ∈ H | h(x ) = h(y )}| 1 ≤ |H| m ∈ Zp }. mit a ∈ Z∗p , b ∈ Zp } ist für alle x , y ∈ U Äquivalent dazu ist: |{(a, b) | ha,b (x ) = ha,b (y )}| ≤ Satz p(p − 1) . m H ist eine universelle Klasse von Hashfunktionen. 45 / 86 46 / 86 Hashverfahren zu zeigen : |{(a, b) | ha,b (x ) = ha,b (y )}| ≤ Hashverfahren • Zp ist ein Körper (p prim) • daher liefert jede Wahl von (a, b) ∈ Z∗p × Zp • eineindeutig ein Paar u, v mit u 6= v als Lösung von p(p − 1) . m u = ax + b mod p • seien x , y ∈ U , x 6= y v = ay + b mod p • sei u = ax + b mod p und v = ay + b mod p • denn sind x , y , u, v gegebenen, • dann lassen sich a und b bestimmen als: • dann ist u − v = a(x − y ) mod p • damit ist u 6= v , da a 6= 0 mod p und x − y 6= 0 mod p • d.h. keine Kollision auf der mod p-Ebene. a = (u − u)((x − y )−1 mod p) mod p b = (u − ax ) mod p 47 / 86 48 / 86 Hashverfahren Hashverfahren Anzahl der Kollisionen = |{(a, b) | ha,b (x ) = ha,b (y )}| Damit folgt: = |{(u, v ) | 0 ≤ u, v ≤ p − 1, u = v mod m und u 6= v }|. • durchläuft (a, b) den Bereich Z∗p × Zp , • so durchlaufen u, v den Bereich 0 ≤ u, v ≤ p − 1 und u 6= v • somit: Wieviele solcher Paare u, v kann es geben? Anzahl der Kollisionen = |{(a, b) | ha,b (x ) = ha,b (y )}| = |{(u, v ) | 0 ≤ u, v ≤ p − 1, u = v mod m und u 6= v }|. • sei u ∈ {0, . . . , p − 1} fest • sei v ∈ {0, . . . , p − 1} mit u 6= v und u = v mod m • dann ist v von der Form u ± i · m mit i = 6 0 p−1 • davon kann es aber höchstens m viele geben Damit folgt: |{(a, b) | ha,b (x ) = ha,b (y )}| ≤ p(p − 1) . m 50 / 86 49 / 86 Hashverfahren Hashverfahren Gliederung Daraus ergibt sich die Vorgehensweise für das Hashing: • Adressberechnung durch Hashing • sei die Größe von |K | in etwa bekannt • Hashfunktionen • wähle eine Primzahl p so dass p ≥ |K | • Kollisionsbehandlung • wähle zufällig und gleichverteilt a ∈ {1, . . . , p − 1} • wähle zufällig und gleichverteilt b ∈ {0, . . . , p − 1} • Verkettung • offenes Hashing • dann ist ha,b eine Hashfunktion mit geringer Kollisionszahl • Double Hashing • Anwendung von Hashfunktionen 51 / 86 52 / 86 Hashverfahren Hashverfahren U K Wie behandeln wir Kollisionen? k1 k1 k4 k5 k2 k4 k5 Verkettung der Überläufer k7 k7 k2 Jedes Element der Hashtabelle ist ein Zeiger auf eine Überlaufkette, die als verkettete lineare Liste implementiert ist. k3 k3 k8 k8 k6 k61 54 / 86 53 / 86 Hashverfahren Hashverfahren Durchführung der Operationen Analyse für suche • suche(T,k): worst-case: • beginne bei T (h(k )) und folge der Überlaufkette • T (n) = Θ(n) • alle Elemente werden auf denselben Platz abgebildet • bis k gefunden oder Ende der Kette erreicht ist • füge_ein(T,x): average case • suche nach k = key (x ) • ist die Suche erfolgreich: füge x nicht ein 1 • Annahmen: • ansonsten: hänge x an das Ende seiner Überlaufkette (1) ein gegebenes Element wird auf jeden der m Plätze mit gleicher Wahrscheinlichkeit m1 abgebildet, unabhängig von den anderen Elementen, • entferne(T,x): • suche nach k = key (x ) • ist die Suche erfolgreich: lösche x (2) jeder der n gespeicherten Schlüssel ist mit gleicher Wahrscheinlichkeit der gesuchte, • ansonsten: stop (3) die Berechnung von h(k ) benötigt konstante Zeit. 55 / 86 56 / 86 Hashverfahren Hashverfahren Analyse für suche • sei der Auslastungsgrad gegeben durch α= n m T (n) = « n „ 1X i −1 1+ n m i =1 =1+ • α ist die durchschnittliche Anzahl von Elementen in einer Überlaufkette n 1 X (i − 1) nm i =1 =1+ • average case: • erfolglose Suche: offensichtlich T (n) = α. =1+ • erfolgreiche Suche: =1+ • die Suche benötigt einen Schritt mehr als das vorherige Einfügen des gesuchten Elements. • die durchschnittliche Listenlänge beim Einfügen des i -ten Elements ist i −1 . m =1+ 1 n(n − 1) · nm 2 n−1 2m n 1 − 2m 2m α 1 − 2 2m 57 / 86 58 / 86 Hashverfahren Hashverfahren Analyse für suche T (n) = 1 + Gliederung 1 α − 2 2m • Adressberechnung durch Hashing Bemerkung: • Hashfunktionen • sei die Anzahl der Plätze proportional zur Anzahl der Elemente (d.h. n= • Kollisionsbehandlung O(m) bzw. α = O(1)), • Verkettung • offenes Hashing • dann benötigt suche im Durchschnitt konstante Zeit. Analyse für füge_ein: • Double Hashing wie für suche • Anwendung von Hashfunktionen Analyse für entferne: wie für suche 59 / 86 60 / 86 Hashverfahren Hashverfahren Sondierungsreihenfolge Idee: • Erweiterung der Hashfunktion auf zwei Argumente: • anstatt neue Elemente durch Verkettung einzufügen • benutze freie Tabellenplätze zum Speichern h : U × {0, 1, . . . , m − 1} → {0, 1, . . . , m − 1}, • so, dass die Reihenfolge Methode: hh(k , 0), h(k , 1), . . . , h(k , m − 1)i • alle Elemente werden in der Hashtabelle gespeichert • wenn ein Platz belegt ist, so werden in einer bestimmten Reihenfolge eine Permutation ist von h0, 1, . . . , m − 1i. weitere Plätze ausprobiert Annahme im weiteren: Die Hashtabelle enthält immer wenigstens einen unbelegten Platz. 61 / 86 62 / 86 Hashverfahren Hashverfahren ideale Sondierungsreihenfolge: „uniformes Sondieren” • Eigenschaften: • jeder Schlüssel erhält mit gleicher Wahrscheinlichkeit jede der m! • stößt man bei der Suche auf einen unbelegten Platz, so wird die Suche als erfolglos abgebrochen Permutationen von {0, 1, . . . , m − 1} als Sondierungsreihenfolge zugeordnet • wird ein Element entfernt, so könnte ein später eingefügtes Element nicht wiedergefunden werden • daher wird nicht wirklich entfernt, sondern nur als „entfernt” markiert • beim Einfügen wird dieser Platz als frei angesehen • uniformes Sondieren verallgemeinert uniformes Hashing: • es liefert nicht nur eine Adresse, sondern eine Sondierungsreihenfolge • beim Suchen wird er als belegt betrachtet • in der Praxis Annäherung durch: • schwierig zu implementieren • lineares Sondieren • quadratisches Sondieren • Nachteil: Die Suchzeit ist nicht mehr proportional zu (1 + α), • (da der wahre Auslastungsgrad α nicht der aktuelle ist) • deshalb: Wenn lösche benötigt wird, besser Verkettung der • double hashing Überläufer. • uniformes Sondieren dient als Vergleichsstandard 63 / 86 64 / 86 Hashverfahren Hashverfahren Lineares Sondieren • gegeben eine normale Hashfunktion h 0 : U → {0, 1, . . . , m − 1} Analyse zum uniformen Sondieren • setze Die durchschnittliche Anzahl von Sondierungen (ohne Beweis): • erfolglose Suche ≈ • Einfügen ≈ h(k , i ) = (h 0 (k ) + i ) mod m für i = 0, 1, . . . , m − 1. 1 1−α • zu Schlüssel k werden nacheinander die Adressen 1 1−α h 0 (k ), h 0 (k ) + 1, h 0 (k ) + 2, h 0 (k ) + 3, . . . • erfolgreiche Suche ≈ 1 α ln 1 1−α (jeweils modulo m) getestet Nachteil: • die erste Position legt die gesamte Sequenz fest • es gibt nur m verschiedene Sondierungsfolgen • anstatt der m! bei uniformer Sondierung 66 / 86 65 / 86 Hashverfahren Hashverfahren Beispiel: m = 8, h 0 (k ) = k mod m Lineares Sondieren neigt zu „primärer Häufung”: Schlüssel k h 0 (k ) 10 2 19 3 31 7 22 6 14 6 16 0 • lange belegte Teilstücke tendieren dazu, schneller zu wachsen als kurze • Begründung: 0 1 2 3 4 5 14 16 10 19 6 7 • die Hashfunktion fülle jede Position mit gleicher Wahrscheinlichkeit 22 31 (uniform) • betrachte eine freie Position x der Tabelle Durchschnittliche Zeit für erfolgreiche Suche k ist 9 6 10 1 + 19 1 + 31 1 + 22 1 • die davor liegenden i Tabellenelemente seien bereits belegt + 14 3 + 16 2 = • dann wird x beim nächsten Einfügen mit Wahrscheinlichkeit 9 i +1 m = 1, 5. ebenfalls belegt • damit wächst die Wahrscheinlichkeit mit der Länge der belegten Kette 67 / 86 68 / 86 Hashverfahren Hashverfahren Quadratisches Sondieren h(k , i ) = (h 0 (k ) ± c1 i ± c2 i 2 ) mod m Analyseergebnisse (ohne Beweis): • sei 0 < α = n m <1 Beispiel: • Anzahl Sondierungen im Durchschnitt: m = 8, h 0 (k ) = k mod m, c1 = c2 = 12 , gleiche Schlüssel wie vorhin “ ” 1 • bei erfolglose Suche ≈ 1 + (1−α) 2 “ ” 1 • bei erfolgreiche Suche ≈ 12 1 + 1−α 1 2 k h 0 (k ) 0 10 2 1 19 3 2 3 10 19 31 7 22 6 4 5 14 6 6 16 0 7 22 31 69 / 86 70 / 86 Hashverfahren k h 0 (k ) 10 2 19 3 31 7 22 6 14 6 0 16 0 1 2 3 4 5 10 19 Hashverfahren 6 7 Bemerkungen: 22 31 • wie beim linearen Sondieren gibt es nur m verschiedene −→ 14 6 → 6+ 1 (1 2 2 + 1 ) mod 8 = 7 → 6 + 1 (2 2 Sondierungsfolgen („sekundäre Häufung“) 2 + 2 ) mod 8 = 1 • aber: geeignete Wahl von m, c1 , c2 führt in der Praxis zu besserer 0 1 2 3 4 5 16 14 10 19 6 Verteilung 7 • die Wahl muss sicherstellen, dass ein freier Platz gefunden wird 22 31 • kann u.a. gezeigt werden für (jeweils mod m) h 0 (k , i ) = h(k ) − (−1)i · Die durchschnittliche Zeit für erfolgreiche Suche k ist 8 6 10 1 + 19 1 + 31 1 + 22 1 + 14 3 + 16 1 = “l i m”2 2 • ergibt die die Folge h(k ), h(k ) + 1, h(k ) − 2, h(k ) + 4, h(k ) − 4, . . . • für Primzahlen der Form m = 4j + 3 durchläuft die Sondierung alle 8 Adressen = 1, 33. 71 / 86 72 / 86 Hashverfahren Hashverfahren durchschnittliche Anzahl von Sondierungen: • Analyseergebnisse (ohne Beweis): “ ” 1 − α + ln 1−α ” “ 1 • erfolgreiche Suche ≈ 1 + ln 1−α − α2 • erfolglose Suche ≈ α 1 1−α 0.5 0.9 0.95 1.0 Sondierungen im Durchschnitt. Verkettung erfolgreich erfolglos 1.250 0.50 1.450 0.90 1.475 0.95 1.500 1.00 offene Hashverfahren lineares S. quadr. S. uniform er el er el er el 1.5 2.5 1.44 2.19 1.39 2 5.5 50.5 2.85 11.40 2.56 10 10.5 200.5 3.52 22.05 3.15 20 ——– ——– ——- Quadratisches Sondieren ist schon sehr gut. 74 / 86 73 / 86 Hashverfahren Hashverfahren Double Hashing Gliederung • seien h1 , h2 : U → {0, 1, . . . , m − 1} zwei normale Hashfunktionen • setze • Adressberechnung durch Hashing • Hashfunktionen h(k , i ) = (h1 (k ) + i · h2 (k )) mod m. • Kollisionsbehandlung • Verkettung • offenes Hashing Die Sondierungsfolge hängt in zweifacher Weise vom Schlüssel ab: • erste Sondierungsposition (wie gehabt) • Schrittweite (neu) • double Hashing • Anwendung von Hashfunktionen Es ergeben sich Θ(m 2 ) verschiedene Sondierungsfolgen. 75 / 86 76 / 86 Hashverfahren Hashverfahren Beispiel für (2): m = 7, h1 (k ) = k mod 7, h2 (k ) = 1 + (k mod 5) Bedingung an h2 : k h1 (k ) h2 (k ) • für alle Schlüssel k muss h2 (k ) relativ prim zu m sein • d.h. ggT(h2 (k ), m) = 1,, ansonsten wird die Tabelle nicht vollständig 10 3 1 19 5 5 31 3 2 22 1 3 14 0 5 16 2 2 durchsucht • falls ggT(h2 (k ), m) = d > 1, so wird nur 1 -tel d 0 durchsucht. 1 2 3 10 Zwei Vorschläge: 4 5 6 0 19 1 2 3 4 5 6 0 1 2 3 4 5 6 31 22 10 19 31 22 16 10 19 14 (3) (1) (2) (1) (2) (4) (3) (5) Durchschnittliche Zeit für erfolgreiche Suche (1) m = 2p (schlecht für Divisionsmethode), h2 (k ) immer ungerade (2) m Primzahl, 0 < h2 (k ) < m, z.B. h1 (k ) = k mod m k h2 (k ) = 1 + (k mod m 0 ) mit m 0 = m − 1 oder m 0 = m − 2. ist 12 6 10 1 + 19 1 + 31 3 + 22 1 + 14 5 + 16 1 = 12 = 2.00. (Untypisch schlechtes Beispiel für double hashing.) Double Hashing ist eine gute Approximation an uniformes Hashing 77 / 86 78 / 86 Hashverfahren Hashverfahren Verbesserung von Brent • sei k ein Schlüssel, der eingefügt werden soll • sei j der sondierte Platz • falls j bereits mit Schlüssel k 0 belegt ist, setze Beobachtung: • es werde eine Folge von Schlüsseln in die Hashtabelle eingefügt, • dann hängt die Anzahl der erforderlichen Schritte von der Reihenfolge j1 = j + h2 (k ) mod m der Schlüssel ab j2 = j + h2 (k 0 ) mod m • ist j1 frei oder j2 belegt, so fahre fort wie im Original mit j = j1 • sonst (j1 belegt und j2 frei) setze • ; versuche, die Reihenfolge nachträglich zu ändern T (j2 ) = T (j ) T (j ) = k 79 / 86 80 / 86 Hashverfahren Hashverfahren Beispiel: m = 7, h1 (k ) = k mod 7, h2 (k ) = 1 + (k mod 5) k h1 (k ) h2 (k ) 10 3 1 0 19 5 5 31 3 2 1 2 22 1 3 3 4 10 0 1 2 3 4 5 6 14 0 5 5 16 2 2 Analyseergebnis (ohne Beweis): 6 • erfolglose Suche ≈ 19 1 1−α (wie uniform) • erfolgreiche Suche < 2.5 (unabhängig von α) 31 14 22 16 10 10 19 0 31 1 2 3 4 5 6 Sondierungen im Durchschnitt. 31 10 19 Rest immer frei Durchschnittliche Zeit für erfolgreiche Suche k ist 7 6 10 2 + 19 1 + 31 1 + 22 1 + 14 1 + 16 1 = 7 = 1.17. 81 / 86 82 / 86 Hashverfahren Hashverfahren • oft sind Hashfunktionen Einwegfunktionen: Gliederung • Adressberechnung durch Hashing • gegeben f und x , so ist f (x ) schnell zu berechnen • Hashfunktionen • gegeben f und f (x ), so ist x kaum oder nur sehr aufwendig zu berechnen • Kollisionsbehandlung • diese Eigenschaft wird in der sicheren Übermittlung von Daten • Anwendung von Hashfunktionen ausgenutzt 83 / 86 84 / 86 Hashverfahren Hashverfahren • Übermittlung von offenen Dokumenten, so dass • Empfänger • Verfälschungen entdeckt werden können, • wendet Hashfunktion auf Dokument x an, erzeugt so den Wert f (x ) • entschlüsselt p(f (x )) mit öffentlichem Schlüssel o • der Absender sich identifizieren kann • Verwendung von privaten (geheimen) und öffentlichen Schlüsseln • erhält somit f 0 (x ) = o(p(f (x ))) • Absender • ist f (x ) 6= f 0 (x ), so ist • wendet Hashfunktion auf Dokument x an, • Unterschrift falsch, oder • erzeugt so den Wert f (x ) • verschlüsselt f (x ) mit seinem privaten Schüssel p • Dokument verfälscht • sendet x und p(f (x )) als „Unterschrift” 85 / 86 86 / 86