8.1 Einführendes Beispiel

Ähnliche Dokumente
Hashing. Algorithmen und Datenstrukturen II 1

Algorithmen und Datenstrukturen II

13. Hashing. AVL-Bäume: Frage: Suche, Minimum, Maximum, Nachfolger in O(log n) Einfügen, Löschen in O(log n)

12. Hashing. Hashing einfache Methode um Wörtebücher zu implementieren, d.h. Hashing unterstützt die Operationen Search, Insert, Delete.

Programmieren in Haskell Felder

Teil VII. Hashverfahren

Themen. Hashverfahren. Stefan Szalowski Programmierung II Hashverfahren

6/23/06. Universelles Hashing. Nutzen des Universellen Hashing. Problem: h fest gewählt es gibt ein S U mit vielen Kollisionen

Programmieren in Haskell Felder (Arrays)

Vorlesung Informatik 2 Algorithmen und Datenstrukturen

Vorlesung Informatik 2 Algorithmen und Datenstrukturen

Algorithmen und Datenstrukturen

Informatik II, SS 2014

Algorithmen und Datenstrukturen II: Hashverfahren

Hashing. Überblick Aufgabe Realisierung

Algorithmen und Datenstrukturen II: Hashverfahren

Programmiertechnik II

8. Hashing Lernziele. 8. Hashing

5.8.2 Erweiterungen Dynamische Hash-Funktionen (mit variabler Tabellengröße)?

ALP II Dynamische Datenmengen Datenabstraktion (Teil 2)

12 Abstrakte Klassen, finale Klassen und Interfaces

4. Hashverfahren. geg.: Wertebereich D, Schlüsselmenge S = {s 1,..., s n } D. Menge A von Speicheradressen; oft: A = {0,..., m 1}

Grundlagen der Algorithmen und Datenstrukturen Kapitel 4

