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

Posted

技术标签:

【中文标题】为啥 5381 和 33 在 djb2 算法中如此重要?【英文标题】:Why are 5381 and 33 so important in the djb2 algorithm?为什么 5381 和 33 在 djb2 算法中如此重要? 【发布时间】:2010-12-07 11:23:05 【问题描述】:

djb2 algorithm 有一个字符串哈希函数。

unsigned long hash = 5381;
int c;

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

为什么 5381 和 33 如此重要?

【问题讨论】:

Reason for 5381 number in DJB hash function?的可能重复 DJB2 在雪崩方面很糟糕。例如,它为这些字符串返回相同的哈希值:xyyXz7。有better alternatives。 【参考方案1】:

选择 33 是​​因为:

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

2) 从 shift 和 add 实现中可以看出,使用 33 会复制哈希累加器中的大部分输入位,然后将这些位分散到相对较远的位置。这有助于产生良好的雪崩。使用较大的移位将复制较少的位,使用较小的移位将使位交互更加局部,并使交互传播所需的时间更长。

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

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

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

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

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

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

【讨论】:

这个答案实际上回答了这个问题。谢谢!【参考方案2】:

此哈希函数类似于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的唯一质因数) a-1 是 4 的倍数,如果 M 是 4 的倍数(是和是)

此外,cM 应该是互质的(对于 c 的奇数值也是如此)。 p>

如您所见,这个散列函数有点像一个好的 LCG。当涉及到哈希函数时,您需要一个在给定一组实际输入字符串的情况下产生“随机”分布的哈希值。

至于为什么这个散列函数对字符串有好处,我认为它在极快的同时提供了散列值的合理分布之间取得了很好的平衡。但我见过许多其他散列函数声称具有更好的输出特性,但涉及更多的代码行。例如见this page about hash functions

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

【讨论】:

【参考方案3】:

在 5381 上,Dan Bernstein (djb2) 在this article 中说:

[...] 几乎任何好的乘数都有效。我觉得你很担心 关于 31c + d 不涵盖任何合理的哈希范围的事实 如果 c 和 d 介于 0 和 255 之间,则值。这就是为什么,当我发现 33 哈希函数并开始在我的压缩器中使用它,我开始 哈希值为 5381。我想你会发现这就像 以及一个261乘法器。

如果您有兴趣,整个帖子是here。

Ozan Yigit 有 a page on hash functions,上面写着:

[...] 数字 33 的魔力(为什么它比许多其他常数更好,无论是否素数)从未得到充分解释。

【讨论】:

注意,hash的起始值(5381)对于等长的字符串没有区别,但是会起到为不同长度的字符串生成不同的hash值的作用。【参考方案4】:

可能是因为33 == 2^5 + 1 和许多哈希算法使用2^n + 1 作为它们的乘数?

感谢Jerome Berger

更新:

这似乎印证了当前版本的软件包djb2最初来自:cdb

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

【讨论】:

以上是关于为啥 5381 和 33 在 djb2 算法中如此重要?的主要内容,如果未能解决你的问题,请参考以下文章

了解散列算法的位和字节

为啥像djb2这样的hash,与ulong一起使用时,会大于32位

Dan Bernstein 的 Djb2 哈希函数:当我们只能乘以 33 时,为啥还要使用按位运算符?

我如何将 djb2 映射到哈希表?

哈希算法-Time33

DJB 哈希函数中数字 5381 的原因?