为什么5381和33在djb2algorithm中如此重要?

djb2algorithm具有string的散列函数。

unsigned long hash = 5381; int c; while (c = *str++) hash = ((hash << 5) + hash) + c; /* hash * 33 + c */ 

为什么5381和33如此重要?

这个哈希函数类似于线性同余发生器 (LCG-一个简单的函数类,可以产生一系列伪随机数),它通常具有如下forms:

 X = (a * X) + c; // "mod M", where M = 2^32 or 2^64 typically 

注意与djb2哈希函数的相似性… a = 33,M = 2 ^ 32。 为了使LCG具有“全部期限”(即尽可能地是随机的), a必须具有某些性质:

  • a-1可以被M的所有素数因子整除(a-1是32,可以被2整除,唯一的素数因子是2 ^ 32)
  • 如果M是4的倍数,那么a-1是4的倍数(是和是)

另外, cM应该是相对的素数(这对于c的奇数值是成立的)。

正如你所看到的,这个散列函数有点类似于一个好的LCG。 当涉及散列函数时,您需要一个给定一组真实的inputstring来产生散列值的“随机”分布。

至于为什么这个散列函数对string有好处,我认为它具有非常快的平衡性,同时提供合理的散列值分布。 但是我见过许多其他的散列函数,它们声称具有更好的输出特性,但涉及更多的代码行。 例如看到这个页面有关哈希函数

编辑: 这个好的答案解释了为什么33和5381被选为实际的原因。

在5381年,丹·伯恩斯坦(djb2)在这篇文章中说:

几乎任何好的倍增工作。 我认为你担心的是,如果c和d在0到255之间,31c + d不包括任何合理的散列值范围。这就是为什么当我发现33散列函数并开始在我的压缩器中使用它时,我开始用一个散列值5381.我想你会发现这和261乘法器一样好。

如果你有兴趣,整个线程就在这里 。

Ozan Yigit有一个散列函数的页面,它说:

33号的魔力(为什么它比许多其他常数,优质或不优质)都没有得到充分的解释。

33被选中是因为:

1)如前所述,使用移位和相加很容易计算乘法。

2)你可以从shift和add实现中看到,使用33在散列累加器中产生大部分input比特的两个副本,然后把这些比特分散得相当远。 这有助于产生良好的雪崩。 使用更大的移位会复制更less的位,使用更小的移位会使位相互作用更加局部化,并且使交互的传播花费更长的时间。

3)5的移位与32(寄存器中的位数)相对,这有助于雪崩。 当string中剩下足够的字符时,input字节的每一位最终都会与input的每一位相互作用。

4)当考虑ASCII字符数据时,5的移位是一个好的移位量。 一个ASCII字符可以被认为是一个4位字符typesselect器和一个4位字符typesselect器。 例如,前4位的数字都是0x3。 所以8位的移位会导致具有一定含义的位主要与具有相同含义的其他位交互。 4位或2位移位同样会在志同道合的位之间产生强烈的相互作用。 5位移位使字符的四个低位中的许多与同一字符中的许多4位高位强烈地相互作用。

如其他地方所述,5381的select不是太重要,许多其他的select也应该在这里工作。

这不是一个快速的散列函数,因为它一次处理一个字符的input,而不会尝试使用指令级并行。 但是,这很容易写。 输出的质量除以易于编写的代码很可能会达到最佳状态。

在现代处理器上,乘法运算比开发这种algorithm快得多,其他乘法因子(如2 ^ 13 + 2 ^ 5 + 1)可能具有相似的性能,稍好的输出,稍微容易编写。

与上面的答案相反,一个好的非密码散列函数不希望产生一个随机的输出。 相反,给定两个几乎相同的input,它想要产生广泛不同的输出。 如果你input的值是随机分布的,你不需要一个好的散列函数,你可以只使用你input的任意一组位。 一些现代散列函数(Jenkins 3,Murmur,可能是CityHash)产生的输出分布比随机input高度相似。

也许是因为33 == 2^5 + 1和许多哈希algorithm使用2^n + 1作为乘数?

感谢Jerome Berger

更新:

这似乎是由当前版本的软件包djb2最初来自: cdb

我链接的笔记描述哈希algorithm的核心是使用h = ((h << 5) + h) ^ c做散列… x << 5是使用2 ^ 5的快速硬件方法乘数。