为什么在重写ReSharper GetHashCode方法时要使用“397”?

169

和许多人一样,我使用ReSharper来加速开发过程。当你用它来覆盖类的相等成员时,它为GetHashCode()生成的代码看起来像这样:

    public override int GetHashCode()
    {
        unchecked
        {
            int result = (Key != null ? Key.GetHashCode() : 0);
            result = (result * 397) ^ (EditableProperty != null ? EditableProperty.GetHashCode() : 0);
            result = (result * 397) ^ ObjectId;
            return result;
        }
    }
当然,我有一些自己的成员在里面,但我想知道的是为什么是397?
编辑:那么我的问题更好的表述应该是,除了它是一个质数之外,397这个质数有没有什么“特别”的地方?
2个回答

182

可能是因为397是一个足够大的质数,会导致结果变量溢出,并使哈希的比特位混合,从而提供更好的哈希码分布。397并没有什么特别之处,与同一数量级的其他质数相比也没有区别。


81
397很快乐。难道我们不都希望自己快乐吗? - Russell B
2
好的,但为什么它必须是质数,而且为什么它必须是那个确切的大小?如果它必须是质数,为什么不是2或2147483647? 我想要得到良好的变异(这种乘法的唯一原因就是变异),我们不需要数字是质数。我们需要乘数具有相对相同数量的零和一,最好没有明显的模式。 397 = 110001101b 符合要求。仍然不确定大小。 - Andriy K
5
正如Nick所说,这并没有什么特别之处。它不需要那么大,那只是一个足够大的数字,当你计算哈希值时,结果会溢出(因为GetHashCode()返回Int32)。选择一个质数只是为了更好地分布,我没有数学学位,所以我不会试图解释,但是用质数进行乘法运算将得到比使用任何其他任意数字更平均分布的结果。 - Ben Randall
@AndriyK,2对于哈希表来说是非常小的尺寸。您的负载因子将是基于质数的哈希表大小的最小可能负载因子。随着负载因子趋近于0,哈希表中未使用区域的比例增加,但搜索成本不一定会降低。因此,这实际上是哈希表的最差尺寸。换句话说,您可以认为*397定义了哈希表的大小,这就是FNV哈希算法所做的(但它建议在64位哈希中使用1099511628211,这不适用于32位整数)。 - John Zabroski
值得注意的是,FNV推荐的数字用于哈希字节序列,而ReSharper的数字用于哈希整数序列(内部哈希码)。这意味着乘以一个大数并使结果模2^32易于收敛,这对于哈希函数是不可取的。 - fernacolo

19
resharper使用的哈希看起来像是FNV哈希的变体。FNV经常使用不同的质数进行实现。有一个关于选择适当的FNV质数的讨论在这里

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接