xorè una pericolosa funzione predefinita da utilizzare durante l'hash. È meglio di ande or, ma questo non dice molto.
xorè simmetrico, quindi l'ordine degli elementi viene perso. Così"bad" hash combina lo stesso di "dab".
xor associa valori identici a coppie a zero e dovresti evitare di mappare valori "comuni" a zero:
Così (a,a) viene mappato su 0 e(b,b) anche mappato su 0. Poiché tali coppie sono quasi sempre più comuni di quanto la casualità possa implicare, si finisce con molte collisioni a zero di quanto si dovrebbe.
Con questi due problemi, xorfinisce per essere un combinatore di hash che sembra mezzo decente in superficie, ma non dopo un'ulteriore ispezione.
Su hardware moderno, l'aggiunta di solito è più veloce di xor(probabilmente utilizza più potenza per farlo, lo ammetto). L'aggiunta della tabella di verità è simile axor quella del bit in questione, ma invia anche un bit al bit successivo quando entrambi i valori sono 1. Ciò significa che cancella meno informazioni.
Quindi hash(a) + hash(b)è meglio che hash(a) xor hash(b)in questo se a==b, il risultato èhash(a)<<1 invece di 0.
Questo rimane simmetrico; quindi lo "bad"e "dab"ottenere lo stesso risultato rimane un problema. Possiamo rompere questa simmetria per un costo modesto:
hash(a)<<1 + hash(a) + hash(b)
aka hash(a)*3 + hash(b). ( hash(a)si consiglia di calcolare una volta e memorizzare se si utilizza la soluzione a turni). Qualsiasi costante dispari invece di 3mappare biiettivamente un kintero senza segno " -bit" su se stesso, poiché la mappa su numeri interi senza segno è un modulo di matematica 2^kper alcuni k, e qualsiasi costante dispari è relativamente primaria 2^k.
Per una versione ancora più elaborata, possiamo esaminare boost::hash_combine, che è effettivamente:
size_t hash_combine( size_t lhs, size_t rhs ) {
lhs ^= rhs + 0x9e3779b9 + (lhs << 6) + (lhs >> 2);
return lhs;
}
qui sommiamo alcune versioni spostate di seedcon una costante (che è sostanzialmente casuale 0s e 1s - in particolare è l'inverso del rapporto aureo come una frazione di punto fisso a 32 bit) con qualche aggiunta e uno xor. Ciò interrompe la simmetria e introduce un po 'di "rumore" se i valori di hash in entrata sono scarsi (cioè immagina che ogni componente abbia l'hash su 0 - quanto sopra lo gestisce bene, generando una sbavatura di 1e 0s dopo ogni combinazione. La mia ingenua 3*hash(a)+hash(b)semplicemente genera un 0in questo caso).
(Per coloro che non hanno familiarità con C / C ++, a size_tè un valore intero senza segno che è abbastanza grande da descrivere la dimensione di qualsiasi oggetto in memoria. Su un sistema a 64 bit, di solito è un numero intero senza segno a 64 bit. Su un sistema a 32 bit , un numero intero senza segno a 32 bit.)