为什么5381和33在djb2算法中如此重要?

时间:2009-10-16 18:44:56

标签: hash

djb2 algorithm具有字符串的哈希函数。

unsigned long hash = 5381;
int c;

while (c = *str++)
    hash = ((hash << 5) + hash) + c; /* hash * 33 + c */

为什么5381和33如此重要?

4 个答案:

答案 0 :(得分:37)

此哈希函数类似于Linear Congruential Generator(LCG - 一组生成一系列伪随机数的简单函数),通常具有以下形式:

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的倍数

此外, c M 应该是相对素数(对于 c 的奇数值都是如此)。

正如您所看到的,此哈希函数有点类似于良好的LCG。当涉及到散列函数时,你需要一个在给定一组输入字符串的情况下产生散列值“随机”分布的函数。

至于为什么这个哈希函数对于字符串是好的,我认为它具有非常快的平衡,同时提供合理的哈希值分布。但我已经看到许多其他散列函数声称具有更好的输出特性,但涉及更多的代码行。例如,请参阅this page about hash functions

编辑:This good answer解释了为什么选择33和5381是出于实际原因。

答案 1 :(得分:21)

选择33是因为:

1)如前所述,使用shift和add很容易计算乘法。

2)从移位和添加实现中可以看出,使用33在散列累加器中生成大部分输入位的两个副本,然后将这些位相对较远地分开。这有助于产生良好的雪崩。使用较大的移位会重复较少的位,使用较小的移位会使位交互更加局部化并使交互传播需要更长的时间。

3)5的移位相对于32(寄存器中的位数),这有助于雪崩。虽然字符串中有足够的字符,但输入字节的每个位最终都会与输入的每个前一位进行交互。

4)考虑ASCII字符数据时,5的移位是一个很好的移位量。 ASCII字符可以被认为是4位字符类型选择器和4位字符类型选择器。例如。这些数字在前4位中都有0x3。因此,8位移位将导致具有特定含义的位主要与具有相同含义的其他位交互。 4位或2位移位同样会在相似的位之间产生强烈的相互作用。 5位移位导致字符的四个低位中的许多位与同一字符中的许多4位高位强烈交互。

如其他地方所述,5381的选择并不太重要,许多其他选择在这里也应该有效。

这不是快速哈希函数,因为它一次处理它的输入字符并且不尝试使用指令级并行。但是,编写起来很容易。输出的质量除了编写代码的容易程度可能会达到最佳点。

在现代处理器上,乘法比开发此算法时快得多,其他乘法因子(例如2 ^ 13 + 2 ^ 5 + 1)可能具有相似的性能,稍微好一点的输出,并且稍微容易编写

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

答案 2 :(得分:20)

在5381年,丹·伯恩斯坦(djb2)在this article中说:

[...]几乎任何好的乘数都有效。我觉得你很担心 关于31c + d没有覆盖任何合理范围的哈希的事实 如果c和d在0到255之间,那就是值。这就是我发现的原因 我启动了33个哈希函数并开始在我的压缩器中使用它 哈希值为5381.我想你会发现这就是这样 以及261乘数。

如果您有兴趣,整个主题是here

Ozan Yigit a page on hash functions说:

[...] 33号的神奇之处(为什么它比许多其他常数更好,无论是否为素数)从未被充分解释过。

答案 3 :(得分:9)

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

归功于Jerome Berger

更新

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

我链接的注释描述了哈希算法的核心,即使用h = ((h << 5) + h) ^ c进行哈希... x << 5是一种使用2 ^ 5作为乘数的快速硬件方法。