it-swarm.cn

像0x9e3779b9和0x9e3779b1这样的“魔术”哈希常量从何而来?

在处理哈希表的代码中,我经常发现常量0x9e3779b9或有时是0x9e3779b1。例如

hash = n * 0x9e3779b1 >>> 24

为什么使用此特定值?

137
bkgs

0x9e3779b9是黄金比例的小数部分0.61803398875…(sqrt(5)-1)/ 2乘以2 ^ 32的整数部分。

因此,如果φ=(sqrt(5)+1)/ 2 = 1.61803398875是黄金比例,则哈希函数将计算n *φ的分数部分,该分数具有不错的散射特性。为了说服自己,只需在您最喜欢的电子表格中创建(n, n*c-FLOOR(n*c))的散点图,然后用φ,e,π等替换chttps://lkml.org/lkml/2016/4/29/838

这种方法通常被称为“黄金比例散列”或“斐波那契散列”,并由Donald Knuth推广(计算机编程艺术:第3卷:排序和搜索)。从理论上讲,它主要归结为Steinhaus猜想( https://en.wikipedia.org/wiki/Three-gap_theorem )和倍数小数部分的递归对称性黄金分割率φ。

有时,您可能还会看到0x9e3779b1,它是最接近0x9e3779b9的素数(并且似乎有点“货运邪教”,因为这不是模块化哈希)。同样,0x9e3779b97f4a7c150x9e3779b97f4a7c55是这些数字的64位等效项。

220
32f

其他答案说明了这些神奇数字背后的意图,这可能是您想知道的。但是,可以说“它们来自”的地方来自不良的编程实践。幻数是不好的,永远不要使用。诸如上述常量之类的常量应被赋予适当的描述性变量名,甚至应在其定义位置添加注释。然后,代码中值的每次出现都应采用命名变量的形式。如果您在满足这些值的代码中遇到这种情况,那么您一开始就不会因它们的意图而感到困惑。

示例:

错误的例子-使用 幻数

hash = n * 0x9e3779b1

更好的例子-带有注释和有意义的变量

# Golden Ratio constant used for better hash scattering
# See https://softwareengineering.stackexchange.com/a/402543 
GOLDEN_RATIO = 0x9e3779b1
hash = n * GOLDEN_RATIO
30
isilanes
在处理哈希表的代码中,我经常发现常量0x9e3779b9或有时为0x9e3779b1

另一个答案正确地解释了为什么使用此值。但是,如果您经常发现此常数,则可能没有意识到,您经常发现容易受到哈希洪泛攻击的代码。

有两种针对哈希洪泛攻击的策略:

  1. 使用具有秘密随机种子的安全哈希函数。您的哈希函数没有秘密的随机种子。 Murmurhash3_32有一个秘密的随机种子,但是由于内部状态小,它具有与种子无关的多重碰撞。具有近乎密码安全性且仍几乎可以接受的性能的最佳哈希函数可能是SipHash。不幸的是,它很慢,尽管不如SHA512等慢。

  2. 使用快速计算的哈希函数(例如找到的哈希函数或Murmurhash3_32),并将每个哈希存储桶放入平衡的二进制搜索树的根中。因此,普通的单独链接的哈希表将每个存储桶都作为一个链表,如果将很多值哈希到同一存储桶,这将很慢。通过使其成为平衡的二进制搜索树(例如AVL树或红黑树),您仍然可以保证最坏情况下的性能。

我的观点是(2)更好,因为SipHash太慢了。同样,在操作系统内核空间中,可能没有足够的熵来在启动阶段的早期创建秘密的随机种子,因此在内核空间中,您可能没有能力在启动初期创建随机数。

哈希表被广泛滥用。仅通过发送散列到同一存储桶的大量值,很容易使许多系统停机。

5
juhist