计数位设置
在密码学和其他应用中经常需要比特串的人口数,并且该问题已被广泛研究。
天真的方式需要每位迭代一次:
unsigned value = 1234;
unsigned bits = 0; // accumulates the total number of bits set in `n`
for (bits = 0; value; value >>= 1)
bits += value & 1;
一个很好的技巧(基于删除最右边的设置位 )是:
unsigned bits = 0; // accumulates the total number of bits set in `n`
for (; value; ++bits)
value &= value - 1;
它经历了与设置位一样多的迭代,因此当 value
预期具有很少的非零位时,这是好的。
该方法最初是由 Peter Wegner 提出的(在 CACM 3/322 - 1960 中),因为它出现在 *C 编程语言中,*由 Brian W. Kernighan 和 Dennis M. Ritchie 提出。
这需要 12 次算术运算,其中一次是多次运算:
unsigned popcount(std::uint64_t x)
{
const std::uint64_t m1 = 0x5555555555555555; // binary: 0101...
const std::uint64_t m2 = 0x3333333333333333; // binary: 00110011..
const std::uint64_t m4 = 0x0f0f0f0f0f0f0f0f; // binary: 0000111100001111
x -= (x >> 1) & m1; // put count of each 2 bits into those 2 bits
x = (x & m2) + ((x >> 2) & m2); // put count of each 4 bits into those 4 bits
x = (x + (x >> 4)) & m4; // put count of each 8 bits into those 8 bits
return (x * h01) >> 56; // left 8 bits of x + (x<<8) + (x<<16) + (x<<24) + ...
}
这种实现具有最好的最坏情况行为( 有关详细信息,请参阅汉明重量 )。
许多 CPU 都有特定的指令(如 x86 的 popcnt
),编译器可以提供特定的( 非标准 )内置函数。例如,使用 g ++有:
int __builtin_popcount (unsigned x);