【问题标题】:Why setting HashTable's length to a Prime Number is a good practice?为什么将 HashTable 的长度设置为素数是一个好习惯?
【发布时间】:2011-07-06 08:08:04
【问题描述】:

当我点击这个段落时,我正在浏览 Eric Lippert 为 Guidelines and rules for GetHashCode 发布的最新博客文章:

我们可以在这里更聪明;就像 List 在满时调整自身大小一样,桶集也可以调整自身大小,以确保平均桶长度保持较低。此外,出于技术原因,将桶集长度设为素数而不是 100 通常是一个好主意。我们可以对这个哈希表进行大量改进。但是这个简单的哈希表实现的草图现在就可以了。我想保持简单。

所以看起来我错过了一些东西。为什么将其设置为素数是一个好习惯?

【问题讨论】:

  • 当 .Net 框架中内置了更好、经过测试和快速的实现时,为什么还要编写自己的实现?检查 System.Collections。
  • @Will 我不会自己写.. 而且 Eric 只是在那里举个例子...

标签: arrays hash primes


【解决方案1】:

因为这会产生更好的哈希函数并减少可能的冲突次数。这在Choosing a good hashing function中有解释:

一个基本要求是 功能应提供统一的 散列值的分布。一种 非均匀分布增加 碰撞次数和成本 解决它们。

分布需要统一 仅适用于出现在 应用程序。特别是,如果一个 使用精确的动态调整大小 s的加倍和减半,哈希 只有当函数需要统一时 s 是 2 的幂。在另一 手,一些散列算法提供 仅当 s 是素数时才进行统一哈希 号码。

【讨论】:

  • +1 这是一个很好的答案......但是我们不要失去在给定范围内可以拥有的哈希数......
【解决方案2】:

假设您的存储桶集长度是 2 的幂 - 这使得 mod 计算非常快。这也意味着桶的选择仅由哈希码的顶部m 位决定。 (其中m = 32 - n,其中 n 是使用的 2 的幂)。所以这就像你立即扔掉有用的哈希码。

或者就像 2006 年的 this blog post 所说的那样:

假设您的 hashCode 函数产生以下 hashCodes 以及其他 {x , 2x, 3x, 4x, 5x, 6x...},那么所有这些都将聚集在 m 个桶中,其中 m = table_length /GreatestCommonFactor(table_length,x)。 (验证/推导这一点很简单)。现在您可以执行以下操作之一来避免集群:

...

或者简单地通过使 GreatestCommonFactor(table_length, x) 等于 1 使 m 等于 table_length,即通过使 table_length 与 x 互质。如果 x 可以是任何数字,那么请确保 table_length 是一个素数。

【讨论】:

  • 我知道这是在复活一个旧线程,但你的第一段让我感到困惑。考虑你的 2 的幂是 128,在这种情况下 hash%128 将与 hash&127 相同,这意味着只有最后 7 位将决定桶的选择。还是我的理解有误?
【解决方案3】:

您可以找到提出频谱两端的人。一方面,为哈希表的大小选择一个素数将减少冲突的机会,即使哈希函数不太有效地分布结果。请注意,如果(在争论的最简单的示例中)确定了 2 大小的幂,则只有较低的位会影响存储桶,而对于素数,哈希结果中的大多数位将被使用。

另一方面,您可以通过选择更好的散列函数获得更多收益,甚至可以通过应用一些位操作重新散列散列函数的结果,并使用 2 的散列大小的幂来加速计算。

作为现实生活中的一个例子,Java HashTable 最初是使用素数(或几乎素数大小)实现的,但从 Java 1.4 开始,设计更改为使用两个桶数的幂并添加了第二个快速哈希函数应用于初始哈希的结果。可以在here 找到一篇评论该更改的有趣文章。

所以基本上:

  • 即使在哈希函数不太好的情况下,素数也有助于将输入分散到不同的存储桶中。

  • 通过对哈希函数的结果进行后处理,并使用 2 的幂来加速模运算(位掩码)并补偿后处理,可以实现类似的效果。

【讨论】:

    猜你喜欢
    • 2020-01-03
    • 1970-01-01
    • 1970-01-01
    • 2019-01-11
    • 1970-01-01
    • 1970-01-01
    • 2021-05-02
    • 2016-04-01
    • 2020-01-25
    相关资源
    最近更新 更多