13. Kapitel: Hashfunktionen 1) Divisionsrest-Verfahren ( kurz: Divisionsverfahren) h(Ki) = Ki mod q, (q ~ m) Der entstehende Rest ergibt die relative Adresse in HT. Beispiel: Die Funktion nat wandle Namen in natürliche Zahlen um: nat(Name) = ord (1. Buchstabe von Name ) - ord (“A”) h (Name) = nat (Name) mod m G.Heyer 1 Algorithmen und Datenstrukturen Hash-Tabelle: m = 10 Schlüssel 0 Daten 1 BOHR D1 2 CURIE D2 3 DIRAC D3 4 EINSTEIN D4 5 PLANCK D5 7 HEISENBERG D7 8 SCHRÖDINGER D8 6 9 G.Heyer 2 Algorithmen und Datenstrukturen Divisionsrest- Verfahren: Forderungen an Divisor q 1) m > n • Belegungsfaktor von HT: Verhältnis von aktuell belegten Speicherplätzen (n) zur gesamten Anzahl der Speicherplätze (m) = na / m • Für 0.85 erzeugen alle Hash-Funktionen viele Kollisionen und damit einen hohen Zusatzaufwand. 2) q gerade Zahl sonst bleibt h (Ki) bei geradem Ki gerade und bei ungeradem Ki ungerade. 3) q bk b sei die Basis der Schlüsseldarstellung. Wenn q = bk ist, dann liefert h (Ki) die letzten k Stellen von Ki. h(Ki) = Ki mod q G.Heyer 3 Algorithmen und Datenstrukturen 4) q a * bk c a und c seien kleine ganze Zahlen. Der Divisor q soll nicht benachbart zu einer Potenz des Zahlensystems (in dem die Division durchgeführt wird) liegen, da sonst (x + a * bk c ) mod q ~ x mod q ist, d. h., bei gleichen Endziffern wiederholt sich fast die gleiche Menge von Adressen in verschiedenen Zahlenbereichen. 5) q = Primzahl ( größte Primzahl <= m) Die Hash-Funktion muss etwaige Regelmäßigkeiten in der Schlüsselverteilung eliminieren, damit nicht ständig die gleichen Plätze der HT getroffen werden. Bei äquidistantem Abstand der Schlüssel Ki + j * K, j = 0, 1, 2, ,... maximiert eine Primzahl die Distanz, nach der eine Kollision auftritt. G.Heyer 4 Algorithmen und Datenstrukturen Eine Kollision ergibt sich, wenn Ki mod q = (Ki + j * K) mod q j * K = k * q, oder k = 1, 2, 3, ... Eine Primzahl kann keine gemeinsamen Faktoren mit K besitzen, die den Kollisionsabstand verkürzen würden. ==> wichtigste Forderung an q ! 2) Faltung • Schlüssel wird in Teile zerlegt, die bis auf das letzte die Länge einer Adresse für HT besitzen. • Schlüsselteile werden dann übereinander gefaltet und addiert. G.Heyer 5 Algorithmen und Datenstrukturen 3) Mid-Square-Methode • Schlüssel Ki wird quadriert, t aufeinanderfolgende Stellen werden aus der Mitte des Ergebnisses für die Adressierung ausgewählt. • Es muss also bt = m gelten. • Mittlere Stellen lassen beste Gleichverteilung der Werte erwarten. • Beispiel für b = 2, t = 4, m = 16 : Ki = 1100100 Ki2 = 10011100010000 h (Ki) = 1000 t G.Heyer 6 Algorithmen und Datenstrukturen 4) Weitere Verfahren • Zufallsmethode: Ki dient als Saat für Zufallszahlengenerator • Ziffernanalyse: setzt Kenntnis der Schlüsselmenge K voraus. Die t Stellen mit der besten Gleichverteilung der Ziffern oder Zeichen in K werden von Ki zur Adressierung ausgewählt. Bewertung Das Verhalten einer Hash-Funktion hängt von der gewählten Schlüsselmenge ab. Deshalb lassen sie sich auch nur unzureichend theoretisch oder mit Hilfe von analytischen Modellen untersuchen. G.Heyer 7 Algorithmen und Datenstrukturen Über die Güte der verschiedenen Hash-Funktionen liegen jedoch eine Reihe von empirischen Untersuchungen vor. • Das Divisionsrest-Verfahren ist im Mittel am leistungsfähigsten; für bestimmte Schlüsselmengen können jedoch andere Techniken besser abschneiden. • Keine Hash-Funktion ist immer besser als alle anderen. • Wenn die Schlüsselverteilung nicht bekannt ist, dann ist das Divisionsrest-Verfahren die bevorzugte Hash-Technik. ==> Wenn eine Hash-Funktion gegeben ist, lässt sich immer eine Schlüsselmenge finden, bei der sie besonders viele Kollisionen erzeugt. G.Heyer 8 Algorithmen und Datenstrukturen Behandlung von Kollisionen Zwei Ansätze, wenn h (Kq) = h (Kp): • Es wird für Kp eine freier Platz in HT gesucht ; alle Überläufer werden im Primärbereich untergebracht (open adressing). • Kp wird in einem separaten Überlaufbereich zusammen mit allen anderen Überläufern gespeichert (separate overflow) Die Methode der Kollisions-Auflösung entscheidet darüber, welche Folge und wie viele relative Adressen zur Ermittlung eines freien Platzes aufgesucht werden. G.Heyer 9 Algorithmen und Datenstrukturen Adressfolge bei Speicherung und Suche für Schlüssel Kp sei h0(Kp), h1(Kp), h2(Kp), ... • Bei einer Folge der Länge n treten also n-1 Kollisionen auf • Primärkollision: h (K p) = h (K q) • Sekundärkollision: h i (Kp) = h j(Kq) , i j Offene Hash-Verfahren Speicherung der Synonyme (Überläufer) im Primärbereich Das eingesetzte Hash-Verfahren muss in der Lage sein, eine Sondierungsfolge, d. h. eine Permutation aller Hash-Adressen, zu berechnen. G.Heyer 10 Algorithmen und Datenstrukturen 1) Lineares Sondieren (linear probing) Von der Hausadresse aus wird sequentiell (modulo) gesucht. Diese Vorgehensweise kann mit jedem Hash-Verfahren kombiniert werden. Offensichtlich werden dabei alle Plätze in HT erreicht: h0 (K p) = h (K p) h i (K p) = ( h 0( K p ) - i ) mod m, G.Heyer 11 i = 1, 2, ... Algorithmen und Datenstrukturen Beispiel: Einfüge-Reihenfolge: BECKETT, HESSE, BÖLL, HAUPTMANN, STEINBECK, SACHS, HAMSUN, SARTRE HT: m = 8 Schlüssel Schlüssel 0 1 2 3 4 5 6 7 G.Heyer Lösche 12 0 1 2 3 4 5 6 7 Algorithmen und Datenstrukturen • Irgendeine Primär- oder Sekundärkollision kann eine Häufung von Primär- oder Sekundärkollision auslösen. • Löschen: implizit oft Verschiebungen. Entstehende Lücken in Suchsequenzen sind auszufüllen, da das Antreffen eines freien Platzes die Suche beendet. G.Heyer 13 Algorithmen und Datenstrukturen Suche in einer Hash-Tabelle bei linearem Sondieren void Linsuche (Key X, Hashtab HT, Cardinal m, Cardinal j) { /* Suche in HT bei linearem Sondieren /*Bei erfolgreicher Suche zeigt j auf Position von X in HT Cardinal i; i = H [X]; /* H sei global definierte Hash-Funktion j=i; /* unbelegter Eintrag in HT sei durch /* „ - “ - Zeichen charakterisiert while ( (HT [ j ] != X) && (HT[ j ] != „ - „ ) ) { j = (j -1 ) % m; if ( i == j) { printf ( „ X ist nicht in HT \n“); return ; } if ( HT [ j ] == „ - „ ) printf („ X ist nicht in HT \n“); } return; } G.Heyer 14 */ */ */ */ */ Algorithmen und Datenstrukturen Verbesserung: Modifikation der Überlauffolge (z.B. durch quadratisches Sondieren) h0 ( K p) = h ( K p) h i+1 ( K p ) = ( hi ( K p ) + f ( i ) ) mod m oder h i+1 (K p) = (h i ( Kp ) + f ( i, h ( K p ) ) ) mod m , i= 1, 2, ... 2) Sondieren mit Zufallszahlen Mit Hilfe eines deterministischen Pseudo-ZufallszahlenGenerators wird die Folge der Adressen [1 ... m-1] mod m genau einmal erzeugt. Abhängig von k wird eine zufällige Hashadresse s(j, k) gewählt. G.Heyer h0 (K p) = h (K p) h i (K p) = ( h0 (K p) + z i ) mod m 15 , i = 1, 2, ... Algorithmen und Datenstrukturen 3) Double-Hashing Einsatz einer zweiten Funktion für die Sondierungsfolge h0 (Kp) = h (Kp) hi (Kp) = ( h0 (Kp) + i * h‘ (Kp) ) mod m , i = 1, 2, ... Dabei ist h‘ (K) so zu wählen, dass für alle Schlüssel K die resultierende Sondierungsfolge eine Permutation aller Hash-Adressen bildet. 4) Kettung von Synonymen • Explizite Kettung aller Sätze einer Kollisionsklasse verringert nicht die Anzahl der Kollisionsklassen; verkürzt jedoch den Suchpfad beim Aufsuchen eines Synonyms. • Bestimmung eines freien Überlaufplatzes (Kollisionsbehandlung) mit beliebiger Methode G.Heyer 16 Algorithmen und Datenstrukturen Hash-Verfahren mit separatem Überlaufbereich Dynamische Speicherplatz-Belegung für Synonyme • Alle Sätze, die nicht auf ihrer Hausadresse unterkommen, werden in einem separaten Bereich gespeichert. • Die Bestimmung der Überlaufadresse kann entweder durch Double Hashing oder durch Kettung der Synonyme erfolgen. • Die Synonym-Kettung erlaubt auch die Möglichkeit, den Speicherplatz für Überläufer dynamisch zu belegen. • Suchen, Einfügen und Löschen sind auf Kollisionsklasse beschränkt. • Unterscheidung nach Primär- und Sekundärbereich G.Heyer 17 Algorithmen und Datenstrukturen Beispiel: HT: m=7 Schlüssel 0 HAYDN * 1 BEETHOVEN * 2 CORELLI * 3 * 4 SCHUBERT * 5 MOZART * 6 * G.Heyer HÄNDEL * VIVALDI BACH * BRAHMS * LISZT * 18 * Algorithmen und Datenstrukturen Analyse des Hashing Kostenmaße • = n / m : Belegung von HT mit n Schlüsseln • Sn = # Suchschritte für das Auffinden eines Schlüssels entspricht den Kosten für erfolgreiche Suche und Löschen (ohne Reorganisation) • Un = # der Suchschritte für die erfolglose Suche das Auffinden des ersten freien Platzes entspricht den Einfügekosten Grenzwerte: G.Heyer best case worst case Sn = 1 Sn = n Un = 1 Un = n + 1 19 Algorithmen und Datenstrukturen Modell für das lineare Sondieren • sobald eine gewisse Größe überschreitet, verschlechtert sich das Zugriffsverhalten sehr stark. 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 • Je länger eine Liste ist, um so schneller wird sie noch länger werden. • Zwei Listen können zusammen wachsen (Platz 3 und 14), so dass durch neue Schlüssel eine Art Verdopplung der Listenlänge eintreten kann. G.Heyer 20 Algorithmen und Datenstrukturen Ergebnisse für das lineare Sondieren nach Knuth 1 Sn 0.5 1 + ------1- 1 Un 0.5 1 + --------( 1 - )2 n mit 0 = --- < 1 m Abschätzung für offene Hash-Verfahren mit optimierter Kollisions-Behandlung (gleichmäßige HT-Verteilung von Kollisionen) 1 Un ~ -------1- 1 Sn ~ - --- * ln ( 1 - ) G.Heyer 21 Algorithmen und Datenstrukturen Anzahl der Suchschritte in HT Sn, Un Sn, Un 10 9 8 7 6 5 4 3 2 1 Un Sn 0.1 0.3 0.5 0.7 0.9 10 9 8 7 6 5 4 3 2 1 Sn 0.1 0.3 0.5 0.7 0.9 bei „unabhängiger“ KollisionsAuflösung bei linearem Sondieren G.Heyer Un 22 Algorithmen und Datenstrukturen Analyse des Hashing (2) Modell für separate Überlaufbereiche • Annahme: n Schlüssel verteilen sich gleichförmig über die m möglichen Ketten. • Jede Synonym-Kette hat also im Mittel n/m = Schlüssel. Wenn der i-te Schlüssel Ki in HT eingefügt wird, sind in jeder Kette ( i -1 ) / m Schlüssel. Die Suche nach Ki kostet also 1 + ( i -1 ) / m Schritte, da Ki an das jeweilige Ende einer Kette angehängt wird. Erwartungswert für erfolgreiche Suche: n Sn = 1/n * i=1 G.Heyer n-1 = 1 + ------- 1 + 2*m 2 i-1 1 + -----m 23 Algorithmen und Datenstrukturen Bei der erfolglosen Suche muss immer die ganze Kette durchlaufen werden. Die Kostenformel hat somit folgende Struktur: Un = 1 + 1 * WS ( zu einer Hausadresse existiert ein Überläufer) + 2 * WS (zu einer Hausadresse existieren zwei Überläufer) + 3 * ..... Un - e - 0.5 Sn 1.25 1.37 1.5 Un 1.11 1.22 1.37 1.72 G.Heyer 0.75 1 1.5 2 3 4 5 1.75 2 2.5 3 3.5 2.14 3.05 4.02 5.01 24 Algorithmen und Datenstrukturen Separate Kettung ist auch der „unabhängigen“ KollisionsAuflösung überlegen. Hashing ist i. a. ein sehr leistungsstarkes Verfahren. Selbst bei starker Überbelegung ( > 1 ) erhält man bei separater Kettung noch günstige Werte. G.Heyer 25 Algorithmen und Datenstrukturen Dynamische Hash-Verfahren Wachstumsprobleme bei statischen Verfahren • Statische Allokation von Speicherbereichen: Speicherausnutzung ? • Bei Erweiterung des Adressraumes: Rehashing ==> Kosten, Verfügbarkeit, Adressierbarkeit S A‘ A h h‘ ==> alle Sätze erhalten eine neue Adresse G.Heyer 26 Algorithmen und Datenstrukturen Entwurfsziele • Eine im Vergleich zu statischen Hashing dynamische Struktur erlaubt Wachstum und Schrumpfung des Hash-Bereichs ( Datei ) • keine Überlauftechniken • Zugriffsfaktor 2 für direkte Suche Viele konkurrierende Ansätze • Extendible Hashing ( Fagin et al., 1978 ) • Virtual Hashing und Linear Hashing ( Letwin, 1978, 1980 ) • Dynamic Hashing (Larson, 1978 ) G.Heyer 27 Algorithmen und Datenstrukturen Zusammenfassung Hash-Funktion • berechnet Speicheradresse des Satzes • zielt auf bestmögliche Gleichverteilung der Sätze im Hash-Bereich Hashing bietet im Vergleich zu Bäumen eine eingeschränkte Funktionalität • direkter Schlüsselzugriff • i. a. kein sortiert sequentieller Zugriff • ordnungserhaltendes Hashing nur in Sonderfällen anwendbar • statisches Verfahren G.Heyer 28 Algorithmen und Datenstrukturen Idealfall: Direkte Adressierung • nur in Ausnahmefällen möglich ( „dichte“ Schlüsselmenge) • jeder Satz kann mit einem Zugriff referenziert, eingefügt oder gelöscht werden Hash-Verfahren im Hauptspeicher • Standard: Divisions-Rest-Verfahren • bei offenen Hash-Verfahren ist der Belegungsgrad 0.85 dringend zu empfehlen • Kollisionsbehandlung mit separatem Überlaufbereich i. a. effizienter und einfacher zu realisieren G.Heyer 29 Algorithmen und Datenstrukturen Erweiterungen: dynamische Hashing-Verfahren • Reorganisationsfreiheit • Viele Vorschläge: Erweiterbares Hashing, Lineares Hashing, ... ( 2 Seitenzugriffe ) G.Heyer 30 Algorithmen und Datenstrukturen