C++ 解释在32位整数中计算集合位的方法
在这篇文章中,我查看了计算集合位的方法,发现了以下方法 作者说: 对32位整数v中的位进行计数的最佳方法是 以下: 最好的位计数方法只需要12次运算,这是 与查找表方法相同,但避免了内存和潜力 缓存表的未命中。它是纯平行线和平行线的混合体 方法以及使用倍数的早期方法(在第节中 在使用64位指令计算位时),尽管它不使用 64位指令。字节中设置的位的计数在中完成 并行,并计算字节中设置的位的总和 乘以0x1010101并右移24位C++ 解释在32位整数中计算集合位的方法,c++,c,algorithm,bit-manipulation,C++,C,Algorithm,Bit Manipulation,在这篇文章中,我查看了计算集合位的方法,发现了以下方法 作者说: 对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的链接中可以看到,简要地解释了一些看起来非常相似的东西