是否可以在不使用%运算符的情况下实现分布良好的哈希表?


11

我希望在C#中实现一个快速,分布均匀的哈希表。我在选择需要任意哈希码并“约束”它的哈希约束函数时遇到麻烦,因此可以将其用于索引存储桶。到目前为止,有两种选择:

  • 一方面,您可以确保存储桶中始终有素数个元素,而要限制哈希,您只需对存储桶数进行模运算即可。实际上,这就是.NET词典所做的这种方法的问题在于,与其他操作相比,使用%的速度非常慢。如果您查看Agner Fog指令表idiv(这是为%生成的汇编代码)对于较新的Intel处理器,其指令等待时间约为25个周期。与此相比,大约3 mul或1逐位OPS像andorxor

  • 另一方面,您可以使存储桶的数量始终为2的幂。您仍将必须计算哈希的模数,因此您不必尝试在数组外部进行索引,但这一次的开销较小。由于对于2的幂% N& (N - 1),约束被减少到仅需要1-2个周期的屏蔽操作。这是由Google的sparsehashash完成缺点是我们指望用户提供良好的哈希值;屏蔽哈希基本上会切断一部分哈希,因此我们不再考虑哈希的所有位。如果用户的哈希值分布不均匀,例如仅填充了较高的位或较低的位始终相同,则此方法的冲突率就会高得多。

我正在寻找一种我可以使用的兼具两全其美的算法:它考虑了哈希的所有位,并且比使用%还要快。它不一定必须是模数,而是一定要保证在一定范围内0..N-1(其中N是铲斗的长度)并且在所有插槽中具有均匀的分布。是否存在这样的算法?

感谢您的帮助。


1
查找雪崩效果以及murmurhash3(smhasher)中的说明。但是,采用更好的哈希函数无法解决您问题的基本要点。相反,这是一个问题,即为什么用户首先不会采用相同的更好的哈希函数,并提出对策的要求(就好像用户是恶意的懒惰者)。
rwong


有关快速模的信息(2^N +/- 1),请参见stackoverflow.com/questions/763137/…–
rwong

@rwong对不起,但我不太确定您的评论与我的帖子有何关系。我不控制用户提供的哈希,所以我不是在寻找更好的哈希函数。我也不理解“恶意懒惰用户”的意思。
詹姆斯·柯

4
如果哈希函数很差,则哈希表实现者无法采取任何措施来“修复”劣势分布。模素数不能修复可怜的哈希。考虑一个散列函数产生一个质数的倍数作为输出。我已经在实际的生产代码中看到了这样的问题。
Frank Hileman '16

Answers:


9

现代哈希表实现不使用模函数。他们经常使用两个大小的表的功能,并切掉不需要的位。理想的哈希函数将允许这样做。在哈希函数通常较差的日子(通常在.net开发中),出现了将模数与质数表大小结合使用的情况。我建议阅读有关SipHash(现代哈希函数)的文章,然后阅读有关其他一些现代函数(例如xxHash)的文章

我应该解释为什么.net哈希函数通常很差。在.net中,程序员通常被迫通过重写GetHashcode来实现哈希函数。但是.net没有提供确保程序员创建的函数高质量的所需工具,即:

  • 将散列状态封装在结构或类中
  • 哈希“添加”功能,可将新数据添加到哈希状态(例如,添加字节数组或双精度型)
  • 散列“ finalize”函数,以产生雪崩
  • 哈希结果的封装-在.net中,您可以选择一个32位带符号整数。

有关将哈希函数结果用作哈希表索引的更多信息,请参见本文中哈希的通用形式的定义:使用无进位乘法的更快的64位通用哈希


3

要在仍保留所有位的同时使用AND,请也使用XOR。

例如,temp = (hash & 0xFFFF) ^ ( hash >> 16); index = (temp & 0xFF) ^ (temp >> 8);

在此示例中,没有取模,而所有32位hash效果都是8位index。但是,它是否比DIV更快取决于很多因素,并且在某些情况下(例如大哈希和微小索引),它很容易比DIV慢。


这总是比DIV / IDIV快,但是我认为它不能回答我的问题- index会在范围之内[0..255]。我需要范围内的东西[0..n-1],其中n桶数是多少。
詹姆斯·柯

@JamesKo但是,如果您要实现字典,则还可以在一定程度上控制存储桶的数量。因此,您可以选择二的幂而不是质数。(这样做是否实际上是个好主意,我不能告诉你。)
2016年

@svick对于2的幂,我们可以做一个简单的遮罩操作。正如问题中提到的那样,我正在寻找一种用质数来实现此目的的廉价方法,以便甚至可以容纳分布不均的散列。
James Ko

1

您可以利用以下事实:许多质数整数都有模乘乘法逆。看到这篇文章。通过使存储桶索引为质数和模数2 ^ n(它们本质上是相对质数的)满足了约束之一。

本文介绍了一种算法,该算法可找到一个数字,使其乘以该数字,而忽略溢出,将产生与将结果除以存储区索引大小相同的结果。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.