Algorithmen und Datenstrukturen II Peter Steffen AG Praktische Informatik Technische Fakultät Universität Bielefeld Vorlesung Sommer 2009 Peter Steffen A&D II, Vorlesung 2009 Universität Bielefeld Einführendes Beispiel Allgemeine Definitionen Strategien zur Behandlung von Kollisionen Klasse Hashtable Teil I Hashing Peter Steffen A&D II, Vorlesung 2009 Universität Bielefeld Einführendes Beispiel Allgemeine Definitionen Strategien zur Behandlung von Kollisionen Klasse Hashtable Einführendes Beispiel Ein Pizza-Lieferservice in Bielefeld speichert die Daten seiner Kunden: Name, Vorname, Adresse und Telefonnummer. Wenn ein Kunde seine Bestellung telefonisch aufgibt, um dann mit der Pizza beliefert zu werden, dann muss er seine Telefonnummer angeben, da er über diese Nummer eindeutig identifiziert werden kann. Peter Steffen A&D II, Vorlesung 2009 Universität Bielefeld Einführendes Beispiel Allgemeine Definitionen Telefonnummer 00000000 00000001 00000002 ... 99999997 99999998 99999999 Peter Steffen A&D II, Vorlesung 2009 Name Müller Schmidt Schultz ... Meier Neumann Schröder Strategien zur Behandlung von Kollisionen Vorname Heinz Werner Hans ... Franz Herbert Georg PLZ 33615 33615 33602 ... 33609 33612 33647 Klasse Hashtable Straße Unistraße 15 Grünweg 1 Arndtstraße 12 ... Kirchweg 4 Jägerallee 15 Mühlweg 2 Universität Bielefeld Einführendes Beispiel Allgemeine Definitionen Strategien zur Behandlung von Kollisionen Klasse Hashtable Bielefeld hat ca. 300.000 Einwohner, dann gibt es vielleicht 200.000 Telefonnummern. Davon bestellt jeder fünfte eine Pizza – bleiben 40.000 potentielle Einträge, verteilt auf mehrere Pizza-Lieferservices. Optimistisch geschätzt wird unsere Pizzeria also ca. 10.000 Kunden haben. Peter Steffen A&D II, Vorlesung 2009 Universität Bielefeld Einführendes Beispiel Allgemeine Definitionen Strategien zur Behandlung von Kollisionen Klasse Hashtable Da stellt sich folgende Frage: Wir wissen doch gar nicht, welche Telefonnummern bestellen werden – wie sollen denn dann die Zeilen benannt werden? Unsere Aufgabe ist es, alle 100 Millionen Telefonnummern (denn jede einzelne könnte ja theoretisch bestellen) so abzubilden, dass sie in eine 10.000 Zeilen große Tabelle passen. Peter Steffen A&D II, Vorlesung 2009 Universität Bielefeld Einführendes Beispiel Allgemeine Definitionen Strategien zur Behandlung von Kollisionen Klasse Hashtable Modulo Hierzu machen wir uns jetzt eine mathematische Operation zunutze, die Modulo-Operation: x mod y liefert als Ergebnis den Rest der ganzzahligen Division x/y . Beispielsweise ergibt 117 mod 20 = 17, da 117 = 5 · 20 + 17. Peter Steffen A&D II, Vorlesung 2009 Universität Bielefeld Einführendes Beispiel Allgemeine Definitionen Strategien zur Behandlung von Kollisionen Klasse Hashtable Hash-Funktion Beispiel h(Telefonnummer) = Telefonnummer mod Tabellenlänge Peter Steffen A&D II, Vorlesung 2009 Universität Bielefeld Einführendes Beispiel Allgemeine Definitionen Strategien zur Behandlung von Kollisionen Klasse Hashtable Hash-Funktion Beispiel h(Telefonnummer) = Telefonnummer mod Tabellenlänge oder allgemein: Beispiel h(k) = k mod m mit h für Hashfunktion, k für key und m für Tabellenlänge. Peter Steffen A&D II, Vorlesung 2009 Universität Bielefeld Einführendes Beispiel Allgemeine Definitionen Strategien zur Behandlung von Kollisionen Klasse Hashtable Kollisionen Wir benutzen also diese Hashfunktion, um jedem Schlüssel einen Index (hier eine Zahl zwischen 0 und 9999) in einer verkleinerten Tabelle (der sogenannten Hashtabelle) zuzuordnen und damit eine Menge Platz zu sparen. Leider kann es allerdings passieren, dass in ungünstigen Fällen zwei oder mehr Schlüssel (Telefonnummern) auf denselben Index in der Hashtabelle abgebildet werden, Beispiel z.B. ist 01063852 mod 10000 = 08153852 mod 10000 = 3852. Peter Steffen A&D II, Vorlesung 2009 Universität Bielefeld Einführendes Beispiel Allgemeine Definitionen Strategien zur Behandlung von Kollisionen Klasse Hashtable Allgemeine Definitionen Formal gesehen ist Hashing ein abstrakter Datentyp, der die Operationen insert, delete und search auf (dynamischen) Mengen effizient unterstützt. Peter Steffen A&D II, Vorlesung 2009 Universität Bielefeld Einführendes Beispiel Allgemeine Definitionen Strategien zur Behandlung von Kollisionen Klasse Hashtable Direkte Adressierung Hashing ist im Durchschnitt sehr effizient – unter vernünftigen Bedingungen werden obige Operationen in O(1) Zeit ausgeführt (im worst-case kann search O(n) Zeit benötigen). Wenn die Menge U aller Schlüssel relativ klein ist, können wir sie injektiv auf ein Feld abbilden; dies nennt man direkte Adressierung Peter Steffen A&D II, Vorlesung 2009 Universität Bielefeld Einführendes Beispiel Allgemeine Definitionen Strategien zur Behandlung von Kollisionen Klasse Hashtable Direkte Adressierung 0 U (Universum der Schl"ussel) 0 9 6 2 7 4 1 1 3 4 K (Aktuelle Schl"ussel) 2 5 3 6 5 7 8 8 9 T Peter Steffen A&D II, Vorlesung 2009 Universität Bielefeld Einführendes Beispiel Allgemeine Definitionen Strategien zur Behandlung von Kollisionen Klasse Hashtable Hashing Ist die Menge U aller Schlüssel aber sehr groß (wie im obigen Beispiel des Pizza-Services), so können wir nicht mehr direkt adressieren. Unter der Voraussetzung, dass die Menge K aller Schlüssel, die tatsächlich gespeichert werden, relativ klein ist gegenüber U, kann man die Schüssel effizient in einer Hashtabelle abspeichern. Dazu verwendet man allgemein eine Hashfunktion Definition h : U → {0, 1, . . . , m − 1}, die Schlüssel abbildet auf Werte zwischen 0 und m − 1 (dabei ist m die Größe der Hashtabelle). Peter Steffen A&D II, Vorlesung 2009 Universität Bielefeld Einführendes Beispiel Allgemeine Definitionen Strategien zur Behandlung von Kollisionen Klasse Hashtable Hashing 0 U h(k1) (Universum der Schl"ussel) h(k4) k1 K (Aktuelle Schl"ussel) h(k2) = h(k5) k4 k2 h(k3) k5 k3 m−1 T Peter Steffen A&D II, Vorlesung 2009 Universität Bielefeld Einführendes Beispiel Allgemeine Definitionen Strategien zur Behandlung von Kollisionen Klasse Hashtable Typische Hashfunktion Eine typische Hashfunktion h für U = N ist Beispiel h(k) = k mod m. Peter Steffen A&D II, Vorlesung 2009 Universität Bielefeld Einführendes Beispiel Allgemeine Definitionen Strategien zur Behandlung von Kollisionen Klasse Hashtable Kollisionen da Hashfunktionen nicht injektiv sind, tritt das Problem der Kollision auf: zwei Schlüsseln wird der gleiche Platz in der Hashtabelle zugewiesen. Kollisionen sind natürlich unvermeidbar, jedoch wird eine “gute”Hashfunktion h die Anzahl der Kollisionen gering halten. D.h. h muss die Schlüssel gleichmäßig auf die Hashtabelle verteilen. Außerdem sollte h einfach zu berechnen sein. Peter Steffen A&D II, Vorlesung 2009 Universität Bielefeld Einführendes Beispiel Allgemeine Definitionen Strategien zur Behandlung von Kollisionen Klasse Hashtable Strategien zur Behandlung von Kollisionen Peter Steffen A&D II, Vorlesung 2009 Universität Bielefeld Einführendes Beispiel Allgemeine Definitionen Strategien zur Behandlung von Kollisionen Klasse Hashtable Direkte Verkettung k4 k4 k2 k2 k5 k5 Peter Steffen A&D II, Vorlesung 2009 Universität Bielefeld Einführendes Beispiel Allgemeine Definitionen Strategien zur Behandlung von Kollisionen Klasse Hashtable worst-case Laufzeiten insert: O(1) (neues Element vor die Liste hängen) search: O(n) (n = Länge der Liste) delete: O(1) (vorausgesetzt, man verwendet doppelt verkettete Listen und hat das Element schon gefunden) Peter Steffen A&D II, Vorlesung 2009 Universität Bielefeld Einführendes Beispiel Allgemeine Definitionen Strategien zur Behandlung von Kollisionen Klasse Hashtable Open Hashing Beim Open Hashing werden alle Einträge in der Hashtabelle gehalten. Ist eine Komponente der Tabelle schon belegt, so wird ein freier Platz für einen weiteren Eintrag gesucht. Peter Steffen A&D II, Vorlesung 2009 Universität Bielefeld Einführendes Beispiel Allgemeine Definitionen Strategien zur Behandlung von Kollisionen Klasse Hashtable Open Hashing, lineare Verschiebung k4 k2 k5 Peter Steffen A&D II, Vorlesung 2009 Universität Bielefeld Einführendes Beispiel Allgemeine Definitionen Strategien zur Behandlung von Kollisionen Klasse Hashtable Lineare Verschiebung Falls h(k) bereits durch einen anderen Schlüssel besetzt ist, wird versucht, k in den Adressen h(k) + 1, h(k) + 2, . . . unterzubringen Präziser gesagt, wird folgende Hashfunktion verwendet: h0 (k, i) = h(k) + i mod m mit i = 0, 1, 2, . . . , m − 1. Peter Steffen A&D II, Vorlesung 2009 Universität Bielefeld Einführendes Beispiel Allgemeine Definitionen Strategien zur Behandlung von Kollisionen Klasse Hashtable Hash-Insert mit linearer Verschiebung Hash-Insert(T , k) 1 i ←0 2 repeat 3 j ← h0 (k, i) 4 if T [j] = NIL then 5 T [j] ← k 6 return j 7 i ←i +1 8 until i = m 9 error “hash table overflow” Peter Steffen A&D II, Vorlesung 2009 Universität Bielefeld Einführendes Beispiel Allgemeine Definitionen Strategien zur Behandlung von Kollisionen Klasse Hashtable Hash-Search mit linearer Verschiebung Hash-Search(T , k) 1 i ←0 2 repeat 3 j ← h0 (k, i) 4 if T [j] = k then 5 return j 6 i ←i +1 7 until T [j] = NIL or i = m 8 error NIL Peter Steffen A&D II, Vorlesung 2009 Universität Bielefeld Einführendes Beispiel Allgemeine Definitionen Strategien zur Behandlung von Kollisionen Klasse Hashtable Quadratische Verschiebung Es wird die Hashfunktion h0 (k, i) = (h(k) + c1 i + c2 i 2 ) mod m mit i = 0, 1, 2, . . . , m − 1 verwendet. Dabei sind c1 , c2 ∈ N und c2 6= 0 geeignete Konstanten (s. Cormen et al. [2]). Peter Steffen A&D II, Vorlesung 2009 Universität Bielefeld Einführendes Beispiel Allgemeine Definitionen Strategien zur Behandlung von Kollisionen Klasse Hashtable Double Hashing Die Hashfunktion h0 wird definiert durch h0 (k, i) = (h1 (k) + i · h2 (k)) mod m mit i = 0, 1, 2, . . . , m − 1, wobei h1 und h2 Hashfunktionen sind. Die Verschiebung wird dabei durch eine zweite Hashfunktion realisiert. D.h. es wird zweimal, also doppelt, gehasht. Peter Steffen A&D II, Vorlesung 2009 Universität Bielefeld Einführendes Beispiel Allgemeine Definitionen Strategien zur Behandlung von Kollisionen Klasse Hashtable Beispiel lineare Verschiebung 0 1 0 1 0 insert 11 2 - 1 2 0 1 11 0 insert 10 - 1 2 3 3 3 4 4 4 Peter Steffen A&D II, Vorlesung 2009 0 1 11 10 0 delete 1 - 1 2 3 0 d 11 10 search 10 - 4 Universität Bielefeld Einführendes Beispiel Allgemeine Definitionen Strategien zur Behandlung von Kollisionen Klasse Hashtable Man erkennt: Gelöschte Felder müssen markiert werden, so dass ein Suchalgorithmus nicht abbricht, obwohl das Element doch in der Liste gewesen wäre. Natürlich kann in die gelöschten Felder wieder etwas eingefügt werden. Dieses Problem muss in der obigen Implementierung zusätzlich berücksichtigt werden. Peter Steffen A&D II, Vorlesung 2009 Universität Bielefeld Einführendes Beispiel Allgemeine Definitionen Strategien zur Behandlung von Kollisionen Klasse Hashtable Ladefaktor n Der Ladefaktor α für eine Hashtabelle T ist definiert als m , wobei n die Anzahl der gespeicherten Schlüssel und m die Kapazität der Tabelle sind. Peter Steffen A&D II, Vorlesung 2009 Universität Bielefeld Einführendes Beispiel Allgemeine Definitionen Strategien zur Behandlung von Kollisionen Klasse Hashtable Ladefaktor n Der Ladefaktor α für eine Hashtabelle T ist definiert als m , wobei n die Anzahl der gespeicherten Schlüssel und m die Kapazität der Tabelle sind. Theoretische Untersuchungen und praktische Messungen haben ergeben, dass der Ladefaktor einer Hashtabelle den Wert 0.8 nicht überschreiten sollte (d.h. die Hashtabelle darf höchstens zu 80% gefüllt werden). Ist der Ladefaktor ≤ 0.8, so treten beim Suchen im Durchschnitt ≤ 3 Kollisionen auf. Bei einem höheren Ladefaktor steigt die Zahl der Kollisionen rasch an. Peter Steffen A&D II, Vorlesung 2009 Universität Bielefeld Einführendes Beispiel Allgemeine Definitionen Strategien zur Behandlung von Kollisionen Klasse Hashtable Die Klasse Hashtable in Java Die Klasse java.util.Hashtable implementiert alle Methoden der abstrakten Klasse java.util.Dictionary. Außerdem enthält Hashtable noch folgende Methoden Peter Steffen A&D II, Vorlesung 2009 Universität Bielefeld Einführendes Beispiel Allgemeine Definitionen Strategien zur Behandlung von Kollisionen Klasse Hashtable public synchronized boolean containsKey(Object key) Es wird true zurückgegeben gdw. die Hashtabelle ein Element unter key verzeichnet hat. Peter Steffen A&D II, Vorlesung 2009 Universität Bielefeld Einführendes Beispiel Allgemeine Definitionen Strategien zur Behandlung von Kollisionen Klasse Hashtable public synchronized boolean containsKey(Object key) Es wird true zurückgegeben gdw. die Hashtabelle ein Element unter key verzeichnet hat. public synchronized boolean contains(Object element) Gdw. das angegebene element ein Element der Hashtabelle ist, wird true zurückgegeben. Diese Operation ist teurer als die containsKey-Methode, da Hashtabellen nur beim Suchen nach Schlüsseln effizient sind. Peter Steffen A&D II, Vorlesung 2009 Universität Bielefeld Einführendes Beispiel Allgemeine Definitionen Strategien zur Behandlung von Kollisionen Klasse Hashtable public synchronized boolean containsKey(Object key) Es wird true zurückgegeben gdw. die Hashtabelle ein Element unter key verzeichnet hat. public synchronized boolean contains(Object element) Gdw. das angegebene element ein Element der Hashtabelle ist, wird true zurückgegeben. Diese Operation ist teurer als die containsKey-Methode, da Hashtabellen nur beim Suchen nach Schlüsseln effizient sind. public synchronized void clear() Alle Schlüssel in der Hashtabelle werden gelöscht. Wenn es keine Referenzen mehr auf die Elemente gibt, werden sie vom Garbage-Collector aus dem Speicher entfernt. Peter Steffen A&D II, Vorlesung 2009 Universität Bielefeld Einführendes Beispiel Allgemeine Definitionen Strategien zur Behandlung von Kollisionen Klasse Hashtable public synchronized boolean containsKey(Object key) Es wird true zurückgegeben gdw. die Hashtabelle ein Element unter key verzeichnet hat. public synchronized boolean contains(Object element) Gdw. das angegebene element ein Element der Hashtabelle ist, wird true zurückgegeben. Diese Operation ist teurer als die containsKey-Methode, da Hashtabellen nur beim Suchen nach Schlüsseln effizient sind. public synchronized void clear() Alle Schlüssel in der Hashtabelle werden gelöscht. Wenn es keine Referenzen mehr auf die Elemente gibt, werden sie vom Garbage-Collector aus dem Speicher entfernt. public synchronized Object clone() Es wird ein Klon der Hashtabelle erzeugt. Die Elemente und Schlüssel selbst werden aber nicht geklont. Peter Steffen A&D II, Vorlesung 2009 Universität Bielefeld Einführendes Beispiel Allgemeine Definitionen Strategien zur Behandlung von Kollisionen Klasse Hashtable Konstruktoren der Klasse Hashtable public Hashtable() Es wird eine neue, leere Hashtabelle mit einer voreingestellten Anfangskapazität von 11 und einem Ladefaktor von 0.75 erzeugt. Peter Steffen A&D II, Vorlesung 2009 Universität Bielefeld Einführendes Beispiel Allgemeine Definitionen Strategien zur Behandlung von Kollisionen Klasse Hashtable Konstruktoren der Klasse Hashtable public Hashtable() Es wird eine neue, leere Hashtabelle mit einer voreingestellten Anfangskapazität von 11 und einem Ladefaktor von 0.75 erzeugt. public Hashtable(int initialCapacity) Eine neue, leere Hashtabelle mit der Anfangskapazität initialCapacity und dem Ladefaktor 0.75 wird generiert. Peter Steffen A&D II, Vorlesung 2009 Universität Bielefeld Einführendes Beispiel Allgemeine Definitionen Strategien zur Behandlung von Kollisionen Klasse Hashtable Konstruktoren der Klasse Hashtable public Hashtable() Es wird eine neue, leere Hashtabelle mit einer voreingestellten Anfangskapazität von 11 und einem Ladefaktor von 0.75 erzeugt. public Hashtable(int initialCapacity) Eine neue, leere Hashtabelle mit der Anfangskapazität initialCapacity und dem Ladefaktor 0.75 wird generiert. public Hashtable(int initialCapacity, float loadFactor) Es wird eine neue, leere Hastabelle erzeugt, die eine Anfangskapazität der Größe initialCapacity und einen Ladefaktor von loadFactor besitzt. Der Ladefaktor ist eine Zahl zwischen 0.0 und 1.0 und definiert den Beginn eines rehashings der Tabelle in eine größere. Peter Steffen A&D II, Vorlesung 2009 Universität Bielefeld Einführendes Beispiel Allgemeine Definitionen Strategien zur Behandlung von Kollisionen Klasse Hashtable Beispiel (Name-Wert-Paare) class Pair { private String name; private Object value; public Pair(String name, Object value) { this.name = name; this.value = value; } public String name() { return name; } public Object value() { return value; } public Object value(Object newValue) { Object oldValue = value; value = newValue; return oldValue; } } Peter Steffen A&D II, Vorlesung 2009 Universität Bielefeld Einführendes Beispiel Allgemeine Definitionen Strategien zur Behandlung von Kollisionen Klasse Hashtable Interface Dic Beispiel interface Dic { void add(Pair newPair); Pair find(String pairName); Pair delete(String pairName); } Peter Steffen A&D II, Vorlesung 2009 Universität Bielefeld Einführendes Beispiel Allgemeine Definitionen Strategien zur Behandlung von Kollisionen Klasse Hashtable Beispiel (Implementierung von Dic) import java.util.Hashtable; class DicImpl implements Dic { protected Hashtable pairTable = new Hashtable(); public void add(Pair newPair) { pairTable.put(newPair.name(), newPair); } public Pair find(String name) { return (Pair) pairTable.get(name); } public Pair delete(String name) { return (Pair) pairTable.remove(name); } } Peter Steffen A&D II, Vorlesung 2009 Universität Bielefeld Einführendes Beispiel Allgemeine Definitionen Strategien zur Behandlung von Kollisionen Klasse Hashtable K. Arnold, J. Gosling: JavaTM - Die Programmiersprache. Addison-Wesley, 1996. T.H. Cormen, C.E. Leierson, R.L. Rivest: Introduction to Algorithms. MIT Press, 1990. D. Flanagan: Java in a Nutshell. O’Reilly & Associates Inc., 1996. F. Jobst: Programmieren in Java. Hanser Verlag, 1996. H. Klaeren: Vom Problem zum Programm. 2.Auflage, B.G. Teubner Verlag, 1991. K. Echtle, M. Goedicke: Lehrbuch der Programmierung mit Java. dpunkt-Verlag, 2000. Peter Steffen A&D II, Vorlesung 2009 Universität Bielefeld