Eine gute Möglichkeit, Hash-Tabellen anzuzeigen, ist wie eine Nachschlagetabelle mit einem unendlichen Indexbereich (nicht wirklich unendlich, Sie sind immer noch durch die Wertbegrenzung des von Ihnen verwendeten Schlüssels eingeschränkt).
Nehmen wir an, Sie versuchen, bestimmte Werte von sqrt (x) in einer Nachschlagetabelle zu speichern, in der X eine Ganzzahl ist. Das würde ungefähr so aussehen:
[1] = 1
[3] = 1.732
[10000] = 100
Dies führt zu einer sehr günstigen Quadratwurzelung, da Sie anstelle der teuren Berechnung einfach den Wert aus dem Array abrufen können. Es ist jedoch eine sehr ineffiziente Speichernutzung, da [2] und [4 - 9999] leer sind.
Zur Rettung kommt die Hash-Funktion. Der Zweck einer Hash-Funktion in diesem Zusammenhang besteht darin, den Index in etwas umzuwandeln, das tatsächlich in ein Array mit angemessener Größe passt. So könnte es beispielsweise Folgendes tun:
(1) = [5] = 1
(3) = [2] = 1.732
(10000) = [3] = 100
Jetzt passen alle 3 Werte in ein Array mit der Größe 6.
Wie erreicht die Hash-Funktion dies? Die grundlegendste Hash-Funktion ist (Index% ArraySize). Der Modulo-Operator dividiert den von Ihnen ausgewählten Index durch die Größe des Arrays und gibt Ihnen den Rest, der immer kleiner als die Array-Größe ist.
Was aber, wenn mehrere Indizes zum gleichen Ergebnis führen? Dies wird als Hash-Kollision bezeichnet und es gibt verschiedene Möglichkeiten, damit umzugehen. Am einfachsten ist es, jeden Wert zusammen mit seinem ursprünglichen Index im Array zu speichern. Wenn dieser Array-Slot belegt ist, gehen Sie um 1 vorwärts, bis ein leerer Slot gefunden wird. Gehen Sie beim Abrufen des Werts zu der durch die Hash-Funktion angegebenen Position und durchlaufen Sie die Elemente, bis die mit dem geeigneten Originalindex gefunden wird.
Aus diesem Grund eignet sich eine gute Hash-Funktion auch hervorragend zum Verteilen der Daten, sodass das Hash-Ergebnis unabhängig davon, ob die eingehenden Indizes sequentiell oder zufällig sind, so weit wie möglich verteilt werden sollte, um die Kosten für den Zugriff auf Daten relativ konstant zu halten.
Je größer das zugrunde liegende Array ist, desto weniger Kollisionen treten auf, sodass ein Kompromiss zwischen Geschwindigkeit und Größeneffizienz besteht. Moderne Hash-Tabellen füllen normalerweise bis zu ~ 70%, während weniger als 10 Kollisionen pro Zugriff auftreten. Zusammen mit der Hash-Funktion bedeutet dies, dass jeder Datenabruf ~ 20 Zyklen kostet, was (für einige Zwecke) ein guter Kompromiss zwischen Geschwindigkeit (Nachschlagetabelle) und Effizienz (Liste) ist.
hash % table size
, dass der Hash gleichmäßig verteilt sein sollte, nicht der Hash selbst.