可能重复:
Best algorithm to count the number of set bits in a 32-bit integer?
给定一个32位整数N,设计一个算法来查找N的二进制位表示中的零个数。
我能想到的最简单的算法是检查零的二进制表示,在C中是这样的:
int num_of_zero(int num)
{
if(0 == num) return 1; /*For the input 0 it should output 1 */
int Count = 0;
while(num>0){
if(0 == (num&1)) Count++;
num >>= 1;
}
return Count;
}
如果有一些算法在恒定时间计算,我就会徘徊。
对于输入 0 ,它应返回1 而不是32 。
对于 5 ,输出应为1.二进制表示为 101 。
对于 7 ,输出应为0。
准确地说,我正在寻找一种更好的算法来计算32位整数的二进制解释中的(非前导)零的数量。希望问题现在很明显。
编辑正如Alex Martelli指出的那样,我正在修改我的代码以使其更具可读性和安全性。这次使用迭代。
答案 0 :(得分:5)
执行此操作的简单方法是迭代数字的二进制表示的每个位,测试每个位的值,并计算其中有多少为零。一个循环比递归更清晰。
但是,有许多更优化的方法可以做到这一点。您可以在这个问题的答案中找到一些更好的问题,"Best algorithm to count the number of set bits in a 32-bit integer"(显然,零位数是从总位数中减去的设置位数)。
答案 1 :(得分:5)
网上有一个名为Bit Twiddling Hacks的优秀资源,其中包含各种精彩的小C技巧。您可能对Counting bits set部分特别感兴趣。
答案 2 :(得分:4)
快速而愚蠢的方式 - 在重复的问题中有更多异国情调的实现,但我使用过类似的东西,过去没有太大的影响。
我们在这里使用一个半字节表来减少循环运行的次数 - 如果你正在进行这些计算的大量工作,那么构建一个更大的数组(例如,在字节处)可能更有效水平,削减循环一半。
/* How many bits are set in every possible nibble. */
static size_t BIT_TABLE[] = {
0, 1, 1, 2, /* 0, 1, 2, 3 */
1, 2, 2, 3, /* 4, 5, 6, 7 */
1, 2, 2, 3, /* 8, 9, A, B */
2, 3, 3, 4 /* C, D, E, F */
};
size_t num_of_bits(int num) {
/* Little reworking could probably shrink the stack space in use here. */
size_t ret = 0, i;
register int work = num;
/* Iterate every nibble, rotating the integer in place. */
for(i = 0; i < (sizeof(num) * 2); i++) {
/* Pointer math to get a member of the static array. */
ret += *(BIT_TABLE + (work & 0xF));
work >>= 4;
}
return ret;
}
答案 3 :(得分:2)
递归肯定是矫枉过正 - 而且,你的代码非常错误(它不会计算num
的任何前导零)。一个简单的迭代,例如:
int num_of_zero(int num) {
unsigned int unum = (unsigned int)num;
int count;
int i;
for(i = 0; i < 32; ++i) {
if(!(unum & 1)) ++count;
unum >>= 1;
}
return count;
}
正确且速度更快(可以更简洁地编码,但我认为这是最清晰的表达方式)。
如果必须多次执行此计算,请考虑预先计算(例如)256“零计数”的数组(每个值给出其索引的计数,包括0到255,作为8位数)。然后你可以循环4次(屏蔽并一次移动8位),并轻松展开循环 - 如果你的编译器不够聪明,不能代表你这么做; - )。
答案 4 :(得分:2)
我猜这是一个家庭作业问题。没问题!这是最快的解决方案(在很长的启动成本之后):
制作一个长度为2 32 的byte
数组。为每个可能的int
值预先计算二进制表示中零的数值,以填充该数组。从那时起,你将拥有一个数组,它将为每个值提供零个数。
是的,这个解决方案很愚蠢 - 这是一项很少收获的工作 - 但将其与另一个想法结合起来:
如果您只预先计算8位长的值,会发生什么?您是否能够编写代码虽然速度不是很快但仍会返回int中的0位数?
如果您只预先计算4位长的值,会发生什么? 2位长? 1位长?
我希望这可以为你提供一个更好的想法......
答案 5 :(得分:1)
这不是你的主要问题的答案,但你应该重写你的递归函数:
int num_of_zero(int num)
{
int left_part_zeros;
if (num == 0)
return 0;
left_part_zeros = num_of_zero(num >> 1);
if ((num & 1) == 0)
return left_part_zeros + 1;
else
return left_part_zeros;
}
除了完全不可读之外,你的实施还有很多问题。
答案 6 :(得分:1)
我找到的最简单的方法是将它基于那些计数的东西,然后简单地从32减去(假设你确定 int
大小是32位)。
int numberOfOnes (int num) {
int count = 0;
unsigned int u = (unsigned int)num;
while (u != 0) {
if ((u&1) == 1)
count++;
u >>= 1;
}
return count;
}
int numberOfZeros (int num) {
return 32 - numberOfOnes (num);
}
这实际上为你提供了两种变体(1和0) - 有更快的方法可以做到但我不会考虑它们,除非你知道存在性能问题。我倾向于首先编写可读性代码。
您可能还想至少测试表查找可能更快的可能性(优化的主要指令是度量,不要猜测!
有一种可能性是将numberOfOnes
函数替换为一次运行nybble的东西:
int numberOfOnes (int num) {
static const count[] = {
0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4
};
int retval = 0;
unsigned int u = (unsigned int)num;
while (u != 0) {
retval += count[u & 0x0f]
u >>= 4;
}
return retval;
}