Java 关于整数乘法、溢出和信息丢失

Java 关于整数乘法、溢出和信息丢失,java,overflow,hashcode,multiplication,Java,Overflow,Hashcode,Multiplication,我正在通读约书亚·布洛赫的《有效的Java》。在第8项:覆盖等于时始终覆盖哈希代码中,作者在其哈希函数中使用以下组合步骤: result = 37 * result + c; 然后,他解释了为什么选择37个(重点加上): 选择乘法器37是因为它是奇数素数如果它是均匀的 乘法溢出,信息会因为乘法而丢失 乘2等于移位。使用素数的优点更少 很清楚,但使用素数是传统的 我的问题是,为什么组合因子(37)是奇数?无论因子是奇数还是偶数,乘法溢出不会导致信息丢失吗?考虑一下,当一个正数在一个基数为2的表示

我正在通读约书亚·布洛赫的《有效的Java》。在第8项:覆盖等于时始终覆盖哈希代码中,作者在其哈希函数中使用以下组合步骤:

result = 37 * result + c;
然后,他解释了为什么选择37个(重点加上):

选择乘法器37是因为它是奇数素数如果它是均匀的 乘法溢出,信息会因为乘法而丢失 乘2等于移位。使用素数的优点更少 很清楚,但使用素数是传统的


我的问题是,为什么组合因子(37)是奇数?无论因子是奇数还是偶数,乘法溢出不会导致信息丢失吗?

考虑一下,当一个正数在一个基数为2的表示法中重复乘以2时,会发生什么情况——所有的设置位最终都会从末尾移走,留下零

偶数乘法器将导致具有较少分集的散列码


另一方面,奇数可能会导致溢出,但不会丢失分集

哈希码的目的是基于输入具有随机位(尤其是较低的位,因为它们经常被更多地使用)

当您乘以2时,最低位只能是0,这缺乏随机性。如果乘以奇数,则最低位可以是奇数或偶数


一个类似的问题是你得到了什么

public static void main(String... args) {
    System.out.println(factorial(66));
}

public static long factorial(int n) {
    long product = 1;
    for (; n > 1; n--)
        product *= n;
    return product;
}
印刷品

0

第二个数是偶数,第四个数是4的倍数,等等。

解决办法在于数论和乘数与模数的关系

举个例子可能会有所帮助。假设您只得到2位来表示一个数字,而不是32位。所以你有4个数字(类)。0、1、2和3

CPU中的溢出与模运算相同

Class - x2 - mod 4 - x2 - mod 4

0       0      0     0     0

1       2      2     4     0

2       4      0     0     0

3       6      2     4     0
经过2次操作后,您只剩下1个可能的数字(类)。所以你已经“丢失”了信息

Class - x3 - mod 4 - x3 - mod 4 ...

0       0      0     0     0

1       3      3     9     1

2       6      2     6     2

3       9      1     3     3
这可以永远持续下去,你仍然有全部4门课。这样你就不会丢失信息

Class - x3 - mod 4 - x3 - mod 4 ...

0       0      0     0     0

1       3      3     9     1

2       6      2     6     2

3       9      1     3     3

关键是,您的乘法器和模类的LCD是1。这适用于所有奇数,因为模数当前总是2的幂。他们不必是素数,也不必是37。但信息丢失只是选择37个的一个标准,其他标准是值的分布等。

非数学简单版的原因

素数用于散列以保持多样性

由于集合和映射的实现,多样性可能更为重要。这些实现使用对象散列数的最后一位来索引条目的内部数组

例如,对于大小为8的条目,在具有内部表(数组)的哈希映射中,它将使用哈希数的最后3位来寻址表条目

static int indexFor(int h, int length) { return h & (length-1); } 静态int indexFor(int h,int length){ 返回h&(长度-1); } 事实上不是,但如果Integer对象

hash = 4 * number; 散列=4*个; 大多数表元素将为空,但有些元素将包含太多的条目。这将导致在搜索特定条目时进行额外的迭代和比较操作


我想Joshua Bloch主要关心的是尽可能均匀地分布散列整数,通过在映射和集合中均匀分布对象来优化集合的性能。从直觉上看,素数似乎是一个很好的分布因子。

素数并不是确保多样性的严格必要条件;所需要的是,因子相对于模量是相对素数


因为二进制算术的模总是二的幂,所以任何奇数都是相对素数,就足够了。但是,如果要采用除溢出以外的模,素数将继续确保多样性(假设您没有选择相同的素数…。

Ah,因此我们担心的不仅仅是溢出带来的一点点信息损失,将结果归零会导致信息完全丢失?@BilltheLizard:实际上,这是来自多个相互模拟的属性的数据。假设使用上述算法的三个属性a、b和c
result=2*(2*a+b)+c
,您可以看到在许多可能常见的
a、b、c
集合中存在重复。如果使用奇数素数作为常量,则拥有具有相同哈希值的集合的可能性将大大降低。甚至在将结果完全归零之前,问题就显现出来了。考虑将一个8位散列乘以两个乘法器一次,它从256个可能的值开始,以128个可能的值结束。可爱的,你可以用手显示它溢出到0。所以没有因子作为散列函数。。。不是我会这么做的。部分诀窍是找出为什么66是第一个0的阶乘。而不是128,例如有64个偶数因子。