Long.bitCount()如何找到设置位数?

时间:2017-05-21 05:01:56

标签: java long-integer bitcount

我知道这是代码。但我无法理解它的作用

 `public static int bitCount(long i){
         i = i - ((i  > > > 1) & 0x5555555555555555L);
         i = (i & 0x3333333333333333L) + ((i  > > > 2) & 0x3333333333333333L);
         i = (i + (i  > > > 4)) & 0x0f0f0f0f0f0f0f0fL;
         i = i + (i  > > > 8);
         i = i + (i  > > > 16);
         i = i + (i  > > > 32);
       return (int)i & 0x7f;
 }`

3 个答案:

答案 0 :(得分:3)

我们以255为例。随着我们的进展,这些位被合并。首先我们从255开始0b1111.1111(二进制为8 1)

第一行代码是:

i = i - ((i  > > > 1) & 0x5555555555555555L);

这条线正在梳理每一对1。由于我们有8个1,我们期望将我们的对组合起来,并得到像2,2,2,2这样的东西。由于它是二进制的,我们期望10101010。

让我们看看i > > > 1。我是0b1111.1111,这是向下移动1,所以我们得到0b0111.1111。我们将交叉点&0b0101.0101(这是5为二进制的101)。这样做可以保留一半的比特,特别是最初位于偶数点的所有比特(从我们的初始数字开始的第2,第4,第6,第8位)。

然后我们从我们的初始值中减去它,这有点黑客。我们试图将我们的最高位添加到底部位,所以我们可以这样做:

((i > > > 1) & 0x5555) + (i & 0x5555)

左边的术语是顶部位,右边的术语是底部位。但我们知道i = 2 *(顶部位)+ 1 *(底部位),因为顶部位被提升1(与乘以2相同)。因此,通过减去顶部位1次,我们得到了相同的结果。

好的,现在我们已经为第二行代码做好了准备。我们目前有i 0b1010.1010,我们准备添加每对2.我们希望得到4,4(每半部分使用4位)或二进制0100.0100。代码是:

i = (i & 0x3333333333333333L) + ((i  > > > 2) & 0x3333333333333333L);

我们得到的每组4个前两个数字和最后2个数字,我们正在添加它们。 0x3333 = 0b0011.0011.0011.0011所以我们可以看到将交集&与3'保持在一个组中的底部2个数字。我们首先得到底部的两个数字,然后我们将我移动超过2个点以获得前2个数字。然后我们添加:0010.0010 + 0010.0010 = 0100.0100。完全符合预期。

接下来,我们将2组4组合在一起。

 i = (i + (i  > > > 4)) & 0x0f0f0f0f0f0f0f0fL;

0x0f0f = 0b0000111100001111,所以如果我们采用这个交叉点,我们会保留每4个数字。我们将i添加到自身降档4,因此我们计算0100.0100 + 0000.0100 = 0100.1000。 4 + 4应该返回8和8 = 0b1000,但仍然需要删除顶部的“4”。与0f0f0f0f的交集执行此操作。

所以现在我们有0b1000,这是8。其余的步骤加上更高的位(比如2组8个一起,而不是2组16 ..)但是因为我们的数字(255)只有8位长,高位都是0,所以这不会影响我们的结果。

答案 1 :(得分:1)

说明:

此方法返回您的long将采用的二进制数:https://www.tutorialspoint.com/java/lang/long_bitcount.htm

工作原理:

  • 例如,让我们取i = 10 decimal = 1010 binary
  • 第一行:i = i - ((i > > > 1) & 0x5555555555555555L);
    • 0x5555555555555555L十六进制= 101010101010101010101010101010101010101二进制
    • 1010移位一位:0101
    • 0101101010101010101010101010101010101010101 : {0 = 1} = 0 , {1 = 0} = 0 , {0 = 1} = 0 , {1 = 0} = 0 : 0000
    • 进行逐位比较
    • 0000 binary = 0十进制
    • 10(i)减去0
  • 第二行:i = (i & 0x3333333333333333L) + ((i > > > 2) & 0x3333333333333333L);
    • 0x3333333333333333L十六进制= 11001100110011001100110011001100110011二进制
    • (i > > > 2)表示我换了两个:i = 10,二进制:1010; 转移后:0010
    • i(1010)与11001100110011001100110011001100110011相比1001 = 9十进制
    • 0010比较为0001,最新是什么。
    • 9 + 1 = 10 = I

我认为这就足够了。 希望它有所帮助。

答案 2 :(得分:0)

算法是:

要计算2 ^ n位数字中的1位数目(在这种情况下,n = 6,但是您可以为任何机器数大小编写此算法),将其分成两半,并使用移位运算,同时计算左半部分和右半部分中1位的数目,并将结果存储在每一半的最右(最低有效)位中。然后通过将左侧移到右侧并相加来合并这些结果。

这是一个递归算法-您现在将其应用于双方。使其快速的聪明之处在于,您可以使用同时对两个半部执行算法的移位运算。因此,算法为O(log(N)),其中N = 2 ^ n是您的机器编号中的位数。

所以最后几行代码是对每个季度,然后每个一半,然后整个事情进行“重组”。