Kapitel 6
Kapitel 6
Kapitel 6
Erfolgreiche ePaper selbst erstellen
Machen Sie aus Ihren PDF Publikationen ein blätterbares Flipbook mit unserer einzigartigen Google optimierten e-Paper Software.
<strong>Kapitel</strong> 6 <br />
HASHING <br />
Algorithmen & Datenstrukturen <br />
Prof. Dr. Wolfgang Schramm
Übersicht <br />
1 <br />
1. Einführung <br />
2. Algorithmen <br />
3. Eigenscha?en von <br />
Programmiersprachen <br />
4. Algorithmenparadigmen <br />
5. Suchen & SorGeren <br />
6. Hashing <br />
7. Komplexität von Algorithmen <br />
8. Abstrakte Datentypen (ADT) <br />
9. Listen <br />
10. Bäume <br />
11. Graphen
Lernziele des <strong>Kapitel</strong>s <br />
2 <br />
2<br />
¨ Kennenlernen von Hashing bzw. <br />
was die MoGvaGon für Hashing <br />
ist? <br />
¨ Verstehen wie Hashing <br />
funkGoniert. <br />
¨ Verstehen, was eine <br />
HashfunkGon ist. <br />
¨ Behandlung von Kollisionen <br />
beim Hashing verstehen. <br />
¨ Einsatzmöglichkeiten für <br />
Hashing kennenlernen.
Inhalt <br />
3 <br />
o<br />
o<br />
o<br />
o<br />
o<br />
o<br />
o<br />
o<br />
o<br />
o<br />
o<br />
Hashing <br />
NotaGonen <br />
HashfunkGon bzw. Streuwer_unkGon <br />
Hashtabelle <br />
Beispiele <br />
Kollisionen und Kollisionsstrategien <br />
Offenes bzw. Geschlossenes Hashing <br />
Komplexität <br />
Anwendungsgebiete von Hashing <br />
Java Hashtable-‐Klasse <br />
Programmierbeispiel
Hashing <br />
4 <br />
o<br />
o<br />
o<br />
Speichermethode <br />
¤<br />
¤<br />
bei großen Datenbanken <br />
beschleunigt das Finden von Daten <br />
Die Grundidee des Hashing-‐Verfahrens <br />
¤<br />
Hash-‐FunkGon: Schlüsselwert à Speicheradresse <br />
GrundoperaBonen <br />
¤<br />
¤<br />
¤<br />
Einfügen <br />
Löschen <br />
Suchen <br />
0<br />
1<br />
2<br />
3<br />
4<br />
5<br />
6<br />
7
Anwendungsgebiete <br />
5 <br />
o<br />
o<br />
o<br />
o<br />
Datenbanken <br />
¤<br />
Index für Tabellen <br />
à unter günsGgen Bedingungen <br />
„ideale“ Zugriffszeiten <br />
Compiler <br />
¤<br />
InterpretaGon von Symboltabellen <br />
Betriebssysteme <br />
¤<br />
ImplemenGerung von Seitentabellen <br />
SonsGge ApplikaGonen <br />
¤<br />
¤<br />
ImplemenGerung von Caches <br />
ImplemenGerung von Mengen
Hashing: DefiniGonen 1/6 <br />
6 <br />
o<br />
o<br />
o<br />
o<br />
U sei die Menge der möglichen Schlüssel. <br />
S ⊆ U sei die Menge der zu speichernden Schlüssel mit |S| = n. <br />
Ein Behälter (Bucket) kann ein mit einem Schlüssel zu idenGfizierendes <br />
Element aufnehmen. <br />
Eine Hashtabelle H ist eine Menge von nummerierten Behältern <br />
B 0 ,B 1 ,B 2 ,….B m-‐1 mit |H| = m. <br />
¤<br />
Anmerkung: <br />
häufig ist eine Hashtabelle ein Array und <br />
der Bucket ein Arrayplatz
7 <br />
¨<br />
Eine HashfunkBon ist eine ganzzahlige FunkGon <br />
Hashing: DefiniGonen 2/6 <br />
h : U →{0,...,<br />
h( u ) = a<br />
m −1}<br />
die einem Schlüssel u den Hashwert a zuordnet, der den Behälter B a <br />
bezeichnet. <br />
Anmerkung: bei Hasharray: statt Hashwert auch oft Hashindex
Beispiel Namen <br />
8 <br />
Schlüssel: mögliche Schlüssel U = [A-‐Z][a-‐z]* <br />
zu speichernde Schlüssel S = <br />
Hashtabelle: Array 0..7 of Integer Array 0..7 of List of Integer<br />
HashfunkGon: h(u) = <br />
Gelöst (1):<br />
h(u) = mod 8<br />
Zu lösen (1): Länge der Hash-Tabelle müsste unendlich sein.<br />
mod <br />
Zu lösen (2): „Eva“ und „Ann“<br />
haben gleich viele Buchstaben<br />
à „Kollision“<br />
Gelöst (2):<br />
Liste <br />
alternativ zu (2):<br />
falls a[i] besetzt,<br />
wähle a[i+1], usw.
Hashing: DefiniGonen 3/6 <br />
11 <br />
¨<br />
Anmerkung: <br />
¤ Eine HashfunkBon wird o? noGert als <br />
mit <br />
h : f ( u)<br />
mod m<br />
f (u)∈ Ν<br />
m = |H| <br />
H Hashtabelle<br />
|H| Länge der<br />
Hashtabelle<br />
u ganzzahliger<br />
Schlüssel<br />
d.h. <br />
<br />
<br />
f liefert eine „gut verteilte“ Abbildung auf N. <br />
Die modulo-‐OperaGon reduziert die Zahlen auf die Länge der Hash-‐<br />
Tabelle
Hashing: DefiniGonen 4/6 <br />
12 <br />
¨<br />
Die Schlüsseldichte ist das Verhältnis zu speichernde zu mögliche Schlüssel, <br />
d.h. <br />
S / U<br />
¨<br />
Der Belegungsfaktor ist das Verhältnis zu speichernde Schlüssel zu Anzahl <br />
der Behälter <br />
S /<br />
B<br />
U: mögliche Schlüssel<br />
S: zu speichernde Schlüssel<br />
B: Behälter
Hashing: DefiniGonen 5/6 <br />
14 <br />
o Der Füllgrad α ist das Verhältnis <br />
¤<br />
¤<br />
aktuell gespeicherte Schlüssel zu <br />
Länge der Hashtabelle, d.h. <br />
α = a / m<br />
mit<br />
m = |H| bzw. m = |B| <br />
a = Anzahl gespeicherter Schlüssel <br />
Anmerkung <br />
Offensichtlich gilt: <br />
je höher der Füllgrad, um so größer die Wahrscheinlichkeit, dass zwei Schlüssel <br />
auf den gleichen Hash-‐Wert abgebildet werden („Kollision“).
Beispiel 1/4 <br />
15 <br />
Aufgabe <br />
o<br />
Verteilung von Monatsnamen auf 17 <br />
Behälter 0..16 <br />
Lösung <br />
o<br />
Namen werden als Strings dargestellt <br />
→ Umwandlung in Zahlen notwendig <br />
¤<br />
¤<br />
nur Großbuchstaben <br />
f("A") = 1, f("B") = 2, usw. <br />
A 1<br />
B 2<br />
C 3<br />
D 4<br />
E 5<br />
F 6<br />
G 7<br />
H 8<br />
I 9<br />
J 10<br />
K 11<br />
L 12<br />
M 13<br />
N 14<br />
O 15<br />
P 16<br />
Q 17<br />
R 18<br />
S 19<br />
T 20<br />
U 21<br />
V 22<br />
W 23<br />
X 24<br />
Y 25<br />
Z 26
Beispiel 2/4 <br />
16 <br />
¨<br />
Als HashfunkGon nehmen wir <br />
¤<br />
¤<br />
f<br />
=∑<br />
( x) ( x)<br />
3<br />
= 〈〈 Summe der Ordinalzahl der ersten 3 Buchstaben von<br />
h(x) = f x<br />
( ) mod 17 = ( x ) 3<br />
! mod 17<br />
Beispiel: <br />
h(Februar) = (6+5+2) mod 17 = 13 mod 17 = 13 <br />
h(August) = (1+21+7) mod 17 = 29 mod 17 = 12 <br />
x〉〉
Beispiel 3/4 <br />
17 <br />
0 NOV 9 JUL<br />
1 APR , DEZ<br />
10<br />
2 MAE 11 JUN<br />
3 12 AUG<br />
4 13 FEB , OKT<br />
5 14<br />
6 MAI , SEP<br />
15<br />
7 16<br />
8 JAN<br />
• Etliche Buckets bleiben leer <br />
– Füllgrad α = Anteil der belegten Plätze in %, d.h. <br />
α = m / n mit m := Anzahl der Elemente <br />
• Es kann zu Kollisionen kommen! <br />
Es fehlen noch:<br />
SEP, OKT, DEZ<br />
A 1<br />
B 2<br />
C 3<br />
D 4<br />
E 5<br />
F 6<br />
G 7<br />
H 8<br />
I 9<br />
J 10<br />
K 11<br />
L 12<br />
M 13<br />
N 14<br />
O 15<br />
P 16<br />
Q 17<br />
R 18<br />
S 19<br />
T 20<br />
U 21<br />
V 22<br />
W 23<br />
X 24<br />
Y 25<br />
Z 26
Beispiel 4/4 <br />
18 <br />
APR, DEZ à 1 <br />
MAI, SEP à 6 <br />
FEB, OKT à 13 <br />
… mehrere Schlüssel werden auf denselben Behälter abgebildet. <br />
à Kollision <br />
è Auflösung<br />
l dem Behälter hinzufügen à Verketten (lineare Liste)<br />
l neuen Behälter suchen à Sondieren<br />
l vermeiden à perfektes Hashing
Wahrscheinlichkeit für Kollision<br />
19 <br />
P<br />
k<br />
= 1−<br />
mit 1 ≤ m ≤ n <br />
n⋅(<br />
n −1)<br />
⋅(<br />
n − 2) ⋅...<br />
⋅(<br />
n − m + 1)<br />
m<br />
n<br />
n ⋅ (n-‐1) ⋅ … ⋅ (n-‐m+1): Anzahl der Möglichkeiten, kollisionsfrei m Elemente <br />
zu verteilen <br />
n m : Anzahl m Elemente <br />
zu verteilen <br />
=<br />
n!<br />
( n − m)!<br />
n<br />
m<br />
¨<br />
Beispiele <br />
¤ Monatsnamen <br />
¤ „Geburtstage in Schulklassen“ <br />
n m P k<br />
17 12 0,99<br />
365 22 0,48<br />
365 23 0,51<br />
365 50 0,97
Hashing: DefiniGonen 6/6 <br />
20 <br />
¨<br />
Eine Kollision tri auf, wenn zwei Schlüssel auf den gleichen Hashwert <br />
abgebildet werden: <br />
h( a ) = h( b)<br />
mit <br />
a ≠ b<br />
.
Eigenscha?en einer HashfunkGon <br />
21 <br />
o<br />
o<br />
o<br />
surjekBv <br />
¤<br />
d.h. alle Behälter sollten erfasst werden. <br />
gleichverteilend <br />
¤<br />
d.h. jeder Behälter sollte mit gleicher Wahrscheinlichkeit getroffen <br />
werden. <br />
einfach <br />
¤<br />
d.h. sie sollte mit minimalen Aufwand berechenbar sein.
Kollisionsstrategien <br />
22 <br />
o<br />
VerkeSen <br />
o<br />
Sondieren <br />
¤<br />
¤<br />
Lineares Sondieren <br />
QuadraGsches Sondieren
Offenes bzw. geschlossenes Hashing <br />
23 <br />
Problem : Was passiert wenn Anzahl Schlüssel > Anzahl Speicherplätze <br />
Lösung : <br />
1. Offenes Hashing:<br />
manchmal auch als<br />
geschlossen bzgl. der<br />
Indexpositionen bezeichnet<br />
!<br />
Jeder Behälter kann beliebig viele Elemente aufnehmen. Für jeden Behälter wird <br />
eine verkeete Liste angelegt, in die alle Schlüssel eingefügt werden, die auf <br />
diesen Behälter abgebildet werden. <br />
2. Geschlossenes Hashing:<br />
manchmal auch als<br />
offen bzgl. der<br />
Indexpositionen bezeichnet<br />
Hier darf jeder Behälter nur eine Konstante Anzahl b ≥ 1 von Schlüsseln <br />
aufnehmen.<br />
!
Offenes Hashing: Verkeen <br />
24 <br />
…<br />
…<br />
3<br />
4<br />
5<br />
…<br />
…<br />
Maerz<br />
Januar<br />
April<br />
Dezember<br />
o<br />
o<br />
o<br />
Ein Behälter kann mehr als ein Element fassen <br />
Alle Schlüssel s mit h(s) = a werden in B a abgelegt <br />
als lineare Liste <br />
Gefahr: Entartung zur linearen Liste à Zugriffszeit wächst rapide
Geschlossenes Hashing: Lineares Sondieren 1/3 <br />
25 <br />
o<br />
o<br />
Pro Behälter ein Schlüssel <br />
Bei Kollision <br />
¤<br />
Linear in einer Richtung nächsten freien Behälter suchen <br />
o<br />
o<br />
Formal <br />
h i<br />
( h(<br />
x)<br />
i) mod m<br />
( x)<br />
= +<br />
i=0;<br />
while (occupied(h i (x)) do<br />
i++;<br />
od;<br />
// hash-key is h i (x) <br />
Gefahr: Folge von besetzten Feldern vergrößert sich (Verklumpung) <br />
à Kollisionswahrscheinlichkeit steigt.
26 <br />
Geschlossenes Hashing: Lineares Sondieren 2/3
Geschlossenes Hashing: Lineares Sondieren 3/3 <br />
27 <br />
o<br />
Varianten <br />
1. Linear in einer Richtung <br />
den nächsten freien Behälter suchen, <br />
mit Sprüngen der Länge c <br />
Beispiel <br />
c = 7; h(a) = 27 <br />
falls 27 besetzt, … 27 + 7 = 34 mod m <br />
falls 34 besetzt, … 34 + 7 = 41 mod m etc. <br />
2. Linear in beiden Richtungen (alternierend) <br />
Beispiel <br />
h(a) = 27 <br />
falls 27 besetzt, … 27 – 1*7 = 20 mod m <br />
falls 20 besetzt, … 20 + 2*7 = 34 mod m <br />
falls 34 besetzt, … 34 – 3*7 = 13 mod m etc.
28 <br />
¨<br />
Geschlossenes Hashing: QuadraGsches Sondieren <br />
Wie lineares Sondieren, jedoch <br />
¤ Schriweite quadraGsch (nicht linear) / alternierend <br />
Beispiel <br />
h(a) = 27 <br />
falls besetzt, … 27 + 1 2 = 28 mod m <br />
27 – 1 2 = 26 mod m <br />
27 + 2 2 = 31 mod m <br />
27 – 2 2 = 23 mod m <br />
27 + 3 2 = 36 mod m etc. <br />
¨<br />
Formal: <br />
h ( x)<br />
i<br />
=<br />
⎛⎛<br />
⎜⎜h(<br />
x)<br />
+<br />
⎜⎜<br />
⎝⎝<br />
( −1)<br />
i+<br />
1<br />
⎡⎡ i ⎤⎤<br />
.<br />
⎢⎢2⎥⎥<br />
2<br />
⎞⎞<br />
⎟⎟<br />
⎟⎟<br />
⎠⎠<br />
mod m
29 <br />
Liste: kein Problem <br />
Sondieren <br />
¤<br />
¤<br />
Element kann nicht <br />
einfach gelöscht <br />
werden, da sonst die <br />
Kee unterbrochen <br />
wäre. <br />
Bsp.: FEB verursacht <br />
Lücke, OKT wird nicht <br />
gefunden. <br />
Mögliche Lösungen <br />
¤<br />
Element wird nicht <br />
gelöscht, sondern nur <br />
zum Überschreiben <br />
markiert. <br />
Löschen von Elementen <br />
0 NOV 9 JUL<br />
1 APR 10<br />
2 MAE Lineares 11 Sondieren JUN<br />
3 DEZ 12 AUG<br />
4 13 FEB<br />
5 14 OKT<br />
6 MAI 15<br />
7 SEP 16<br />
8 JAN !<br />
Prof. Dr. M. Gumbel • WS09 • ADS: Hashing<br />
Folie 29
Komplexität 1/2 <br />
30 <br />
Größe der Hashtabelle: N <br />
o<br />
o<br />
Aufwand im besten Fall <br />
¤<br />
Berechnung des Hashwertes unabhängig von n: <br />
O(1) <br />
Aufwand im schlechtesten Fall <br />
¤<br />
Ganze Hashtabelle muss durchsucht werden <br />
O(N) <br />
(Hashtabelle wg. Kollision zu lin. Liste entartet)
Komplexität 2/2 <br />
31 <br />
Größe der Hashtabelle: N <br />
o Aufwand im mileren Fall (bei sondieren) <br />
¤ Wahrscheinlichkeit für Behälter j: 1/N <br />
¤ Wahrscheinlichkeit einer Kollision: <br />
n abhängig vom Füllgrad α <br />
n Wahrscheinlichkeit für „Behälter belegt“: α <br />
n …nächster Behälter belegt: α 2 <br />
n … übernächstes Bucket belegt: α 3 etc <br />
¤ erfolgloses Suchen: 1 + α + α 2 + α 3 + ... = <br />
1 ⎛⎛ 1 ⎞⎞<br />
¤ erfolgreiches Einfügen: ln⎜⎜<br />
⎟⎟<br />
α ⎝⎝1−α<br />
⎠⎠<br />
1<br />
1−α
32 <br />
Prof. Dr. M. Gumbel • WS09 • ADS: Hashing<br />
Komplexität: Übersicht <br />
Operation Fall Liste oder Sondieren<br />
add bester O(1)<br />
durchschnittlich<br />
⎛⎛ 1 ⎞⎞<br />
O⎜⎜<br />
⎟⎟<br />
⎝⎝1−α<br />
⎠⎠<br />
schlechtester<br />
O(m)<br />
contains bester O(1)<br />
durchschnittlich<br />
⎛⎛ 1 1 ⎞⎞<br />
O⎜⎜<br />
ln ⎟⎟ /<br />
erfolgreich/-los ⎝⎝α 1−α ⎠⎠<br />
schlechtester<br />
O(m)<br />
remove bester O(1)<br />
durchschnittlich<br />
⎛⎛ 1 1 ⎞⎞<br />
O⎜⎜<br />
ln ⎟⎟<br />
⎝⎝α 1−α ⎠⎠<br />
schlechtester<br />
O(m)<br />
⎛⎛ 1 ⎞⎞<br />
O⎜⎜<br />
⎟⎟<br />
⎝⎝1−α<br />
⎠⎠<br />
Folie 32
Optimaler Füllgrad α<br />
33 <br />
¨ Ab Füllgrad von ca. 80 % ist das Verhalten schlecht. <br />
Erfolgreiche Suche<br />
Erfolglose Suche,<br />
Einfügen, Löschen<br />
1 1<br />
ln<br />
α 1−α<br />
1<br />
1−α<br />
Prof. Dr. M. Gumbel • WS09 • ADS: Hashing<br />
Folie 33
Rehashing<br />
34 <br />
¨ Füllgrad zu groß oder das Array voll: à Rehashing <br />
¤ Array wird vergrößert und <br />
¤ alle Elemente werden neu eingefügt. <br />
¨ Vorteil: Zum Löschen markierte Elemente können ebenfalls <br />
freigegeben werden. <br />
¨ Sog. dynamisches Hashen passt Arraygröße automaGsch an. <br />
Prof. Dr. M. Gumbel • WS09 • ADS: Hashing<br />
Folie 34
Java Hashtable-‐Klasse <br />
35 <br />
Für die ImplementaGon von Hashtabellen steht uns in Java unmielbar die <br />
Klasse Hashtable zur Verfügung. Sie hat die folgenden Methoden: <br />
Hashtable(): der Konstruktor für die DefiniGon einer leeren Hashtabelle <br />
elements(): Rückgabe aller Daten aus der Hashtabelle <br />
isEmpty(): Abfrage, ob die Hashtabelle leer ist <br />
get(): liefert Element gemäß Schlüsselwertangabe
Java Hashtable-‐Klasse <br />
36 <br />
keys(): gibt alle (belegten) Schlüsselwerte der Hashtabelle zurück <br />
put(): speichert Element gemäß Schlüsselwert in Hashtabelle <br />
remove(): en_ernt referenziertes Hashtabellenelement <br />
size(): gibt Anzahl gespeicherter Elemente in der Hashtabelle zurück <br />
clear(): en_ernt alle Schlüssel und die Elemente aus der Hashtabelle
Java Hashtable-‐Klasse <br />
37 <br />
contains(): prü?, ob sich ein Element in der Hashtabelle befindet <br />
containsKey(): prü?, ob sich ein Schlüsselwert in der Hashtabelle befindet <br />
clone(): erzeugt einen Klone einer Hashtabelle <br />
toString(): generiert eine String-‐RepräsentaGon einer Hashtabelle <br />
rehash(): führt das Rehashing für eine Hashtabelle durch.
38 <br />
Programmierbeispiel
39 <br />
Programmierbeispiel