uint32_t h(const char *kbuf, int ksiz){
uint32_t hash = 751;
const char *rp = kbuf + ksiz;
while(ksiz--){
hash = (hash * 31) ^ *(uint8_t *)--rp;
}
return hash;
}
为什么要以这种方式计算哈希值,理由是什么?
答案 0 :(得分:3)
您的哈希算法遵循相同的想法,导致修改后的Bernstein哈希和Fowler / Noll / Vo(参见例如this overview of existing hash algorithms)。
XORing字节是一种经典的哈希算法。但是,哈希值的最终分布远非最优,因此,增加了一个额外的混合步骤(在这种情况下乘以31
)。
使用31
作为乘数,由Josh Bloch在Effective Java中解释:
选择值31是因为它是奇数素数。如果它是均匀的 并且乘法溢出,信息将丢失,如 乘以2相当于移位。使用的好处 素数不太清楚,但它是传统的。 31的不错的财产是 乘法可以用移位和减法代替 为了更好的性能:31 * i ==(i <&lt; 5) - i。现代VM执行此操作 自动进行优化。
选择33
作为原始伯恩斯坦哈希中的乘数可能遵循类似的推理;但是,如果散列性能不是您的主要考虑因素,那么通过选择一个能够实现更好分布的乘数,您可能会更好。如果您不想自己试验,Fowler / Noll / Vo可能是一个不错的选择。
答案 1 :(得分:1)
此实现尝试使输入到输出映射均匀,以便冲突也是一致的。换句话说,它试图避免这样的情况:对于某些哈希值,存在许多冲突而不是其他冲突的方式。它通过将原始伪随机生成器的值混合到生成的散列值中来实现。或者您可以反过来考虑它,作为输入数据中的PRNG混合。素数(751和31)有助于实现均匀性。当然,没有任何保证,因为精心挑选的输入你可以无视这些尝试。
有关详细信息,请参阅以下文章:
Hash Function - Uniformity
Linear congruential generator