我见过几个问题,问“两个 16 位散列是否具有与 32 位散列相同的冲突率?” 或“两个 32 位散列是否具有与 64 位散列相同的冲突率?” 答案似乎是“是的,如果它们是不相关的体面散列函数”。但是,这是什么意思?
MurmurHash3 的作者这样说:
MurmurHash2_x86_64 并行计算两个 32 位结果并在最后混合它们,这速度很快,但意味着抗碰撞性仅与 32 位散列一样好。我建议避免使用这种变体。
他建议不要使用MurmurHash2_x86_64
,但没有提到关于MurmurHash3_x86_128
哪个似乎混合四个32 位结果以产生 128 位结果的建议。
而且这个功能看起来更糟:如果消息小于 8 字节,则h3
and的输出h4
总是会发生冲突。h2
也容易发生碰撞,100% 的时间会产生这样的结果:
种子 = 0,dataArr = {0} h1 = 2294590956, h2 = 1423049145 h3 = 1423049145 , h4 = 1423049145 种子 = 0,dataArr = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0} h1 = 894685359, h2 = 2425853539 , h3 = 2425853539 , h4 = 2425853539 另一个例子:“bryc”的哈希 - e87e2554db409442db409442db409442 db409442 重复 3 次
任何长度小于 16 的空字节组合都将导致这些冲突,无论种子如何。
无论如何,如果Appleby所说的关于他的功能是真的,两个32位结果的抗碰撞性不比单个32位结果好,为什么每次我强制碰撞一个结果,都没有失败,其他不受影响?仅一个哈希中的冲突呈指数级增长。
MurmurHash2_x86_64 中 h1 的碰撞... [ 2228688450,3117914388 ]!== [ 2228688450,2877485180 ] [ 957654412,3367924496 ]!== [ 957654412,762057742 ] [ 1904489323,1019367692 ]!== [ 1904489323,1894970953 ] [ 2752611220,3095555557 ]!==[ 2752611220,2609462765 ]
我问这个的原因是因为我想在 JavaScript 中实现一个 64 位(或更大)的哈希来进行体面的错误检测。32 位散列函数还不够好。GitHub 上目前没有任何可用的解决方案足够快。由于 JavaScript 使用 32 位按位整数,因此只有使用算术运算的函数uint32_t
在 JS 中是兼容的。许多 32 位函数似乎能够产生更大的输出而不会造成太多的性能损失。
我已经(在 JavaScript 中)实现了MurmurHash2_x86_64和MurmurHash3_x86_128,它们的性能令人印象深刻。我还实现了 MurmurHash2_160。
所有这些都具有与 32 位哈希相同的抗碰撞性吗?您如何判断结果是否足够相关以成为问题?我希望 64 位输出具有 64 位散列的强度,160 位输出与 160 位散列等一样强 - 同时满足 32 位算术的要求(JavaScript 限制)。
更新:这是我的自定义 64 位哈希,专为速度而设计(比我在 Chrome/Firefox 下优化的 32 位 MurmurHash3 更快)。
function cyb_beta3(key, seed = 0) {
var m1 = 1540483507, m2 = 3432918353, m3 = 433494437, m4 = 370248451;
var h1 = seed ^ Math.imul(key.length, m3) + 1;
var h2 = seed ^ Math.imul(key.length, m1) + 1;
for (var k, i = 0, chunk = -4 & key.length; i < chunk; i += 4) {
k = key[i+3] << 24 | key[i+2] << 16 | key[i+1] << 8 | key[i];
k ^= k >>> 24;
h1 = Math.imul(h1, m1) ^ k; h1 ^= h2;
h2 = Math.imul(h2, m3) ^ k; h2 ^= h1;
}
switch (3 & key.length) {
case 3: h1 ^= key[i+2] << 16, h2 ^= key[i+2] << 16;
case 2: h1 ^= key[i+1] << 8, h2 ^= key[i+1] << 8;
case 1: h1 ^= key[i], h2 ^= key[i];
h1 = Math.imul(h1, m2), h2 = Math.imul(h2, m4);
}
h1 ^= h2 >>> 18, h1 = Math.imul(h1, m2), h1 ^= h2 >>> 22;
h2 ^= h1 >>> 15, h2 = Math.imul(h2, m3), h2 ^= h1 >>> 19;
return [h1 >>> 0, h2 >>> 0];
}
它基于 MurmurHash2。每个内部状态h1
,h2
都单独初始化,但与相同的密钥块混合。然后将它们与备用状态(例如h1 ^= h2
)混合。作为最终确定的一部分,它们在最后再次混合。
有什么迹象表明这比真正的 64 位散列更弱吗?它正确地通过了我自己的基本雪崩/碰撞测试,但我不是专家。