我知道这是代码。但我无法理解它的作用
`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;
}`
答案 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
> > >
是逻辑转变:它将值移动后面的数字n;前n位用零填充:Difference between >>> and >>
&
是按位的:https://www.tutorialspoint.com/java/java_bitwise_operators_examples.htm
0x3333333333333333L
只是表达一些与十六进制比较的比特的方法。转换器:http://coderstoolbox.net/number/
i = i - ((i > > > 1) & 0x5555555555555555L);
0x5555555555555555L
十六进制= 101010101010101010101010101010101010101
二进制1010
移位一位:0101
0101
与101010101010101010101010101010101010101 : {0 = 1} = 0 , {1 = 0} = 0 , {0 = 1} = 0 , {1 = 0} = 0 : 0000
0000
binary = 0
十进制i = (i & 0x3333333333333333L) + ((i > > > 2) & 0x3333333333333333L);
0x3333333333333333L
十六进制= 11001100110011001100110011001100110011
二进制(i > > > 2)
表示我换了两个:i = 10,二进制:1010
;
转移后:0010
1010
)与11001100110011001100110011001100110011
相比1001
= 9十进制0010
比较为0001
,最新是什么。我认为这就足够了。 希望它有所帮助。
答案 2 :(得分:0)
算法是:
要计算2 ^ n位数字中的1位数目(在这种情况下,n = 6,但是您可以为任何机器数大小编写此算法),将其分成两半,并使用移位运算,同时计算左半部分和右半部分中1位的数目,并将结果存储在每一半的最右(最低有效)位中。然后通过将左侧移到右侧并相加来合并这些结果。
这是一个递归算法-您现在将其应用于双方。使其快速的聪明之处在于,您可以使用同时对两个半部执行算法的移位运算。因此,算法为O(log(N)),其中N = 2 ^ n是您的机器编号中的位数。
所以最后几行代码是对每个季度,然后每个一半,然后整个事情进行“重组”。