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

时间:2011-12-20 15:14:34

标签: java overflow hashcode multiplication

我正在阅读Joshua Bloch的 Effective Java Chapter 3。在项目8:覆盖等于时始终覆盖hashCode,作者在其散列函数中使用以下组合步骤:

result = 37 * result + c;

然后他解释了为什么选择37(强调添加):

  

选择乘法器37是因为它是奇数素数。 如果它是偶然的   乘法溢出,信息会因为乘法而丢失   两个相当于移位。使用素数的优点较少   很清楚,但为此目的使用素数是传统的。

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

5 个答案:

答案 0 :(得分:15)

考虑当一个正值在一个base-2表示中重复乘以2时会发生什么 - 所有的设置位最终都会从结尾开始,让你为零。

偶数乘数会导致哈希码具有较少的多样性。

另一方面,奇数可能会导致溢出,但不会损失多样性。

答案 1 :(得分:4)

hashCode的目的是根据输入获得随机位(特别是低位,因为这些位通常使用更多)

当你乘以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 :(得分:2)

解决方案在于数论和乘数的Lowest common denominator以及你的模数。

一个例子可能有所帮助。让我们说,而不是32位你只有2位代表一个数字。所以你有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个班级。所以你不要丢失信息。

关键是,你的muliplier和你的modulo类的LCD是1.这适用于所有奇数,因为你的模数目前总是2的幂。他们不必是素数而他们没有必要特别是37。但信息丢失只是选择37的标准,其他标准是价值分配等。

答案 3 :(得分:0)

非数学简单版为什么......

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

由于Set和Map实现,也许多样性更重要。这些实现使用对象哈希数的最后一位来索引内部条目数组。

例如,在带有内部表(数组)的HashMap中,对于大小为8的条目,它将使用最后3位哈希数来对表项进行地址。


    static int indexFor(int h, int length) {
        return h & (length-1);
    }

实际上它不是,但如果Integer对象有


    hash = 4 * number;

大多数表格元素都是空的,但有些会包含太多条目。这将导致在搜索特定条目时进行额外的迭代和比较操作。

我想Joshua Bloch的主要关注点是尽可能地分配散列整数,以通过在Maps和Sets中均匀分布对象来优化集合的性能。直觉上的素数似乎是一个很好的分配因素。

答案 4 :(得分:0)

素数不是确保多样性所必需的;必要的是因子是模数的相对质数。

由于二进制算术的模数总是2的幂,所以任何奇数都是相对素数,并且就足够了。但是,如果您采用溢出以外的模数,则素数将继续确保多样性(假设您没有选择相同的素数......)。