[英]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.