xor
ist eine gefährliche Standardfunktion, die beim Hashing verwendet wird. Es ist besser als and
und or
, aber das sagt nicht viel.
xor
ist symmetrisch, so dass die Reihenfolge der Elemente verloren geht. Also "bad"
wird Hash das gleiche kombinieren wie "dab"
.
xor
Ordnet paarweise identische Werte Null zu, und Sie sollten vermeiden, "allgemeine" Werte Null zuzuordnen:
Also (a,a)
auf 0 abgebildet wird, und (b,b)
auch auf 0 abgebildet wird als solche Paare sind fast immer häufiger als Zufälligkeit könnte bedeuten, Sie am Ende mit viel zu vielen Kollisionen auf Null , als Sie sollten.
Mit diesen beiden Problemen wird xor
es zu einem Hash-Kombinierer, der auf der Oberfläche halbwegs anständig aussieht, aber nicht nach weiterer Prüfung.
Bei moderner Hardware ist das Hinzufügen normalerweise ungefähr so schnell wie xor
(es verbraucht wahrscheinlich mehr Strom, um dies zu erreichen, zugegebenermaßen). Die Wahrheitstabelle des Hinzufügens ähnelt der xor
des betreffenden Bits, sendet jedoch auch ein Bit zum nächsten Bit, wenn beide Werte 1 sind. Dies bedeutet, dass weniger Informationen gelöscht werden.
Ist also hash(a) + hash(b)
besser als hash(a) xor hash(b)
wenn a==b
, wenn das Ergebnis hash(a)<<1
statt 0 ist.
Dies bleibt symmetrisch; so das "bad"
und "dab"
das gleiche Ergebnis erhalten bleibt ein Problem. Wir können diese Symmetrie für bescheidene Kosten brechen:
hash(a)<<1 + hash(a) + hash(b)
aka hash(a)*3 + hash(b)
. (Einmaliges Berechnen hash(a)
und Speichern wird empfohlen, wenn Sie die Schichtlösung verwenden). Jede ungerade Konstante anstelle von 3
wird eine k
vorzeichenlose Ganzzahl mit " -bit" bijektiv auf sich selbst abbilden, da die Zuordnung auf vorzeichenlosen Ganzzahlen 2^k
für einige mathematisch modulo k
ist und jede ungerade Konstante relativ prim ist 2^k
.
Für eine noch schickere Version können wir untersuchen boost::hash_combine
, was effektiv ist:
size_t hash_combine( size_t lhs, size_t rhs ) {
lhs ^= rhs + 0x9e3779b9 + (lhs << 6) + (lhs >> 2);
return lhs;
}
Hier addieren wir einige verschobene Versionen von seed
mit einer Konstanten (die im Grunde genommen zufällige 0
s und 1
s sind - insbesondere ist es die Umkehrung des Goldenen Schnitts als 32-Bit-Festkommafraktion) mit einer Addition und einem xor. Dies unterbricht die Symmetrie und führt zu einem gewissen "Rauschen", wenn die eingehenden Hash-Werte schlecht sind (dh stellen Sie sich vor, dass jede Komponente auf 0 gehasht wird - das oben Gesagte behandelt dies gut 1
und erzeugt 0
nach jedem Mähdrescher einen Abstrich von und s. Meine Naivität 3*hash(a)+hash(b)
gibt einfach ein 0
In aus dieser Fall).
(Für diejenigen, die mit C / C ++ nicht vertraut sind, size_t
ist a ein vorzeichenloser Ganzzahlwert, der groß genug ist, um die Größe eines Objekts im Speicher zu beschreiben. Auf einem 64-Bit-System ist es normalerweise eine 64-Bit-Ganzzahl ohne Vorzeichen. Auf einem 32-Bit-System , eine 32-Bit-Ganzzahl ohne Vorzeichen.)