C++ 解释在32位整数中计算集合位的方法

C++ 解释在32位整数中计算集合位的方法,c++,c,algorithm,bit-manipulation,C++,C,Algorithm,Bit Manipulation,在这篇文章中,我查看了计算集合位的方法,发现了以下方法 作者说: 对32位整数v中的位进行计数的最佳方法是 以下: 最好的位计数方法只需要12次运算,这是 与查找表方法相同,但避免了内存和潜力 缓存表的未命中。它是纯平行线和平行线的混合体 方法以及使用倍数的早期方法(在第节中 在使用64位指令计算位时),尽管它不使用 64位指令。字节中设置的位的计数在中完成 并行,并计算字节中设置的位的总和 乘以0x1010101并右移24位 有没有解释过这种方法是如何计算设置位的?这是因为你可以将设置位的总数

在这篇文章中,我查看了计算集合位的方法,发现了以下方法

作者说:

对32位整数v中的位进行计数的最佳方法是 以下:

最好的位计数方法只需要12次运算,这是 与查找表方法相同,但避免了内存和潜力 缓存表的未命中。它是纯平行线和平行线的混合体 方法以及使用倍数的早期方法(在第节中 在使用64位指令计算位时),尽管它不使用 64位指令。字节中设置的位的计数在中完成 并行,并计算字节中设置的位的总和 乘以0x1010101并右移24位


有没有解释过这种方法是如何计算设置位的?

这是因为你可以将设置位的总数分成两半,计算两半中的设置位数,然后相加。也称为“分而治之”范式。让我们详细讨论一下

v = v - ((v >> 1) & 0x55555555); 
两位中的位数可以是
0b00
0b01
0b10
。让我们试着在2位上解决这个问题

 ---------------------------------------------
 |   v    |   (v >> 1) & 0b1010   |   v - x   |
 ---------------------------------------------
   0b00           0b00               0b00   
   0b01           0b00               0b01     
   0b10           0b01               0b01
   0b11           0b01               0b10
这是必需的,最后一列显示每两位对中的设置位计数。如果两位数字
>=2(0b10)
,则
产生
0b01
,否则产生
0b00

v = (v & 0x33333333) + ((v >> 2) & 0x33333333); 
这句话应该很容易理解。在第一次操作之后,我们每两位就有一个设置位的计数,现在我们每4位就把这个计数加起来

v & 0b11001100         //masks out even two bits
(v >> 2) & 0b11001100  // masks out odd two bits
然后我们总结上述结果,给出4位中的集合位总数。最后一个语句是最棘手的

c = ((v + (v >> 4) & 0xF0F0F0F) * 0x1010101) >> 24;
让我们更进一步

v + (v >> 4)
它类似于第二个语句,我们将以4为一组来计算设置位。我们知道,由于我们以前的操作,每个半字节都有一个设置位的计数。让我们看一个例子,假设我们有字节
0b01000010
。这意味着第一个半字节设置了4位,第二个半字节设置了2位。现在我们把这些小口加在一起

0b01000010 + 0b01000000
它给出了第一个半字节
0b01100010
中一个字节的设置位计数,因此我们屏蔽了数字中所有字节的最后四个字节(丢弃它们)

现在每个字节中都有设置位的计数。我们需要把它们加起来。诀窍是将结果乘以具有有趣属性的
0b10101010
。如果我们的数字有四个字节,
A B C D
,它将产生一个包含这些字节的新数字
A+B+C+D B+C+D C+D D D
。一个4字节的数字最多可以设置32位,可以表示为
0b00100000

v = (v & 0x33333333) + ((v >> 2) & 0x33333333); 

我们现在需要的是第一个字节,它包含所有字节中所有设置位的总和,我们通过
>24
得到它。该算法是为
32位
字设计的,但对于
64位
字可以很容易地修改。

它之所以有效,是因为您可以将设置位的总数除以两半,计算两半中的设置位数,然后将它们相加。也称为“分而治之”范式。让我们详细讨论一下

v = v - ((v >> 1) & 0x55555555); 
两位中的位数可以是
0b00
0b01
0b10
。让我们试着在2位上解决这个问题

 ---------------------------------------------
 |   v    |   (v >> 1) & 0b1010   |   v - x   |
 ---------------------------------------------
   0b00           0b00               0b00   
   0b01           0b00               0b01     
   0b10           0b01               0b01
   0b11           0b01               0b10
这是必需的,最后一列显示每两位对中的设置位计数。如果两位数字
>=2(0b10)
,则
产生
0b01
,否则产生
0b00

v = (v & 0x33333333) + ((v >> 2) & 0x33333333); 
这句话应该很容易理解。在第一次操作之后,我们每两位就有一个设置位的计数,现在我们每4位就把这个计数加起来

v & 0b11001100         //masks out even two bits
(v >> 2) & 0b11001100  // masks out odd two bits
然后我们总结上述结果,给出4位中的集合位总数。最后一个语句是最棘手的

c = ((v + (v >> 4) & 0xF0F0F0F) * 0x1010101) >> 24;
让我们更进一步

v + (v >> 4)
它类似于第二个语句,我们将以4为一组来计算设置位。我们知道,由于我们以前的操作,每个半字节都有一个设置位的计数。让我们看一个例子,假设我们有字节
0b01000010
。这意味着第一个半字节设置了4位,第二个半字节设置了2位。现在我们把这些小口加在一起

0b01000010 + 0b01000000
它给出了第一个半字节
0b01100010
中一个字节的设置位计数,因此我们屏蔽了数字中所有字节的最后四个字节(丢弃它们)

现在每个字节中都有设置位的计数。我们需要把它们加起来。诀窍是将结果乘以具有有趣属性的
0b10101010
。如果我们的数字有四个字节,
A B C D
,它将产生一个包含这些字节的新数字
A+B+C+D B+C+D C+D D D
。一个4字节的数字最多可以设置32位,可以表示为
0b00100000

v = (v & 0x33333333) + ((v >> 2) & 0x33333333); 

我们现在需要的是第一个字节,它包含所有字节中所有设置位的总和,我们通过
>24
得到它。该算法是为
32位
word设计的,但对于
64位
word可以很容易地进行修改。

手工在纸上进行,以查看发生了什么。第一行存储该对位中每对位的设置位数。第二种方法将两个相邻比特对的计数相加,并将其存储在由这两个比特对组成的半字节中。第三个将构成一个字节的两个半字节的计数相加,然后将所有四个字节的计数相加;与
0x1010101
相乘会将每个字节的内容移动到最重要的位置,并导致加法。如果平均而言,在单词中设置了许多位,则称为此算法的书才是“最佳”的。如果您经常设置很少的位,那么其他算法所需的时间可能比12次运算少得多。从Bo的链接中可以看到,简要地解释了一些看起来非常相似的东西