Hashing 2006 Jiri Spale, Algorithmen und Datenstrukturen - Hashing 1 Dünn besetzte Mengen Beispiel: Wörterbuch Grundmenge (Kombinationen und Permutationen des gesamten Alphabets) riesig: |G| = 261 + 262 + ... 2610 ~ 1014 Lebende Sprachen: ca. 1 Mio Wörter => nur jeder 100 000 000. Eintrag wird genutzt Wanted: Effektive Datenstruktur für solche Datenbestände 2006 Jiri Spale, Algorithmen und Datenstrukturen - Hashing 2 Datenstruktur mit Buckets 0 1 hash(Wort) i Wort1 Wort2 Wort3 BUCKETS-1 2006 Jiri Spale, Algorithmen und Datenstrukturen - Hashing 3 Buckets, Hashfunktion #define BUCKET 5 typedef typedef struct cell *LIST; struct cell { STRING element; LIST next; } CELL; typedef HASHTABLE ...... typedef char LIST int { HASHTABLE[BUCKET]; headers; STRING[10]; hash(STRING x) int i, sum = 0; for (i = 0; i < 10; i++) sum = sum + x[i]; return (sum % BUCKET); } 2006 Jiri Spale, Algorithmen und Datenstrukturen - Hashing 4 Beispiel Wort Summe Bucket 778 692 471 385 808 558 648 3 2 1 0 3 3 3 (10 Zeichen-Strings; Rest mit Leerzeichen gefüllt) anyone lived in a pretty how town 2006 Jiri Spale, Algorithmen und Datenstrukturen - Hashing 5 Beispiel (Fortsetzung) 0 1 2 3 4 a 0 in 0 lived 0 anyone pretty how town 0 Ablegen/Suchen im Feld von Bucket ... schnell Durchgehen von Listen ~ der Anzahl der Elemente i.d. jeweiligen Liste Schnelles Ablegen/Suchen => möglichst kurze Listen (optimal: 1-Element-Listen) Dazu Voraussetzungen: 1. Anzahl Buckets ≥ Anzahl der zu speichernden Worte 2. Qualitäts-Hash-Funktion 3. Mechanismus zur Behandlung von Kollisionen 2006 Jiri Spale, Algorithmen und Datenstrukturen - Hashing 6 Hash-Verfahren • Suchaufwand völlig unabhängig von der Größe des Datenvolumens • die den Hash-Verfahren zugrunde liegende Ordnung ≈ Chaos • Nachteil: es entstehen Kollisionen, diese kann man nicht ausschließen Arten von Hash-Verfahren: 1. Statische Hash-Verfahren - Buckets gespeichert im Feld - Art der verwendeten Hash-Funktion liegt fest 2. Dynamische Hash-Verfahren - Buckets gespeichert in Liste - eine Klasse aufeinander abgestimmter Hash-Funktionen wird verwendet Anforderungen an die Hash-Funktion • schnell berechenbar • Anzahl Kollisionen minimal 2006 Jiri Spale, Algorithmen und Datenstrukturen - Hashing 7 Statische Hash-Verfahren#1 Beispiele von Hash-Funktionen: • Divisionsmethode H(k) = k % m oder H(k) = k % m + 1, m ... Primzahl • Mittquadratmethode Der Schlüssel k wird quadriert, vorne und hinten werden vom Quadrat von k Stellen abgeschnitten; das Ergebnis wird als Adresse verwandt. • Zerlegungsmethode Der Schlüssel wird in verschiedene Teile zerlegt, z.B. in Ziffernpaare bei numerischen Schlüsseln; diese Teile werden aufaddiert und ergeben nach weiteren numerischen Behandlungen den Adresswert • Ziffernanalyse Bei numerischen Schlüsseln mit vielen Stellen werden beim Zusammenbau der Adresse nur die Stellen berücksichtigt, die in etwa eine Gleichverteilung der einzelnen Ziffern aufweisen. Aus diesen Stellen wird wieder mit einfachen arithmetischen Operationen der Adresswert zusammengebaut. • Quersumme Berechnung der Quersumme eines Schlüssels als Adresswert. • Bitfummelei Jiri Spale,wenn Algorithmen Datenstrukturen Alles2006 ist erlaubt, es nurundschnell geht. - Hashing 8 Statische Hash-Verfahren#2 Beispiele von Kollisionsalgorithmen: • Gestreute Verkettung (Listenköpfe in der Bucket-Tabelle) • Lineares Sondieren (linear probing) adr = (adr + n) % prim n ... Kollisionszähler • Quadratisches Sondieren adr = (adr + n * n) % prim n ... Kollisionszähler • Rehashing Die Adresse wird mit einer anderen Hashfunktion errechnet. Bsp. h(0)=h1('KEY')=452 i=h2 ('KEY')=233 h(i)=(452+233) % Tablänge 2006 Jiri Spale, Algorithmen und Datenstrukturen - Hashing 9 Dynamische Hash-Verfahren Vorteile • Es gibt keine obere Grenze für das Datenvolumen • Einträge können ohne Probleme gelöscht werden • Adresskollisionen führen nicht zur Clusterbildung. Als Nachteile bleiben bestehen: Nicht möglich: • effektives Durchlaufen der Einträge nach einer Ordnung • effektive Suche nach dem Eintrag mit dem kleinsten oder größten Schlüssel Verbesserungsmöglichkeit bei zu langen Listen: • Die Anzahl der Vektorelemente (der Listenköpfe) wird erhöht, z.B. verdoppelt • eine neue Hash-Funktion nehmen, die der größeren Anzahl der Listenköpfe entspricht. • Der Datenbestand wird während seiner Benutzung reorganisiert, um die lange Totzeit bei einer vollständigen Reorganisation zu vermeiden. 2006 Jiri Spale, Algorithmen und Datenstrukturen - Hashing 10 Wirksamkeit von Hashing Anzahl Zugriffe n 5 5 4 3 2.5 2 1.5 1.06 1.17 1 10 30 50 70 90 100 > Auslastung in % % Füllfaktor in Füllgrad (Füllfaktor, LoadAbb.48 Factor) = Anzahl belegter Plätze : Anzahl aller Plätze Fazit: Falls >25% Plätze frei sind, steigt die Anzahl Zugriffe mit der Auslastung nur langsam => soviel freier Platz sollen wir immer zur Verfügung haben. 2006 Jiri Spale, Algorithmen und Datenstrukturen - Hashing 11