Welcher Hashing-Algorithmus eignet sich am besten für Eindeutigkeit und Geschwindigkeit? Beispiel (gute) Verwendungen umfassen Hash-Wörterbücher.
Ich weiß, dass es Dinge wie SHA-256 und dergleichen gibt, aber diese Algorithmen sind als sicher konzipiert, was normalerweise bedeutet, dass sie langsamer als Algorithmen sind das sind weniger einzigartig . Ich möchte einen Hash-Algorithmus, der schnell ausgelegt ist und dennoch ziemlich einzigartig bleibt, um Kollisionen zu vermeiden.
Kommentare
- Zu welchem Zweck, zu welcher Sicherheit oder zu anderen Zwecken?
- @Orbling, zur Implementierung eines Hash-Wörterbuchs. Kollisionen sollten daher auf ein Minimum beschränkt werden, haben jedoch überhaupt keinen Sicherheitszweck.
- Beachten Sie, dass Sie mindestens einige Kollisionen in Ihrer Hash-Tabelle erwarten müssen, andernfalls die Der Tisch muss riesig sein, um auch nur eine relativ kleine Anzahl von Schlüsseln verarbeiten zu können …
- Großartiger Beitrag! Könnten Sie auch ‚ s Yann Collet ‚ s xxHash (Schöpfer oder LZ4) überprüfen, das doppelt so schnell ist wie Murmeln? Startseite: code.google.com/p/xxhash Weitere Informationen: fastcompression.blogspot.fr/2012/ 04 / …
- @zvrba Abhängig vom Algorithmus. bcrypt ist so konzipiert, dass es langsam ist.
Antwort
Ich habe verschiedene Algorithmen getestet, um die Geschwindigkeit und die Anzahl der Kollisionen zu messen
Ich habe drei verschiedene Schlüsselsätze verwendet:
- Eine Liste von 216.553 englischen Wörtern 🕗 Archiv (in Kleinbuchstaben)
- Die Zahlen
"1"
bis"216553"
(denken Sie an Postleitzahlen und , wie ein schlechter Hash msn.com eruntergefahren hat 🕗 Archiv ) - 216.553 “ zufällige „(dh Typ 4-UUID ) GUIDs
Für jeden Korpus die Anzahl der Kollisionen und die durchschnittliche Zeit, die für das Hashing aufgewendet wurde wurde aufgezeichnet.
Ich habe getestet:
- DJB2
- DJB2a (Variante mit
xor
anstelle von+
) - FNV-1 (32-Bit)
- FNV-1a (32-Bit)
- SDBM
- CRC32
- Murmur2 (32-Bit)
- SuperFastHash
Ergebnisse
Jedes Ergebnis enthält die durchschnittliche Hash-Zeit und die Anzahl der Kollisionen
Hash Lowercase Random UUID Numbers ============= ============= =========== ============== Murmur 145 ns 259 ns 92 ns 6 collis 5 collis 0 collis FNV-1a 152 ns 504 ns 86 ns 4 collis 4 collis 0 collis FNV-1 184 ns 730 ns 92 ns 1 collis 5 collis 0 collis▪ DBJ2a 158 ns 443 ns 91 ns 5 collis 6 collis 0 collis▪▪▪ DJB2 156 ns 437 ns 93 ns 7 collis 6 collis 0 collis▪▪▪ SDBM 148 ns 484 ns 90 ns 4 collis 6 collis 0 collis** SuperFastHash 164 ns 344 ns 118 ns 85 collis 4 collis 18742 collis CRC32 250 ns 946 ns 130 ns 2 collis 0 collis 0 collis LoseLose 338 ns - - 215178 collis
Hinweise :
- Die Der LoseLose-Algorithmus (wobei Hash = Hash + Zeichen) ist wirklich schrecklich . Alles kollidiert in denselben 1.375 Eimern.
- SuperFastHash ist schnell und die Dinge sehen ziemlich verstreut aus. Meine Güte, die Zahl Kollisionen. Ich hoffe, der Typ, der es portiert hat, hat etwas falsch gemacht; es ist ziemlich schlecht
- CRC32 ist ziemlich gut . Langsamer und eine 1k-Nachschlagetabelle
Treten tatsächlich Kollisionen auf?
Ja. Ich habe angefangen, mein Testprogramm zu schreiben, um festzustellen, ob Hash-Kollisionen tatsächlich auftreten – und nicht nur ein theoretisches Konstrukt sind.Sie treten tatsächlich auf:
FNV-1-Kollisionen
-
creamwove
kollidiert mitquists
FNV -1a Kollisionen
-
costarring
kollidiert mitliquid
-
declinate
kollidiert mitmacallums
-
altarage
kollidiert mitzinke
-
altarages
kollidiert mitzinkes
Murmel2-Kollisionen
-
cataract
kollidiert mitperiti
-
roquette
kollidiert mitskivie
-
shawl
kollidiert mitstormbound
-
dowlases
kollidiert mittramontane
-
cricketings
kollidiert mittwanger
-
longans
kollidiert mitwhigs
DJB2-Kollisionen
-
hetairas
kollidiert mitmentioner
-
heliotropes
kollidiert mitneurospora
-
depravement
kollidiert mitserafins
-
stylist
kollidiert mitsubgenera
-
joyful
kollidiert mitsynaphea
-
redescribed
kollidiert miturites
-
dram
kollidiert mitvivency
DJB2a-Kollisionen
-
haggadot
kollidiert mitloathsomenesses
-
adorablenesses
kollidiert mitrentability
-
playwright
kollidiert mitsnush
-
playwrighting
kollidiert mitsnushing
-
treponematoses
kollidiert mitwaterbeds
CRC32-Kollisionen
-
codding
kollidiert mitgnu
-
exhibiters
kollidiert mitschlager
SuperFastHash-Kollisionen
-
dahabiah
kollidiert mitdrapability
-
encharm
kollidiert mitenclave
-
grahams
kollidiert mitgramary
- … schnitt 79 Kollisionen ab …
-
night
kollidiert mitvigil
- kollidiert mit
vigils
-
finks
kollidiert mitvinic
Randomnessification
Das andere subjektive Maß ist die zufällige Verteilung der Hashes. Die Zuordnung der resultierenden HashTables zeigt, wie gleichmäßig die Daten verteilt sind. Alle Hash-Funktionen zeigen eine gute Verteilung, wenn die Tabelle linear zugeordnet wird:
Oder als Hilbert Map ( XKCD ist immer relevant ):
Außer beim Hashing von Zahlenfolgen ("1"
, , …, "216553"
) (z. B. Postleitzahlen ), wo Muster beginnen in den meisten Hashing-Algorithmen:
SDBM :
DJB2a :
FNV-1 :
Alle außer
FNV-1a , die für mich immer noch ziemlich zufällig aussehen:
Tatsächlich scheint Murmur2 eine noch bessere Zufälligkeit mit zu haben Numbers
als FNV-1a
:
Wenn ich mir die Zuordnung
FNV-1a
„number“ ansehe, denke Ich sehe subtile vertikale Muster. Mit Murmeln sehe ich überhaupt keine Muster. Was denken Sie?
Die zusätzliche *
in der Tabelle gibt an, wie schlecht die Zufälligkeit ist. Mit FNV-1a
als bestem und DJB2x
ist das Schlimmste:
Murmur2: . FNV-1a: . FNV-1: ▪ DJB2: ▪▪ DJB2a: ▪▪ SDBM: ▪▪▪ SuperFastHash: . CRC: ▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪ Loselose: ▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪ ▪ ▪▪▪▪▪▪▪▪▪▪▪▪▪ ▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪ ▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪
Ich habe dieses Programm ursprünglich geschrieben, um zu entscheiden, ob ich mir überhaupt Sorgen um Kollisionen machen muss: Das tue ich.
Und dann stellte sich heraus, dass die Hash-Funktionen ausreichend zufällig waren.
FNV-1a-Algorithmus
Der FNV1-Hash gibt es in Varianten, die 32-, 64-, 128-, 256-, 512- und 1024-Bit-Hashes zurückgeben.
Der FNV-1a-Algorithmus lautet:
hash = FNV_offset_basis for each octetOfData to be hashed hash = hash xor octetOfData hash = hash * FNV_prime return hash
Dabei hängen die Konstanten FNV_offset_basis
und FNV_prime
von der gewünschten Rückgabe-Hash-Größe ab :
Hash Size =========== 32-bit prime: 2^24 + 2^8 + 0x93 = 16777619 offset: 2166136261 64-bit prime: 2^40 + 2^8 + 0xb3 = 1099511628211 offset: 14695981039346656037 128-bit prime: 2^88 + 2^8 + 0x3b = 309485009821345068724781371 offset: 144066263297769815596495629667062367629 256-bit prime: 2^168 + 2^8 + 0x63 = 374144419156711147060143317175368453031918731002211 offset: 100029257958052580907070968620625704837092796014241193945225284501741471925557 512-bit prime: 2^344 + 2^8 + 0x57 = 35835915874844867368919076489095108449946327955754392558399825615420669938882575126094039892345713852759 offset: 9659303129496669498009435400716310466090418745672637896108374329434462657994582932197716438449813051892206539805784495328239340083876191928701583869517785 1024-bit prime: 2^680 + 2^8 + 0x8d = 5016456510113118655434598811035278955030765345404790744303017523831112055108147451509157692220295382716162651878526895249385292291816524375083746691371804094271873160484737966720260389217684476157468082573 offset: 1419779506494762106872207064140321832088062279544193396087847491461758272325229673230371772250864096521202355549365628174669108571814760471015076148029755969804077320157692458563003215304957150157403644460363550505412711285966361610267868082893823963790439336411086884584107735010676915
Weitere Informationen finden Sie unter auf der FNV-Hauptseite .
Alle meine Ergebnisse sind mit der 32-Bit-Variante.
FNV-1 besser als FNV-1a?
Nein. FNV-1a ist rundum besser. Bei Verwendung des englischen Wortkorpus gab es mehr Kollisionen mit FNV-1a:
Hash Word Collisions ====== =============== FNV-1 1 FNV-1a 4
Vergleichen Sie nun Klein- und Großbuchstaben:
Hash lowercase word Collisions UPPERCASE word collisions ====== ========================= ========================= FNV-1 1 9 FNV-1a 4 11
In diesem Fall ist FNV-1a nicht“ 400% „schlechter als FN-1, nur 20% schlechter.
Ich denke, das Wichtiger ist, dass es bei Kollisionen zwei Klassen von Algorithmen gibt:
- seltene Kollisionen : FNV-1, FNV-1a, DJB2, DJB2a, SDBM
- häufige Kollisionen : SuperFastHash, Loselose
Und dann ist da, wie gleichmäßig die Hashes verteilt sind:
- Hervorragende Verteilung: Murmeln2, FNV-1a, SuperFastHas
- Hervorragende Verteilung: FNV-1
- gute Verteilung: SDBM, DJB2, DJB2a
-
schreckliche Verteilung: Loselose
Update
Murmeln? Sicher, warum nicht
Update
@whatshisname fragte sich, wie ein CRC32 funktionieren würde, fügte der Tabelle Zahlen hinzu.
CRC32 ist ziemlich gut . Nur wenige Kollisionen, aber langsamer, und der Overhead einer 1k-Nachschlagetabelle.
Snip alle fehlerhaften Dinge über die CRC-Verteilung – mein schlechtes
Up Bis heute wollte ich FNV-1a als meinen de facto Hash-Table-Hashing-Algorithmus verwenden. Aber jetzt wechsle ich zu Murmur2:
- Schneller
- Bessere Zufälligkeit aller Eingabeklassen
Und ich hoffe wirklich, wirklich , dass mit dem SuperFastHash
Algorithmus, den ich gefunden habe, etwas nicht stimmt / a>; Es ist schade, so beliebt zu sein wie es ist.
Update: Von die MurmurHash3-Homepage bei Google :
(1) – SuperFastHash hat sehr schlechte Kollisionseigenschaften wurden an anderer Stelle dokumentiert.
Ich denke, es ist nicht nur ich.
Update: Ich habe festgestellt, warum Murmur
schneller ist als die anderen. MurmurHash2 arbeitet mit jeweils vier Bytes. Die meisten Algorithmen sind Byte für Byte :
for each octet in Key AddTheOctetToTheHash
Dies bedeutet, dass Murmur mit zunehmender Länge die Chance erhält, zu glänzen. P. >
Update
GUIDs sind so konzipiert, dass sie eindeutig und nicht zufällig sind
Ein zeitnaher Beitrag von Raymond Chen bekräftigt die Tatsache, dass „zufällige“ GUIDs nicht für ihre Zwecke bestimmt sind Zufälligkeit. Sie oder eine Teilmenge davon sind als Hash-Schlüssel ungeeignet:
Selbst der GUID-Algorithmus der Version 4 ist aufgrund des Algorithmus nicht unvorhersehbar gibt nicht die Qualität des Zufallszahlengenerators an. Der Wikipedia-Artikel für GUID enthält Primärrecherchen, die darauf hinweisen, dass zukünftige und frühere GUIDs auf der Grundlage der Kenntnis des Zustands des Zufallszahlengenerators vorhergesagt werden können, da der Generator nicht kryptografisch ist stark.
Zufälligkeit ist nicht dasselbe wie Kollisionsvermeidung. Aus diesem Grund wäre es ein Fehler, einen eigenen „Hashing“ -Algorithmus zu erfinden, indem Sie eine Teilmenge einer „zufälligen“ Guid verwenden:
int HashKeyFromGuid(Guid type4uuid) { //A "4" is put somewhere in the GUID. //I can"t remember exactly where, but it doesn"t matter for //the illustrative purposes of this pseudocode int guidVersion = ((type4uuid.D3 & 0x0f00) >> 8); Assert(guidVersion == 4); return (int)GetFirstFourBytesOfGuid(type4uuid); }
Hinweis : Wiederum setze ich „zufällige GUID“ in Anführungszeichen, weil es „zufällig“ ist. Variante von GUIDs. Eine genauere Beschreibung wäre Type 4 UUID
. Aber niemand weiß, was Typ 4 oder Typ 1, 3 und 5 sind. Es ist also einfacher, sie zufällig zu nennen „GUIDs.
Alle englischen Wörter spiegeln
- https://web.archive.org/web/20070221060514/http://www.sitopreferito.it/html/all_english_words.html
- https://drive.google.com/file/d/0B3BLwu7Vb2U-dEw1VkUxc3U4SG8/view?usp=sharing
Kommentare
- Es wäre wirklich interessant zu sehen, wie SHA verglichen wird, nicht weil es ‚ ein guter Kandidat für einen Hashing-Algorithmus ist, aber es Es wäre wirklich interessant zu sehen, wie ein kryptografischer Hash mit diesen für Geschwindigkeitsalgorithmen erstellten verglichen wird.
- Ein neuer Hash mit dem Namen e von ‚ xxHash ‚ von Yann Collet machte kürzlich die Runde. Ich ‚ bin immer misstrauisch gegenüber einem neuen Hash. Es wäre interessant, dies in Ihrem Vergleich zu sehen (wenn Sie ‚ nicht müde sind von Leuten, die zufällige Hashes vorschlagen, von denen sie ‚ gehört haben hinzugefügt werden …)
- In der Tat. Die auf der xxHash-Projektseite angekündigten Leistungszahlen sehen beeindruckend aus, vielleicht zu viel, um wahr zu sein. Zumindest ist ‚ ein Open-Source-Projekt: code.google.com/p/xxhash
- Hallo Ian, meine Delphi-Implementierung von SuperFastHash ist korrekt. Bei der Implementierung habe ich einen Testsatz in C und Delphi erstellt, um die Ergebnisse meiner Implementierung und die Referenzimplementierung zu vergleichen. Es gibt keine Unterschiede. Was Sie also sehen, ist die tatsächliche Schlechtigkeit des Hash … (Deshalb habe ich auch eine MurmurHash-Implementierung veröffentlicht: landman-code.blogspot.nl/2009/02/ … )
- Ist dem Poster bewusst, dass dies nicht nur eine großartige Antwort ist – dies ist die Welt ‚ s de facto Referenzressource zu diesem Thema? Immer wenn ich mich mit Hashes befassen muss, löst dies mein Problem so schnell und maßgeblich, dass ich ‚ nie etwas anderes benötige.
Antwort
Wenn Sie eine Hash-Map aus einem unveränderlichen Wörterbuch erstellen möchten, sollten Sie ein perfektes Hashing in Betracht ziehen. https://en.wikipedia.org/wiki/Perfect_hash_function – Während der Erstellung der Hash-Funktion und der Hash-Tabelle können Sie für einen bestimmten Datensatz garantieren, dass keine Kollisionen auftreten.
Kommentare
- Hier ‚ erfahren Sie mehr über (minimales) perfektes Hashing burtleburtle.net/bob/hash/perfect.html einschließlich Leistungsdaten, obwohl ‚ nicht den aktuellsten Prozessor usw. verwendet.
- ‚ ist ziemlich offensichtlich, aber es sollte darauf hingewiesen werden, dass die Schlüssel dieselbe Größe wie die Werte haben müssen, um keine Kollisionen zu gewährleisten, es sei denn, th Es gibt Einschränkungen für die Werte, die der Algorithmus nutzen kann.
- @ devios1 Ihre Aussage ist bedeutungslos. Erstens sind die Werte in einer Hash-Tabelle, ob perfekt oder nicht, unabhängig von den Schlüsseln. Zweitens ist eine perfekte Hash-Tabelle nur ein lineares Array von Werten, das durch das Ergebnis der Funktion indiziert wird, die so erstellt wurde, dass alle Indizes eindeutig sind.
- @MarcusJ Perfektes Hash wird normalerweise mit weniger als 100 verwendet Schlüssel, aber werfen Sie einen Blick auf cmph.sourceforge.net … immer noch weit außerhalb Ihrer Reichweite.
- @DavidCary Nichts bei Ihnen Link unterstützt Ihren Anspruch. Möglicherweise haben Sie O (1) mit “ keine Kollisionen “ verwechselt, aber sie sind nicht ‚ überhaupt nicht dasselbe. Natürlich garantiert perfektes Hashing keine Kollisionen, aber es erfordert, dass alle Schlüssel im Voraus bekannt sind und dass es relativ wenige davon gibt. (Siehe jedoch den Link zu cmph oben.)
Antwort
Hier ist eine Liste von Hash-Funktionen, aber die Kurzversion lautet:
Wenn Sie nur eine gute Hash-Funktion haben möchten und kann es kaum erwarten,
djb2
ist eine der besten String-Hash-Funktionen, die ich kenne. Es verfügt über eine hervorragende Verteilung und Geschwindigkeit auf vielen verschiedenen Sätzen von Schlüsseln und Tabellengrößen.
unsigned long hash(unsigned char *str) { unsigned long hash = 5381; int c; while (c = *str++) hash = ((hash << 5) + hash) + c; /* hash * 33 + c */ return hash; }
Kommentare
- Tatsächlich ist djb2 wie die meisten einfachen Hash-Funktionen nullempfindlich, sodass Sie solche Hashes leicht brechen können.Es hat eine schlechte Tendenz zu viele Kollisionen und eine schlechte Verteilung, es bricht bei den meisten smhasher-Qualitätstests: Siehe github.com/rurban/smhasher/blob/master/doc/bernstein Seine CDB-Datenbank verwendet es, aber ich würde es nicht ‚ mit öffentlichem Zugriff verwenden.
- DJB ist vom Standpunkt der Leistung und Verteilung ziemlich schlecht. Ich würde ‚ es heute nicht verwenden.
- @ConradMeyer Ich ‚ würde wetten, dass DJB durch beschleunigt werden kann ein Faktor von drei, genau wie in dieser Frage von mir und dann ‚ wahrscheinlich die meisten verwendbaren Algorithmen schlagen würde. In Bezug auf die Verteilung stimme ich zu. Ein Hash, der selbst für zwei Buchstabenketten Kollisionen erzeugt, kann ‚ nicht wirklich gut sein.
- Leute, ich habe Zweifel. Sie sagen,
djb2
ist schlecht, aber die Testergebnisse der akzeptierten Antwort zeigen, dass es gut ist. - Sie könnten zumindest eine sinnvolle Primzahl verwenden, die weniger Kollisionen erzeugt statt 33. stackoverflow.com/a/2816747/21499
Antwort
CityHash von Google ist der gesuchte Algorithmus. Es ist nicht gut für die Kryptografie, aber gut für die Erzeugung eindeutiger Hashes.
Lesen Sie den Blog für weitere Details und den Blog Code ist hier verfügbar .
CityHash ist in C ++ geschrieben. Es gibt auch einen einfachen C-Port .
Informationen zur 32-Bit-Unterstützung:
Alle CityHash-Funktionen sind für 64-Bit-Prozessoren optimiert. Das heißt, sie werden (mit Ausnahme der neuen, die SSE4.2 verwenden) in 32-Bit-Code ausgeführt. Sie werden jedoch nicht sehr schnell sein. Möglicherweise möchten Sie Murmeln oder etwas anderes in 32-Bit-Code verwenden.
Kommentare
- Wird CityHash ähnlich wie “ City Sushi ausgesprochen? “
- Haben Sie eine Schauen Sie sich auch SipHash an, es soll MurmurHash / CityHash / etc ersetzen. 131002.net/siphash
- Siehe auch FarmHash, a Nachfolger von CitHash. code.google.com/p/farmhash
- xxHash behauptet, 5x schneller als CityHash zu sein.
-
plain C port
Link ist unterbrochen
Antwort
Ich habe einen kurzen Geschwindigkeitsvergleich verschiedener Hashing-Algorithmen beim Hashing von Dateien erstellt.
Die einzelnen Diagramme unterscheiden sich nur geringfügig in der Lesemethode und können hier ignoriert werden, da alle Dateien in einem tmpfs gespeichert wurden. Daher war der Benchmark nicht an E / A gebunden, wenn Sie sich fragen.
Zu den Algorithmen gehören: SpookyHash, CityHash, Murmur3, MD5, SHA{1,256,512}
.
Schlussfolgerungen:
- Nicht kryptografische Hash-Funktionen wie Murmur3, Cityhash und Spooky liegen ziemlich nahe beieinander. Man sollte beachten, dass Cityhash auf CPUs mit SSE 4.2s
CRC
-Anweisung, die meine CPU nicht hat, möglicherweise schneller ist. SpookyHash war in meinem Fall immer ein kleines bisschen vor CityHash. - MD5 scheint ein guter Kompromiss bei der Verwendung kryptografischer Hash-Funktionen zu sein, obwohl SHA256 für Kollisionsschwachstellen von MD5 und SHA1.
- Die Komplexität aller Algorithmen ist linear – was wirklich nicht überraschend ist, da sie blockweise arbeiten. (Ich wollte sehen, ob die Lesemethode einen Unterschied macht, damit Sie nur die Werte ganz rechts vergleichen können.)
- SHA256 war langsamer als SHA512.
- Ich habe die Zufälligkeit von nicht untersucht Die Hash-Funktionen. Aber hier ist ein guter Vergleich der Hash-Funktionen, die in der Antwort von Ian Boyds fehlen. Dies weist darauf hin, dass CityHash in Eckfällen einige Probleme hat.
Die für die Diagramme verwendete Quelle:
- https://github.com/sahib/rmlint/tree/gh-pages/plots (Entschuldigung für den hässlichen Code)
Kommentare
- Das lineare Skalendiagramm schneidet die Beschriftung der y-Achse ab, die angibt, welche Menge gezeichnet wird. Ich denke, es wäre wahrscheinlich “ Zeit in Sekunden „, genau wie die logarithmische Skala. Es lohnt sich, ‚ zu beheben.
Antwort
Ich weiß, dass es Dinge wie SHA-256 und dergleichen gibt, aber diese Algorithmen sind entworfen um sicher zu sein , was normalerweise bedeutet, dass sie langsamer sind als Algorithmen, die weniger eindeutig sind.
Die Annahme, dass kryptografische Hash-Funktionen eindeutiger sind, ist falsch und kann in der Praxis häufig als rückwärts gezeigt werden. In Wahrheit:
- Kryptografische Hash-Funktionen sollten idealerweise nicht von zufälligen ;
- Bei nicht kryptografischen Hash-Funktionen ist es jedoch wünschenswert, dass günstig mit wahrscheinlichen Eingaben interagiert.
Dies bedeutet, dass eine nicht kryptografische Hash-Funktion möglicherweise weniger Kollisionen aufweist als a kryptografischer Datensatz für „guten“ Datensatz – Datensätze, für die er entwickelt wurde.
Wir können dies anhand der Daten in Ian Boyds Antwort und ein wenig Mathematik demonstrieren: die Geburtstagsproblem . Die Formel für die erwartete Anzahl kollidierender Paare bei Auswahl von n
Ganzzahlen aus der Menge [1, d]
lautet wie folgt (aus Wikipedia):
n - d + d * ((d - 1) / d)^n
Einstecken von n
= 216.553 und d
= 2 ^ 32 Wir erhalten ungefähr 5.5 erwartete Kollisionen . Ians Tests zeigen meistens Ergebnisse in dieser Nachbarschaft, aber mit einer dramatischen Ausnahme: Die meisten Funktionen haben keine Kollisionen in der Tests aufeinanderfolgender Zahlen. Die Wahrscheinlichkeit, 216.553 32-Bit-Zahlen zufällig auszuwählen und keine Kollisionen zu erhalten, liegt bei etwa 0,43%. Und das nur für eine Funktion – hier haben wir fünf verschiedene Hash-Funktionsfamilien mit Null Kollisionen!
Was wir hier also sehen, ist, dass die von Ian getesteten Hashes günstig mit dem Datensatz für fortlaufende Zahlen interagieren – dh sie verteilen sich minimal unterschiedlich Eingaben weiter als eine ideale kryptografische Hash-Funktion. (Randnotiz: Dies bedeutet, dass Ians grafische Einschätzung, dass FNV-1a und MurmurHash2 für ihn im Zahlendatensatz „zufällig“ aussehen, aus seinen eigenen Daten widerlegt werden kann. Keine Kollisionen mit einem Datensatz dieser Größe für beide Hash-Funktionen sind auffallend nicht zufällig!)
Dies ist keine Überraschung, da dies ein wünschenswertes Verhalten für viele Verwendungen von Hash-Funktionen ist. Beispielsweise sind Hash-Tabellenschlüssel häufig sehr ähnlich. In Ians Antwort wird ein Problem erwähnt, das MSN einmal mit Postleitzahl-Hash-Tabellen hatte . Dies ist eine Verwendung, bei der die Kollisionsvermeidung bei wahrscheinlichen Eingaben das zufällige Verhalten gewinnt.
Ein weiterer lehrreicher Vergleich ist der Kontrast in den Entwurfszielen zwischen CRC- und kryptografischen Hash-Funktionen:
- CRC wurde entwickelt, um Fehler abzufangen, die aus verrauschten Kommunikationskanälen resultieren eine kleine Anzahl von Bit-Flips;
- Crypto-Hashes sind so konzipiert, dass sie Änderungen abfangen, die von böswilligen Angreifern vorgenommen wurden , denen begrenzte Rechenressourcen, aber willkürlich viel Klugheit zugewiesen werden.
Für CRC ist es also wieder gut , bei minimal unterschiedlichen Eingaben weniger Kollisionen als zufällig zu haben. Bei Krypto-Hashes ist dies ein Nein-Nein!
Antwort
Die SHA-Algorithmen (einschließlich SHA-256) sind hat so konzipiert, dass es schnell ist
Tatsächlich kann ihre Geschwindigkeit manchmal ein Problem sein. Insbesondere besteht eine übliche Technik zum Speichern eines von einem Passwort abgeleiteten Tokens darin, einen Standard-Fast-Hash-Algorithmus 10.000 Mal auszuführen (Speichern des Hash des Hash des Hash des Hash des … Passworts).
#!/usr/bin/env ruby require "securerandom" require "digest" require "benchmark" def run_random_digest(digest, count) v = SecureRandom.random_bytes(digest.block_length) count.times { v = digest.digest(v) } v end Benchmark.bmbm do |x| x.report { run_random_digest(Digest::SHA256.new, 1_000_000) } end
Ausgabe:
Rehearsal ------------------------------------ 1.480000 0.000000 1.480000 ( 1.391229) --------------------------- total: 1.480000sec user system total real 1.400000 0.000000 1.400000 ( 1.382016)
Kommentare
- ‚ ist relativ schnell, sicher, für einen kryptografischen Hashing-Algorithmus . Aber das OP möchte nur Werte in einer Hashtabelle speichern, und ich glaube nicht, dass eine kryptografische Hash-Funktion dafür wirklich geeignet ist.
- Die aufgeworfene Frage (tangential erscheint es jetzt) das Thema der kryptografischen Hash-Funktionen. Das ‚ ist das Bit, auf das ich antworte.
- Nur um die Leute von der Idee von “ abzuhalten Eine übliche Technik zum Speichern eines von einem Passwort abgeleiteten Tokens besteht darin, einen Standard-Fast-Hash-Algorithmus 10.000 Mal “ auszuführen – während dies üblich ist, ist ‚ ist einfach nur dumm. Es gibt Algorithmen, die für diese Szenarien entwickelt wurden, z. B.
bcrypt
. Verwenden Sie die richtigen Tools. - Kryptografische Hashes sind für einen hohen Durchsatz ausgelegt. Dies bedeutet jedoch häufig, dass sie einen hohen Einrichtungs-, Abbau-,
.rodata
und / oder staatliche Kosten aufweisen .Wenn Sie einen Algorithmus für eine Hashtabelle wünschen, haben Sie normalerweise sehr kurze Schlüssel und viele davon, benötigen jedoch nicht die zusätzlichen Garantien eines kryptografischen Schlüssels. Ich verwende selbst einen optimierten Jenkins-Effekt. - @ChrisMorgan: Anstatt einen kryptografisch sicheren Hash zu verwenden, kann HashTable DoS mithilfe der Hash-Randomisierung viel effizienter gelöst werden, sodass jeder Lauf von die Programme oder sogar auf jeder Hashtabelle, damit die Daten nicht ‚ jedes Mal in demselben Bucket gruppiert werden.
Antwort
Verwenden Sie SipHash . Es hat viele wünschenswerte Eigenschaften:
-
Schnell. Eine optimierte Implementierung dauert ungefähr 1 Zyklus pro Byte.
-
Sicher. SipHash ist eine starke PRF (Pseudozufallsfunktion). Dies bedeutet, dass es nicht von einer Zufallsfunktion zu unterscheiden ist (es sei denn, Sie kennen den 128-Bit-Geheimschlüssel). Daher:
-
Sie müssen sich keine Sorgen machen, dass Ihre Hash-Tabellensonden aufgrund von Kollisionen zu einer linearen Zeit werden. Mit SipHash wissen Sie , dass Sie unabhängig von den Eingaben im Durchschnitt eine durchschnittliche Fallleistung erzielen.
-
Immunität gegen Hash-basierte Denial-of-Service-Angriffe.
-
Sie können SipHash (insbesondere die Version mit 128-Bit-Ausgabe) als MAC verwenden (Nachrichtenauthentifizierungscode). Wenn Sie eine Nachricht und ein SipHash-Tag erhalten und das Tag mit dem aus dem Ausführen von SipHash mit Ihrem geheimen Schlüssel identisch ist, wissen Sie, dass derjenige, der den Hash erstellt hat, auch im Besitz Ihres geheimen Schlüssels war und dass weder die Nachricht noch der Der Hash wurde seitdem geändert.
-
Kommentare
- Isn ‚ t SipHash-Overkill, es sei denn, Sie benötigen Sicherheit? Benötigt einen 128-Bit-Schlüssel, der nur ein verherrlichter Hash-Samen ist. Ganz zu schweigen von MurmurHash3 mit 128-Bit-Ausgabe und SipHash nur mit 64-Bit-Ausgabe. Offensichtlich hat der größere Digest eine geringere Kollisionswahrscheinlichkeit.
- @bryc Der Unterschied besteht darin, dass sich SipHash auch bei böswilligen Eingaben weiterhin gut benimmt. Eine auf SipHash basierende Hash-Tabelle kann für Daten aus potenziell feindlichen Quellen verwendet werden und einen Algorithmus wie die lineare Prüfung verwenden, der sehr empfindlich auf die Details der Hash-Funktion reagiert.
- Siphash (und verwandte neuere prng Stilfunktionen) ist meine Standardauswahl für Sicherheit. Für die Leistung ist xxhash schwer zu schlagen. Selbst in den Diskussionen hier gibt es im Internet unzählige schlechte Hashing-Ratschläge. Eine gute Leistung bei zufälligen oder halbzufälligen Eingaben ist bedeutungslos. Was ist die Worst-Case-Leistung mit Eingaben aus der realen Welt? Was ist das Ergebnis mit böswilligen Eingaben? Ihre Hash-Tabelle wird schließlich zu einem Angriffsvektor.
Antwort
Dies hängt von den Daten ab, die Sie hashen. Einige Hashing-Vorgänge funktionieren besser mit bestimmten Daten wie Text. Einige Hashing-Algorithmen wurden speziell für bestimmte Daten entwickelt.
Paul Hsieh hat einmal schnellen Hash erstellt. Er listet Quellcode und Erklärungen auf. Aber es wurde schon geschlagen. 🙂
Antwort
Java verwendet diese einfache Multiplikation -und-Add-Algorithmus:
Der Hash-Code für ein String-Objekt wird berechnet als
s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
unter Verwendung von Int-Arithmetik, wobei
s[i]
das i -te Zeichen der Zeichenfolge ist,n
ist die Länge der Zeichenfolge, und^
gibt die Potenzierung an. (Der Hash-Wert der leeren Zeichenfolge ist Null.)
Es gibt wahrscheinlich viel bessere, aber dies ist ziemlich weit verbreitet und scheint gut zu sein Kompromiss zwischen Geschwindigkeit und Einzigartigkeit.
Kommentare
- Ich würde ‚ nicht genau dasselbe verwenden eine, die hier verwendet wird, da es ‚ immer noch relativ leicht ist, Kollisionen damit zu erzeugen. Es ‚ ist definitiv nicht schrecklich, aber es gibt viel bessere da draußen. Und wenn es ‚ keinen wesentlichen Grund gibt, mit Java kompatibel zu sein, sollte nicht ausgewählt werden.
- Wenn Sie dies immer noch auswählen Aus irgendeinem Grund könnte man zumindest eine bessere Primzahl wie 92821 als Multiplikator verwenden. Das reduziert Kollisionen erheblich. stackoverflow.com/a/2816747/21499
- Sie können stattdessen auch FNV1a verwenden. ‚ ist ebenfalls ein einfacher multiplikationsbasierter Hash, verwendet jedoch einen größeren Multiplikator, der den Hash besser verteilt.
- Sie verwenden nicht ‚ möchte
s[0]*31^3 + s[1]*31^2 + s[2]*31 + s[3]
nicht ausführen. Vermeiden Sie den Energieversorger (^) und gehen Sie folgendermaßen vor:((s[0]*31 + s[1])*31 + s[2])*31 + s[3]
. - @LeopoldoSanczyk Ja, in dem Code, der iterativ ausgeführt wird (und ausgeführt werden sollte), war es in einer geschlossenen Formel nur einfacher zu verstehen.
Antwort
Warum müssen Sie zunächst Ihr eigenes Hashing implementieren? Für die meisten Aufgaben sollten Sie mit Datenstrukturen aus einer Standardbibliothek gute Ergebnisse erzielen, vorausgesetzt, es ist eine Implementierung verfügbar (es sei denn, Sie tun dies nur für Ihre eigene Ausbildung).
Mein persönlicher Favorit ist FNV. 1
Hier ist eine Beispielimplementierung der 32-Bit-Version in C:
unsigned long int FNV_hash(void* dataToHash, unsigned long int length) { unsigned char* p = (unsigned char *) dataToHash; unsigned long int h = 2166136261UL; unsigned long int i; for(i = 0; i < length; i++) h = (h * 16777619) ^ p[i] ; return h; }
Kommentare
- Die FNV-1a-Variante ist mit Zufälligkeit etwas besser. Tauschen Sie die Reihenfolge der
*
und^
:h = (h * 16777619) ^ p[i]
== >h = (h ^ p[i]) * 16777619