Algorithmen I Prof. Peter Sanders 16.05.2017 Institut für Theoretische Informatik Web: https://crypto.iti.kit.edu/index.php?id=799 (Folien von Peter Sanders) KIT Institut für Theoretische Informatik 1 Hashing (Streuspeicherung) to hash ≈ völlig durcheinander bringen. Paradoxerweise hilft das, Dinge wiederzunden KIT Institut für Theoretische Informatik 2 Hashtabellen speichere Menge M ⊆ Element. e ∈ M. key(e) ist eindeutig für unterstütze Wörterbuch-Operationen in Zeit O(1). M.insert(e : Element): M := M ∪ {e} M.remove(k : Key): M := M \ {e}, M.nd(k : Key): return e ∈M key(e) with key(e) =k = k; ⊥ falls nichts gefunden Anderes Interface: map/partielle Funktion Key→Element M[k] = M.nd(k) KIT Institut für Theoretische Informatik 3 Exkurs: Konventionen für Elemente Viele Datenstrukturen repräsentieren Mengen (engl. auch collection classes). Die Mengenelemente e haben Schlüssel key(e). Elementvergleich hier gleichbedeutend mit Schlüsselvergleich. e = e0 gdw. key(e) = key(e 0 ) (analog für e < e0 und e > e 0 ). KIT Institut für Theoretische Informatik 4 Hashing: Anwendungen I Auslieferungsregale der UB Karlsruhe I Entfernen exakter Duplikate I Schach (oder andere kombinatorische Suchprogramme): welche Stellungen wurden bereits durchsucht? I Symboltabelle bei Compilern I Assoziative Felder bei Script-Sprachen wie perl oder python I Datenbank-Gleichheits-Join (wenn eine Tabelle in den Speicher passt) I Routenplaner: Teilmengen von Knoten, z. B. Suchraum I ... KIT Institut für Theoretische Informatik 5 Überblick I Grundidee I Hashing mit verketteten Listen I Analyse I Hashing mit Arrays KIT Institut für Theoretische Informatik 6 Erste Ideen zu Implementierungen M ⊆ Element. eindeutig für e ∈ M . speichere Menge key(e) ist unterstütze Wörterbuch-Operationen in Zeit O(1). Implementierung mit Listen: Wörterbuchoperationen zu aufwändig Implementierung mit Feldern: Elemente wo ablegen? key(e) legt fest, wo e abgelegt wird KIT Institut für Theoretische Informatik 7 Ein (über)optimistischer Ansatz Eine perfekte Hash-Funktion bildet Elemente von M h injektiv h auf eindeutige Einträge t[0..m − 1] t[h(key(e))] = e der Tabelle ab, d. h., Datenstrukturinvariante: ∀e ∈ M : t[h(key(e))] = e ∧ ∀0 ≤ i < m : t[i] ∈ M ∪ {⊥} M t KIT Institut für Theoretische Informatik 8 Kollisionen Perfekte Hash-Funktionen sind schwer zu nden h M t Beispiel: Geburtstagsparadox KIT Institut für Theoretische Informatik 9 Kollisionsauösung Eine Möglichkeit: Tabelleneinträge: Elemente Folgen von Elementen h k M < > < > <> <> <> < >t[h(k)] <> <> < > <> <> t <> KIT Institut für Theoretische Informatik 10 Hashing mit verketteten Listen Implementiere die Folgen in den Tabelleneinträgen durch einfach verkettete Listen Datenstrukturinvariante: h ∀e ∈ M : e ∈ t[h(key(e))] ∧ ∀0 ≤ i < m : t[i] ⊆ M k M < > < > <> <> <> < >t[h(k)] <> <> < > <> <> t <> KIT Institut für Theoretische Informatik 11 Hashing mit verketteten Listen Implementiere die Folgen in den Tabelleneinträgen durch einfach verkettete Listen insert(e): Füge e am Anfang von remove(k): Durchlaufe Element e t[h(key(e))] ein. t[h(k)]. mit key(e) =k gefunden? löschen und zurückliefern. nd(k) : Durchlaufe Element e t[h(k)]. mit key(e) =k gefunden? h zurückliefern. Sonst: ⊥ zurückgeben. k M < > < > <> <> <> < >t[h(k)] <> <> < > <> <> t <> KIT Institut für Theoretische Informatik 12 Beispiel 00000000001111111111222222 01234567890123456789012345 abcdefghijklmnopqrstuvwxyz t t t <axe,dice,cube> <axe,dice,cube> <axe,dice,cube> <hash> <slash,hash> <slash,hash> <hack> <fell> <hack> <fell> <hack> <fell> <chop, clip, lop> <chop, clip, lop> <chop, lop> insert remove "slash" "clip" KIT Institut für Theoretische Informatik 13 Analyse h insert(e): konstante Zeit remove(k): nd(k) O(Listenlänge) : O(Listenlänge) Aber wie lang werden die Listen? Schlechtester Fall: O(|M|) Besser wenn wir genug Chaos anrichten? k M < > < > <> <> <> < >t[h(k)] <> <> < > <> <> t <> KIT Institut für Theoretische Informatik 14 Etwas Wahrscheinlichkeitstheorie für den Hausgebrauch 1 Hash-Beispiel Elementarereignisse Ω {0..m − 1}Key E42 = {h ∈ Ω : h(4) = h(2)} Hash-Funktionen Ω von x ∈ Ω. ∑x px = 1 Ereignisse: Teilmengen von px =Wahrscheinlichkeit 1 Gleichverteilung: px = |Ω| P [E ] = ∑x∈E px Zufallsvariable (ZV) X : Ω → R ! ph = m−|Key| P [E42 ] = m1 X = | {e ∈ M : h(e) = 0} | 0-1-Zufallsvariable (Indikator-ZV) I : Ω → {0, 1} Erwartungswert E[X ] = ∑y ∈Ω py X (y ) E[X ] = |M| m Linearität des Erwartungswerts: E[X + Y ] = E[X ] + E[Y ] KIT Institut für Theoretische Informatik 15 Beispiel: Variante des Geburtstagsparadoxon Wieviele Gäste muss eine Geburtstagsparty im Mittel haben, damit mindestens zwei Gäste den gleichen Geburtstag haben? Gäste (Keys) 1..n. h ∈ Ω = {0..364}{1..n} . Deniere Indikator-ZV Iij = 1 gdw h(i) = h(j). n n Anzahl Paare mit gleichem Geburtstag: X = ∑i=1 ∑j=i+1 Iij . Elementarereignisse: n n E[X ] =E[ ∑ n ∑ Iij ] = i=1 j=i+1 n n =∑ ∑ i=1 j=i+1 ! =1 ⇔ n = n ∑ ∑ P [Iij = 1] = 1 2 E[Iij ] i=1 j=i+1 r + 1 22 n(n − 1) 2 · 1 365 + 730≈ 27.52 KIT Institut für Theoretische Informatik 16 Mehr zum Geburtstagsparadoxon Standardfomulierung: Ab wann lohnt es sich zu wetten, dass es zwei Gäste mit gleichem Geburtstag gibt? Etwas komplizierter. Antwort: n ≥ 23 m = Hashtabelle der Gröÿe m: h : 1..n → 0..m − 1 ist nur dann mit 2 Wahrscheinlichkeit perfekt wenn m = Ω(n ). Verallgemeinerung: Jahreslänge eine zufällige Hashfunktion vernünftiger Riesige Platzverschwendung. KIT Institut für Theoretische Informatik 17 Analyse für zufällige Hash-Funktionen Theorem 1 h ∀k : die erwartete Anzahl kollidierender Elemente ist O(1) falls |M| ∈ O(m). M Beweis. < > < > <> <> <> < >t[h(k)] <> <> < > <> <> t <> k deniere Kollisionslänge X : h(e) = h(k)} | mit M 0 = {e ∈ M : key(e) 6= k}. 0 0-1 ZV Xe = 1 für h(e) = h(k), e ∈ M und Xe = 0 Für festen Schlüssel X := | {e ∈ M0 Betrachte die E[X ] = E[ ∑ e∈M 0 Xe ] = ∑ E[Xe ] = e∈M 0 ∑ e∈M 0 P [Xe = 1] = sonst. |M 0 | m ∈ O(1) Das gilt unabhängig von der Eingabe M. KIT Institut für Theoretische Informatik 18 Zufällige Hash-Funktionen? Naive Implementierung: ein Tabelleneintrag pro Schlüssel. meist zu teuer Weniger naive Lösungen: kompliziert, immer noch viel Platz. meist unsinnig unrealistisch KIT Institut für Theoretische Informatik 19 Universelles Hashing Idee: nutze nur bestimmte einfache Hash-Funktionen Denition 2 H ⊆ {0..m − 1}Key ist universell falls für alle x , y in Key mit x 6= y und zufälligem P [h(x) = h(y )] = 1 m h∈H , . Theorem 3 Theorem 1 gilt auch für universelle Familien von Hash-Funktionen. Beweis. Für Ω=H haben wir immer noch P [Xe = 1] = 1 m. Der Rest geht wie vorher. H Ω KIT Institut für Theoretische Informatik 20 Eine einfache universelle Familie m sei eine Primzahl, Key ⊆ {0, . . . , m − 1}k Theorem 4 Für a = (a1 , . . . , ak ) ∈ {0, . .n. , m − 1}k deniere o ha (x) = a·x mod m, H · = ha : a ∈ {0..m − 1}k . H · ist eine universelle Familie von Hash-Funktionen x1 * a1 + x2 * a2 + x3 * a3 mod m = ha(x) KIT Institut für Theoretische Informatik 21 Beispiel für H · a = (a1 , . . . , ak ) ∈ {0, . .n. , m − 1}k deniere o ha (x) = a·x mod m, H · = ha : a ∈ {0..m − 1}k . Für k = 3, m = 11 wähle a = (8, 1, 5). ha ((1, 1, 2)) = (8, 1, 5) · (1, 1, 2) = 8 · 1 + 1 · 1 + 5 · 2 = 19 ≡ 8 mod 11 KIT Institut für Theoretische Informatik 22 Beweis. x = (x1 , . . . , xk ), y = (y1 , . . . , yk ) a mit ha (x) = ha (y). Betrachte zähle Für jede Wahl der ∑ ai , i 6= j , ∃ ai x i ≡ 1≤i≤k ∑ 1≤i≤k ⇔ aj (xj − yj ) ≡ ⇔ aj ≡ genau ein mit xj 6= yj mit ha (x) = ha (y): aj ai yi ( mod m) ai (yi − xi )( mod m) i6=j,1≤i≤k (xj − yj )−1 ai (yi − xi )( i6=j,1≤i≤k ∑ ∑ mk−1 Möglichkeiten die ai (mit i 6= j ) mk ist die Gesamtzahl der a, d. h., mod m) auszuwählen. P [ha (x) = ha (y)] = mk−1 1 = . k m m KIT Institut für Theoretische Informatik 23 Bit-basierte Universelle Familien Sei m = 2w , Key = {0, 1}k Bit-Matrix Multiplikation: H ⊕ = hM : M ∈ {0, 1}w ×k wobei hM (x) = Mx (Arithmetik mod 2, d. h., xor, and) n Tabellenzugri:H ⊕[] = wobei ⊕[] o n o a ⊕[] h(t1 ,...,tb ) : ti ∈ {0..m − 1}{0..2 −1} h(t1 ,...,tb ) ((x0 , x1 , . . . , xb )) = x0 ⊕ Lb i=1 ti [xi ] k x x2 x1 a a x0 w KIT Institut für Theoretische Informatik 24 Hashing mit Linearer Suche (Linear Probing) Zurück zur Ursprungsidee. Elemente werden direkt in der Tabelle gespeichert. Kollisionen werden durch Finden anderer Stellen aufgelöst. linear probing: Suche nächsten freien Platz. h Am Ende fange von vorn an. I einfach I platz-ezient I cache-ezient M t KIT Institut für Theoretische Informatik 25 Der einfache Teil Class 0 : N; h : Key → 0..m − 1) t=[⊥, . . . , ⊥] : Array [0..m + m0 − 1] of Element invariant ∀i : t[i] 6= ⊥ ⇒ ∀j ∈ {h(t[i])..i − 1} : t[j] 6= ⊥ BoundedLinearProbing(m, m Procedure insert(e : Element) for (i := h(e); t[i] 6= ⊥; i++ assert i < m + m0 − 1 h ) ; m t[i] := e Function nd(k : Key) : Element for (i := h(k); t[i] 6= ⊥; i++ ) if t[i] = k then return t[i] return ⊥ M t m’ KIT Institut für Theoretische Informatik 26 Remove Beispiel: t = [. . . , x , y , z, . . .], h(z) remove(x) invariant ∀i : t[i] 6= ⊥ ⇒ ∀j ∈ {h(t[i])..i − 1} : t[j] 6= ⊥ Procedure remove(k : Key) for (i := h(k); k 6= t[i]; i++ ) // search k if t[i] = ⊥ then return // nothing to do // we plan for a hole at i. for (j := i + 1; t[j] 6= ⊥; j++ ) // Establish invariant for t[j]. if h(t[j]) ≤ i then t[i] := t[j] // Overwrite removed element i := j // move planned hole t[i] := ⊥ // erase freed entry KIT Institut für Theoretische Informatik 27 an tt 0 insert : axe, chop, clip, cube, dice, fell, hack, hash, lop, slash bo cp dq er fs gt hu iv jw kx 1 5 7 9 10 2 3 4 6 8 axe chop axe chop clip axe chop clip axe cube chop clip axe cube dice ly 11 chop chop chop chop clip clip clip clip axe axe axe axe cube cube cube cube dice dice dice hash dice hash lop hack fell fell fell fell chop clip axe cube dice hash lop slash hack fell hash lop slash hack hash lop slash hack hash slash slash hack hash slash hack fell fell fell fell remove chop chop chop chop clip lop lop lop axe axe axe axe cube cube cube cube hack mz 12 clip dice dice dice dice KIT Institut für Theoretische Informatik 28