求二进制数中1的个数:
对于一个字节(8bit)的无符号整型变量,求其二进制表示中"1"的个数,要求算法的执行效率尽可能高.
当拿到这道题的时候,我也觉得很简单,这是<<编程之美>>中的一道题.于是我写下了如下程序:
#include<iostream>
using namespace std;
int main(int argc, char* argv[])
{
unsigned int n;
int i = 0;
cin>>n;
while(n)
{
if(n%2==1)
i++;
n/=2;
}
cout<<i<<endl;
return 0;
}
这个解法是正确的,也是微软提供的第一个解法.
解法一:
int Count(BYTE v)
{
int num = 0;
while(v)
{ if(v%2==1)
num++;
v/=2;
}
}
解法二:使用位操作
向右移位操作时,我们只要判断最后一位是否为1即可,一个字节与"0000 0001"相与,如果结果为1,则表示当前八位数的最后一位为1,否则为0.如代码:
int Count(BYTE v)
{
int num = 0;
whlie(v)
{
num+=v&0x01;
v>>=1;
}
}
解法三: 只与1相关的操作
解法三还是比较经典的,微软的说法是:"位操作比除、余操作的效率高了很多。但是,即使采用位操作,时间复杂度仍为0(log2(V)),log2(V)为二进制的位数。那么,还能不能再降低一些复杂度呢?如果有办法让算法的复杂度只与"1"的个数有关,复杂度不就能进一步降低了吗?
例如:0100 0000
要进行的操作就是: 0100 0000 & (0100 0000 - 0000 0001) = 0100 0000 & 0011 1111 = 0;
int Count(BYTE v)
{
int num = 0;
while(v)
{
v&=(v -1);
num++;
}
}
解法四:使用分支操作
解法三的复杂度降低到O(M),其中M是v中1的个数,因为只有八位数据索性将0 ~ 255的情况都罗列出来,并使用分支操作,可以得到答案。
int Count(BYTE v)
{
int num = 0;
switch(v)
{
case 0x0:
num = 0;
break;
case 0x1:
case 0x2:
................
}
return num;
}
解法四看似很直接,但实际执行效率可能会低于解法二和解法三,因为分支语句的执行情况要看具休字节的值,如果a=255,要比较225次,所以.........
解法五:查表法
int countTable[256] =
{
0,1,1,2,1,2,2,3....................
}
int Count(BYTE v)
{
return countTable[v];
}
算法的直接复杂度为O(1),这是通过“空间换时间”来获取高的时间效率的方法。
本人自己对扩展问题的回答:
1.如果变量是32位的DWORD,你会使用上述的哪一上算法,或者改进哪一个算法?
2.另一个相关的问题,给定两个正整数(二进制形式表示)A和B,问把A变为B需要改变多少位(bit)?
也就是说,整数A和B需要改变多少位(bit)?也就是说,整数A和B的二进制表示中有多少位是不同的?