Hallo Statistiker,
Ich habe eine Quelle, die Hashes generiert (z. B. das Berechnen eines Strings mit einem Zeitstempel und anderen Informationen und das Hashing mit md5) und ich möchte es in eine feste Anzahl von Buckets projizieren (z. B. 100).
Beispiel-Hash: 0fb916f0b174c66fd35ef078d861a367
Was ich zuerst dachte, war, nur das erste Zeichen des Hashs zu verwenden, um einen Eimer auszuwählen, aber dies führt zu einer wild ungleichmäßigen Projektion (dh einige Buchstaben erscheinen sehr selten und andere sehr häufig).
Dann habe ich versucht, diese Hexa-Zeichenfolge mit der Summe der Zeichenwerte in eine Ganzzahl umzuwandeln, und dann mit dem Modulo einen Bucket ausgewählt:
import sys
for line in sys.stdin:
i = 0
for c in line:
i += ord(c)
print i%100
Es scheint in der Praxis zu funktionieren, aber ich weiß nicht, ob es einen gesunden Menschenverstand oder theoretische Ergebnisse gibt, die erklären könnten, warum und inwieweit dies zutrifft.
[Bearbeiten] Nach einigem Überlegen kam ich zu folgendem Schluss: Theoretisch können Sie den Hash in eine (sehr große) Ganzzahl umwandeln, indem Sie ihn als Zahl interpretieren: i = h [0] + 16 * h [1] + 16 * 16 * h [2] ... + 16 ^ 31 * h [31] (jeder Buchstabe steht für eine Hexadezimalzahl). Dann könnten Sie diese große Zahl modulieren, um sie auf den Bucket Space zu projizieren. [/Bearbeiten]
Vielen Dank !
Antworten:
NB: Formulieren Sie die Antwort, die aus der Diskussion in Kommentaren hervorgegangen ist, so, dass sie für interessierte Personen leichter zu lesen ist
(aktualisierte Version)
Die wichtigsten Schritte sind:
Für 1. ist eine beliebte Lösung die Verwendung von MurmurHash , um eine 64- oder 128-Bit-Ganzzahl zu generieren.
Im (Python-) Pseudocode könnte die Gesamtprozedur sein:
(vorherige Version, wirklich nicht optimal)
Die erste Beobachtung ist, dass der n- te Buchstabe des Hashs in Bezug auf das Alphabet gleichmäßig verteilt sein sollte (das hier 16 Buchstaben lang ist - danke an @leonbloy für den Hinweis).
Um es dann auf einen Bereich von [0,100 [zu projizieren, besteht der Trick darin, 2 Buchstaben aus dem Hash (z. B. 1. und 2. Position) zu nehmen und damit eine ganze Zahl zu generieren:Dieser Wert lebt im Bereich [0,16+ (16-1) * 16 [, daher müssen wir nur Modulo auf 100 einen Eimer in dem [0, 100 [Bereich zu erzeugen:Wie in den Kommentaren darauf hingewiesen, tun Dies wirkt sich auf die Gleichmäßigkeit der Verteilung aus, da der erste Buchstabe einflussreicher ist als der zweite.Theoretisch können Sie den gesamten Hash in eine (sehr große) Ganzzahl umwandeln, indem Sie ihn als Zahl interpretieren: i = h [0] + 16 * h [1] + 16 * 16 * h [2] ... + 16 ^ 31 * h [31] (jeder Buchstabe steht für eine Hexadezimalzahl). Dann könnten Sie diese große Zahl modulieren, um sie auf den Bucket Space zu projizieren. Man kann dann feststellen, dass das Modulo von i in eine verteilende und additive Operation zerlegt werden kann:
quelle
Ich hatte ein ähnliches Problem und fand eine andere Lösung, die in jeder Sprache schneller und einfacher implementiert werden kann.
Mein erster Gedanke war, Artikel schnell und gleichmäßig in einer festen Anzahl von Eimern zu versenden, und um skalierbar zu sein, sollte ich die Zufälligkeit nachahmen.
Also habe ich diese kleine Funktion codiert, die eine Gleitkommazahl in [0, 1 [mit einem String (oder einer beliebigen Art von Daten) zurückgibt.
Hier in Python:
Natürlich ist es nicht zufällig, tatsächlich ist es nicht einmal pseudozufällig, dieselben Daten geben immer dieselbe Prüfsumme zurück. Aber es verhält sich zufällig und ist ziemlich schnell.
Sie können Artikel in N Buckets einfach versenden und später abrufen, indem Sie jeden Artikel einfach der Bucket-Nummer math.floor (N * pseudo_random_checksum (item)) zuweisen.
quelle