xor
是在散列时使用的危险的默认函数。它比and
和更好or
,但这并不多。
xor
是对称的,因此元素的顺序丢失了。因此,"bad"
哈希组合与相同"dab"
。
xor
将成对的相同值映射为零,并且应避免将“公共”值映射为零:
因此,(a,a)
被映射为0,(b,b)
也被映射为0。由于此类对几乎总是比随机性所暗示的更为普遍,因此最终在零处产生的碰撞要多得多。
遇到这两个问题,xor
最终成为一个哈希混合器,看起来表面上还不错,但经过进一步检查后却没有。
在现代硬件上,添加速度通常与添加速度差不多xor
(公认的,它可能会使用更多功能来实现此目的)。加法运算的真值表与所xor
讨论的位类似,但是当两个值均为1时,它还会向下一位发送一个位。这意味着它将删除较少的信息。
因此hash(a) + hash(b)
要比hash(a) xor hash(b)
if 更好a==b
,hash(a)<<1
而不是0。
这保持对称;所以"bad"
并"dab"
得到同样的结果仍然是一个问题。我们可以以适度的成本打破这种对称性:
hash(a)<<1 + hash(a) + hash(b)
又名hash(a)*3 + hash(b)
。(hash(a)
如果使用班次解决方案,则建议一次计算并存储)。而不是任何奇数常量,3
将双射地将一个“ k
-bit”无符号整数映射到其自身,因为无符号整数的映射对2^k
某些对象而言是数学模k
,并且任何奇数常量都相对于2^k
。
对于更高级的版本,我们可以检查boost::hash_combine
,这实际上是:
size_t hash_combine( size_t lhs, size_t rhs ) {
lhs ^= rhs + 0x9e3779b9 + (lhs << 6) + (lhs >> 2);
return lhs;
}
在这里,我们将一些seed
带有常数的移位版本加在一起(基本上是随机的0
s和1
s,尤其是32位固定点分数的黄金分割率的倒数),加上一些加法和一个xor。这打破对称,并介绍了一些“噪声”,如果传入的散列值是差(即,每一个部件散列想象到0 -上述处理得很好,产生的涂抹1
和0
。s各自结合后我的幼稚3*hash(a)+hash(b)
简单地输出一个0
在这种情况)。
(对于不熟悉C / C ++的人,a size_t
是一个无符号整数值,该值足以描述内存中任何对象的大小。在64位系统上,它通常是64位无符号整数。在32位系统上,一个32位无符号整数。)