Zum Inhalt springen

„Multiplikative Methode“ – Versionsunterschied

aus Wikipedia, der freien Enzyklopädie
[ungesichtete Version][gesichtete Version]
Inhalt gelöscht Inhalt hinzugefügt
K Literatur: Tippfehler entfernt |
 
(28 dazwischenliegende Versionen von 18 Benutzern werden nicht angezeigt)
Zeile 1: Zeile 1:
Die '''multiplikative Methode''' ist ein Schema, nach dem [[Hash-Funktion]]en entwickelt werden können. Dabei wird das Produkt des Schlüssels mit einer Zahl <math>A</math> gebildet und der ganzzahlige Anteil abgeschnitten, so dass der Schlüssel in das [[Intervall (Mathematik)|Intervall]] <math>[0,1]</math> abgebildet wird. Das Ergebnis wird mit der Anzahl der Hashtabellenadressen <math>m</math> multipliziert und nach unten abgerundet. In Formelschreibweise ist eine Hash-Funktion <math>h</math>, die die multiplikative Methode implementiert, definiert als:
Die '''multiplikative Methode''' ist eine [[Hash-Funktion]].
:<math>h(k) = \left\lfloor m \cdot \left(kA\bmod 1\right)\right\rfloor = \left\lfloor m \cdot \left(k A - \left\lfloor k A \right\rfloor\right)\right\rfloor,\qquad 0<A<1</math>
Bei der multiplikativen Methode wird das Produkt des Schlüssels mit einer Zahl <math>\phi</math> gebildet und der ganzzahlige Anteil abgeschnitten, so dass der Schlüssel in das [[Intervall (Mathematik)|Intervall]] [0,1[ abgebildet wird. Das Ergebnis wird mit der Anzahl der Hashtabellenadressen <math>m</math> multiplizert und nach unten abgerundet, d. h.


== Algorithmus ==
<math>h(k) = \lfloor m (k \phi - \lfloor k \phi \rfloor)\rfloor. </math>


Entscheidend für die Verteilung der Schlüssel auf den Adressbereich der Hashtabelle ist bei dieser Methode die Wahl von <math>\phi</math>, die unabhängig von der Wahl von <math>m</math> ist. In der Literatur wird der [[Goldene Schnitt]] für eine gute Wahl von <math>\phi</math> vorgeschlagen [1c] (Seite 516), [2], mit dem ähnliche Ergebnisse wie mit der [[Gleichverteilung]] erreicht werden.
Entscheidend für die Verteilung der Schlüssel auf den Adressbereich der Hashtabelle ist bei dieser Methode die Wahl von <math>A</math>, die unabhängig von der Wahl von <math>m</math> ist. In der Literatur wird der [[Goldener Schnitt|Goldene Schnitt]] für eine gute Wahl von <math>A</math> vorgeschlagen,<ref>Donald E. Knuth: ''The Art of Computer Programming / Sorting and Searching''. Volume 3. Addison-Wesley, Massachusetts, 2rd edition, 1997; Seite 516</ref><ref>Thomas Ottmann, Peter Widmayer: ''Algorithmen und Datenstrukturen''. zweite Edition. B.I. Wissenschaftsverlag, Mannheim / Leipzig / Wien / Zürich 1993</ref> mit dem in der Praxis mit vielen typischen Eingabedaten die Verteilung der Hash-Werte der Schlüssel nahezu [[Gleichverteilung|gleichverteilt]] ist. Wird diese Zahl verwendet, so nennt man die resultierende Hash-Funktion auch den ''Fibonacci-Hash''.


<math>\phi = \frac{\sqrt{5} - 1}{2} \approx 0{,}6180339887 \ldots</math>
<math>A = \Phi^{-1} = \frac{\sqrt{5} - 1}{2} \approx 0{,}6180339887 \ldots</math>


Die multiplikative Methode lässt sich auch als Verallgemeinerung der [[Divisionsrestmethode]] sehen.
Die multiplikative Methode lässt sich als Verallgemeinerung der [[Divisionsrestmethode]] sehen.
Setzt man <math>\phi = \frac{1}{m}</math> so ist
Setzt man <math>A = \frac{1}{m}</math>, so ist


<math>h(k) = \left \lfloor m \left( k \frac{1}{m} - \left\lfloor k \frac{1}{m} \right\rfloor \right) \right\rfloor = \left \lfloor m \left ( \left(k \frac{1}{m} \right) \ \bmod \ 1 \right) \right\rfloor = \left\lfloor m \frac{(k \ \bmod \ m)}{m} \right\rfloor = k \ \bmod \ m</math>
<math>h(k) = \left \lfloor m \left( k \frac{1}{m} - \left\lfloor k \frac{1}{m} \right\rfloor \right) \right\rfloor = \left \lfloor m \left ( \left(k \frac{1}{m} \right) \ \bmod \ 1 \right) \right\rfloor = \left\lfloor m \frac{(k \ \bmod \ m)}{m} \right\rfloor = k \ \bmod \ m</math>


In der Theorie des Hashens wird durch die Gleichverteilung der Schlüssel auf die Adressen der Hashtabelle im Allgemeinen versucht die Schlüssel gleichmäßig auf die Tabelle zu verteilen und dadurch eine Minimierung von Kollisionen zu erreichen.
In der Theorie des Hashens wird durch die Gleichverteilung der Schlüssel auf die Adressen der Hashtabelle üblicherweise versucht, die Schlüssel gleichmäßig auf die Tabelle zu verteilen und dadurch eine Minimierung von Kollisionen zu erreichen.


Die [[Gleichverteilung]] ist ein [[Zufallsexperiment]] mit der [[Wahrscheinlichkeit]] <math>P(\omega)=\frac{1}{n}</math>, wenn <math>\omega</math> ein Elementarereignis ist und <math>n</math> die Anzahl der Elementarereignisse, z.B. der Münzwurf <math>p=\frac{1}{2}</math> oder der Würfel <math>p= \frac{1}{6}</math>.
Die Gleichverteilung ist ein [[Zufallsexperiment]] mit der [[Wahrscheinlichkeit]] <math>P(\omega)=\frac{1}{n}</math>, wenn <math>\omega</math> ein Elementarereignis ist und <math>n</math> die Anzahl der Elementarereignisse, z.&nbsp;B. der Münzwurf <math>p=\frac{1}{2}</math> oder der Würfel <math>p= \frac{1}{6}</math>.


Der goldene Schnitt ist eine irrationale Zahl, d.h. sie ist aus <math>R \setminus Q</math> ("eine echte reelle Zahl", wie z.B. auch <math>e</math> und <math>\pi</math>). Der goldene Schnitt erreicht eine gute Verteilung der Schlüssel auf den Adressbereich der Hashtabelle durch folgende Eigenschaft von irrationalen Zahlen:
Der Goldene Schnitt ist eine [[Irrationale Zahlen|irrationale Zahl]], d.&nbsp;h. sie ist aus <math>\mathbb{R} \setminus \mathbb{Q}</math>, und er erreicht eine gute Verteilung der Schlüssel auf den Adressbereich der Hashtabelle durch folgende Eigenschaft von irrationalen Zahlen:


Sei <math>\phi</math> eine irrationale Zahl. Platziert man die Punkte <math>\phi - \lfloor \phi \rfloor, 2\phi - \lfloor 2\phi \rfloor, 3\phi - \lfloor 3\phi \rfloor, \ldots, n\phi - \lfloor n\phi \rfloor</math> in das Intervall <math>[0,1]</math>, dann haben die <math>n+1</math> Intervallteile höchstens drei verschiedene Längen. Außerdem fällt der nächste Punkt, <math>(n+1) \phi - \lfloor (n+1) \phi \rfloor</math>, in einen der größten Intervallteile.
Sei <math>A</math> eine irrationale Zahl. Platziert man die Punkte <math>A - \lfloor A \rfloor, 2A - \lfloor 2A \rfloor, 3A - \lfloor 3A \rfloor, \ldots, nA - \lfloor nA \rfloor</math> in das Intervall <math>[0,1]</math>, dann haben die <math>n+1</math> Intervallteile höchstens drei verschiedene Längen. Außerdem fällt der nächste Punkt, <math>(n+1) A - \lfloor (n+1) A \rfloor</math>, in einen der größten Intervallteile.<ref name="sos">Vera Turan Sós (Acta Math. Acad. Sci. Hung. 8 (1957), 461–471)</ref>
(Vera Turan Sos (Acta Math. Acad. Sci. Hung. 8 (1957), 461-471))


Jeder periodische Dezimalbruch bzw. Dualbruch stellt eine rationale Zahl dar. Natürliche oder rationale Zahlen mit endlich vielen Stellen, z.&nbsp;B. <math>\frac{1}{2}=0{,}5</math> sind 0-periodisch, d.&nbsp;h. man setzt die restlichen unendlichen Stellen gleich 0.
Nun sollte man aus der Mathematik wissen:
Jede rationale Zahl lässt sich als Dezimalbruch bzw. Dualbruch darstellen.
Jeder periodische Dezimalbruch, bzw. Dualbruch stellt eine rationale Zahlen dar.
Natürliche oder rationale Zahlen mit endlich vielen Stellen, z. B. <math>\frac{1}{2}=0.5</math> sind 0-periodisch, d. h. man setzt die restlichen unendlichen Stellen gleich 0.


Jede irrationale Zahle lässt sich nur durch einen nicht periodischen unendlichen Dezimalbruch, bzw. Dualbruch darstellen. Ist <math>\phi</math> aus <math>R \setminus Q </math>so lässt es sich als Grenzwert eines <math>b</math>-<math>a</math>-dischen Bruches darstellen.
Jede irrationale Zahl lässt sich nur durch einen nicht periodischen unendlichen Dezimalbruch bzw. Dualbruch darstellen. Ist <math>A</math> aus <math>\mathbb{R} \setminus \mathbb{Q}</math>, so lässt es sich als Grenzwert eines <math>b</math>-<math>a</math>-dischen Bruches darstellen.
Die Gleichverteilung und auch der goldene Schnitt lassen sich somit nur näherungsweise durch Algorithmen errechnen.


Die Gleichverteilung und der Goldene Schnitt lassen sich somit nur näherungsweise durch Algorithmen errechnen.
Dadurch erhält man folgendes Ergebnis:


== Bewertung ==
Die Gleichverteilung erreicht man nur unzureichende und dadurch werden die Schlüssel, gerade von gut sortierten Mengen (z.B. 1,2,3, ..), ungleichmäßig auf die Hashtabelle verteilt, was lange Ketten und leere Adressplätze zur Folge haben kann und damit einen langsameren Zugriff auf die Daten.


Den goldenen Schnitt kann man wohl auf beliebig viele Stelle genau berechnen und damit gut annähern, hat jedoch das Problem, dass gleichverteilte Mengen nicht optimal auf die Hashtabelle verteilt werden, da nach dem Satz von Vera Turan Sos nur gut sortierte Mengen optimal zwischen [0,1] verteilt werden.
Die Gleichverteilung erreicht man nur unzureichend, und dadurch werden die Schlüssel, gerade von gut sortierten Mengen (z.&nbsp;B. 1, 2, 3, ..), ungleichmäßig auf die Hashtabelle verteilt, was lange Ketten und leere Adressplätze zur Folge haben kann und damit einen langsameren Zugriff auf die Daten.


Den Goldenen Schnitt kann man auf beliebig viele Stellen genau berechnen und damit gut annähern, hat jedoch das Problem, dass gleichverteilte Mengen nicht optimal auf die Hashtabelle verteilt werden, da nach dem Satz von [[Vera Turan Sós]]<ref name="sos" /> nur gut sortierte Mengen optimal zwischen <math>[0,1]</math> verteilt werden.
Da man in der Praxis im Allgemeinen weder gleichverteilte noch sortierte Mengen vorfindet, ist der goldene Schnitt eine gute Wahl für die multiplikative Methode.


Da man allgemein in der Praxis weder gleichverteilte noch sortierte Mengen vorfindet, ist der Goldene Schnitt eine gute Wahl für die multiplikative Methode.
Es wurden noch viele andere Methoden vorgeschlagen (siehe Hash-Funktion). Lum, Yuen und Dodd [3] haben das Verhalten verschiedener Hash-Funktions-Methoden miteinander verglichen und stellten fest, dass die Divisionsrestmethode im Allgemeinen nicht schlechtere Resultate als andere Hash-Funktionen liefert.


Lum, Yuen und Dodd haben das Verhalten unterschiedlicher Hashfunktionen miteinander verglichen und stellten fest, dass die [[Divisionsrestmethode]] statistisch keine schlechteren Resultate als andere Hash-Funktionen liefert.<ref>V. Y. Lum, P. S. T. Yuen, M. Dodd: ''Key-to-address transform techniques: Performance study on large existing formatted files''. In: ''Communications of the ACM'', 14(4), April 1971, S. 228–239.</ref>


== Implementation ==
== Implementierung ==


Sei beispielsweise <math>A = s/w = (\sqrt{5}-1)/2 = 0.6180339887</math> mit <math>w = 2^{32}</math>. Dann ist <math>s = 2654435769</math>.
Die allgemeine Form der Implementation in C sieht wie folgt aus:


Dann lautet die zugehörige multiplikative Hash-Funktion <math>h</math>
<code><pre><nowiki>

<math>h(k) = \left\lfloor m (\frac{ks}{w} \bmod 1) \right\rfloor = \left\lfloor m \frac{ks \bmod w}{w} \right\rfloor</math>

wobei <math>m</math> mit <math>0<m<2^{32}-1</math> die Hashtablegröße und <math>k</math> mit <math>0\le k<2^{32}</math> einen Schlüsselwert bezeichnen.

Eine allgemeine Implementierung in der [[C (Programmiersprache)|Programmiersprache C]]:

<syntaxhighlight lang="C">
#include <stdio.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdlib.h>
#include <math.h>
#include <math.h>
#include <inttypes.h>


static const double s = 2654435769.0, w = 4294967296.0;
#define m 1024 /* Anzahl der Hashtabellenadressen */
#define A 2654435761.0 /* \phi = A/W */
#define w 4294967296.0 /* w =2^32 => A => 2654435761.0 / 4294967296.0 = 0.618033987 */


uint32_t mult_hash(uint32_t k, uint32_t m)
{
return (uint32_t) floor( ( (double) m * fmod( s * (double) k, w) ) / w );
}


int main(int argc, char **argv)
unsigned long int multiplication_hash(unsigned long int Key)
{
{
uint32_t l = mult_hash(atoi(argv[1]), 1024);
return((unsigned long int) floor( ( ((double)m) * fmod( ((((double)A)*((double)Key))),((double)w)) ) / ((double)w) ) );
printf("%" PRIu32 "\n", l);
return 0;
}
}
</syntaxhighlight>
</nowiki></pre></code>


Wählt man <math>m = 2^i</math> als Zweierpotenz, so lässt sich der Algorithmus wesentlich effizienter Implementieren:


<syntaxhighlight lang="C">
Wählt man <math>m</math> als Zweierpotenz, so lässt sich der Algorithmus wesentlich effizienter Implementieren:


<code><pre><nowiki>
#include <stdio.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdlib.h>
#include <math.h>
#include <math.h>
#include <inttypes.h>


static const uint32_t s = 2654435769;
#define m 10 /* m = 2^10 = 1024 */
#define w 32 /* bitsizeof (unsigned int) */
#define A 2654435761 /* */


uint32_t mult_hash(uint32_t kk, uint32_t i)
unsigned long int multiplication_hash(unsigned long int Key)
{
{
uint64_t k = kk;
return((A * Key) >> (w - m));
uint64_t t = s * k & 0x00000000FFFFFFFF;
return t >> (32 - i);
}
}
</nowiki></pre></code>


int main(int argc, char **argv)
== Literatur ==
{

uint32_t l = mult_hash(atoi(argv[1]), 10);
[1a] [[Donald E. Knuth]]. The Art of Computer Programming/Fundamental Algorithms. Volume 1. Addison-Wesley, Massachusetts, 3rd edition, 1997.
printf("%" PRIu32 "\n", l);
return 0;
}
</syntaxhighlight>


== Literatur ==
[1b] Donald E. Knuth. The Art of Computer Programming/Seminumerical Algorithms, Volume 2. Addison-Wesley, Massachusetts, 3rd edition, 1997.
* {{Literatur

|Autor=[[Donald E. Knuth]]
[1c] Donald E. Knuth. The Art of Computer Programming/Sorting and Searching. Volume 3.Addison-Wesley, Massachusetts, 2rd edition, 1997.
|Titel=[[The Art of Computer Programming]]

|Band=Volume 2
[2] Thomas Ottmann and Peter Widmayer. Algorithmen und Datenstrukturen. B.I. Wissenschaftsverlag, Mannheim, Leipzig, Wien, Zürich, zweite Edition, 1993.
|Auflage=3.

|Verlag=Addison-Wesley
[3] V. Y. Lum, P. S. T. Yuen, and M. Dodd. Key-to-adress transform techniques: Performance study on large existing formatted files. Communications of the ACM, 14(4):228--239, April 1971.
|Ort=
|Jahr=1998
|ISBN=0-201-89684-2
|Seiten=70 ff}}
* {{Literatur
|Autor=[[Donald E. Knuth]]
|Titel=[[The Art of Computer Programming]]
|Band=Volume 3
|Auflage=2.
|Verlag=Addison-Wesley
|Ort=
|Jahr=1998
|ISBN=0-201-89685-0
|Seiten=516 ff.}}
* {{Literatur
|Autor=[[Cormen]] et al.
|Titel=Introduction to Algorithms
|Auflage=2.
|Verlag=MIT Press
|Ort=
|Jahr=2001
|ISBN=0-262-03293-7
|Seiten=231 ff.}}
* William H. Press, Saul A. Teukolsky, William T. Vetterling, Brian P. Flannery: ''Numerical Recipes in C++ / The Art of Scientific Computing''. Cambridge University Press, 2nd edition, 1988.


== Einzelnachweise ==
[4] William H. Press, Saul A. Teukolsky, William T. Vetterling, and Brian P. Flannery. Numerical Recipes in C++/ The Art of Scientific Computing. Cambridge University Press, 2nd edition, 1988.
<references />


[[Kategorie:Algorithmus]]
[[Kategorie:Hash]]

Aktuelle Version vom 2. Mai 2020, 09:45 Uhr

Die multiplikative Methode ist ein Schema, nach dem Hash-Funktionen entwickelt werden können. Dabei wird das Produkt des Schlüssels mit einer Zahl gebildet und der ganzzahlige Anteil abgeschnitten, so dass der Schlüssel in das Intervall abgebildet wird. Das Ergebnis wird mit der Anzahl der Hashtabellenadressen multipliziert und nach unten abgerundet. In Formelschreibweise ist eine Hash-Funktion , die die multiplikative Methode implementiert, definiert als:

Entscheidend für die Verteilung der Schlüssel auf den Adressbereich der Hashtabelle ist bei dieser Methode die Wahl von , die unabhängig von der Wahl von ist. In der Literatur wird der Goldene Schnitt für eine gute Wahl von vorgeschlagen,[1][2] mit dem in der Praxis mit vielen typischen Eingabedaten die Verteilung der Hash-Werte der Schlüssel nahezu gleichverteilt ist. Wird diese Zahl verwendet, so nennt man die resultierende Hash-Funktion auch den Fibonacci-Hash.

Die multiplikative Methode lässt sich als Verallgemeinerung der Divisionsrestmethode sehen. Setzt man , so ist

In der Theorie des Hashens wird durch die Gleichverteilung der Schlüssel auf die Adressen der Hashtabelle üblicherweise versucht, die Schlüssel gleichmäßig auf die Tabelle zu verteilen und dadurch eine Minimierung von Kollisionen zu erreichen.

Die Gleichverteilung ist ein Zufallsexperiment mit der Wahrscheinlichkeit , wenn ein Elementarereignis ist und die Anzahl der Elementarereignisse, z. B. der Münzwurf oder der Würfel .

Der Goldene Schnitt ist eine irrationale Zahl, d. h. sie ist aus , und er erreicht eine gute Verteilung der Schlüssel auf den Adressbereich der Hashtabelle durch folgende Eigenschaft von irrationalen Zahlen:

Sei eine irrationale Zahl. Platziert man die Punkte in das Intervall , dann haben die Intervallteile höchstens drei verschiedene Längen. Außerdem fällt der nächste Punkt, , in einen der größten Intervallteile.[3]

Jeder periodische Dezimalbruch bzw. Dualbruch stellt eine rationale Zahl dar. Natürliche oder rationale Zahlen mit endlich vielen Stellen, z. B. sind 0-periodisch, d. h. man setzt die restlichen unendlichen Stellen gleich 0.

Jede irrationale Zahl lässt sich nur durch einen nicht periodischen unendlichen Dezimalbruch bzw. Dualbruch darstellen. Ist aus , so lässt es sich als Grenzwert eines --dischen Bruches darstellen.

Die Gleichverteilung und der Goldene Schnitt lassen sich somit nur näherungsweise durch Algorithmen errechnen.

Die Gleichverteilung erreicht man nur unzureichend, und dadurch werden die Schlüssel, gerade von gut sortierten Mengen (z. B. 1, 2, 3, ..), ungleichmäßig auf die Hashtabelle verteilt, was lange Ketten und leere Adressplätze zur Folge haben kann und damit einen langsameren Zugriff auf die Daten.

Den Goldenen Schnitt kann man auf beliebig viele Stellen genau berechnen und damit gut annähern, hat jedoch das Problem, dass gleichverteilte Mengen nicht optimal auf die Hashtabelle verteilt werden, da nach dem Satz von Vera Turan Sós[3] nur gut sortierte Mengen optimal zwischen verteilt werden.

Da man allgemein in der Praxis weder gleichverteilte noch sortierte Mengen vorfindet, ist der Goldene Schnitt eine gute Wahl für die multiplikative Methode.

Lum, Yuen und Dodd haben das Verhalten unterschiedlicher Hashfunktionen miteinander verglichen und stellten fest, dass die Divisionsrestmethode statistisch keine schlechteren Resultate als andere Hash-Funktionen liefert.[4]

Implementierung

[Bearbeiten | Quelltext bearbeiten]

Sei beispielsweise mit . Dann ist .

Dann lautet die zugehörige multiplikative Hash-Funktion

wobei mit die Hashtablegröße und mit einen Schlüsselwert bezeichnen.

Eine allgemeine Implementierung in der Programmiersprache C:

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <inttypes.h>

static const double s = 2654435769.0,  w = 4294967296.0;

uint32_t mult_hash(uint32_t k, uint32_t m)
{
  return (uint32_t) floor( ( (double) m * fmod( s * (double) k, w) ) / w );
}

int main(int argc, char **argv)
{
  uint32_t l = mult_hash(atoi(argv[1]), 1024);
  printf("%" PRIu32 "\n", l);
  return 0;
}

Wählt man als Zweierpotenz, so lässt sich der Algorithmus wesentlich effizienter Implementieren:

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <inttypes.h>

static const uint32_t s = 2654435769;

uint32_t mult_hash(uint32_t kk, uint32_t i)
{
  uint64_t k = kk;
  uint64_t t = s * k & 0x00000000FFFFFFFF;
  return t >> (32 - i);
}

int main(int argc, char **argv)
{
  uint32_t l = mult_hash(atoi(argv[1]), 10);
  printf("%" PRIu32 "\n", l);
  return 0;
}

Einzelnachweise

[Bearbeiten | Quelltext bearbeiten]
  1. Donald E. Knuth: The Art of Computer Programming / Sorting and Searching. Volume 3. Addison-Wesley, Massachusetts, 2rd edition, 1997; Seite 516
  2. Thomas Ottmann, Peter Widmayer: Algorithmen und Datenstrukturen. zweite Edition. B.I. Wissenschaftsverlag, Mannheim / Leipzig / Wien / Zürich 1993
  3. a b Vera Turan Sós (Acta Math. Acad. Sci. Hung. 8 (1957), 461–471)
  4. V. Y. Lum, P. S. T. Yuen, M. Dodd: Key-to-address transform techniques: Performance study on large existing formatted files. In: Communications of the ACM, 14(4), April 1971, S. 228–239.