Hashing I Datenstrukturen und Algorithmen Vorlesung 12: Hashing I Joost-Pieter Katoen Lehrstuhl für Informatik 2 Software Modeling and Verification Group http://moves.rwth-aachen.de/teaching/ss-15/dsal/ 20. Mai 2015 Joost-Pieter Katoen Datenstrukturen und Algorithmen 1/37 Hashing I Übersicht 1 Direkte Adressierung Counting Sort 2 Grundlagen des Hashings 3 Verkettung 4 Hashfunktionen Joost-Pieter Katoen Datenstrukturen und Algorithmen 2/37 Hashing I Einführung (I) Dictionary (Wörterbuch) Das Dictionary (auch: Map, assoziatives Array) speichert Informationen, die jederzeit anhand ihres Schlüssels abgerufen werden können. Joost-Pieter Katoen Datenstrukturen und Algorithmen 3/37 Hashing I Einführung (I) Dictionary (Wörterbuch) Das Dictionary (auch: Map, assoziatives Array) speichert Informationen, die jederzeit anhand ihres Schlüssels abgerufen werden können. Weiterhin: I Die Daten sind dynamisch gespeichert. Joost-Pieter Katoen Datenstrukturen und Algorithmen 3/37 Hashing I Einführung (I) Dictionary (Wörterbuch) Das Dictionary (auch: Map, assoziatives Array) speichert Informationen, die jederzeit anhand ihres Schlüssels abgerufen werden können. Weiterhin: I Die Daten sind dynamisch gespeichert. I Element dictSearch(Dict d, int k) gibt die in d zum Schlüssel k gespeicherten Informationen zurück. Joost-Pieter Katoen Datenstrukturen und Algorithmen 3/37 Hashing I Einführung (I) Dictionary (Wörterbuch) Das Dictionary (auch: Map, assoziatives Array) speichert Informationen, die jederzeit anhand ihres Schlüssels abgerufen werden können. Weiterhin: I Die Daten sind dynamisch gespeichert. I Element dictSearch(Dict d, int k) gibt die in d zum Schlüssel k gespeicherten Informationen zurück. I void dictInsert(Dict d, Element e) speichert Element e unter seinem Schlüssel e.key in d. Joost-Pieter Katoen Datenstrukturen und Algorithmen 3/37 Hashing I Einführung (I) Dictionary (Wörterbuch) Das Dictionary (auch: Map, assoziatives Array) speichert Informationen, die jederzeit anhand ihres Schlüssels abgerufen werden können. Weiterhin: I Die Daten sind dynamisch gespeichert. I Element dictSearch(Dict d, int k) gibt die in d zum Schlüssel k gespeicherten Informationen zurück. I void dictInsert(Dict d, Element e) speichert Element e unter seinem Schlüssel e.key in d. I void dictDelete(Dict d, Element e) löscht das Element e aus d, wobei e in d enthalten sein muss. Joost-Pieter Katoen Datenstrukturen und Algorithmen 3/37 Hashing I Einführung (I) Dictionary (Wörterbuch) Das Dictionary (auch: Map, assoziatives Array) speichert Informationen, die jederzeit anhand ihres Schlüssels abgerufen werden können. Weiterhin: I Die Daten sind dynamisch gespeichert. I Element dictSearch(Dict d, int k) gibt die in d zum Schlüssel k gespeicherten Informationen zurück. I void dictInsert(Dict d, Element e) speichert Element e unter seinem Schlüssel e.key in d. I void dictDelete(Dict d, Element e) löscht das Element e aus d, wobei e in d enthalten sein muss. Beispiel Symboltabelle eines Compilers, wobei die Schlüssel Strings (etwa Bezeichner) sind. Joost-Pieter Katoen Datenstrukturen und Algorithmen 3/37 Hashing I Einführung (II) Problem Welche Datenstrukturen sind geeignet, um ein Dictionary zu implementieren? Joost-Pieter Katoen Datenstrukturen und Algorithmen 4/37 Hashing I Einführung (II) Problem Welche Datenstrukturen sind geeignet, um ein Dictionary zu implementieren? I Heap: Einfügen und Löschen sind effizient. Aber was ist mit Suche? Joost-Pieter Katoen Datenstrukturen und Algorithmen 4/37 Hashing I Einführung (II) Problem Welche Datenstrukturen sind geeignet, um ein Dictionary zu implementieren? I Heap: Einfügen und Löschen sind effizient. Aber was ist mit Suche? I Sortiertes Array/Liste: Einfügen ist im Worst-Case linear. Joost-Pieter Katoen Datenstrukturen und Algorithmen 4/37 Hashing I Einführung (II) Problem Welche Datenstrukturen sind geeignet, um ein Dictionary zu implementieren? I Heap: Einfügen und Löschen sind effizient. Aber was ist mit Suche? I Sortiertes Array/Liste: Einfügen ist im Worst-Case linear. I Rot-Schwarz-Baum: Alle Operationen sind im Worst-Case logarithmisch. Joost-Pieter Katoen Datenstrukturen und Algorithmen 4/37 Hashing I Einführung (II) Problem Welche Datenstrukturen sind geeignet, um ein Dictionary zu implementieren? I Heap: Einfügen und Löschen sind effizient. Aber was ist mit Suche? I Sortiertes Array/Liste: Einfügen ist im Worst-Case linear. I Rot-Schwarz-Baum: Alle Operationen sind im Worst-Case logarithmisch. Lösung Unter realistischen Annahmen benötigt eine Hash-Tabelle im Durchschnitt O(1) für alle Operationen. Joost-Pieter Katoen Datenstrukturen und Algorithmen 4/37 Hashing I Direkte Adressierung Übersicht 1 Direkte Adressierung Counting Sort 2 Grundlagen des Hashings 3 Verkettung 4 Hashfunktionen Joost-Pieter Katoen Datenstrukturen und Algorithmen 5/37 Hashing I Direkte Adressierung Direkte Adressierung (I) Direkte Adressierung I Alloziere ein Array (die Direkte-Adressierungs-Tabelle), so dass es für jeden möglichen Schlüssel eine(1) Position gibt. Joost-Pieter Katoen Datenstrukturen und Algorithmen 6/37 Hashing I Direkte Adressierung Direkte Adressierung (I) Direkte Adressierung I I Alloziere ein Array (die Direkte-Adressierungs-Tabelle), so dass es für jeden möglichen Schlüssel eine(1) Position gibt. Jedes Array-Element enthält einen Pointer auf die gespeicherte Information. I Der Einfachheit halber vernachlässigen wir in der Vorlesung die zu den Schlüsseln gehörenden Informationen. Joost-Pieter Katoen Datenstrukturen und Algorithmen 6/37 Hashing I Direkte Adressierung Direkte Adressierung (I) Direkte Adressierung I I Alloziere ein Array (die Direkte-Adressierungs-Tabelle), so dass es für jeden möglichen Schlüssel eine(1) Position gibt. Jedes Array-Element enthält einen Pointer auf die gespeicherte Information. I I Der Einfachheit halber vernachlässigen wir in der Vorlesung die zu den Schlüsseln gehörenden Informationen. Mit Schlüsselmenge U = {0, 1, . . . , n − 1} ergibt sich: I Eine Direkte-Adressierungs-Tabelle T[0..n-1], wobei T[k] zu Schlüssel k gehört. Joost-Pieter Katoen Datenstrukturen und Algorithmen 6/37 Hashing I Direkte Adressierung Direkte Adressierung (I) Direkte Adressierung I I Alloziere ein Array (die Direkte-Adressierungs-Tabelle), so dass es für jeden möglichen Schlüssel eine(1) Position gibt. Jedes Array-Element enthält einen Pointer auf die gespeicherte Information. I I Der Einfachheit halber vernachlässigen wir in der Vorlesung die zu den Schlüsseln gehörenden Informationen. Mit Schlüsselmenge U = {0, 1, . . . , n − 1} ergibt sich: I I Eine Direkte-Adressierungs-Tabelle T[0..n-1], wobei T[k] zu Schlüssel k gehört. datSearch(T, int k): return T[k]; Joost-Pieter Katoen Datenstrukturen und Algorithmen 6/37 Hashing I Direkte Adressierung Direkte Adressierung (I) Direkte Adressierung I I Alloziere ein Array (die Direkte-Adressierungs-Tabelle), so dass es für jeden möglichen Schlüssel eine(1) Position gibt. Jedes Array-Element enthält einen Pointer auf die gespeicherte Information. I I Der Einfachheit halber vernachlässigen wir in der Vorlesung die zu den Schlüsseln gehörenden Informationen. Mit Schlüsselmenge U = {0, 1, . . . , n − 1} ergibt sich: I I I Eine Direkte-Adressierungs-Tabelle T[0..n-1], wobei T[k] zu Schlüssel k gehört. datSearch(T, int k): return T[k]; datInsert(T, Element e): T[e.key] = e; Joost-Pieter Katoen Datenstrukturen und Algorithmen 6/37 Hashing I Direkte Adressierung Direkte Adressierung (I) Direkte Adressierung I I Alloziere ein Array (die Direkte-Adressierungs-Tabelle), so dass es für jeden möglichen Schlüssel eine(1) Position gibt. Jedes Array-Element enthält einen Pointer auf die gespeicherte Information. I I Der Einfachheit halber vernachlässigen wir in der Vorlesung die zu den Schlüsseln gehörenden Informationen. Mit Schlüsselmenge U = {0, 1, . . . , n − 1} ergibt sich: I I I I Eine Direkte-Adressierungs-Tabelle T[0..n-1], wobei T[k] zu Schlüssel k gehört. datSearch(T, int k): return T[k]; datInsert(T, Element e): T[e.key] = e; datDelete(T, Element e): T[e.key] = null; Joost-Pieter Katoen Datenstrukturen und Algorithmen 6/37 Hashing I Direkte Adressierung Direkte Adressierung (I) Direkte Adressierung I I Alloziere ein Array (die Direkte-Adressierungs-Tabelle), so dass es für jeden möglichen Schlüssel eine(1) Position gibt. Jedes Array-Element enthält einen Pointer auf die gespeicherte Information. I I Mit Schlüsselmenge U = {0, 1, . . . , n − 1} ergibt sich: I I I I I Der Einfachheit halber vernachlässigen wir in der Vorlesung die zu den Schlüsseln gehörenden Informationen. Eine Direkte-Adressierungs-Tabelle T[0..n-1], wobei T[k] zu Schlüssel k gehört. datSearch(T, int k): return T[k]; datInsert(T, Element e): T[e.key] = e; datDelete(T, Element e): T[e.key] = null; Die Laufzeit jeder Operation ist im Worst-Case Θ(1). Joost-Pieter Katoen Datenstrukturen und Algorithmen 6/37 Hashing I Direkte Adressierung Direkte Adressierung (II) Direkte-Adressierungs-Tabelle T Schlüssel 0 U 1 0 2 4 1 6 3 K 2 4 7 5 6 3 9 5 7 8 8 9 benutzte Schlüssel n = 10 Schlüsselmenge Joost-Pieter Katoen Datenstrukturen und Algorithmen 7/37 Hashing I Direkte Adressierung Duplikate in Linearzeit erkennen Alle Elemente seien ganze Zahlen zwischen 0 und k, wobei k ∈ Θ(n). 1 2 3 4 5 6 7 8 9 10 11 bool checkDuplicates(int E[n], int n, int k) { int histogram[k] = 0; // "Direkte-Adressierungs-Tabelle" for (int i = 0; i < n; i++) { if (histogram[E[i]] > 0) { return true; // Duplikat gefunden } else { histogram[E[i]]++; // Zähle Häufigkeit } } return false; // keine Duplikate } Joost-Pieter Katoen Datenstrukturen und Algorithmen 8/37 Hashing I Direkte Adressierung Counting Sort – Idee 1. Berechne Häufigkeit Joost-Pieter Katoen Datenstrukturen und Algorithmen 9/37 Hashing I Direkte Adressierung Counting Sort – Idee 1. Berechne Häufigkeit 2. Berechne „Position von x “ = „Anzahl der Elemente 6 x “ Joost-Pieter Katoen Datenstrukturen und Algorithmen 9/37 Hashing I Direkte Adressierung Counting Sort – Idee 1. Berechne Häufigkeit 2. Berechne „Position von x “ = „Anzahl der Elemente 6 x “ 3. Erzeuge Ausgabearray anhand dieser neuen Positionen Joost-Pieter Katoen Datenstrukturen und Algorithmen 9/37 Hashing I Direkte Adressierung Counting Sort Alle Elemente seien ganze Zahlen zwischen 0 und k, wobei k ∈ Θ(n). Joost-Pieter Katoen Datenstrukturen und Algorithmen 10/37 Hashing I Direkte Adressierung Counting Sort Alle Elemente seien ganze Zahlen zwischen 0 und k, wobei k ∈ Θ(n). 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 int[n] countSort(int E[n], int n, int k) { int histogram[k] = 0; // "Direkte-Adressierungs-Tabelle" for (int i = 0; i < n; i++) { histogram[E[i]]++; // Zähle Häufigkeit } for (int i = 1; i < k; i++) { // Berechne Position histogram[i] = histogram[i] + histogram[i - 1]; } // Erzeuge Ausgabe int result[n]; for (int i = n - 1; i >= 0; i--) { // stabil: rückwärts histogram[E[i]]--; result[histogram[E[i]]] = E[i]; } return result; } Joost-Pieter Katoen Datenstrukturen und Algorithmen 10/37 Hashing I Direkte Adressierung Counting Sort Alle Elemente seien ganze Zahlen zwischen 0 und k, wobei k ∈ Θ(n). 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 int[n] countSort(int E[n], int n, int k) { int histogram[k] = 0; // "Direkte-Adressierungs-Tabelle" for (int i = 0; i < n; i++) { histogram[E[i]]++; // Zähle Häufigkeit } for (int i = 1; i < k; i++) { // Berechne Position histogram[i] = histogram[i] + histogram[i - 1]; } // Erzeuge Ausgabe int result[n]; for (int i = n - 1; i >= 0; i--) { // stabil: rückwärts histogram[E[i]]--; result[histogram[E[i]]] = E[i]; } return result; } I Worst-Case Zeitkomplexität: Θ(n) Joost-Pieter Katoen Datenstrukturen und Algorithmen 10/37 Hashing I Direkte Adressierung Counting Sort: Beispiel Eingabe 6 8 0 3 5 4 0 4 0 Joost-Pieter Katoen 1 2 3 4 5 6 7 Datenstrukturen und Algorithmen 11/37 Hashing I Direkte Adressierung Counting Sort: Beispiel Eingabe 6 8 0 3 5 4 0 4 0 1 2 3 4 5 6 7 Histogramm 2 0 0 1 2 1 1 0 1 0 1 2 3 4 5 6 7 8 Ausgabe 0 0 3 4 4 5 6 8 0 Joost-Pieter Katoen 1 2 3 4 5 6 7 Datenstrukturen und Algorithmen 11/37 Hashing I Direkte Adressierung Counting Sort: Beispiel Eingabe 6 8 0 3 5 4 0 4 0 1 2 3 4 5 6 7 Histogramm 2 0 0 1 2 1 1 0 1 0 1 2 3 4 5 6 7 8 Positionen 2 2 2 3 5 6 7 7 8 0 1 2 3 4 5 6 7 8 Ausgabe 0 0 3 4 4 5 6 8 0 Joost-Pieter Katoen 1 2 3 4 5 6 7 Datenstrukturen und Algorithmen 11/37 Hashing I Direkte Adressierung Counting Sort: Beispiel Eingabe 6 8 0 3 5 4 0 4 0 1 2 3 4 5 6 7 Positionen 2 2 2 3 5 6 7 7 8 0 1 2 3 4 5 6 7 8 Ausgabe 0 0 3 4 4 5 6 8 0 Joost-Pieter Katoen 1 2 3 4 5 6 7 Datenstrukturen und Algorithmen 11/37 Hashing I Direkte Adressierung Counting Sort: Beispiel Eingabe 6 8 0 3 5 4 0 4 0 1 2 3 4 5 6 7 Positionen 2 2 2 3 4 6 7 7 8 0 1 2 3 4 5 6 7 8 Ausgabe 0 0 3 4 4 5 6 8 0 Joost-Pieter Katoen 1 2 3 4 5 6 7 Datenstrukturen und Algorithmen 11/37 Hashing I Direkte Adressierung Counting Sort: Beispiel Eingabe 6 8 0 3 5 4 0 4 0 1 2 3 4 5 6 7 Positionen 1 2 2 3 4 6 7 7 8 0 1 2 3 4 5 6 7 8 Ausgabe 0 0 3 4 4 5 6 8 0 Joost-Pieter Katoen 1 2 3 4 5 6 7 Datenstrukturen und Algorithmen 11/37 Hashing I Direkte Adressierung Counting Sort: Beispiel Eingabe 6 8 0 3 5 4 0 4 0 1 2 3 4 5 6 7 Positionen 1 2 2 3 3 6 7 7 8 0 1 2 3 4 5 6 7 8 Ausgabe 0 0 3 4 4 5 6 8 0 Joost-Pieter Katoen 1 2 3 4 5 6 7 Datenstrukturen und Algorithmen 11/37 Hashing I Direkte Adressierung Counting Sort: Beispiel Eingabe 6 8 0 3 5 4 0 4 0 1 2 3 4 5 6 7 Positionen 1 2 2 3 3 5 7 7 8 0 1 2 3 4 5 6 7 8 Ausgabe 0 0 3 4 4 5 6 8 0 Joost-Pieter Katoen 1 2 3 4 5 6 7 Datenstrukturen und Algorithmen 11/37 Hashing I Direkte Adressierung Counting Sort: Beispiel Eingabe 6 8 0 3 5 4 0 4 0 1 2 3 4 5 6 7 Positionen 1 2 2 2 3 5 7 7 8 0 1 2 3 4 5 6 7 8 Ausgabe 0 0 3 4 4 5 6 8 0 Joost-Pieter Katoen 1 2 3 4 5 6 7 Datenstrukturen und Algorithmen 11/37 Hashing I Direkte Adressierung Counting Sort: Beispiel Eingabe 6 8 0 3 5 4 0 4 0 1 2 3 4 5 6 7 Positionen 0 2 2 2 3 5 7 7 8 0 1 2 3 4 5 6 7 8 Ausgabe 0 0 3 4 4 5 6 8 0 Joost-Pieter Katoen 1 2 3 4 5 6 7 Datenstrukturen und Algorithmen 11/37 Hashing I Direkte Adressierung Counting Sort: Beispiel Eingabe 6 8 0 3 5 4 0 4 0 1 2 3 4 5 6 7 Positionen 0 2 2 2 3 5 7 7 7 0 1 2 3 4 5 6 7 8 Ausgabe 0 0 3 4 4 5 6 8 0 Joost-Pieter Katoen 1 2 3 4 5 6 7 Datenstrukturen und Algorithmen 11/37 Hashing I Direkte Adressierung Counting Sort: Beispiel Eingabe 6 8 0 3 5 4 0 4 0 1 2 3 4 5 6 7 Positionen 0 2 2 2 3 5 6 7 7 0 1 2 3 4 5 6 7 8 Ausgabe 0 0 3 4 4 5 6 8 0 Joost-Pieter Katoen 1 2 3 4 5 6 7 Datenstrukturen und Algorithmen 11/37 Hashing I Direkte Adressierung Counting Sort: Beispiel Eingabe 6 8 0 3 5 4 0 4 0 1 2 3 4 5 6 7 Positionen 0 2 2 2 3 5 6 7 7 0 1 2 3 4 5 6 7 8 Ausgabe 0 0 3 4 4 5 6 8 0 Joost-Pieter Katoen 1 2 3 4 5 6 7 Datenstrukturen und Algorithmen 11/37 Hashing I Direkte Adressierung Counting Sort (II) Problem Wir sortieren also mit Worst-Case Komplexität Θ(n), obwohl wir als untere Schranke Θ(n · log n) bewiesen hatten? Joost-Pieter Katoen Datenstrukturen und Algorithmen 12/37 Hashing I Direkte Adressierung Counting Sort (II) Problem Wir sortieren also mit Worst-Case Komplexität Θ(n), obwohl wir als untere Schranke Θ(n · log n) bewiesen hatten? Lösung Dieser Algorithmus ist nicht mit Quicksort, Heapsort, usw. vergleichbar Joost-Pieter Katoen Datenstrukturen und Algorithmen 12/37 Hashing I Direkte Adressierung Counting Sort (II) Problem Wir sortieren also mit Worst-Case Komplexität Θ(n), obwohl wir als untere Schranke Θ(n · log n) bewiesen hatten? Lösung Dieser Algorithmus ist nicht mit Quicksort, Heapsort, usw. vergleichbar I denn er basiert nicht auf Vergleich von Elementen, sondern auf Häufigkeiten. I Das funktioniert, indem wir Direkte-Adressierung (Einfügen, Suchen, Löschen in Θ(1)) ausnutzen. Joost-Pieter Katoen Datenstrukturen und Algorithmen 12/37 Hashing I Direkte Adressierung Counting Sort (II) Problem Wir sortieren also mit Worst-Case Komplexität Θ(n), obwohl wir als untere Schranke Θ(n · log n) bewiesen hatten? Lösung Dieser Algorithmus ist nicht mit Quicksort, Heapsort, usw. vergleichbar I denn er basiert nicht auf Vergleich von Elementen, sondern auf Häufigkeiten. I Das funktioniert, indem wir Direkte-Adressierung (Einfügen, Suchen, Löschen in Θ(1)) ausnutzen. Hauptproblem: Übermäßiger Speicherbedarf für das Array. Joost-Pieter Katoen Datenstrukturen und Algorithmen 12/37 Hashing I Direkte Adressierung Counting Sort (II) Problem Wir sortieren also mit Worst-Case Komplexität Θ(n), obwohl wir als untere Schranke Θ(n · log n) bewiesen hatten? Lösung Dieser Algorithmus ist nicht mit Quicksort, Heapsort, usw. vergleichbar I denn er basiert nicht auf Vergleich von Elementen, sondern auf Häufigkeiten. I Das funktioniert, indem wir Direkte-Adressierung (Einfügen, Suchen, Löschen in Θ(1)) ausnutzen. Hauptproblem: Übermäßiger Speicherbedarf für das Array. I Zum Beispiel bei Strings mit 20 Zeichen (5 bit/Zeichen) als Schlüssel benötigt man 25·20 = 2100 Arrayeinträge. Joost-Pieter Katoen Datenstrukturen und Algorithmen 12/37 Hashing I Direkte Adressierung Counting Sort (II) Problem Wir sortieren also mit Worst-Case Komplexität Θ(n), obwohl wir als untere Schranke Θ(n · log n) bewiesen hatten? Lösung Dieser Algorithmus ist nicht mit Quicksort, Heapsort, usw. vergleichbar I denn er basiert nicht auf Vergleich von Elementen, sondern auf Häufigkeiten. I Das funktioniert, indem wir Direkte-Adressierung (Einfügen, Suchen, Löschen in Θ(1)) ausnutzen. Hauptproblem: Übermäßiger Speicherbedarf für das Array. I Zum Beispiel bei Strings mit 20 Zeichen (5 bit/Zeichen) als Schlüssel benötigt man 25·20 = 2100 Arrayeinträge. I Können wir diesen riesigen Speicherbedarf vermeiden und effizient bleiben? Joost-Pieter Katoen Datenstrukturen und Algorithmen 12/37 Hashing I Direkte Adressierung Counting Sort (II) Problem Wir sortieren also mit Worst-Case Komplexität Θ(n), obwohl wir als untere Schranke Θ(n · log n) bewiesen hatten? Lösung Dieser Algorithmus ist nicht mit Quicksort, Heapsort, usw. vergleichbar I denn er basiert nicht auf Vergleich von Elementen, sondern auf Häufigkeiten. I Das funktioniert, indem wir Direkte-Adressierung (Einfügen, Suchen, Löschen in Θ(1)) ausnutzen. Hauptproblem: Übermäßiger Speicherbedarf für das Array. I Zum Beispiel bei Strings mit 20 Zeichen (5 bit/Zeichen) als Schlüssel benötigt man 25·20 = 2100 Arrayeinträge. I Können wir diesen riesigen Speicherbedarf vermeiden und effizient bleiben? Ja! – mit Hashing. Joost-Pieter Katoen Datenstrukturen und Algorithmen 12/37 Hashing I Grundlagen des Hashings Übersicht 1 Direkte Adressierung Counting Sort 2 Grundlagen des Hashings 3 Verkettung 4 Hashfunktionen Joost-Pieter Katoen Datenstrukturen und Algorithmen 13/37 Hashing I Grundlagen des Hashings Hashing (I) Praktisch wird nur ein kleiner Teil der Schlüssel verwendet, d. h. |K | |U|. ⇒ Bei Direkter-Adressierung ist der größte Teil von T verschwendet. Joost-Pieter Katoen Datenstrukturen und Algorithmen 14/37 Hashing I Grundlagen des Hashings Hashing (I) Praktisch wird nur ein kleiner Teil der Schlüssel verwendet, d. h. |K | |U|. ⇒ Bei Direkter-Adressierung ist der größte Teil von T verschwendet. Das Ziel von Hashing ist: I Einen extrem großen Schlüsselraum auf einen vernünftig kleinen Bereich von ganzen Zahlen abzubilden. Joost-Pieter Katoen Datenstrukturen und Algorithmen 14/37 Hashing I Grundlagen des Hashings Hashing (I) Praktisch wird nur ein kleiner Teil der Schlüssel verwendet, d. h. |K | |U|. ⇒ Bei Direkter-Adressierung ist der größte Teil von T verschwendet. Das Ziel von Hashing ist: I Einen extrem großen Schlüsselraum auf einen vernünftig kleinen Bereich von ganzen Zahlen abzubilden. I Dass zwei Schlüssel auf die selbe Zahl abgebildet werden, soll möglichst unwahrscheinlich sein. Joost-Pieter Katoen Datenstrukturen und Algorithmen 14/37 Hashing I Grundlagen des Hashings Hashing (I) Praktisch wird nur ein kleiner Teil der Schlüssel verwendet, d. h. |K | |U|. ⇒ Bei Direkter-Adressierung ist der größte Teil von T verschwendet. Das Ziel von Hashing ist: I Einen extrem großen Schlüsselraum auf einen vernünftig kleinen Bereich von ganzen Zahlen abzubilden. I Dass zwei Schlüssel auf die selbe Zahl abgebildet werden, soll möglichst unwahrscheinlich sein. Hashfunktion, Hashtabelle, Hashkollision Eine Hashfunktion bildet einen Schlüssel auf einen Index der Hashtabelle T ab: h : U −→ { 0, 1, . . . , m−1 } für Tabellengröße m und |U| = n. Joost-Pieter Katoen Datenstrukturen und Algorithmen 14/37 Hashing I Grundlagen des Hashings Hashing (I) Praktisch wird nur ein kleiner Teil der Schlüssel verwendet, d. h. |K | |U|. ⇒ Bei Direkter-Adressierung ist der größte Teil von T verschwendet. Das Ziel von Hashing ist: I Einen extrem großen Schlüsselraum auf einen vernünftig kleinen Bereich von ganzen Zahlen abzubilden. I Dass zwei Schlüssel auf die selbe Zahl abgebildet werden, soll möglichst unwahrscheinlich sein. Hashfunktion, Hashtabelle, Hashkollision Eine Hashfunktion bildet einen Schlüssel auf einen Index der Hashtabelle T ab: h : U −→ { 0, 1, . . . , m−1 } für Tabellengröße m und |U| = n. Wir sagen, dass h(k) der Hashwert des Schlüssels k ist. Joost-Pieter Katoen Datenstrukturen und Algorithmen 14/37 Hashing I Grundlagen des Hashings Hashing (I) Praktisch wird nur ein kleiner Teil der Schlüssel verwendet, d. h. |K | |U|. ⇒ Bei Direkter-Adressierung ist der größte Teil von T verschwendet. Das Ziel von Hashing ist: I Einen extrem großen Schlüsselraum auf einen vernünftig kleinen Bereich von ganzen Zahlen abzubilden. I Dass zwei Schlüssel auf die selbe Zahl abgebildet werden, soll möglichst unwahrscheinlich sein. Hashfunktion, Hashtabelle, Hashkollision Eine Hashfunktion bildet einen Schlüssel auf einen Index der Hashtabelle T ab: h : U −→ { 0, 1, . . . , m−1 } für Tabellengröße m und |U| = n. Wir sagen, dass h(k) der Hashwert des Schlüssels k ist. Das Auftreten von h(k) = h(k 0 ) für k 6= k 0 nennt man eine Kollision. Joost-Pieter Katoen Datenstrukturen und Algorithmen 14/37 Hashing I Grundlagen des Hashings Hashing (II) Schlüsselmenge Hashfunktion 0 Hashtabelle U K k2 k4 h(k1 ) h(k2 ) = h(k3 ) k1 h(k5 ) k3 k5 Kollision h(k4 ) m−1 benutzte Schlüssel Joost-Pieter Katoen Datenstrukturen und Algorithmen 15/37 Hashing I Grundlagen des Hashings Hashing (II) Schlüsselmenge Hashfunktion 0 Hashtabelle U K k2 k4 h(k1 ) h(k2 ) = h(k3 ) k1 h(k5 ) k3 k5 Kollision h(k4 ) m−1 benutzte Schlüssel I Wie finden wir Hashfunktionen, die einfach auszurechnen sind und Kollisionen minimieren? Joost-Pieter Katoen Datenstrukturen und Algorithmen 15/37 Hashing I Grundlagen des Hashings Hashing (II) Schlüsselmenge Hashfunktion 0 Hashtabelle U K k2 k4 h(k1 ) h(k2 ) = h(k3 ) k1 h(k5 ) k3 k5 Kollision h(k4 ) m−1 benutzte Schlüssel I I Wie finden wir Hashfunktionen, die einfach auszurechnen sind und Kollisionen minimieren? Wie behandeln wir dennoch auftretende Kollisionen? Joost-Pieter Katoen Datenstrukturen und Algorithmen 15/37 Hashing I Grundlagen des Hashings Kollisionen: Das Geburtstagsparadoxon (I) Unsere Hashfunktion mag noch so gut sein, wir sollten auf Kollisionen vorbereitet sein! Joost-Pieter Katoen Datenstrukturen und Algorithmen 16/37 Hashing I Grundlagen des Hashings Kollisionen: Das Geburtstagsparadoxon (I) Unsere Hashfunktion mag noch so gut sein, wir sollten auf Kollisionen vorbereitet sein! Das liegt am Geburtstagsparadoxon Joost-Pieter Katoen Datenstrukturen und Algorithmen 16/37 Hashing I Grundlagen des Hashings Kollisionen: Das Geburtstagsparadoxon (I) Unsere Hashfunktion mag noch so gut sein, wir sollten auf Kollisionen vorbereitet sein! Das liegt am Geburtstagsparadoxon I Die Wahrscheinlichkeit, dass dein Nachbar am selben Tag wie du 1 Geburtstag hat ist 365 ≈ 0,027. Joost-Pieter Katoen Datenstrukturen und Algorithmen 16/37 Hashing I Grundlagen des Hashings Kollisionen: Das Geburtstagsparadoxon (I) Unsere Hashfunktion mag noch so gut sein, wir sollten auf Kollisionen vorbereitet sein! Das liegt am Geburtstagsparadoxon I Die Wahrscheinlichkeit, dass dein Nachbar am selben Tag wie du 1 Geburtstag hat ist 365 ≈ 0,027. I Fragt man 23 Personen, wächst die Wahrscheinlichkeit auf 23 365 ≈ 0,063. Joost-Pieter Katoen Datenstrukturen und Algorithmen 16/37 Hashing I Grundlagen des Hashings Kollisionen: Das Geburtstagsparadoxon (I) Unsere Hashfunktion mag noch so gut sein, wir sollten auf Kollisionen vorbereitet sein! Das liegt am Geburtstagsparadoxon I Die Wahrscheinlichkeit, dass dein Nachbar am selben Tag wie du 1 Geburtstag hat ist 365 ≈ 0,027. I Fragt man 23 Personen, wächst die Wahrscheinlichkeit auf 23 365 ≈ 0,063. I Sind aber 23 Personen in einem Raum, dann haben zwei von ihnen den selben Geburtstag mit Wahrscheinlichkeit 1− Joost-Pieter Katoen 365 364 363 343 · · · ... · 365 365 365 365 ≈ 0,5 Datenstrukturen und Algorithmen 16/37 Hashing I Grundlagen des Hashings Kollisionen: Das Geburtstagsparadoxon (II) Auf Hashing angewendet bedeutet das: I Die Wahrscheinlichkeit keiner Kollision nach k Einfügevorgängen in einer m-elementigen Tabelle ist: Y m−i m m−1 m − k + 1 k−1 · · ... · = m m m m i=0 Joost-Pieter Katoen Datenstrukturen und Algorithmen 17/37 Hashing I Grundlagen des Hashings Kollisionen: Das Geburtstagsparadoxon (II) Auf Hashing angewendet bedeutet das: I Die Wahrscheinlichkeit keiner Kollision nach k Einfügevorgängen in einer m-elementigen Tabelle ist: Y m−i m m−1 m − k + 1 k−1 · · ... · = m m m m i=0 I Dieses Produkt geht gegen 0. Joost-Pieter Katoen Datenstrukturen und Algorithmen 17/37 Hashing I Grundlagen des Hashings Kollisionen: Das Geburtstagsparadoxon (II) Auf Hashing angewendet bedeutet das: I Die Wahrscheinlichkeit keiner Kollision nach k Einfügevorgängen in einer m-elementigen Tabelle ist: Y m−i m m−1 m − k + 1 k−1 · · ... · = m m m m i=0 I Dieses Produkt geht gegen 0. I Etwa bei m = 365 ist die Wahrscheinlichkeit für k > 50 praktisch 0. Joost-Pieter Katoen Datenstrukturen und Algorithmen 17/37 Hashing I Grundlagen des Hashings Kollisionen: Das Geburtstagsparadoxon (III) Wahrscheinlichkeit keiner Kollision 1.0 0.8 0.6 0.4 0.2 20 40 60 80 100 Anzahl Einfügungen n Joost-Pieter Katoen Datenstrukturen und Algorithmen 18/37 Hashing I Verkettung Übersicht 1 Direkte Adressierung Counting Sort 2 Grundlagen des Hashings 3 Verkettung 4 Hashfunktionen Joost-Pieter Katoen Datenstrukturen und Algorithmen 19/37 Hashing I Verkettung Kollisionsauflösung durch Verkettung (I) Idee Alle Schlüssel, die zum gleichen Hash führen, werden in einer verketteten Liste gespeichert. Joost-Pieter Katoen Datenstrukturen und Algorithmen [Luhn 1953] 20/37 Hashing I Verkettung Kollisionsauflösung durch Verkettung (I) Idee Alle Schlüssel, die zum gleichen Hash führen, werden in einer verketteten Liste gespeichert. [Luhn 1953] 0 U K k2 k4 k1 k7 k3 k 6 k5 k1 k2 k3 k6 k7 k5 k4 m−1 Joost-Pieter Katoen Datenstrukturen und Algorithmen 20/37 Hashing I Verkettung Kollisionsauflösung durch Verkettung (II) Dictionary-Operationen bei Verkettung (informell) Joost-Pieter Katoen Datenstrukturen und Algorithmen 21/37 Hashing I Verkettung Kollisionsauflösung durch Verkettung (II) Dictionary-Operationen bei Verkettung (informell) I hcSearch(int k): Suche nach einem Element mit Schlüssel k in der Liste T[h(k)]. Joost-Pieter Katoen Datenstrukturen und Algorithmen 21/37 Hashing I Verkettung Kollisionsauflösung durch Verkettung (II) Dictionary-Operationen bei Verkettung (informell) I hcSearch(int k): Suche nach einem Element mit Schlüssel k in der Liste T[h(k)]. I hcInsert(Element e): Setze Element e an den Anfang der Liste T[h(e.key)]. Joost-Pieter Katoen Datenstrukturen und Algorithmen 21/37 Hashing I Verkettung Kollisionsauflösung durch Verkettung (II) Dictionary-Operationen bei Verkettung (informell) I hcSearch(int k): Suche nach einem Element mit Schlüssel k in der Liste T[h(k)]. I hcInsert(Element e): Setze Element e an den Anfang der Liste T[h(e.key)]. I hcDelete(Element e): Lösche Element e aus der Liste T[h(e.key)]. Joost-Pieter Katoen Datenstrukturen und Algorithmen 21/37 Hashing I Verkettung Kollisionsauflösung durch Verkettung (III) Worst-Case Komplexität Angenommen, die Berechnung von h(k) ist recht effizient, etwa Θ(1). Joost-Pieter Katoen Datenstrukturen und Algorithmen 22/37 Hashing I Verkettung Kollisionsauflösung durch Verkettung (III) Worst-Case Komplexität Angenommen, die Berechnung von h(k) ist recht effizient, etwa Θ(1). Die Komplexität ist: Joost-Pieter Katoen Datenstrukturen und Algorithmen 22/37 Hashing I Verkettung Kollisionsauflösung durch Verkettung (III) Worst-Case Komplexität Angenommen, die Berechnung von h(k) ist recht effizient, etwa Θ(1). Die Komplexität ist: Suche: Proportional zur Länge der Liste T [h(k)]. Joost-Pieter Katoen Datenstrukturen und Algorithmen 22/37 Hashing I Verkettung Kollisionsauflösung durch Verkettung (III) Worst-Case Komplexität Angenommen, die Berechnung von h(k) ist recht effizient, etwa Θ(1). Die Komplexität ist: Suche: Proportional zur Länge der Liste T [h(k)]. Einfügen: Konstant (ohne Überprüfung, ob das Element schon vorhanden ist). Joost-Pieter Katoen Datenstrukturen und Algorithmen 22/37 Hashing I Verkettung Kollisionsauflösung durch Verkettung (III) Worst-Case Komplexität Angenommen, die Berechnung von h(k) ist recht effizient, etwa Θ(1). Die Komplexität ist: Suche: Proportional zur Länge der Liste T [h(k)]. Einfügen: Konstant (ohne Überprüfung, ob das Element schon vorhanden ist). Löschen: Proportional zur Länge der Liste T [h(k)]. Joost-Pieter Katoen Datenstrukturen und Algorithmen 22/37 Hashing I Verkettung Kollisionsauflösung durch Verkettung (III) Worst-Case Komplexität Angenommen, die Berechnung von h(k) ist recht effizient, etwa Θ(1). Die Komplexität ist: Suche: Proportional zur Länge der Liste T [h(k)]. Einfügen: Konstant (ohne Überprüfung, ob das Element schon vorhanden ist). Löschen: Proportional zur Länge der Liste T [h(k)]. I Im Worst-Case haben alle Schüssel den selben Hashwert. Joost-Pieter Katoen Datenstrukturen und Algorithmen 22/37 Hashing I Verkettung Kollisionsauflösung durch Verkettung (III) Worst-Case Komplexität Angenommen, die Berechnung von h(k) ist recht effizient, etwa Θ(1). Die Komplexität ist: Suche: Proportional zur Länge der Liste T [h(k)]. Einfügen: Konstant (ohne Überprüfung, ob das Element schon vorhanden ist). Löschen: Proportional zur Länge der Liste T [h(k)]. I Im Worst-Case haben alle Schüssel den selben Hashwert. I Suche und Löschen hat dann die selbe Worst-Case Komplexität wie Listen: Θ(n). Joost-Pieter Katoen Datenstrukturen und Algorithmen 22/37 Hashing I Verkettung Kollisionsauflösung durch Verkettung (III) Worst-Case Komplexität Angenommen, die Berechnung von h(k) ist recht effizient, etwa Θ(1). Die Komplexität ist: Suche: Proportional zur Länge der Liste T [h(k)]. Einfügen: Konstant (ohne Überprüfung, ob das Element schon vorhanden ist). Löschen: Proportional zur Länge der Liste T [h(k)]. I Im Worst-Case haben alle Schüssel den selben Hashwert. I Suche und Löschen hat dann die selbe Worst-Case Komplexität wie Listen: Θ(n). I Im Average-Case ist Hashing mit Verkettung aber dennoch effizient! Joost-Pieter Katoen Datenstrukturen und Algorithmen 22/37 Hashing I Verkettung Average-Case-Analyse von Verkettung (I) Annahmen: I Es gebe n mögliche Schlüssel und m Hashtabellenpositionen, n m. Joost-Pieter Katoen Datenstrukturen und Algorithmen 23/37 Hashing I Verkettung Average-Case-Analyse von Verkettung (I) Annahmen: I Es gebe n mögliche Schlüssel und m Hashtabellenpositionen, n m. I Gleichverteiltes Hashing: Jeder Schlüssel wird mit gleicher Wahrscheinlichkeit und unabhängig von den anderen Schlüssel auf jedes der m Slots abgebildet. Joost-Pieter Katoen Datenstrukturen und Algorithmen 23/37 Hashing I Verkettung Average-Case-Analyse von Verkettung (I) Annahmen: I Es gebe n mögliche Schlüssel und m Hashtabellenpositionen, n m. I Gleichverteiltes Hashing: Jeder Schlüssel wird mit gleicher Wahrscheinlichkeit und unabhängig von den anderen Schlüssel auf jedes der m Slots abgebildet. I Der Hashwert h(k) kann in konstanter Zeit berechnet werden. Joost-Pieter Katoen Datenstrukturen und Algorithmen 23/37 Hashing I Verkettung Average-Case-Analyse von Verkettung (I) Annahmen: I Es gebe n mögliche Schlüssel und m Hashtabellenpositionen, n m. I Gleichverteiltes Hashing: Jeder Schlüssel wird mit gleicher Wahrscheinlichkeit und unabhängig von den anderen Schlüssel auf jedes der m Slots abgebildet. I Der Hashwert h(k) kann in konstanter Zeit berechnet werden. O, Θ, Ω erweitert Aus technischen Gründen erweitern wir die Definition von O, Θ und Ω auf Funktionen mit zwei Parametern. Joost-Pieter Katoen Datenstrukturen und Algorithmen 23/37 Hashing I Verkettung Average-Case-Analyse von Verkettung (I) Annahmen: I Es gebe n mögliche Schlüssel und m Hashtabellenpositionen, n m. I Gleichverteiltes Hashing: Jeder Schlüssel wird mit gleicher Wahrscheinlichkeit und unabhängig von den anderen Schlüssel auf jedes der m Slots abgebildet. I Der Hashwert h(k) kann in konstanter Zeit berechnet werden. O, Θ, Ω erweitert Aus technischen Gründen erweitern wir die Definition von O, Θ und Ω auf Funktionen mit zwei Parametern. I Beispielsweise ist g ∈ O(f ) gdw. ∃c > 0, n0 , m0 mit ∀n > n0 , m > m0 : 0 6 g(n, m) 6 c · f (n, m) Joost-Pieter Katoen Datenstrukturen und Algorithmen 23/37 Hashing I Verkettung Average-Case-Analyse von Verkettung (II) I Der Füllgrad der Hashtabelle T ist α(n, m) = Joost-Pieter Katoen n m. Datenstrukturen und Algorithmen 24/37 Hashing I Verkettung Average-Case-Analyse von Verkettung (II) I Der Füllgrad der Hashtabelle T ist α(n, m) = n m. ⇒ Auch die durchschnittliche Länge der Liste T[h(k)] ist α! Joost-Pieter Katoen Datenstrukturen und Algorithmen 24/37 Hashing I Verkettung Average-Case-Analyse von Verkettung (II) I Der Füllgrad der Hashtabelle T ist α(n, m) = n m. ⇒ Auch die durchschnittliche Länge der Liste T[h(k)] ist α! I Wieviele Elemente aus T[h(k)] müssen nun im Schnitt untersucht werden, um den Schlüssel k zu finden? Joost-Pieter Katoen Datenstrukturen und Algorithmen 24/37 Hashing I Verkettung Average-Case-Analyse von Verkettung (II) I Der Füllgrad der Hashtabelle T ist α(n, m) = n m. ⇒ Auch die durchschnittliche Länge der Liste T[h(k)] ist α! I Wieviele Elemente aus T[h(k)] müssen nun im Schnitt untersucht werden, um den Schlüssel k zu finden? ⇒ Unterscheide erfolgreiche von erfolgloser Suche (wie in Vorlesung 1). Joost-Pieter Katoen Datenstrukturen und Algorithmen 24/37 Hashing I Verkettung Average-Case-Analyse von Verkettung (III) Erfolglose Suche Die erfolglose Suche benötigt Θ(1 + α) Zeit im Average-Case. Joost-Pieter Katoen Datenstrukturen und Algorithmen 25/37 Hashing I Verkettung Average-Case-Analyse von Verkettung (III) Erfolglose Suche Die erfolglose Suche benötigt Θ(1 + α) Zeit im Average-Case. I Die erwartete Zeit, um Schlüssel k zu finden ist gerade die Zeit, um die Liste T[h(k)] zu durchsuchen. Joost-Pieter Katoen Datenstrukturen und Algorithmen 25/37 Hashing I Verkettung Average-Case-Analyse von Verkettung (III) Erfolglose Suche Die erfolglose Suche benötigt Θ(1 + α) Zeit im Average-Case. I Die erwartete Zeit, um Schlüssel k zu finden ist gerade die Zeit, um die Liste T[h(k)] zu durchsuchen. I Die erwartete Länge dieser Liste ist α. Joost-Pieter Katoen Datenstrukturen und Algorithmen 25/37 Hashing I Verkettung Average-Case-Analyse von Verkettung (III) Erfolglose Suche Die erfolglose Suche benötigt Θ(1 + α) Zeit im Average-Case. I Die erwartete Zeit, um Schlüssel k zu finden ist gerade die Zeit, um die Liste T[h(k)] zu durchsuchen. I Die erwartete Länge dieser Liste ist α. I Das Berechnen von h(k) benötige nur eine Zeiteinheit. Joost-Pieter Katoen Datenstrukturen und Algorithmen 25/37 Hashing I Verkettung Average-Case-Analyse von Verkettung (III) Erfolglose Suche Die erfolglose Suche benötigt Θ(1 + α) Zeit im Average-Case. I Die erwartete Zeit, um Schlüssel k zu finden ist gerade die Zeit, um die Liste T[h(k)] zu durchsuchen. I Die erwartete Länge dieser Liste ist α. I Das Berechnen von h(k) benötige nur eine Zeiteinheit. ⇒ Insgesamt erhält man 1 + α Zeiteinheiten im Durchschnitt. Joost-Pieter Katoen Datenstrukturen und Algorithmen 25/37 Hashing I Verkettung Average-Case-Analyse von Verkettung (IV) Erfolgreiche Suche Die erfolgreiche Suche benötigt im Average-Case auch Θ(1 + α). Joost-Pieter Katoen Datenstrukturen und Algorithmen 26/37 Hashing I Verkettung Average-Case-Analyse von Verkettung (IV) Erfolgreiche Suche Die erfolgreiche Suche benötigt im Average-Case auch Θ(1 + α). I Sei ki der i-te eingefügte Schlüssel und A(ki ) die erwartete Zeit, um ki zu finden: Joost-Pieter Katoen Datenstrukturen und Algorithmen 26/37 Hashing I Verkettung Average-Case-Analyse von Verkettung (IV) Erfolgreiche Suche Die erfolgreiche Suche benötigt im Average-Case auch Θ(1 + α). I Sei ki der i-te eingefügte Schlüssel und A(ki ) die erwartete Zeit, um ki zu finden: A(ki ) = 1 + Joost-Pieter Katoen Durchschnittliche Anzahl Schlüssel, die in T[h(k_i)] erst nach ki eingefügt wurden Datenstrukturen und Algorithmen 26/37 Hashing I Verkettung Average-Case-Analyse von Verkettung (IV) Erfolgreiche Suche Die erfolgreiche Suche benötigt im Average-Case auch Θ(1 + α). I Sei ki der i-te eingefügte Schlüssel und A(ki ) die erwartete Zeit, um ki zu finden: A(ki ) = 1 + I Durchschnittliche Anzahl Schlüssel, die in T[h(k_i)] erst nach ki eingefügt wurden Annahme von gleichverteiltem Hashing ergibt: A(ki ) = 1 + n X 1 j=i+1 Joost-Pieter Katoen Datenstrukturen und Algorithmen m 26/37 Hashing I Verkettung Average-Case-Analyse von Verkettung (IV) Erfolgreiche Suche Die erfolgreiche Suche benötigt im Average-Case auch Θ(1 + α). I Sei ki der i-te eingefügte Schlüssel und A(ki ) die erwartete Zeit, um ki zu finden: A(ki ) = 1 + I I Durchschnittliche Anzahl Schlüssel, die in T[h(k_i)] erst nach ki eingefügt wurden Annahme von gleichverteiltem Hashing ergibt: A(ki ) = 1 + Durchschnitt über alle n Einfügungen in die Hashtabelle: Joost-Pieter Katoen Datenstrukturen und Algorithmen 1 n n X 1 j=i+1 n X m A(ki ) i=1 26/37 Hashing I Verkettung Average-Case-Analyse von Verkettung (IV) Die erwartete Anzahl an untersuchten Elementen bei einer erfolgreichen Suche ist: Joost-Pieter Katoen Datenstrukturen und Algorithmen 27/37 Hashing I Verkettung Average-Case-Analyse von Verkettung (IV) Die erwartete Anzahl an untersuchten Elementen bei einer erfolgreichen Suche ist: n 1X n 1 + i=1 n X 1 m j=i+1 Summe aufteilen n n X n 1X 1 X = 1+ 1 n i=1 nm i=1 j=i+1 Vereinfachen n 1 X =1+ (n − i) nm i=1 Summe 1 . . . n − 1 1 n(n − 1) · nm 2 n−1 α α =1+ =1+ − 2m 2 2n =1+ Joost-Pieter Katoen Vereinfachen und damit in Θ(1 + α) Datenstrukturen und Algorithmen 27/37 Hashing I Verkettung Komplexität der Dictionary-Operationen mit Verkettung I Vorausgesetzt die Anzahl der Einträge m ist (wenigstens) proportional zu n, Joost-Pieter Katoen Datenstrukturen und Algorithmen 28/37 Hashing I Verkettung Komplexität der Dictionary-Operationen mit Verkettung I Vorausgesetzt die Anzahl der Einträge m ist (wenigstens) proportional zu n, I dann ist der Füllgrad α(n, m) = Joost-Pieter Katoen n m ∈ O(m) m = O(1). Datenstrukturen und Algorithmen 28/37 Hashing I Verkettung Komplexität der Dictionary-Operationen mit Verkettung I Vorausgesetzt die Anzahl der Einträge m ist (wenigstens) proportional zu n, I dann ist der Füllgrad α(n, m) = I Damit benötigen alle Operationen im Durchschnitt O(1). Joost-Pieter Katoen n m ∈ O(m) m = O(1). Datenstrukturen und Algorithmen 28/37 Hashing I Verkettung Komplexität der Dictionary-Operationen mit Verkettung I Vorausgesetzt die Anzahl der Einträge m ist (wenigstens) proportional zu n, I dann ist der Füllgrad α(n, m) = I Damit benötigen alle Operationen im Durchschnitt O(1). I Weil das auch Suche mit einschließt, können wir im Average-Case mit O(n) sortieren. Joost-Pieter Katoen n m ∈ O(m) m = O(1). Datenstrukturen und Algorithmen 28/37 Hashing I Hashfunktionen Übersicht 1 Direkte Adressierung Counting Sort 2 Grundlagen des Hashings 3 Verkettung 4 Hashfunktionen Joost-Pieter Katoen Datenstrukturen und Algorithmen 29/37 Hashing I Hashfunktionen Anwendungen von Hashfunktionen I I Digitale Zertifikate (SSL: sha1 bzw. md5) Digitale Signaturen I I Passwortverschlüsselung I I I in der Praxis unterschreibt man meist nicht die Nachricht, sondern ihren Hashwert z.B. htpasswd (Apache): md5, sha1 und Mediawiki: md5 Verifikation von Downloads (üblich: md5) Versionskontrollsysteme I z.B. subversion: md5, git: sha1 I Datenblockverifikation bei Filesharingprogrammen (wie Bittorrent) I ...... Joost-Pieter Katoen Datenstrukturen und Algorithmen 30/37 Hashing I Hashfunktionen Gängige (kryptographische) Hashfunktionen Joost-Pieter Katoen Datenstrukturen und Algorithmen 31/37 Hashing I Hashfunktionen Hashfunktionen Hashfunktion I Eine Hashfunktion bildet einen Schlüssel auf eine ganze Zahl (d. h. einen Index) ab. Joost-Pieter Katoen Datenstrukturen und Algorithmen 32/37 Hashing I Hashfunktionen Hashfunktionen Hashfunktion I Eine Hashfunktion bildet einen Schlüssel auf eine ganze Zahl (d. h. einen Index) ab. I Was macht eine „gute“ Hashfunktion aus? Joost-Pieter Katoen Datenstrukturen und Algorithmen 32/37 Hashing I Hashfunktionen Hashfunktionen Hashfunktion I Eine Hashfunktion bildet einen Schlüssel auf eine ganze Zahl (d. h. einen Index) ab. I Was macht eine „gute“ Hashfunktion aus? I Die Hashfunktion h(k) sollte einfach zu berechnen sein, Joost-Pieter Katoen Datenstrukturen und Algorithmen 32/37 Hashing I Hashfunktionen Hashfunktionen Hashfunktion I Eine Hashfunktion bildet einen Schlüssel auf eine ganze Zahl (d. h. einen Index) ab. I Was macht eine „gute“ Hashfunktion aus? I I Die Hashfunktion h(k) sollte einfach zu berechnen sein, sie sollte surjektiv auf der Menge 0 . . . m−1 sein, Joost-Pieter Katoen Datenstrukturen und Algorithmen 32/37 Hashing I Hashfunktionen Hashfunktionen Hashfunktion I Eine Hashfunktion bildet einen Schlüssel auf eine ganze Zahl (d. h. einen Index) ab. I Was macht eine „gute“ Hashfunktion aus? I I I Die Hashfunktion h(k) sollte einfach zu berechnen sein, sie sollte surjektiv auf der Menge 0 . . . m−1 sein, sie sollte alle Indizes mit möglichst gleicher Häufigkeit verwenden, und Joost-Pieter Katoen Datenstrukturen und Algorithmen 32/37 Hashing I Hashfunktionen Hashfunktionen Hashfunktion I Eine Hashfunktion bildet einen Schlüssel auf eine ganze Zahl (d. h. einen Index) ab. I Was macht eine „gute“ Hashfunktion aus? I I I I Die Hashfunktion h(k) sollte einfach zu berechnen sein, sie sollte surjektiv auf der Menge 0 . . . m−1 sein, sie sollte alle Indizes mit möglichst gleicher Häufigkeit verwenden, und ähnliche Schlüssel möglichst breit auf die Hashtabelle verteilen. Joost-Pieter Katoen Datenstrukturen und Algorithmen 32/37 Hashing I Hashfunktionen Hashfunktionen Hashfunktion I Eine Hashfunktion bildet einen Schlüssel auf eine ganze Zahl (d. h. einen Index) ab. I Was macht eine „gute“ Hashfunktion aus? I I I I I Die Hashfunktion h(k) sollte einfach zu berechnen sein, sie sollte surjektiv auf der Menge 0 . . . m−1 sein, sie sollte alle Indizes mit möglichst gleicher Häufigkeit verwenden, und ähnliche Schlüssel möglichst breit auf die Hashtabelle verteilen. Drei Basistechniken, eine „gute“ Hashfunktion zu erhalten: Joost-Pieter Katoen Datenstrukturen und Algorithmen 32/37 Hashing I Hashfunktionen Hashfunktionen Hashfunktion I Eine Hashfunktion bildet einen Schlüssel auf eine ganze Zahl (d. h. einen Index) ab. I Was macht eine „gute“ Hashfunktion aus? I I I I I Die Hashfunktion h(k) sollte einfach zu berechnen sein, sie sollte surjektiv auf der Menge 0 . . . m−1 sein, sie sollte alle Indizes mit möglichst gleicher Häufigkeit verwenden, und ähnliche Schlüssel möglichst breit auf die Hashtabelle verteilen. Drei Basistechniken, eine „gute“ Hashfunktion zu erhalten: I Die Divisionsmethode, Joost-Pieter Katoen Datenstrukturen und Algorithmen 32/37 Hashing I Hashfunktionen Hashfunktionen Hashfunktion I Eine Hashfunktion bildet einen Schlüssel auf eine ganze Zahl (d. h. einen Index) ab. I Was macht eine „gute“ Hashfunktion aus? I I I I I Die Hashfunktion h(k) sollte einfach zu berechnen sein, sie sollte surjektiv auf der Menge 0 . . . m−1 sein, sie sollte alle Indizes mit möglichst gleicher Häufigkeit verwenden, und ähnliche Schlüssel möglichst breit auf die Hashtabelle verteilen. Drei Basistechniken, eine „gute“ Hashfunktion zu erhalten: I I Die Divisionsmethode, die Multiplikationsmethode, und Joost-Pieter Katoen Datenstrukturen und Algorithmen 32/37 Hashing I Hashfunktionen Hashfunktionen Hashfunktion I Eine Hashfunktion bildet einen Schlüssel auf eine ganze Zahl (d. h. einen Index) ab. I Was macht eine „gute“ Hashfunktion aus? I I I I I Die Hashfunktion h(k) sollte einfach zu berechnen sein, sie sollte surjektiv auf der Menge 0 . . . m−1 sein, sie sollte alle Indizes mit möglichst gleicher Häufigkeit verwenden, und ähnliche Schlüssel möglichst breit auf die Hashtabelle verteilen. Drei Basistechniken, eine „gute“ Hashfunktion zu erhalten: I I I Die Divisionsmethode, die Multiplikationsmethode, und universelles Hashing. Joost-Pieter Katoen Datenstrukturen und Algorithmen 32/37 Hashing I Hashfunktionen Divisionsmethode Divisionsmethode Hashfunktion: h(k) = k mod m Joost-Pieter Katoen Datenstrukturen und Algorithmen 33/37 Hashing I Hashfunktionen Divisionsmethode Divisionsmethode Hashfunktion: h(k) = k mod m I Bei dieser Methode muss der Wert von m sorgfältig gewählt werden. Joost-Pieter Katoen Datenstrukturen und Algorithmen 33/37 Hashing I Hashfunktionen Divisionsmethode Divisionsmethode Hashfunktion: h(k) = k mod m I Bei dieser Methode muss der Wert von m sorgfältig gewählt werden. I Für m = 2p ist h(k) einfach die letzte p Bits. Joost-Pieter Katoen Datenstrukturen und Algorithmen 33/37 Hashing I Hashfunktionen Divisionsmethode Divisionsmethode Hashfunktion: h(k) = k mod m I Bei dieser Methode muss der Wert von m sorgfältig gewählt werden. I I Für m = 2p ist h(k) einfach die letzte p Bits. Besser ist es, h(k) abhängig von mehreren Bits zu machen. Joost-Pieter Katoen Datenstrukturen und Algorithmen 33/37 Hashing I Hashfunktionen Divisionsmethode Divisionsmethode Hashfunktion: h(k) = k mod m I Bei dieser Methode muss der Wert von m sorgfältig gewählt werden. I I I Für m = 2p ist h(k) einfach die letzte p Bits. Besser ist es, h(k) abhängig von mehreren Bits zu machen. Gute Wahl ist m prim und nicht zu nah an einer Zweierpotenz. Joost-Pieter Katoen Datenstrukturen und Algorithmen 33/37 Hashing I Hashfunktionen Divisionsmethode Divisionsmethode Hashfunktion: h(k) = k mod m I Bei dieser Methode muss der Wert von m sorgfältig gewählt werden. I I I Für m = 2p ist h(k) einfach die letzte p Bits. Besser ist es, h(k) abhängig von mehreren Bits zu machen. Gute Wahl ist m prim und nicht zu nah an einer Zweierpotenz. Beispiel I Strings mit 2000 Zeichen als Schlüssel. Joost-Pieter Katoen Datenstrukturen und Algorithmen 33/37 Hashing I Hashfunktionen Divisionsmethode Divisionsmethode Hashfunktion: h(k) = k mod m I Bei dieser Methode muss der Wert von m sorgfältig gewählt werden. I I I Für m = 2p ist h(k) einfach die letzte p Bits. Besser ist es, h(k) abhängig von mehreren Bits zu machen. Gute Wahl ist m prim und nicht zu nah an einer Zweierpotenz. Beispiel I Strings mit 2000 Zeichen als Schlüssel. I Wir erlauben durchschnittlich 3 Sondierungen für die erfolglose Suche. Joost-Pieter Katoen Datenstrukturen und Algorithmen 33/37 Hashing I Hashfunktionen Divisionsmethode Divisionsmethode Hashfunktion: h(k) = k mod m I Bei dieser Methode muss der Wert von m sorgfältig gewählt werden. I I I Für m = 2p ist h(k) einfach die letzte p Bits. Besser ist es, h(k) abhängig von mehreren Bits zu machen. Gute Wahl ist m prim und nicht zu nah an einer Zweierpotenz. Beispiel I Strings mit 2000 Zeichen als Schlüssel. I Wir erlauben durchschnittlich 3 Sondierungen für die erfolglose Suche. ⇒ Wähle m ≈ 2000/3 −→ 701. Joost-Pieter Katoen Datenstrukturen und Algorithmen 33/37 Hashing I Hashfunktionen Multiplikationsmethode (I) Multiplikationsmethode Hashfunktion: h(k) = bm·(k·c mod 1)c für 0 < c < 1 Joost-Pieter Katoen Datenstrukturen und Algorithmen 34/37 Hashing I Hashfunktionen Multiplikationsmethode (I) Multiplikationsmethode Hashfunktion: h(k) = bm·(k·c mod 1)c für 0 < c < 1 I k·c mod 1 ist der Nachkommateil von k·c, d. h. k·c − bk·cc. Joost-Pieter Katoen Datenstrukturen und Algorithmen 34/37 Hashing I Hashfunktionen Multiplikationsmethode (I) Multiplikationsmethode Hashfunktion: h(k) = bm·(k·c mod 1)c für 0 < c < 1 I I k·c mod 1 ist der Nachkommateil von k·c, d. h. k·c − bk·cc. √ Knuth empfiehlt c ≈ ( 5 − 1)/2 ≈ 0,62. Joost-Pieter Katoen Datenstrukturen und Algorithmen 34/37 Hashing I Hashfunktionen Multiplikationsmethode (I) Multiplikationsmethode Hashfunktion: h(k) = bm·(k·c mod 1)c für 0 < c < 1 I I k·c mod 1 ist der Nachkommateil von k·c, d. h. k·c − bk·cc. √ Knuth empfiehlt c ≈ ( 5 − 1)/2 ≈ 0,62. ⇒ Der Wert von m ist hier nicht kritisch. Joost-Pieter Katoen Datenstrukturen und Algorithmen 34/37 Hashing I Hashfunktionen Multiplikationsmethode (II) Hashfunktion: h(k) = bm·(k·c mod 1)c. Joost-Pieter Katoen Datenstrukturen und Algorithmen 35/37 Hashing I Hashfunktionen Multiplikationsmethode (II) Hashfunktion: h(k) = bm·(k·c mod 1)c. I Das übliche Vorgehen nimmt m = 2p und c = Joost-Pieter Katoen s 2w , wobei 0 < s < 2w . Datenstrukturen und Algorithmen 35/37 Hashing I Hashfunktionen Multiplikationsmethode (II) Hashfunktion: h(k) = bm·(k·c mod 1)c. I Das übliche Vorgehen nimmt m = 2p und c = Dann: I s 2w , wobei 0 < s < 2w . Berechne zunächst k·s (= k·c·2w ). Joost-Pieter Katoen Datenstrukturen und Algorithmen 35/37 Hashing I Hashfunktionen Multiplikationsmethode (II) Hashfunktion: h(k) = bm·(k·c mod 1)c. I Das übliche Vorgehen nimmt m = 2p und c = Dann: I I s 2w , wobei 0 < s < 2w . Berechne zunächst k·s (= k·c·2w ). Teile durch 2w , verwende nur die Nachkommastellen. Joost-Pieter Katoen Datenstrukturen und Algorithmen 35/37 Hashing I Hashfunktionen Multiplikationsmethode (II) Hashfunktion: h(k) = bm·(k·c mod 1)c. I Das übliche Vorgehen nimmt m = 2p und c = Dann: I I I s 2w , wobei 0 < s < 2w . Berechne zunächst k·s (= k·c·2w ). Teile durch 2w , verwende nur die Nachkommastellen. Multipliziere mit 2p und verwende nur den ganzzahligen Anteil. Joost-Pieter Katoen Datenstrukturen und Algorithmen 35/37 Hashing I Hashfunktionen Multiplikationsmethode (II) Hashfunktion: h(k) = bm·(k·c mod 1)c. I Das übliche Vorgehen nimmt m = 2p und c = Dann: I I I s 2w , wobei 0 < s < 2w . Berechne zunächst k·s (= k·c·2w ). Teile durch 2w , verwende nur die Nachkommastellen. Multipliziere mit 2p und verwende nur den ganzzahligen Anteil. w Bits Schlüssel k ∗ c · 2w p Bits extrahieren h(k) Joost-Pieter Katoen Datenstrukturen und Algorithmen 35/37 Hashing I Hashfunktionen Universelles Hashing (I) Das größte Problem beim Hashing ist, I dass es immer eine ungünstige Sequenz von Schlüsseln gibt, die auf den selben Slot abgebildet werden. Joost-Pieter Katoen Datenstrukturen und Algorithmen 36/37 Hashing I Hashfunktionen Universelles Hashing (I) Das größte Problem beim Hashing ist, I dass es immer eine ungünstige Sequenz von Schlüsseln gibt, die auf den selben Slot abgebildet werden. Idee Wähle zufällig eine Hashfunktion aus einer gegebenen kleinen Menge H, unabhängig von den verwendeten Schlüsseln. Joost-Pieter Katoen Datenstrukturen und Algorithmen 36/37 Hashing I Hashfunktionen Universelles Hashing (I) Das größte Problem beim Hashing ist, I dass es immer eine ungünstige Sequenz von Schlüsseln gibt, die auf den selben Slot abgebildet werden. Idee Wähle zufällig eine Hashfunktion aus einer gegebenen kleinen Menge H, unabhängig von den verwendeten Schlüsseln. Eine Menge Hashfunktionen ist universell, wenn I der Anteil der Funktionen aus H, so dass k und k 0 kollidieren ist Joost-Pieter Katoen Datenstrukturen und Algorithmen |H| m . 36/37 Hashing I Hashfunktionen Universelles Hashing (I) Das größte Problem beim Hashing ist, I dass es immer eine ungünstige Sequenz von Schlüsseln gibt, die auf den selben Slot abgebildet werden. Idee Wähle zufällig eine Hashfunktion aus einer gegebenen kleinen Menge H, unabhängig von den verwendeten Schlüsseln. Eine Menge Hashfunktionen ist universell, wenn I der Anteil der Funktionen aus H, so dass k und k 0 kollidieren ist I d. h., die W’lichkeit einer Kollision von k und k 0 ist 1 |H| · |H| m = |H| m . 1 m. Für universelles Hashing ist die erwartete Länge der Liste T[k] Joost-Pieter Katoen Datenstrukturen und Algorithmen 36/37 Hashing I Hashfunktionen Universelles Hashing (I) Das größte Problem beim Hashing ist, I dass es immer eine ungünstige Sequenz von Schlüsseln gibt, die auf den selben Slot abgebildet werden. Idee Wähle zufällig eine Hashfunktion aus einer gegebenen kleinen Menge H, unabhängig von den verwendeten Schlüsseln. Eine Menge Hashfunktionen ist universell, wenn I der Anteil der Funktionen aus H, so dass k und k 0 kollidieren ist I d. h., die W’lichkeit einer Kollision von k und k 0 ist 1 |H| · |H| m = |H| m . 1 m. Für universelles Hashing ist die erwartete Länge der Liste T[k] 1. Gleich α, wenn k nicht in T enthalten ist. Joost-Pieter Katoen Datenstrukturen und Algorithmen 36/37 Hashing I Hashfunktionen Universelles Hashing (I) Das größte Problem beim Hashing ist, I dass es immer eine ungünstige Sequenz von Schlüsseln gibt, die auf den selben Slot abgebildet werden. Idee Wähle zufällig eine Hashfunktion aus einer gegebenen kleinen Menge H, unabhängig von den verwendeten Schlüsseln. Eine Menge Hashfunktionen ist universell, wenn I der Anteil der Funktionen aus H, so dass k und k 0 kollidieren ist I d. h., die W’lichkeit einer Kollision von k und k 0 ist 1 |H| · |H| m = |H| m . 1 m. Für universelles Hashing ist die erwartete Länge der Liste T[k] 1. Gleich α, wenn k nicht in T enthalten ist. 2. Gleich 1+α, wenn k in T enthalten ist. Joost-Pieter Katoen Datenstrukturen und Algorithmen 36/37 Hashing I Hashfunktionen Universelles Hashing (II) Beispiel Definiere die Elemente der Klasse H von Hashfunktionen durch: ha,b (k) = ((a · k + b) mod p) mod m I p sei Primzahl mit p > m und p > größter Schlüssel. I Die ganzen Zahlen a (1 6 a < p) und b (0 6 b < p) werden erst bei der Ausführung gewählt. Joost-Pieter Katoen Datenstrukturen und Algorithmen 37/37 Hashing I Hashfunktionen Universelles Hashing (II) Beispiel Definiere die Elemente der Klasse H von Hashfunktionen durch: ha,b (k) = ((a · k + b) mod p) mod m I p sei Primzahl mit p > m und p > größter Schlüssel. I Die ganzen Zahlen a (1 6 a < p) und b (0 6 b < p) werden erst bei der Ausführung gewählt. Die Klasse der obigen Hashfunktionen ha,b ist universell. Joost-Pieter Katoen Datenstrukturen und Algorithmen 37/37 Hashing I Hashfunktionen Nächste Vorlesung Nächste Vorlesung Donnerstag 21. Mai, 10:15 (Aula Hauptgebäude). Bis dann! Joost-Pieter Katoen Datenstrukturen und Algorithmen 38/37
© Copyright 2024