两个32位哈希与一个64位哈希的冲突率? (不相关?)

时间:2018-04-04 05:37:56

标签: javascript hash probability hash-collision murmurhash

我看过几个问题,问两个16位哈希值是否与32位哈希值具有相同的冲突率?"或者"两个32位哈希值是否与64位哈希值具有相同的冲突率?"似乎答案是"是的,如果他们是不正确的哈希函数,那么它们就不相关了。#34;但这意味着什么?

MurmurHash3的作者说:

  

MurmurHash2_x86_64并行计算两个32位结果并在末尾混合它们,这很快但意味着碰撞阻力仅与32位散列一样好。我建议避免使用这种变体。

他建议不要使用MurmurHash2_x86_64,但是没有提到MurmurHash3_x86_128这样的建议,它似乎混合了四个 32位结果来产生128位结果。

该功能甚至似乎更糟:如果消息低于8个字节,h3h4的输出将始终发生冲突。 h2也容易发生碰撞,100%的时间都会产生这样的结果:

seed = 0, dataArr = {0}
h1 = 2294590956, h2 = 1423049145 h3 = 1423049145, h4 = 1423049145

seed = 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

Another example: Hash of "bryc" - e87e2554db409442db409442db409442
db409442 repeats 3 times

长度小于16的空字节的任何组合都将导致这些冲突,无论种子如何。

无论如何,如果Appleby说他的功能是正确的,那么两个32位结果的抗冲击性并不比单个32位结果好,为什么每次我在一个结果中强制碰撞,没有失败,另一个不受影响?只有一个哈希值的冲突呈指数级增长。

Collisions of h1 in MurmurHash2_x86_64...
[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_64MurmurHash3_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。每个内部状态h1h2都会单独初始化,但会与相同的密钥块混合使用。然后他们与交替状态混合(例如h1 ^= h2)。最后,他们在最后再次混合。

有什么可以暗示这比真正的64位哈希弱吗?它正确地通过了我自己的基本雪崩/碰撞测试,但我不是专家。

1 个答案:

答案 0 :(得分:1)

MurmurHash2_x86_64MurmurHash3_x86_128之间的区别在于前者只有一个 [32位32位] - &gt; 64位混合,而后者在每16个字节中进行128位混合(虽然不是一个完整的混合,但它足以达到此目的)。

因此,逻辑上,MurmurHash2_x86_64将输入拆分为2个完全独立的流,为每个流计算32位散列,然后将两个32位结果混合为64位结果。所以这不是真正的64位哈希。例如,如果一个流损坏,但偶然保留相同的哈希值,则此损坏不会被注意到。并且此事件具有大致相同的概率,就好像您首先使用32位哈希一样。所以这个哈希的强度低于64位。

另一方面,MurmurHash3_x86_128内部有一个128位状态,每16个输入字节混合一次(即,所有16字节输入几乎立即影响内部状态,而不仅仅是在结尾),所以这是一个真正的64位哈希。