もちろん
i = i - ((i >>> 1) & 0x55555555);
5のビットパターンは0101
(4ビット)なので、マスクはビットパターンを01
16回繰り返したものです。この行は、数値の2ビットペアごとにビット数をカウントします。2ビットのペアを検討する場合(i >>> 1) & 0x1
、上位ビットを下位位置に取得します。現在、4つの可能な2ビットパターンがあります
00 ~> 00 - 00 = 00
01 ~> 01 - 00 = 01
10 ~> 10 - 01 = 01
11 ~> 11 - 01 = 10
そして、各2ビットペアには、元の位置にあったビット数が含まれるようになりました。
i = (i & 0x33333333) + ((i >>> 2) & 0x33333333);
次に、各4ビットグループ(別名ニブル)に含まれていたビットをカウントします。ニブルをでマスクすることにより、ニブル0x3 = 0011(b)
の下位2ビットにあったビット数を取得し、ニブル(i >>> 2) & 0x3
の上位2ビットからカウントを取得します。これらのカウントが追加されました。各カウントは最大2であったため、合計は最大4であり、ニブルを残しません。
i = (i + (i >>> 4)) & 0x0f0f0f0f;
これで、各オクテットのビットがカウントされます。各ニブルには、その場所のオリジナルに設定されているビット数が含まれています。右に4ビットシフトすると、カウントが各場所の上位ニブルから下位ニブルに移動し、これらが追加されます。次に、カウントを低次のニブルから隣接するオクテットの高次のニブルに移動しました。これは、によってマスクされてい& 0x0f0f0f0f
ます。各オクテットには最大8ビットを設定できるため、カウントによってオクテットの下位ニブルが残ることはありません。
i = i + (i >>> 8);
次に、隣接するオクテットのペアのカウントを追加します。
i = i + (i >>> 16);
ここで、上位2オクテットと下位2オクテットのカウントの合計を加算します。
return i & 0x3f;
最後に、低次のオクテットを除くすべてのオクテットがマスクされます。これは、高次のオクテットにまだ中間カウントが含まれているためです。
単純な実装が悪いと見なされる理由は、パフォーマンスです。複雑なビットハックは、使用する操作が少なく、ブランチがないため、高速です。ただし、間違えるのははるかに簡単です。
int
(またはlong
)のセットビットをカウントするもう1つの優れた方法は次のとおりです。
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
最後の1ビットの前のビットn
は同じです。Javaでは、整数のオーバーフローにはラップアラウンドセマンティクスがあるため、負の数でも機能することが保証されています。したがって、の場合n = Integer.MIN_VALUE
、ビットパターンはビットパターンと同じ100...0
にn - 1
なりInteger.MAX_VALUE
ます011...1
。