[英]Binary arithmetic: why hash%n is equivalent to hash&(n-1)?
我一直在研究Java HashMap源代码,它的一部分决定了放置一个对象的内容,并在Java 7(8)中看到了与Java 6相比的变化。另外,我进行了大量的实验,两个表达式都有相同的结果:
hash % n
and
hash & (n - 1)
where n - the array length that must be power of 2.
我只是想不通为什么是真的? 是否有任何定理或某些数学定律证明这些陈述是平等的? 基本上我想理解推论并证明这两个陈述的等价性。
PS。 如果n不是2数的幂,则等价性会立即中断。
如果n是2的幂,则表示其二进制表示为10000....
对于那个问题,n-1是1111111...
少了一个数字。
这意味着带有(n-1)
二进制&-ing恰好保留了n-1
设置的k
中的位数。
实施例n = 8: 1000, n-1 = 7: 111
&-ing例如k = 201: 11001001
k % n = k & (n-1) = 11001001 & 111 = 001 = 1
。
使用2的幂的%-ing意味着在二进制中你只需去掉上面(包括)唯一设置位的所有内容:对于n = 8,这意味着剥离所有内容(包括)第4位。 而这正是&-ing所做的。
副作用是使用&
是可交换的: hash & (n - 1)
等价于(n - 1) & hash
,对于%
不是真的,许多地方的jdk源代码使用后者,例如在getNode
如果n
是2的幂(或((1 << i) - 1)
,如果要简化对n
的约束,请考虑(n - 1)
的位:
如果n
是16( = 1 << 4)
,那么n - 1
是15
和16
(作为32位int
)的位表示是:
1 = 00000000000000000000000000000001 // Shift by 4 to get...
16 = 00000000000000000000000000010000 // Subtract 1 to get...
15 = 00000000000000000000000000001111
因此,只有最低的4位在15中设置。如果你&
另一个int,它只允许在结果中设置该数字的最后4位中的位,因此该值仅在0-范围内15,所以就像在做% 16
。
但请注意,此等效性不适用于负的第一个操作数:
System.out.println(-1 % 2); // -1
System.out.println(-1 & (2-1)); // 1
整数/
和%
的算术规则是:
x*(y/x) + (y%x) = y
负hash
-4和正n
8怎么样?
8*0 + (-4%8) = -4
因此模数保持符号。
-4 % 8 = -4
-4 & 7 = 4
要么:
int t = hash%n;
if (t < 0) {
t += n;
}
assert t == (hash & (n-1));
所以在早期的Java中, %n
hash
必须是积极的开始。 现在哈希可能是负面的,更稳固和更好的散列。 所以这是java源代码中这种微妙变化的合理原因。
背景:
2 n是1,后跟n-1 0 s(二进制)。 2 n - 1是n-1 1 s。
因此,对于n为2的正幂,以及一些正数h:
h % n == h & (n-1)
另一种用法是计算int中的位数。 Integer类具有这样的功能。
int bits = 0;
while (x != 0) {
x &= x - 1;
++bits;
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.