[英]How does this bit manipulation work in Java?
我正在研究Java如何計算int的位集。
在我的腦海里,我有一些像這樣的簡單(我認為是正確的):
public static int bitCount(int number){
final int MASK = 0x1;
int count = 0;
for(int i = 0; i < 32; i++){
if(((number >>> i) & MASK) == MASK){
count++;
}
}
return count;
}
相反,我找到了一種方法,我完全不知道在做什么(對我來說似乎很神奇):
i = i - ((i >>> 1) & 0x55555555);
i = (i & 0x33333333) + ((i >>> 2) & 0x33333333);
i = (i + (i >>> 4)) & 0x0f0f0f0f;
i = i + (i >>> 8);
i = i + (i >>> 16);
return i & 0x3f;
有人可以幫助理解這是什么以及為什么一個簡單的功能,如我最初想到的那個/可能是壞的?
當然
i = i - ((i >>> 1) & 0x55555555);
5的位模式是0101
(4位),因此掩碼是位模式01
的重復16次。 該行計算該數字的每兩位對中的位數。 如果考慮兩位對, (i >>> 1) & 0x1
得到低位的高位。 現在,有四種可能的兩位模式
00 ~> 00 - 00 = 00
01 ~> 01 - 00 = 01
10 ~> 10 - 01 = 01
11 ~> 11 - 01 = 10
現在每個兩位對包含原始位置的位數。
i = (i & 0x33333333) + ((i >>> 2) & 0x33333333);
接下來,我們計算每個四位組(也稱為半字節)中的位數。 通過屏蔽具有0x3 = 0011(b)
的半字節,我們得到半字節的低位兩位的位數,並且(i >>> 2) & 0x3
從高位的兩位得到計數。蠶食。 現在添加了這些計數。 由於每個計數最多為2,因此總和最多為4,因此不會留下半字節。
i = (i + (i >>> 4)) & 0x0f0f0f0f;
現在計算每個八位字節中的位數。 每個半字節包含在該位置原始中設置的位數。 向右移動四位將計數從每個位置的高階半字節移動到低階半字節,這些都被添加。 然后我們還將計數從低階半字節移動到相鄰八位字節的高階nets,由& 0x0f0f0f0f
屏蔽掉。 由於每個八位位組最多可以設置8位,因此計數不會離開八位位組的低位半字節。
i = i + (i >>> 8);
現在我們添加相鄰八位字節對的計數。
i = i + (i >>> 16);
現在我們在高位兩個八位位組和低位二位數中添加計數總數。
return i & 0x3f;
最后,除了最低階八位字節之外的所有八位字節都被屏蔽掉,因為高階八位字節仍然包含中間計數。
您的簡單實現可能被認為是錯誤的原因是性能。 復雜的bit-hack使用較少的操作而沒有分支,因此速度更快。 然而,這更容易出錯。
計算int
(或long
)中的設置位的另一種巧妙方法是
public static int bitCount(int n) {
int count = 0;
while(n != 0) {
++count;
n = n & (n-1);
}
return count;
}
這工作,因為n = n & (n-1)
清除最后一組位n
和離開一切不變。 如果n
的位模式結束
...100...0
n-1
的位模式是
...011...1
並且n
中最后1位之前的位是相同的。 在Java中,保證也可以用於負數,因為整數溢出具有環繞語義,因此如果n = Integer.MIN_VALUE
,則位模式為100...0
並且n - 1
變為具有位模式的Integer.MAX_VALUE
011...1
。
cool方法只能工作,因為計算機的int值有限。 因為你需要在計算之前知道所有需要的掩碼,所以它無法在無限范圍(如BigInteger)中輕松工作(以及其他很酷的位算法)。
無論如何,你可以通過以下方式閱讀它的工作原理: http : //graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel
它在本章的底部。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.