我该如何在 C++ 中查找“零”位的数量?假设我有一个整数;
int value = 276;
我有一串二进制位 100010100,如何计算其中零的个数?
我该如何在 C++ 中查找“零”位的数量?假设我有一个整数;
int value = 276;
我有一串二进制位 100010100,如何计算其中零的个数?
如果你想要效率,那么书籍“Hackers Delight”中有一个很好的实现。
22条指令无分支。
unsigned int count_1bits(unsigned int x)
{
x = x - ((x >> 1) & 0x55555555);
x = (x & 0x33333333) + ((x >> 2) & 0x33333333);
x = x + (x >> 8);
x = x + (x >> 16);
return x & 0x0000003F;
}
unsigned int count_0bits(unsigned int x)
{
return 32 - count_1bits(x);
}
我会尝试解释它的工作原理。这是一种分治算法。
(x >> 1) & 0x55555555
将所有位向右移动一个单位,并取每个位对中的最低有效位。
0x55555555 -> 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 (16x2 bit pairs)
基本上,您将拥有以下所有2位排列的表。
1. (00 >> 1) & 01 = 00
2. (01 >> 1) & 01 = 00
3. (10 >> 1) & 01 = 01
4. (11 >> 1) & 01 = 01
x - ((x >> 1) & 0x55555555);
然后从非移位的成对值中减去这些值。
1. 00 - 00 = 00 => 0 x 1 bits
2. 01 - 00 = 01 => 1 x 1 bits
3. 10 - 01 = 01 => 1 x 1 bits
4. 11 - 01 = 10 => 2 x 1 bits
x = x - ((x >> 1) & 0x55555555);
现在,我们已经改变了每个2位比特对,以便它们的值现在是其对应原始2位比特对的位数...然后我们以类似的方式继续进行4位组、8位组、16位组和最终32位。
如果您想要更好的解释,请购买这本书,其中有很多关于替代算法等方面的良好解释和讨论...
x = (x + (x >> 4)) & 0x0f0f0f0f;
,在 x = x + (x >> 8);
之前。 - libo最简单、最朴素的方法就是遍历位并计数:
size_t num_zeroes = 0;
for(size_t i = 0; i < CHAR_BIT * sizeof value; ++i)
{
if ((value & (1 << i)) == 0)
++num_zeroes;
}
有许多更好(对于不同的“好”价值)的方式,但这种方法相当清晰、非常简洁(代码方面),并且不需要大量设置。
可能被视为改进的一种微小优化是不计算掩码以测试每个位,而是移位该值并始终测试最右边的位:
for(size_t i = 0; i < CHAR_BIT * sizeof value; ++i, value >>= 1)
{
if ((value & 1) == 0)
++num_zeroes;
}
CHAR_BIT * sizeof(int)
,即使这只告诉你内存中的大小,而不是 int 值表示所需的位数。你可以从 std::numeric_limits<int>::digits + std::numeric_limits<int>::is_signed
获取该值。 - MSaltersint __builtin_popcount (unsigned int x)
int __builtin_ctz (unsigned int x)
int __builtin_clz (unsigned int x)
详细信息请参阅GCC文档。
Kernighan算法用于计算二进制位中置为1的数量。
unsigned int v; // count the number of bits set in v
unsigned int c; // c accumulates the total bits set in v
for (c = 0; v; c++)
{
v &= v - 1; // clear the least significant bit set
}
可以轻松地适应所给定的任务。此处的迭代次数等于设置的位数。
我还建议查看上面的链接,了解解决这种和其他类型位相关任务的各种方法。还有一个单行示例,实现了获取位计数的宏。
我很惊讶没有人提到这个:
int num_zero_bits = __builtin_popcount(~num);
使用GCC时,num
中的零位数将被计算。
/* Assuming CHAR_BITS == 8 */
int bitsPerByte[256] = { 8, 7, 7, 6, /* ... */ };
int bitsInByte(unsigned char c) { return bits[c]; }
先执行一次取反操作,然后计算其中的1的个数。
count_zero_bits( x ) = count_one_bits( ~x );
实现代码以计算1的个数。
template< typename I >
int count_one_bits( I i )
{
size_t numbits = 0;
for( ; i != 0; i >>= 1 )
{
numbits += i&1;
}
}
虽然我的函数存在一个问题,如果i是负数,因为>>会将1位放入右侧,所以你会得到一个永不终止的循环。如果有一种模板化的方法来强制使用无符号类型,那就太理想了。
一旦你有了这个,那么:
template< typename I > int count_zero_bits( I i )
{
return count_one_bits( ~i );
}
会起作用。