Algorithm 哈希代码计算为什么要乘法并忽略溢出位?

Algorithm 哈希代码计算为什么要乘法并忽略溢出位?,algorithm,hashcode,Algorithm,Hashcode,这个问题不是关于一个人为什么要繁殖,这是相当明显的——而是关于分布 但这更多的是关于乘法的一个属性,它变得越重要,因为哈希代码计算公式中包含的因子越多 一个简单的计算显然可能会溢出,但这并不重要 a * 31 + b 当公式中有许多项时,真正的问题就被证明了 ((a * 31) + b) * 31 ... 6n. 一旦包含5个或6个以上的术语,第一个术语的值将丢失,因为当哈希代码值达到包含5+术语的程度时,其位已溢出。使用这个系统,只有最后5个左右的术语才是最终价值的真正重要贡献者 31

这个问题不是关于一个人为什么要繁殖,这是相当明显的——而是关于分布

但这更多的是关于乘法的一个属性,它变得越重要,因为哈希代码计算公式中包含的因子越多

一个简单的计算显然可能会溢出,但这并不重要

a * 31 + b
当公式中有许多项时,真正的问题就被证明了

((a * 31) + b) * 31 ... 6n.
一旦包含5个或6个以上的术语,第一个术语的值将丢失,因为当哈希代码值达到包含5+术语的程度时,其位已溢出。使用这个系统,只有最后5个左右的术语才是最终价值的真正重要贡献者

31 ^ 7 > Integer.MAX_VALUE
那么,为什么大多数计算不将溢出的位回滚,并对结果的低位进行异或运算呢。我理解这需要一些位篡改,并且必须使用long(64位)进行计算,这样前32位可以与整数结果进行异或运算,但至少不会丢失任何位

忽略溢出有什么特别的原因吗?它并不像前面所描述的那样使用长时间存储的成本高

编辑

100000*31^7=            2751261411100000       0x9C641F717C560
6553600000*31^7 180306667837849600000    0xC641F717C5600000
请注意,后一个值正好是前一个值的65536倍,这也意味着其答案比前一个值大16位。请注意 0xC641F717C5600000为0xC5600000。16位值的实际有效值丢失

*SAMPLE A*
65536*4096*27512614111  

=7385361114638319616
=0x667E12CDF0000000
   12345678
=0xF0000000

*SAMPLE B*
9*65536*4096*27512614111

=66468250031744876544
=0x9A6EA93D70000000
   12345678
=0x70000000
请注意,样本B的最高位正好是样本A的9倍,在最终的32位值中几乎没有任何差异-如果我将9倍更改为17倍,则较低的位将是相同的。但是,如果最上面的位没有由于溢出和与较低32位的xord而“丢失”,则该值将不同

忽略溢出有什么特别的原因吗?它并不像前面所描述的那样使用长时间存储的成本高


但几乎可以肯定的是,这并没有什么好处。这种方法通常会产生一个良好的值分布。

这是乘以奇数的好处;前面的数字永远不会完全从整数的末尾脱落。对于要丢失的元素,
31^n
需要是2的幂,这是不可能发生的。例如,在您的情况下,使用
31^7
,您将获得32位数字的
0x67E12CDF
;因此,尽管溢出,输入元素乘以该值仍将对结果产生影响。

我看不出示例中的要点。在我看来,它们似乎与计算散列码的方式无关:
a*31+b

您可能会找到一些
a
b
,它们将给出相同的hashcode(但高位不同)。然后将高位异或返回到哈希代码中是有意义的

或者,另一个例子是
((a*31)+b)*31+…+z
。然后找到一些
a
b
,…,
z
,其中hashcode不再依赖于
a
。因此,
a
不会是一个重要的贡献者

当然,如果您通过
65536
更改
31
,很容易找到
a
,…,
z
。任何值都可以,所有的
a
位都会简单地脱落,
a
会向左移位并切断。但是,您能为
31
这样做吗?或者类似的,您可以将高位异或返回。但是,为什么?你能找到一个有帮助的案例吗

65536的问题是,在二进制中它看起来像这样
100000000000000
。所以,当你用一个数字乘以它,在二进制中,它会再次得到16个零。对于二进制的
31
11111
,这不会发生


哦,我不是说这些例子不存在,因为它们确实存在(毕竟这只是一个散列)。但是,你不会发现很多或类似的例子

是的,但随着时间的推移,哈希代码中实际上只存在非常低的位。@mP:你是什么意思?当您使用奇数乘法器时,所有输入元素都会影响最终的哈希代码。@Jeremiah我在我的原始q中回答了一些数学问题和我的pt示例。@mP:我在您的问题中没有看到任何新的编辑。您可能需要尝试代码以查看其行为。只需编写一个函数,实现您在问题中显示的功能,并在具有不同开头但相同结尾的字符串上进行测试。如果使用31之类的常量,则无论更改字符串的哪一部分,哈希代码都将更改。@JW检查粗体编辑,您将看到这些图显示高位最终是如何由于乘法而丢失的。我可以试着用代码设计一个例子,但上面的数学应该足够了。不仅如此,而且长时间会遇到同样的问题,只需要一点
long
er。(对不起,这是一个糟糕的结果…)素数作为乘法因子的全部原因是因为概率意味着值向左移动,最终所有的位都会丢失。然而素数仍然有同样的问题,它们是jsut更好一点,并且需要更长的时间才能让位消失。第一部分试图演示位是如何从乘法中溢出和消失的,但效果很差。您对65536的评论完全正确。上述计算表明,“hi”顺序位很快丢失,因此,如果第一项的哈希代码为0x10001或0x30001,0x70001或0xffff0001很快就会丢失。我的评论试图指出,乘法引入0位的行为,如果不忽略溢出,可以用一些适当的1来替换。@mP-乘法是正确的。但你的问题是关于散列码分发的,对吗?如果您使用
31
而不是
65536
,那么良好的分布和丢失高位是不相关的。使用65536(实际上是16位的移位)只能最好地显示问题。穆特乘31北