为啥哈希表的大小为 127(素数)优于 128?
Posted
技术标签:
【中文标题】为啥哈希表的大小为 127(素数)优于 128?【英文标题】:Why is the size 127 (prime) better than 128 for a hash-table?为什么哈希表的大小为 127(素数)优于 128? 【发布时间】:2011-08-21 06:06:26 【问题描述】:假设简单的统一散列,也就是说,任何给定的值都同样喜欢散列到散列的任何槽中。为什么使用大小为 127 而不是 128 的表更好?我真的不明白 2 数字的幂有什么问题。或者它实际上有什么不同。
使用除法时, 我们通常避免某些值 米(表大小)。例如,米 不应该是 2 的幂,因为如果 m = 2^p ,那么 h(k) 就是 k 的 p 个最低位。
假设可能的元素只有 1 到 10000 之间,我选择表大小为 128。127 怎么能更好? 所以 128 是 2^6 (1000000) 而 127 是 0111111。这有什么区别?对于 127,所有数字(当散列时)仍将是 k 的 p 最低位。我是不是搞错了什么?
我正在寻找一些例子,因为我真的不明白为什么这很糟糕。提前非常感谢!
PS:我知道: Hash table: why size should be prime?
【问题讨论】:
> PS: I am aware of: Hash table: why size should be prime?
- 然后再读一遍,或者链接到this one
@sehe 你链接的线程假设里面的元素有关系(“那么如果输入一堆都有相同第一个字符的字符串,那么结果都是一样的模 k")
@***:抱歉,但如果您坚持不需要针对特定哈希优化冲突,您可能会将索引与哈希混淆。完美的散列可以用作索引,但必须预先知道所有可能的值。有了这样的配置,即使桶的数量实际上是一个阶乘(n!
)也没有关系。但这不是散列背后的通用科学。
OT:***
是一个非常好的网名,在谈论哈希冲突时使用 :)
因为真实数据几乎从不均匀分布。如果您使用 128 对字符串进行哈希处理,您将获得 26 个不均匀填充的存储桶,其余的为空。如果您使用 127,您可能会更均匀地填充它们。
【参考方案1】:
如果你有一个均匀分布的完美哈希函数,那没关系。
【讨论】:
如果不这样做,可能会出现递归冲突,从而无法将某个项目保存在哈希表中。使用素数大小(或完美的散列函数),这不会发生。 这实际上取决于表在碰撞时的作用。 我的哈希函数是取模运算符。这不是一个完美的哈希,是吗?实际上我还没有达到完美的散列,但从我所读到的内容来看,这更多的是因为没有插入新密钥,元素是静态的。 @***,如果您按表大小进行修改,那将是一个非常糟糕的哈希函数,因为哈希函数中不使用高阶位。为什么不直接复制 std::hash首先,这不是选择质数。对于您的示例,如果您知道您的数据集将在 1 到 10,000 的范围内,那么选择 127 或 128 不会有任何区别,因为这是一个糟糕的设计选择。
相反,为您的示例选择一个非常大的素数(例如 3967)会更好,这样每个数据都会有自己唯一的键/值对。您只想尽量减少碰撞。为您的示例选择 127 或 128 不会产生影响,因为所有 127/128 存储桶都将被均匀填充(这很糟糕,并且会降低插入和查找运行时间 O(1) 到 O(n)),而不是 3967 (这将保留 O(1) 运行时间)
编辑#4
“哈希函数”的设计是 有点黑色艺术。有可能 受数据影响很大 打算存储在 基于散列的数据结构,所以 关于合理散列的讨论 函数经常会误入一个 讨论具体意见。
作为“首选”素数的原因,有 考虑“对手”分析, 那就是假设我设计了一个将军 基于散列的数据结构,如何 考虑到最差的输入,它会执行吗 来自对手。自表现 由散列冲突决定 问题变成什么是哈希 使用,最大限度地减少碰撞 最坏的情况。一种这样的条件是 当输入总是数字时 可以被某个整数整除,比如 4。如果 你使用 N = 128 然后任何数字 可被 4 mod 128 整除仍然是 能被 4 整除,这意味着只有 第 4、8、12 号桶……总是永远 使用,导致25%的利用率 数据结构。有效地启动 减少了这种可能性 场景发生,数字 > N。
【讨论】:
如果我错了,请纠正我,但是 3976 在每个存储桶中会有多个值。 @Nick 我认为他读的是 1000。我知道 127 和 128 对 10000 不利。我想了解的是,为什么取质数而不是其他任何数字更好?为什么2的幂不好?然后说我选择了 16384 (2^14)。为什么16381更好?谢谢 抱歉,打错字了:我的意思是 3967。好吧,这又回到了哈希函数的设计上。现在,如果你假设一个裸散列函数只接受一个数字(btwn 1 和 10,000)并以 3967 取模,它实际上可以确保我们在表中的冲突为零。此外,大素数使我们的表格几乎扩大了 4 倍,并确保碰撞发生的可能性很小 我不明白为什么 127 是“小”而 3967 是“真的很大”。重要的是负载因子。如果您要存储 10 个元素,则 127 非常好,并且可能会导致更少的缓存未命中。 @mattkc7,“二进制是二的幂”是什么意思?我认为二进制只是表示数字的另一个基础。我也不知道当使用 2 的幂时,哈希的一半是如何被切掉的。【参考方案3】:***其实有一个很好的总结:
http://en.wikipedia.org/wiki/Hash_table
他们指出,一些散列函数被设计为只对素数进行操作。这篇文章解释了为什么二的幂是不好的:
http://www.concentric.net/~Ttwang/tech/primehash.htm
【讨论】:
【参考方案4】:尼克说得对,一般来说,哈希表的大小并不重要。但是,在使用 双散列 的 开放寻址 的特殊情况下(其中探测之间的间隔由另一个散列函数计算),然后使用素数大小的散列表最好确保所有哈希表条目都可用于新元素(如 Corkscreewe 所述)。
【讨论】:
【参考方案5】:对于 127,所有数字(经过哈希处理后)仍将是 k 的 p 最低位。
那是错误的(或者我误解了..)。 k % 127
取决于 k 的所有位。 k % 128
仅取决于 7 个最低位。
编辑:
如果您有 1 到 10,000 之间的完美分布。 10,000 % 127
和 10,000 % 128
都将把它变成一个极好的更小的分布。所有存储桶将包含 10,000 /128 = 78(或 79)个项目。
如果您的分布在 1 到 10,000 之间存在偏差,因为 x, 2x, 3x, .. 出现的频率更高。然后,如answer 中所述,素数大小将提供更好的分布。 (除非 x 正好是那个素数。)
因此,切断高位(使用大小为 128)是没有问题的,如果低位的分布足够好。但是,对于真实数据和设计糟糕的哈希函数,您将需要那些高位。
【讨论】:
你是对的伊什塔尔。但这相当于说 128 % 128 的任何倍数(高位总是 128 的倍数)将是 0,这对我来说是显而易见的。而 127 则没有这个属性,但是 127 的倍数会更多,所以这应该更糟糕,不是吗?我不明白忽略高位的问题。 @*** - 忽略高位的真正问题是人们编写了糟糕的哈希函数。因此,如果您的桌子需要良好的分布,那么忽略这些额外的努力将是愚蠢的。制作好的散列很难,所以使用素数你只是容忍。 @***:忽略高位的问题是给定数据集仅在某些位上变化是正常的。 (例如,一组表示路径的字符串变量可能在前十几个字符上一致。或者,年龄可能在除了底部 6 位之外的所有位上一致。)如果这些是您要丢弃的位,您将有一个很多碰撞。【参考方案6】:我无法再证明它了,虽然我记得在一百万年前的大学考试中必须这样做,但最佳哈希大小不仅仅是素数。你想选择一个素数 N 使得N = 4*M − 1
(其中 M 也是一个整数)。
这使得 31 比 29 更好。当 N 为 31 时,M 为 8,但当 M 为 31 时,没有整数N 是 29。
正如我所说,我不再记得证明这一点的数学。大约 25 年前,这是 Udi 的妻子 Rachel Manber 教授的理论课程。
【讨论】:
【参考方案7】:划分方法
"在使用除法的时候,我们通常会避免m的某些值 (表大小)。例如,m 不应该是
2
的幂,因为如果 m =2<sup>p</sup>
,那么h(k)
就是p
的最低位k
。"--CLRS
要了解为什么m = 2<sup>p</sup>
只使用k
的最低位p
,您必须首先了解模哈希函数h(k) = k % m
。
密钥可以写成商q
,余数r
。
k = nq + r
选择商为q = m
允许我们将k % m
简单地写为上述等式中的余数:
k % m = r = k - nm, where r < m
因此,k % m
相当于连续减去m
一共n
次(直到r < m
):
k % m = k - m - m - ... - m, until r < m
让我们尝试用m = 2<sup>4</sup> = 16
散列密钥k = 91
。
91 = 0101 1011
- 16 = 0001 0000
----------------
75 = 0100 1011
- 16 = 0001 0000
----------------
59 = 0011 1011
- 16 = 0001 0000
----------------
43 = 0010 1011
- 16 = 0001 0000
----------------
27 = 0001 1011
- 16 = 0001 0000
----------------
11 = 0000 1011
因此,91 % 2<sup>4</sup> = 11
只是91
的二进制形式,只剩下p=4
的最低位。
重要区别:
这特别适用于散列的除法。事实上,对于 CLRS 中所述的 乘法方法,反之亦然:
“乘法方法的一个优点是 m 的值并不重要......我们通常选择 [m] 为 2 的幂,因为这样我们就可以在大多数计算机上轻松实现该功能。”
【讨论】:
【参考方案8】:我相信这只是与计算机工作的事实有关 以 2 为底。以 10 为底也会发生类似的情况。
...
选择一个足够大的非二次幂数将确保哈希函数确实是所有输入位的函数,而不是 它们的一个子集。
来自Why hash tables should use a prime-number size。
【讨论】:
【参考方案9】:这是一种理解“k % 127 取决于 k 的所有位。k % 128 仅取决于 7 个最低位的方法。” . k % 128 等于 k & (2^7-1) 。例如:129 % 128 = 1 ,二进制:1000 0001 & 0111 1111 =0000 0001,(2^7-1) 的任何高位将是0 ,表示高位无关紧要。但此转换对于不等于 2^n 的数字无效。 现在让我们看看我们如何在十进制 129 % 127 中进行除法,首先看最高位置 1,小于 127,然后我们得到下一项 2 与拳头结合得到 12,12 小于 127,然后结合用 9 表示 129,除以 127 余数是 2,我们可以用数学写成:129 = 1 * 127 +2,所以我们得到 2 [所有这些都称为Long_division],在二进制除法中也是如此,现在,我们知道 k % 127 取决于 k 的所有位
【讨论】:
以上是关于为啥哈希表的大小为 127(素数)优于 128?的主要内容,如果未能解决你的问题,请参考以下文章