Es scheint allgemein bekannt zu sein, dass Hash-Tabellen O (1) erreichen können, aber das hat für mich nie Sinn gemacht. Kann es bitte jemand erklären? Hier sind zwei Situationen, die mir in den Sinn kommen:
A. Der Wert ist ein int kleiner als die Größe der Hash-Tabelle. Daher ist der Wert ein eigener Hash, daher gibt es keine Hash-Tabelle. Aber wenn es so wäre, wäre es O (1) und immer noch ineffizient.
B. Sie müssen einen Hash des Werts berechnen. In dieser Situation ist die Reihenfolge O (n) für die Größe der Daten, die nachgeschlagen werden. Die Suche könnte O (1) sein, nachdem Sie O (n) gearbeitet haben, aber das kommt in meinen Augen immer noch zu O (n).
Und wenn Sie keinen perfekten Hash oder eine große Hash-Tabelle haben, gibt es wahrscheinlich mehrere Artikel pro Eimer. Es entwickelt sich also sowieso irgendwann zu einer kleinen linearen Suche.
Ich finde Hash-Tabellen großartig, aber ich bekomme die Bezeichnung O (1) nur, wenn sie nur theoretisch sein soll.
Der Wikipedia- Artikel für Hash-Tabellen verweist konsistent auf die konstante Suchzeit und ignoriert die Kosten der Hash-Funktion vollständig. Ist das wirklich eine faire Maßnahme?
Bearbeiten: Um zusammenzufassen, was ich gelernt habe:
Dies ist technisch richtig, da die Hash-Funktion nicht alle Informationen im Schlüssel verwenden muss und daher eine konstante Zeit sein kann, und weil eine ausreichend große Tabelle Kollisionen auf eine nahezu konstante Zeit reduzieren kann.
In der Praxis ist dies der Fall, da es im Laufe der Zeit nur funktioniert, solange die Hash-Funktion und die Tabellengröße so gewählt werden, dass Kollisionen minimiert werden, obwohl dies häufig bedeutet, dass keine Hash-Funktion mit konstanter Zeit verwendet wird.
quelle
hashCode()
Methode für a implementiertString
. grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/…Antworten:
Sie haben hier zwei Variablen, m und n, wobei m die Länge der Eingabe und n die Anzahl der Elemente im Hash ist.
Der O (1) Lookup-Leistungsanspruch geht von mindestens zwei Annahmen aus:
Wenn Ihre Objekte eine variable Größe haben und eine Gleichheitsprüfung das Betrachten aller Bits erfordert, wird die Leistung zu O (m). Die Hash-Funktion muss jedoch nicht O (m) sein - sie kann O (1) sein. Im Gegensatz zu einem kryptografischen Hash muss eine Hash-Funktion zur Verwendung in einem Wörterbuch nicht jedes Bit in der Eingabe betrachten, um den Hash zu berechnen. Implementierungen können nur eine feste Anzahl von Bits betrachten.
Bei ausreichend vielen Elementen wird die Anzahl der Elemente größer als die Anzahl der möglichen Hashes, und dann erhalten Sie Kollisionen, die zu einem Leistungsanstieg über O (1) führen, z. B. O (n) für eine einfache Durchquerung verknüpfter Listen (oder O (n) * m) wenn beide Annahmen falsch sind).
In der Praxis gilt die O (1) -Angabe zwar technisch falsch, ist jedoch für viele Situationen in der realen Welt ungefähr zutreffend, insbesondere für Situationen, in denen die obigen Annahmen gelten.
quelle
O(1)
Behauptung ist wahr, wenn Sie Hashingint
oder etwas anderes haben, das in ein Maschinenwort passt. Das ist es, was die meisten Hashing-Theorien annehmen.std::hash
Textschlüssel kombinieren 10 Zeichen, die gleichmäßig entlang des Textes verteilt sind, zum Hash-Wert, sodass es unabhängig von der Textlänge O (1) ist (aber massiv kollisionsanfälliger als GCC!). Unabhängig davon haben Ansprüche von O (1) eine andere Annahme (normalerweise korrekt), dass m viel kleiner als n ist .Was? Das Hashing eines einzelnen Elements benötigt konstante Zeit. Warum sollte es etwas anderes sein? Wenn Sie
n
Elemente einfügen , müssen Sien
Hashes berechnen , und das dauert linear. Um ein Element nachzuschlagen, berechnen Sie einen einzelnen Hash von dem, wonach Sie suchen, und finden damit den entsprechenden Bucket . Sie berechnen nicht die Hashes von allem, was bereits in der Hash-Tabelle enthalten ist.Nicht unbedingt. Die Buckets müssen nicht unbedingt Listen oder Arrays sein, sie können beliebige Containertypen sein, z. B. eine ausgeglichene BST. Das bedeutet
O(log n)
schlimmsten Fall. Aus diesem Grund ist es wichtig, eine gute Hashing-Funktion zu wählen, um zu vermeiden, dass zu viele Elemente in einen Bucket eingefügt werden. Wie KennyTM betonte, haben Sie im Durchschnitt immer nochO(1)
Zeit, auch wenn Sie gelegentlich durch einen Eimer graben müssen.Der Kompromiss zwischen Hash-Tabellen ist natürlich die Platzkomplexität. Sie tauschen Raum gegen Zeit, was in der Informatik der übliche Fall zu sein scheint.
Sie erwähnen die Verwendung von Zeichenfolgen als Schlüssel in einem Ihrer anderen Kommentare. Sie sind besorgt darüber, wie lange es dauert, den Hash eines Strings zu berechnen, da er aus mehreren Zeichen besteht? Wie jemand anderes noch einmal betont hat, müssen Sie nicht unbedingt alle Zeichen betrachten, um den Hash zu berechnen, obwohl dies möglicherweise zu einem besseren Hash führt, wenn Sie dies tun. In diesem Fall, wenn
m
Ihr Schlüssel durchschnittlich Zeichen enthält und Sie alle verwendet haben, um Ihren Hash zu berechnen, haben Sie wahrscheinlich Recht, dass Suchvorgänge erforderlich sindO(m)
. Wennm >> n
dann könnten Sie ein Problem haben. In diesem Fall wären Sie mit einem BST wahrscheinlich besser dran. Oder wählen Sie eine günstigere Hashing-Funktion.quelle
O(n)
. Wenn Sie sind viele Kollisionen erwarten, dann sind Sie richtig, wahrscheinlich besser dran , in erster Linie mit einem BST gehen.N
in diesem Fall ist die Länge der Zeichenfolge. Wir müssen nur einen String hashen, um zu bestimmen, in welchen 'Bucket' er gehen soll - er wächst nicht mit der Länge der Hashmap.Der Hash hat eine feste Größe - das Nachschlagen des entsprechenden Hash-Buckets ist eine Fixkostenoperation. Dies bedeutet, dass es O (1) ist.
Das Berechnen des Hashs muss keine besonders teure Operation sein - wir sprechen hier nicht von kryptografischen Hash-Funktionen. Aber das ist vorbei. Die Berechnung der Hash-Funktion selbst hängt nicht von der Anzahl n der Elemente ab. Dies hängt möglicherweise von der Größe der Daten in einem Element ab. Dies ist jedoch nicht das, worauf sich n bezieht. Die Berechnung des Hashs hängt also nicht von n ab und ist auch O (1).
quelle
logn
, siehe meine Antwort unter stackoverflow.com/questions/4553624/hashmap-get-put-complexity/…Hashing ist nur dann O (1), wenn die Tabelle nur eine konstante Anzahl von Schlüsseln enthält und einige andere Annahmen getroffen werden. Aber in solchen Fällen hat es einen Vorteil.
Wenn Ihr Schlüssel eine n-Bit-Darstellung hat, kann Ihre Hash-Funktion 1, 2, ... n dieser Bits verwenden. Denken Sie an eine Hash-Funktion, die 1 Bit verwendet. Die Bewertung ist mit Sicherheit O (1). Sie partitionieren den Schlüsselraum jedoch nur in 2. Sie ordnen also bis zu 2 ^ (n-1) Schlüssel demselben Bin zu. Bei Verwendung der BST-Suche sind bis zu n-1 Schritte erforderlich, um einen bestimmten Schlüssel zu finden, wenn dieser fast voll ist.
Sie können dies erweitern, um zu sehen, dass Ihre Bin-Größe 2 ^ (nk) beträgt, wenn Ihre Hash-Funktion K-Bits verwendet.
also K-Bit-Hash-Funktion ==> nicht mehr als 2 ^ K effektive Bins ==> bis zu 2 ^ (nK) n-Bit-Schlüssel pro Bin ==> (nK) Schritte (BST) zum Auflösen von Kollisionen. Tatsächlich sind die meisten Hash-Funktionen viel weniger "effektiv" und benötigen / verwenden mehr als K Bits, um 2 ^ k Bins zu erzeugen. Auch das ist optimistisch.
Sie können es so anzeigen - Sie benötigen ~ n Schritte, um im schlimmsten Fall ein Schlüsselpaar mit n Bits eindeutig unterscheiden zu können. Es gibt wirklich keine Möglichkeit, diese Grenze der Informationstheorie, die Hash-Tabelle oder nicht zu umgehen.
Dies ist jedoch NICHT, wie / wann Sie die Hash-Tabelle verwenden!
Bei der Komplexitätsanalyse wird davon ausgegangen, dass für n-Bit-Schlüssel O (2 ^ n) -Schlüssel in der Tabelle enthalten sein können (z. B. 1/4 aller möglichen Schlüssel). Aber die meiste, wenn nicht die ganze Zeit, in der wir die Hash-Tabelle verwenden, haben wir nur eine konstante Anzahl der n-Bit-Schlüssel in der Tabelle. Wenn Sie nur eine konstante Anzahl von Schlüsseln in der Tabelle möchten, beispielsweise C ist Ihre maximale Anzahl, können Sie eine Hash-Tabelle mit O (C) -Bins bilden, die eine erwartete konstante Kollision garantiert (mit einer guten Hash-Funktion). und eine Hash-Funktion unter Verwendung von ~ logC der n Bits im Schlüssel. Dann ist jede Abfrage O (logC) = O (1). Auf diese Weise behaupten die Leute, dass der Zugriff auf die Hash-Tabelle O (1) ist.
Hier gibt es ein paar Fänge: Erstens ist es möglicherweise nur ein Abrechnungstrick, zu sagen, dass Sie nicht alle Bits benötigen. Erstens können Sie den Schlüsselwert nicht wirklich an die Hash-Funktion übergeben, da dies n Bits im Speicher verschieben würde, der O (n) ist. Sie müssen also zB eine Referenzübergabe durchführen. Sie müssen es jedoch noch irgendwo speichern, was eine O (n) -Operation war. Sie rechnen es einfach nicht mit dem Hashing ab. Ihre gesamte Rechenaufgabe kann dies nicht vermeiden. Zweitens führen Sie das Hashing durch, suchen den Papierkorb und finden mehr als 1 Schlüssel. Ihre Kosten hängen von Ihrer Auflösungsmethode ab. Wenn Sie einen Vergleich durchführen (BST oder Liste), haben Sie eine O (n) -Operation (Rückrufschlüssel ist n-Bit). Wenn Sie den 2. Hash ausführen, haben Sie das gleiche Problem, wenn der 2. Hash kollidiert.
Betrachten Sie in diesem Fall die Alternative, z. B. BST. Es gibt C-Tasten, daher ist eine ausgeglichene BST in der Tiefe O (logC), sodass eine Suche O (logC) -Schritte ausführt. Der Vergleich in diesem Fall wäre jedoch eine O (n) -Operation ... daher scheint Hashing in diesem Fall die bessere Wahl zu sein.
quelle
TL; DR: Hash-Tabellen garantieren die
O(1)
erwartete Worst-Case-Zeit, wenn Sie Ihre Hash-Funktion gleichmäßig zufällig aus einer universellen Familie von Hash-Funktionen auswählen. Der erwartete schlimmste Fall ist nicht der gleiche wie der durchschnittliche Fall.Haftungsausschluss: Ich beweise formal nicht, dass Hash-Tabellen vorhanden sind.
O(1)
Schauen Sie sich dazu dieses Video von coursera [ 1 ] an. Ich diskutiere auch nicht die amortisierten Aspekte von Hash-Tabellen. Das ist orthogonal zur Diskussion über Hashing und Kollisionen.Ich sehe in anderen Antworten und Kommentaren überraschend viel Verwirrung um dieses Thema und werde versuchen, einige davon in dieser langen Antwort zu korrigieren.
Argumentation über den schlimmsten Fall
Es gibt verschiedene Arten der Worst-Case-Analyse. Die Analyse, die die meisten Antworten hier bisher gemacht haben, ist kein Worst-Case, sondern ein Durchschnittsfall [ 2 ]. Eine durchschnittliche Fallanalyse ist in der Regel praktischer. Möglicherweise hat Ihr Algorithmus eine schlechte Worst-Case-Eingabe, funktioniert aber tatsächlich gut für alle anderen möglichen Eingaben. Unterm Strich hängt Ihre Laufzeit von dem Datensatz ab, auf dem Sie ausgeführt werden.
Betrachten Sie den folgenden Pseudocode der
get
Methode einer Hash-Tabelle. Hier gehe ich davon aus, dass wir Kollisionen durch Verketten behandeln, sodass jeder Eintrag in der Tabelle eine verknüpfte Liste von(key,value)
Paaren ist. Wir gehen auch davon aus, dass die Anzahl der Bucketsm
fest istO(n)
, aber won
ist die Anzahl der Elemente in der Eingabe.Wie andere Antworten gezeigt haben, läuft dies im Durchschnitt
O(1)
und im schlimmsten FallO(n)
. Wir können hier eine kleine Skizze eines Beweises durch Herausforderung machen. Die Herausforderung lautet wie folgt:(1) Sie geben Ihren Hash-Tabellen-Algorithmus an einen Gegner weiter.
(2) Der Gegner kann es studieren und vorbereiten, solange er will.
(3) Schließlich gibt Ihnen der Gegner eine Eingabe der Größe, die
n
Sie in Ihre Tabelle einfügen können.Die Frage ist: Wie schnell ist Ihre Hash-Tabelle in der Eingabe des Gegners?
Ab Schritt (1) kennt der Gegner Ihre Hash-Funktion; während Schritt (2) kann der Gegner eine Liste von
n
Elementen mit denselbenhash modulo m
erstellen, indem er beispielsweise den Hash einer Reihe von Elementen zufällig berechnet; und dann in (3) können sie Ihnen diese Liste geben. Aber siehe da, da allen
Elemente denselben Bucket haben, wird Ihr Algorithmus einigeO(n)
Zeit brauchen, um die verknüpfte Liste in diesem Bucket zu durchlaufen. Egal wie oft wir die Herausforderung wiederholen, der Gegner gewinnt immer, und so schlecht ist Ihr Algorithmus im schlimmsten FallO(n)
.Wie kommt es, dass Hashing O (1) ist?
Was uns bei der vorherigen Herausforderung abschreckte, war, dass der Gegner unsere Hash-Funktion sehr gut kannte und dieses Wissen nutzen konnte, um den schlechtesten Input zu erstellen. Was wäre, wenn wir nicht immer eine feste Hash-Funktion verwenden würden, sondern tatsächlich eine Reihe von Hash-Funktionen hätten
H
, aus denen der Algorithmus zur Laufzeit zufällig auswählen kann? Wenn Sie neugierig sind,H
spricht man von einer universellen Familie von Hash-Funktionen [ 3 ]. Okay, lassen Sie uns versuchen, etwas Zufälligkeit hinzuzufügen .Angenommen, unsere Hash-Tabelle enthält auch einen Startwert
r
undr
wird zur Erstellungszeit einer Zufallszahl zugewiesen. Wir weisen es einmal zu und dann ist es für diese Hash-Tabelleninstanz behoben. Lassen Sie uns nun unseren Pseudocode erneut betrachten.Wenn wir die Herausforderung noch einmal versuchen: Ab Schritt (1) kann der Gegner alle Hash-Funktionen kennen, in denen wir uns befinden
H
, aber jetzt hängt die spezifische Hash-Funktion, die wir verwenden, davon abr
. Der Wert vonr
ist für unsere Struktur privat. Der Gegner kann ihn weder zur Laufzeit überprüfen noch im Voraus vorhersagen, sodass er keine Liste erstellen kann, die für uns immer schlecht ist. Nehmen wir an , dass in Schritt (2) der Gegner eine Funktion wählthash
inH
zufällig, er Handwerk dann eine Liste vonn
Kollisionen unterhash modulo m
, und sendet die für Schritt (3), Kreuzung Finger , die zur LaufzeitH[r]
die gleiche sein wirdhash
sie gewählt haben.Dies ist eine ernsthafte Wette für den Gegner, die Liste, unter der er erstellt wurde, kollidiert
hash
, ist jedoch nur eine zufällige Eingabe unter einer anderen Hash-Funktion inH
. Wenn er diese Wette gewinnt, ist unsere Laufzeit der schlechteste FallO(n)
wie zuvor, aber wenn er verliert, erhalten wir nur eine zufällige Eingabe, die die durchschnittlicheO(1)
Zeit in Anspruch nimmt . Und in der Tat wird der Gegner meistens verlieren, er gewinnt nur einmal bei jeder|H|
Herausforderung, und wir können|H|
sehr groß werden.Vergleichen Sie dieses Ergebnis mit dem vorherigen Algorithmus, bei dem der Gegner immer die Herausforderung gewonnen hat. Handwinken hier ein bisschen, aber da der Gegner meistens scheitert und dies für alle möglichen Strategien gilt, die der Gegner versuchen kann, folgt daraus, dass, obwohl der schlimmste Fall ist
O(n)
, der erwartete schlimmste Fall tatsächlich istO(1)
.Auch dies ist kein formaler Beweis. Die Garantie, die wir aus dieser erwarteten Worst-Case-Analyse erhalten, besteht darin, dass unsere Laufzeit jetzt unabhängig von bestimmten Eingaben ist . Dies ist eine wirklich zufällige Garantie, im Gegensatz zu der durchschnittlichen Fallanalyse, bei der wir gezeigt haben, dass ein motivierter Gegner leicht schlechte Eingaben machen kann.
quelle
Es gibt zwei Einstellungen, unter denen Sie O (1) Worst-Case-Zeiten erhalten können.
Von hier kopiert
quelle
Es scheint hier auf der Diskussion zu beruhen, dass wenn X die Obergrenze von (Anzahl der Elemente in der Tabelle / Anzahl der Bins) ist, eine bessere Antwort O (log (X)) ist, vorausgesetzt, dass die Bin-Suche effizient implementiert wird.
quelle
Dies ist ein Fall, in dem Sie die Schlüssel trivial unterschiedlichen Buckets zuordnen können, sodass ein Array eine bessere Wahl für die Datenstruktur zu sein scheint als eine Hash-Tabelle. Die Ineffizienzen wachsen jedoch nicht mit der Tabellengröße.
(Möglicherweise verwenden Sie weiterhin eine Hash-Tabelle, da Sie nicht darauf vertrauen, dass die Ints während der Entwicklung des Programms kleiner als die Tabellengröße bleiben. Sie möchten den Code möglicherweise wiederverwendbar machen, wenn diese Beziehung nicht besteht oder Sie dies einfach nicht tun möchten, dass Menschen, die den Code lesen / pflegen, geistige Anstrengungen verschwenden müssen, um die Beziehung zu verstehen und aufrechtzuerhalten).
Wir müssen zwischen der Größe des Schlüssels (z. B. in Bytes) und der Größe der Anzahl der Schlüssel unterscheiden, die in der Hash-Tabelle gespeichert sind. Behauptungen, dass Hash-Tabellen O (1) -Operationen bereitstellen, bedeuten, dass Operationen (Einfügen / Löschen / Suchen) nicht weiter verlangsamt werden, da die Anzahl der Schlüssel von Hunderten auf Tausende auf Millionen auf Milliarden steigt (zumindest nicht, wenn alle Daten vorliegen Der Zugriff / die Aktualisierung erfolgt in einem ebenso schnellen Speicher, sei es, dass RAM- oder Festplatten-Cache-Effekte ins Spiel kommen können, aber selbst die Kosten eines Cache-Fehlschlags im ungünstigsten Fall sind in der Regel ein konstantes Vielfaches der Best-Case-Treffer.
Stellen Sie sich ein Telefonbuch vor: Sie haben vielleicht Namen, die ziemlich lang sind, aber ob das Buch 100 Namen oder 10 Millionen hat, die durchschnittliche Namenslänge wird ziemlich konsistent sein und der schlimmste Fall in der Geschichte ...
...
wc
sagt mir, dass das 215 Zeichen sind - das ist keine harte Obergrenze für die Schlüssellänge, aber wir müssen uns keine Sorgen machen, dass es massiv mehr gibt.Dies gilt für die meisten realen Hash-Tabellen: Die durchschnittliche Schlüssellänge wächst nicht mit der Anzahl der verwendeten Schlüssel. Es gibt Ausnahmen, zum Beispiel kann eine Schlüsselerstellungsroutine Zeichenfolgen zurückgeben, in die inkrementierende Ganzzahlen eingebettet sind, aber selbst dann erhöhen Sie jedes Mal, wenn Sie die Anzahl der Schlüssel um eine Größenordnung erhöhen, die Schlüssellänge nur um 1 Zeichen: Dies ist nicht signifikant.
Es ist auch möglich, einen Hash aus einer Menge von Schlüsseldaten fester Größe zu erstellen. Zum Beispiel wird Microsoft Visual C ++ mit einer Standardbibliotheksimplementierung ausgeliefert
std::hash<std::string>
, die einen Hash erstellt, der nur zehn Bytes enthält, die gleichmäßig entlang der Zeichenfolge verteilt sind. Wenn die Zeichenfolgen also nur bei anderen Indizes variieren, kommt es zu Kollisionen (und damit in der Praxis zu Nicht-O (1) -Verhalten auf der Suchseite nach der Kollision), aber die Zeit zum Erstellen des Hashs hat eine harte Obergrenze.Im Allgemeinen stimmt das, aber das Tolle an Hash-Tabellen ist, dass die Anzahl der Schlüssel, die während dieser "kleinen linearen Suche" besucht wurden, - für den separaten Verkettungsansatz bei Kollisionen - eine Funktion des Lastfaktors der Hash-Tabelle (Verhältnis von Schlüsseln zu Buckets) ist.
Bei einem Auslastungsfaktor von 1,0 beträgt die durchschnittliche Länge dieser linearen Suchvorgänge unabhängig von der Anzahl der Schlüssel durchschnittlich ~ 1,58 (siehe meine Antwort hier ). Für geschlossenes Hashing ist es etwas komplizierter, aber nicht viel schlimmer, wenn der Lastfaktor nicht zu hoch ist.
Diese Art verfehlt den Punkt. Jede Art von assoziativer Datenstruktur muss letztendlich manchmal Operationen über jeden Teil des Schlüssels ausführen (Ungleichheit kann manchmal nur aus einem Teil des Schlüssels bestimmt werden, aber Gleichheit erfordert im Allgemeinen, dass jedes Bit berücksichtigt wird). Zumindest kann es den Schlüssel einmal hashen und den Hash-Wert speichern. Wenn es eine ausreichend starke Hash-Funktion verwendet - z. B. 64-Bit-MD5 -, wird möglicherweise sogar die Möglichkeit ignoriert, dass zwei Schlüssel denselben Wert haben (ein Unternehmen) Ich habe genau das für die verteilte Datenbank getan: Die Zeit für die Hash-Generierung war im Vergleich zu WAN-weiten Netzwerkübertragungen immer noch unbedeutend. Es macht also nicht allzu viel Sinn, sich über die Kosten für die Verarbeitung des Schlüssels Gedanken zu machen: Dies ist mit dem Speichern von Schlüsseln unabhängig von der Datenstruktur verbunden, und wie oben erwähnt - nicht.
Bei Hash-Tabellen, die groß genug sind, um Kollisionen zu reduzieren, fehlt auch der Punkt. Für eine separate Verkettung haben Sie bei jedem Lastfaktor immer noch eine konstante durchschnittliche Kollisionskettenlänge - sie ist nur höher, wenn der Lastfaktor höher ist, und diese Beziehung ist nicht linear. Der SO-Benutzer Hans kommentiert meine Antwort auch wie oben verlinkt :
Der Ladefaktor allein bestimmt also die durchschnittliche Anzahl kollidierender Schlüssel, die Sie beim Einfügen / Löschen / Suchen durchsuchen müssen. Bei einer getrennten Verkettung ist es nicht nur konstant, wenn der Lastfaktor niedrig ist, sondern immer konstant. Für die offene Adressierung hat Ihr Anspruch jedoch eine gewisse Gültigkeit: Einige kollidierende Elemente werden in alternative Buckets umgeleitet und können dann die Operationen auf anderen Schlüsseln stören, sodass bei höheren Lastfaktoren (insbesondere> .8 oder .9) die Kollisionskettenlänge dramatischer schlechter wird.
Nun, die Tabellengröße sollte zu einem vernünftigen Auslastungsfaktor führen, wenn man zwischen engem Hashing oder getrennter Verkettung wählt. Aber auch wenn die Hash-Funktion etwas schwach ist und die Schlüssel nicht sehr zufällig sind, hilft eine Primzahl von Buckets oft, sie zu reduzieren Kollisionen ebenfalls (wird
hash-value % table-size
dann so umbrochen, dass Änderungen nur zu einem oder zwei höherwertigen Bits im Hash-Wert immer noch in Eimer aufgelöst werden, die pseudozufällig über verschiedene Teile der Hash-Tabelle verteilt sind).quelle