Counting - Sort [ [ ] [ [ ] 1. SS 2008 Datenstrukturen und Algorithmen Sortieren in linearer Zeit

4.4.1 Statisches perfektes Hashing. des Bildbereichs {0, 1,..., n 1} der Hashfunktionen und S U, S = m n, eine Menge von Schlüsseln.

Kapitel 4. Streuen. (h K injektiv) k 1 k 2 K = h(k 1 ) h(k 2 )

Kollision Hashfunktion Verkettung Offenes Hashing Perfektes Hashing Universelles Hashing Dynamisches Hashing. 4. Hashverfahren

Übung Algorithmen und Datenstrukturen

In C und Java müssen Variablen und Methodenergebnisse durch Typangaben erläutert werden. Welche der folgenden Aussagen sind korrekt und welche nicht:

Algorithmen und Datenstrukturen Hashverfahren

Untersuchen Sie, inwiefern sich die folgenden Funktionen für die Verwendung als Hashfunktion eignen. Begründen Sie Ihre Antwort.

Algorithmen und Datenstrukturen (für ET/IT) Programm heute. Sommersemester Dr. Tobias Lasser

Informatik II, SS 2014

Informatik II, SS 2018

P ( Mindestens zwei Personen haben am gleichen Tag Geb. ) (1) = 1 P ( Alle Personen haben an verschiedenen Tagen Geb. ) (2)

14 Abstrakte Klassen, finale Klassen, Interfaces

Grundlagen: Algorithmen und Datenstrukturen

Kapitel 6 HASHING. Algorithmen & Datenstrukturen Prof. Dr. Wolfgang Schramm

14 Abstrakte Klassen, finale Klassen, Interfaces. Auswertung von Ausdrücken. Beispiel. Abstrakte Methoden und Klassen

Beweis: Die obere Schranke ist klar, da ein Binärbaum der Höhe h höchstens

Einführung in die Informatik: Programmierung und Software-Entwicklung, WS 11/12 1. Kapitel 11. Listen. Listen

Es sei a 2 und b 2a 1. Definition Ein (a, b)-baum ist ein Baum mit folgenden Eigenschaften:

C# - Einführung in die Programmiersprache Arrays, Enumeration und Collections. Leibniz Universität IT Services Anja Aue

Algorithmen und Datenstrukturen

Einführung in die Informatik: Programmierung und Software-Entwicklung, WS 16/17. Kapitel 13. Listen. Listen 1

Einführung in die Informatik: Programmierung und Software-Entwicklung, WS 15/16. Kapitel 12. Listen. Listen 1

Muster. Informatik 3 (Februar 2004) Name: Matrikelnummer: Betrachten Sie den folgenden Suchbaum. A G H J K M N

Humboldt-Universität zu Berlin Berlin, den Institut für Informatik

Hashing Hashfunktionen Kollisionen Ausblick. Hashverfahren. Dank an: Beate Bollig, TU Dortmund! 1/42. Hashverfahren

Übungsklausur Algorithmen I

Vorlesung Datenstrukturen

Überlaufbehandlung ohne Verkettung

Algorithmen und Datenstrukturen in Java Jiri Spale, Algorithmen und Datenstrukturen in Java 1

Klausur Informatik B April Teil I: Informatik 3

Literatur: Jeffrey D. Ullman: Principles of Database Systems, 2 nd Edition 1982, Kapitel 2.2

17. Hashing. Motivation. Naive Ideen. Bessere Idee? k(s) = s i b i

Javakurs FSS Lehrstuhl Stuckenschmidt. Tag 4 ArrayList, PriorityQueue, HashSet und HashMap

Kapitel 12: Induktive

Abstrakter Datentyp (ADT): Besteht aus einer Menge von Objekten, sowie Operationen, die auf diesen Objekten wirken.

Grundlagen der Programmierung

Suchen in Listen und Hashtabellen

Datenstrukturen und Algorithmen. Vorlesung 9

Algorithmen und Datenstrukturen 1 VL Übungstest WS Januar 2011

Assoziative Container in C++ Christian Poulter

Grundlagen: Algorithmen und Datenstrukturen

ALP II Dynamische Datenmengen Datenabstraktion

Gliederung. 5. Compiler. 6. Sortieren und Suchen. 7. Graphen

Praktikum zu Einführung in die Informatik für LogWings und WiMas Wintersemester 2013/14

Verkettete Datenstrukturen: Listen

Einführung in die Programmierung

Datenstrukturen & Algorithmen Lösungen zu Blatt 5 FS 14

14. Hashing. Motivation. Naive Ideen. Bessere Idee? k(s) = s i b i

Algorithmen & Datenstrukturen Lösungen zu Blatt 9 HS 16

Wintersemester 2004/ Dezember 2004

Seminar Datenbanken Martin Gerstmann

Programmieren in Java -Eingangstest-

Informatik II Prüfungsvorbereitungskurs

Fibonacci-Suche. Informatik I. Fibonacci-Suche. Fibonacci-Suche. Einführung. Rainer Schrader. 24. Mai 2005

Was ist ein assoziativer Speicher?

11. Elementare Datenstrukturen

Informatik II, SS 2016

Theorie zu Übung 8 Implementierung in Java

Objektorientierte Programmierung. Kapitel 22: Aufzählungstypen (Enumeration Types)

Praktische Informatik I Algorithmen und Datenstrukturen Wintersemester 2006/07

5 BINÄRE ENTSCHEIDUNGS- DIAGRAMME (BDDS)

Inhaltsverzeichnis. Praktikum Algoritmen und Datenstrukturen WS2004/2005 Paul Litzbarski Stefan Nottorf. Druckmanager allgemein 2.

Schwerpunkte. Verkettete Listen. Verkettete Listen: 7. Verkettete Strukturen: Listen. Überblick und Grundprinzip. Vergleich: Arrays verkettete Listen

8 Elementare Datenstrukturen

Algorithmen und Datenstrukturen 11

Kapitel 9. Programmierkurs. Attribute von Klassen, Methoden und Variablen. 9.1 Attribute von Klassen, Methoden und Variablen

Einstieg in die Informatik mit Java

Institut für Informatik

Bäume, Suchbäume und Hash-Tabellen

1 Abstrakte Klassen, finale Klassen und Interfaces

Software Entwicklung 1

1. Referenzdatentypen: Felder und Strings

1. Referenzdatentypen: Felder und Strings. Referenz- vs. einfache Datentypen. Rückblick: Einfache Datentypen (1) 4711 r

MB2-ALG, SS15 Seite 1 Hauptklausur, geschrieben am

C++ - Objektorientierte Programmierung Vererbung

Transkript:

Kapitel 8 Hashing Dieses Kapitel beschäftigt sich mit einem wichtigen Speicherungs- und Suchverfahren, bei dem die Adressen von Daten aus zugehörigen Schlüsseln errechnet werden, dem Hashing Dabei stehen die Algorithmen und verschiedene Heuristiken der Kollisionsbehandlung im Vordergrund Es wird aber auch auf die in Java vordefinierte Klasse Hashtable eingegangen 5

8 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 Natürlich existiert in Bielefeld jede Telefonnummer nur einmal, während es mehrere Menschen mit gleichem Vornamen, Nachnamen oder Adresse gibt Das bedeutet: Wenn der Telefonist in der Pizzeria die Telefonnummer des Kunden erfragt (oder von dem Display seines Telefons abliest) und diese in seinen Computer eingibt, dann bekommt er genau einen Kunden mit Name, Vorname und Adresse angezeigt, vorausgesetzt, der Kunde wurde schon einmal in die Datenbank eingetragen Die Telefonnummer ist also eine Art Schlüssel für die Suche nach einem Datensatz, in diesem Fall die Kundendaten Abstrakter bedeutet dies, dass wir über einen Schlüssel (key) Zugriff auf einen Wert (value) erhalten Stellen wir uns die Repräsentation der Daten in dem Programm, das die Pizzeria benutzt, so vor: Telefonnummer Name Vorname PLZ Straße Müller Heinz 3365 Unistraße 5 Schmidt Werner 3365 Grünweg 2 Schultz Hans 3362 Arndtstraße 2 99999997 Meier Franz 3369 Kirchweg 4 99999998 Neumann Herbert 3362 Jägerallee 5 99999999 Schröder Georg 33647 Mühlweg 2 Die Daten werden also in einer Tabelle gespeichert Dabei entsprechen die Zeilen den Telefonnummern, die es in Bielefeld möglicherweise geben könnte, nämlich den achtstelligen Zaheln von bis 99999999 wobei natürlich einige Zahlen, wie etwa die keine gültigen Telefonnummern sind und wir vereinfachend annehmen wollen, dass Telefonnummern, die weniger als acht Stellen haben, mit führenden Nullen aufgefüllt werden können Bis hierher würde die Datentabelle also so aussehen wie in der obigen Abbildung mit 8 also Millionen verschiedene Nummern, die jeweils einer Zeile in der Tabelle entsprechen Hierbei fällt schon auf, dass diese Zahl natürlich viel zu groß ist, denn es gibt sicherlich nicht annähernd so viele Telefonanschlüsse in Bielefeld Die Menge der möglichen Schlüssel, also die Zahlen bis 99999999 ist gegenüber der Zahl der tatsächlich informativen Einträge viel zu groß, so dass eine große Menge Speicher für Telefonnummern verbraucht wird, die nie zugeordnet werden Weiterhin ist zu bedenken, dass nicht jeder Einwohner Bielefelds eine 6

Telefonnummer hat und dass nicht alle, die eine Nummer haben, beim Pizza- Service bestellen, und wenn sie doch eine Pizza bestellen, nicht unbedingt bei dieser Pizzeria Gehen wir also davon aus, dass von der Menge aller Schlüssel nur ein kleiner Teil wirklichen Datenbankeinträgen entspricht Bielefeld hat ca 3 Einwohner, dann gibt es vielleicht 2 Telefonnummern Davon bestellt jeder fünfte eine Pizza bleiben 4 potentielle Einträge, verteilt auf mehrere Pizza- Lieferservices Optimistisch geschätzt wird unsere Pizzeria also ca Kunden haben Damit bleiben von den Millionen Schlüsseln nur tatsächlich benutzte übrig, das sind, Prozent Dies bedeutet für die Tabelle der Kundendaten, dass sie erheblich verkleinert werden kann nämlich auf Zeilen, denn mit mehr Kunden braucht die Pizzeria nicht zu rechnen 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 Millionen Telefonnummern (denn jede einzelne könnte ja theoretisch bestellen) so abzubilden, dass sie in eine Zeilen große Tabelle passen 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 7 mod 2 = 7, da 7 = 5 2 + 7 Wenn wir jetzt jede angegebene Telefonnummer modulo nehmen, bekommen wir ein Ergebnis zwischen und 9999 Somit können wir alle theoretischen Telefonnummern und damit Pizza-Besteller in einer Tabelle abbilden, die gerade mal Zeilen hat Die Funktion, die wir dazu benutzen, lautet: h(telefonnummer) = Telefonnummer mod Tabellenlänge, oder allgemein: h(k) = k mod m mit h für Hashfunktion, k für key und m für Tabellenlänge Wir benutzen also diese Hashfunktion, um jedem Schlüssel einen Index (hier eine Zahl zwischen 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, zb ist 63852 mod = 853852 mod = 3852 Wie solche Kollisionen behandelt werden können, wird im übernächsten Abschnitt diskutiert 7

82 Allgemeine Definitionen Formal gesehen ist Hashing ein abstrakter Datentyp, der die Operationen insert, delete und search auf (dynamischen) Mengen effizient unterstützt U (Universum der Schlüssel) 9 6 7 4 K (Aktuelle Schlüssel) 2 3 5 8 T 2 3 4 5 6 7 8 9 Abbildung 8: Direkte Adressierung Hashing ist im Durchschnitt sehr effizient unter vernünftigen Bedingungen werden obige Operationen in O() Zeit ausgeführt (im worst-case kann search O(n) Zeit benötigen) In unserer Darstellung folgen wir Cormen et al [2] Zur Vereinfachung nehmen wir an, dass die Daten keine weiteren Komponenten enthalten (dh mit den Schlüsseln übereinstimmen) Wenn die Menge U aller Schlüssel relativ klein ist, können wir sie injektiv auf ein Feld abbilden; dies nennt man direkte Adressierung (Abbildung??) 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 h : U {,,, m, 8

die Schlüssel abbildet auf Werte zwischen und m (dabei ist m die Größe der Hashtabelle) Dies illustriert Abbildung?? U (Universum der Schlüssel) k k 4 K (Aktuelle Schlüssel) k 2 k 5 k 3 T h(k ) h(k 4 ) h(k 2 ) = h(k 5 ) h(k 3 ) m Abbildung 82: Eine Hashfunktion h weist Schlüsseln ihren Platz in der Hashtabelle T zu Beispiel 82 Eine typische Hashfunktion h für U = N ist h(k) = k mod m Dabei sollte m eine Primzahl sein, die nicht (zu) nahe an Potenzen von 2 liegt Der Grund dafür wird in Cormen et al [2], S 228, erläutert 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 Dh h muss die Schlüssel gleichmäßig auf die Hashtabelle verteilen Außerdem sollte h einfach zu berechnen sein In Cormen et al [2] findet man weitere Erläuterungen zum Thema Hashfunktionen 9

83 Strategien zur Behandlung von Kollisionen 83 Direkte Verkettung Man kann Kollisionen auflösen, indem jeder Tabellenplatz einen Zeiger auf eine verkettete Liste enthält Schlüssel mit demselben Hashwert werden in dieselbe Liste eingetragen (Abbildung??) k 4 k 4 k 5 k 2 k 2 k 5 Abbildung 83: Direkte Verkettung Damit ergeben sich folgende worst-case Laufzeiten: insert: O() (neues Element vor die Liste hängen) search: O(n) (n = Länge der Liste) delete: O() (vorausgesetzt, man verwendet doppelt verkettete Listen und hat das Element schon gefunden) 832 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 Es gibt ua folgende Strategien zur Suche eines freien Platzes: Lineare Verschiebung Falls h(k) bereits durch einen anderen Schlüssel besetzt ist, wird versucht, k in den Adressen h(k) +, h(k) + 2, unterzubringen (Abbildung??) Präziser gesagt, wird folgende Hashfunktion verwendet: h (k, i) = ( h(k) + i ) mod m mit i =,, 2,, m 2

k 4 k 5 k 2 Abbildung 84: Open Hashing mit linearer Verschiebung Unter der Annahme, dass jeder Tabellenplatz entweder einen Schlüssel oder NIL enthält, wenn der Tabellenplatz leer ist, können die Operationen insert und search wie folgt implementiert werden: Hash-Insert(T, k) i 2 repeat 3 j h (k, i) 4 if T [j] = NIL then 5 T [j] k 6 return j 7 i i + 8 until i = m 9 error hash table overflow Hash-Search(T, k) i 2 repeat 3 j h (k, i) 4 if T [j] = k then 5 return j 6 i i + 7 until T [j] = NIL or i = m 8 error NIL 2 Quadratische Verschiebung Wenn die Schlüssel natürliche Zahlen sind, dann können wir zb den Wert zur Markierung eines leeren Tabellenplatzes benutzen 2

Es wird die Hashfunktion h (k, i) = (h(k) + c i + c 2 i 2 ) mod m mit i =,, 2,, m verwendet Dabei sind c, c 2 N und c 2 geeignete Konstanten (s Cormen et al [2]) 3 Double Hashing Die Hashfunktion h wird definiert durch h (k, i) = (h (k) + i h 2 (k)) mod m mit i =,, 2,, m, wobei h und h 2 Hashfunktionen sind Die Verschiebung wird dabei durch eine zweite Hashfunktion realisiert Dh es wird zweimal, also doppelt, gehasht Beispiel 83 Wir illustrieren Open Hashing mit linearer Verschiebung an einem Beispiel Unsere Hashtabelle hat nur fünf Plätze und die Hashfunktion sei h(k) = k mod 5 Die Werte und 5 seien bereits eingetragen 2 3 insert 2 3 insert 2 3 delete 2 3 d search 4 4 4 4 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 Der Ladefaktor α für eine Hashtabelle T ist definiert als n, wobei n die Anzahl m 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 8 nicht überschreiten sollte (dh die Hashtabelle darf höchstens zu 8% gefüllt werden) Ist der Ladefaktor 8, so treten beim Suchen im Durchschnitt 3 Kollisionen auf Bei einem höheren Ladefaktor steigt die Zahl der Kollisionen rasch an 84 Die Klasse Hashtable in Java Die Klasse javautilhashtable implementiert alle Methoden der abstrakten Klasse javautildictionary (vgl Aufgabe??) Außerdem enthält Hashtable 22

noch folgende Methoden 2 : 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 Ein Hashtabellenobjekt wächst automatisch, wenn es zu stark belegt wird Es ist zu stark belegt, wenn der Ladefaktor der Tabelle überschritten wird Wenn eine Hashtabelle wächst, wählt sie eine neue Kapazität, die ungefähr das doppelte der aktuellen beträgt Das Wählen einer Primzahl als Kapazität ist wesentlich für die Leistung, daher wird das Hashtabellenobjekt eine Kapazitätsangabe auf eine nahegelegene Primzahl anpassen Die anfängliche Kapazität und der Ladefaktor kann von den Konstruktoren der Klasse Hashtable gesetzt werden: public Hashtable() Es wird eine neue, leere Hashtabelle mit einer voreingestellten Anfangskapazität von und einem Ladefaktor von 75 erzeugt public Hashtable(int initialcapacity) Eine neue, leere Hashtabelle mit der Anfangskapazität initialcapacity und dem Ladefaktor 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 und und definiert den Beginn eines rehashings der Tabelle in eine größere 2 Das Voranstellen des Schlüsselworts synchronized bewirkt, dass eine Methode nicht in mehreren nebenläufigen Prozessen (threads) gleichzeitig mehrfach ausgeführt wird Ansonsten könnte es zu Inkonsistenzen in der Hashtabelle kommen 23

Beispiel 84 Um die Benutzung der Klasse Hashtable als Wörterbuch (Dictionary) zu demonstrieren, entwerfen wir zunächst eine Klasse Pair zur Speicherung von Name-Wert Paaren Namen sind vom Typ String Werte können beliebigen Typ haben, deshalb wird der Wert in einer Variable vom Typ Object abgelegt class Pair { private String name; private Object value; public Pair(String name, Object value) { thisname = name; thisvalue = value; public String name() { return name; public Object value() { return value; public Object value(object newvalue) { Object oldvalue = value; value = newvalue; return oldvalue; Der Name ist nur lesbar, denn er soll als Schlüssel in einer Hashtabelle benutzt werden Könnte das Datenfeld für den Namen von außerhalb der Klasse modifiziert werden, so könnte der zugehörige Wert verlorengehen: Er würde immer noch unter dem alten Namen eingeordnet sein und nicht unter dem modifizierten Namen Der Wert hingegen kann jederzeit verändert werden Folgende Schnittstelle deklariert drei Methoden: eine, um einem Dic-Objekt ein neues Paar hinzuzufügen; eine, um herauszufinden, ob in einem Dic-Objekt bereits ein Paar mit gegebenem Namen enthalten ist; und eine, um ein Paar aus einem Dic-Objekt zu löschen interface Dic { void add(pair newpair); Pair find(string pairname); 24

Pair delete(string pairname); Hier nun das Beispiel einer einfachen Implementierung von Dic, die die Hilfsklasse javautilhashtable verwendet: import javautilhashtable; class DicImpl implements Dic { protected Hashtable pairtable = new Hashtable(); public void add(pair newpair) { pairtableput(newpairname(), newpair); public Pair find(string name) { return (Pair) pairtableget(name); public Pair delete(string name) { return (Pair) pairtableremove(name); Der Initialisierer für pairtable erzeugt ein Hashtable-Objekt, um Paare zu speichern Die Klasse Hashtable erledigt die meiste anfallende Arbeit Sie verwendet die Methode hashcode des Objekts, um jedes ihr als Schlüssel übergebene Objekt einzuordnen Wir brauchen keine explizite Hashfunktion bereitzustellen, denn String enthält schon eine geeignete hashcode-implementierung Kommt ein neues Paar hinzu, wird das Pair-Objekt in einer Hashtabelle unter seinem Namen gespeichert Wir können dann einfach die Hashtabelle benutzen, um Paare über deren Namen zu finden und zu entfernen 85 Aufgaben Aufgabe 85 Implementieren Sie in Java das Verfahren zum Open-Hashing Beschränken Sie sich dabei auf eine Methode hashinsert zum Einfügen in die Hashtabelle Verwenden Sie die primäre Hashfunktion h(k) = k mod m und die folgenden drei Verfahren zur Kollisionsbehandlung: () lineare Verschiebung; 25

(2) quadratische Verschiebung mit c = und c 2 = 3; (3) double hashing mit h (k) = k mod m und h 2 (k) = + (k mod (m )) Verwenden Sie nun Ihre Implementierung, um die Schlüssel, 22, 3, 4, 5, 28, 7, 88, 59 in eine Hashtabelle der Größe m = einzutragen Geben Sie die Hashtabelle nach dem Einfügen jedes Schlüssels an Aufgabe 852 Sei m die Größe einer Hashtabelle Wir definieren die Hashfunktion hstring, die für einen String s einen ganzzahligen Wert hstring(m, s) liefert, wie folgt: hstring(m, s) = hstring (, s) mod m Dabei ist hstring in Haskell-ähnlicher Notation wie folgt definiert: hstring(i, []) = i und hstring (i, a : w) = hstring (i 3 + ord(a), w) wobei ord eine Abbildung ist, die für ein Zeichen aus dem ASCII-Alphabet eine eindeutige ganze Zahl zwischen und 255 liefert, nämlich ihren ASCII-Wert Implementieren Sie die Hashfunktion hstring in Java hstring sollte dabei iterativ implementiert werden Die Summen zur Berechnung des Hashwertes wachsen sehr schnell Verwenden Sie daher long-werte zur Summation, um einen Überlauf zu vermeiden 2 Erstellen Sie sich eine Datei strings mit ASCII-Zufallssequenzen der Länge 8 Wenden Sie hstring auf jede dieser Sequenzen an Wählen Sie dabei nacheinander in verschiedenen Programmläufen m {67,, 3, 97 3 Eine gute Hashfunktion wird die Strings gleichmäßig auf die m Einträge in der Hashtabelle abbilden, dh im Idealfall werden etwa /m Strings auf jeden Eintrag abgebildet Bestimmen Sie die Güte der Hashfunktion hstring, indem Sie g(m, hstring) = m m i= /m occ(i) berechnen Dabei ist occ(i) die Anzahl der Strings w mit hstring(m, w) = i Welcher Wert ergibt sich für die Güte g(m, hstring) für m {67,, 3, 97? 26