我如何在C ++中找到'零'位的数量。 假设我有一个整数;
int value = 276;
我有100010100位,但我如何计算零?
答案 0 :(得分:26)
如果你想要效率,那么“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
将所有位向右移1步,并取每个位对的最低位。
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位。
如果您想要更好的解释购买本书,有很多很好的解释和讨论替代算法等...
答案 1 :(得分:15)
最简单最天真的方法是迭代位数并计算:
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;
}
答案 2 :(得分:9)
你可以减去32 the number of bits set。
答案 3 :(得分:4)
如果您使用GCC,您可以尝试内置功能:
int __builtin_popcount (unsigned int x)
int __builtin_ctz (unsigned int x)
int __builtin_clz (unsigned int x)
有关详细信息,请参阅GCC Documentation。
答案 4 :(得分:4)
Kernighan way计数设置位
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
}
可以轻松适应给定的任务。这里的多次迭代等于设置的位数。
我还推荐上面的链接,用于解决此问题以及其他类型的位相关任务的各种其他方法。还有一个单行示例获取在宏中实现的位计数。
答案 5 :(得分:3)
到目前为止,最明显的解决方案是查找表。
/* Assuming CHAR_BITS == 8 */
int bitsPerByte[256] = { 8, 7, 7, 6, /* ... */ };
int bitsInByte(unsigned char c) { return bits[c]; }
答案 6 :(得分:3)
这本书有一本很好的书:Hacker's Delight(是的,这个名字很糟糕:它与安全无关,但却完全无聊)。它提供了几种算法来计算'1'位,也可以找到最好的here(虽然这本书解释说这个网站没有)。
一旦知道'1'位计数,只需将其减去类型表示中的位数。
答案 7 :(得分:2)
我很惊讶没有人提到过这个:
int num_zero_bits = __builtin_popcount(~num);
当与GCC一起使用时,这将给出num
中的零位数。
答案 8 :(得分:1)
做一个人的恭维然后算上1秒。
count_zero_bits(x)= count_one_bits(~x);
实施代码来计算代码。
template< typename I >
int count_one_bits( I i )
{
size_t numbits = 0;
for( ; i != 0; i >>= 1 )
{
numbits += i&1;
}
}
虽然我的功能存在问题,如果我是负数,因为&gt;&gt;将1位放入右侧,这样您就可以得到一个永不停止的循环。如果有一种模板化的方法来强制执行理想的无符号类型。
一旦你拥有了那个:
template< typename I > int count_zero_bits( I i )
{
return count_one_bits( ~i );
}
会奏效。
答案 9 :(得分:0)
据我所知,将零位计数置于正整数中的最简单方法是以下代码。
int get_zero_bit_count(int num)
{
int cnt = 0;
while(num > 0)
{
int and_num = num & 1;
if (and_num != num) cnt++;
num >>= 1;
}
return cnt;
}
这段代码很容易理解,并且是自我解释的。这适用于正整数。
答案 10 :(得分:0)
扩展ronag的答案,其他用户提到的导致错误的结果(他的算法最多只能达到x = 15的值),这里是算法的更新版本:
uint8_t count_1bits(uint32_t x) {
x = x - ((x >> 1) & 0x55555555);
x = (x & 0x33333333) + ((x >> 2) & 0x33333333);
x = (x & 0x0F0F0F0F) + ((x >> 4) & 0x0F0F0F0F);
x = (x & 0x00FF00FF) + ((x >> 8) & 0x00FF00FF);
x = (x & 0x0000FFFF) + ((x >> 16) & 0x0000FFFF);
return x & 0x3F;
}
uint8_t count_0bits(uint32_t x) {
return 32 - count_1bits(x);
}
对ronag的第一行的解释是正确的,但是,其余的行使用不同的方法。在第一行中,通过移位和减法,每个2位对将包含原始数中该对中设置的位数。通过将每个2n位组的lsb添加到移位n的该对的msb,其余的行递归地将这些数字折叠在一起,以便2n位组包含在该组中设置的位数。原始号码:
01110110: 0111 (7 bits were set in the original number) 0110 (6 bits were set in the original number)
-> 01110110 & 00001111 + (01110110 >> 4) & 00001111
= 0110 + 0111
= 1101
上述算法适用于32位整数,但可以通过将常数更改为正确的位长度来轻松调整,以使模式保持不变(例如0x5555 ... = 0101 ...,0x0f0f ... = 00001111 ......等)并添加/删除适当的班次
答案 11 :(得分:0)
#include <bits/stdc++.h>
using namespace std;
#define ll long long int
int main() {
ll a;
cin>>a;
ll ones=__builtin_popcountll(~a);
ll zero=__builtin_clzll(a);
ll num=abs(ones-zero);
cout<<num<<"\n";
return 0;
}
借助内置的gcc函数,即popcount(设置位的数量)和clz(前导零的数量),我们可以计算整数中的零位数量
您可以在https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html,https://www.geeksforgeeks.org/builtin-functions-gcc-compiler/
上了解它们。