【问题标题】:Why is an initial prime used in GetHashCode implementations?为什么在 GetHashCode 实现中使用初始素数?
【发布时间】:2017-08-18 10:40:12
【问题描述】:

看着What is the best algorithm for an overridden System.Object.GetHashCode?,我感到震惊的是,在许多建议hash = hash*(prime) + item.GetHashcode() 类型的哈希码的答案中,哈希值最初被播种到另一个素数而不是0。

我了解计算部分中使用素数的原因,互素数在很多方面都很有用。

我不明白为什么哈希首先被初始化为非零数字。

看看具体的例子:

int hash = 17;
hash = hash * 23 + field1.GetHashCode();
hash = hash * 23 + field2.GetHashCode();
hash = hash * 23 + field3.GetHashCode();
return hash;

对于简写,让 field1.GetHashCode() 用 f1 表示(其他的以此类推)和 i 的初始哈希值,然后给出:

int hash = i;
hash = i * 23 + f1;
hash = (i * 23 + f1)* 23 + f2;
hash = ((i * 23 + f1)* 23 + f2)* 23 + f3;

展开最后一行中的括号:

hash = (i*23*23 + f1*23 + f2)* 23 + f3;
hash = i*23*23*23 + f1*23*23 + f2*23 + f3;

所以我们可以看到初始哈希值的唯一效果是通过 i*23*23*23 的常量值增加最终的 has 值,这将推广到 i*23^(字段数)。

那么这有什么帮助呢?如果 f1、f2、f3 都为 0,那么如果最终哈希为 0 是否有问题?它是非零的东西会更好吗?我唯一的想法是,出于某种原因,使用哈希值的字典或哈希集之类的东西的实现更喜欢非零值,但我想不出这个原因可能是什么。或者其他的东西当然是这些东西有点神秘,所以人们使用经过试验和测试的东西,所以即使没有理由,初始值也会被传播。

我尝试查找一些 microsoft 哈希码,但我发现所有这些都使用外部代码来计算它们(对象、字符串)或者有些特殊(在匿名对象上的 GetHashCode 实现种子哈希码是基于属性名称的匿名对象是不同的,因为它不是一个恒定的初始值)。

所以总结一下为什么哈希码实现中的初始常量值?

编辑Why use a prime number in hashCode? 被建议为重复,该网站希望我编辑我的问题以解释为什么它不是重复的...我承认素数被用作乘数在计算中,我明白为什么会这样。这个问题明确地是关于在哈希码算法中用作初始种子的。建议的副本没有明确说明素数的用途,但答案都将其用作与该问题无关的乘数。

【问题讨论】:

  • 我应该注意作为一个帖子脚本,虽然这个问题被标记为 c#,但它可能更普遍地适用。我把那个语言标签放在上面的唯一原因是我不知道其他语言是否确实暗示了同样的事情,所以我想我会把它保留在我正在查看的具体示例中。如果有其他语言经验的人想扩大范围,请随意。

标签: c# hashcode


【解决方案1】:

这个问题有some good answers on the Computer Science SE。简而言之:初始常量改编自可以接受可变数量输入的哈希值,你说得对,在那个例子中它并不重要。

【讨论】:

  • 啊,是的。如果您有可变数量的项目,它确实更有意义。我认为我看到的所有示例都很简单,它们只是使用了像上面这样的静态项目数。
  • 该帖子似乎遇到了与 OP 分析相同的问题,它假设中间总和不会溢出。模块化数学很棘手,LCG 的理论可以在这里应用。 17 是随机数生成器的种子,但它的周期性无法保证。所以你必须选择一个种子,以最大化选择更长周期的几率。嗯。
  • @HansPassant:嗯……这有关系吗?即使使用任何溢出模式,常量也会应用 23**3 * 17 的固定差异。
猜你喜欢
  • 1970-01-01
  • 2010-12-02
  • 2011-02-23
  • 2014-11-08
  • 1970-01-01
  • 2010-10-05
  • 2016-04-21
  • 2011-06-21
  • 1970-01-01
相关资源
最近更新 更多