最近看了这篇文章 的第一道题
题目本意是怎么判断这段代码的输出
int func(x)
{
int countx =0;
while(x)
{
countx ++;
x = x&(x-1);
}
return countx;
}
但深入一想,这不就是求任意整数的二进制表示里有多少个1吗?
要想解决上述问题,我能想到的最快方法是按位右移,累计每个移出的数,直到原数为0
int func2(int x)
{
int countx =0;
while (x)
{
if (x & 1)
countx ++;
x = x >> 1;
}
return countx;
}
但是很明显,我的算法在二进制表示含有较多中间0时,效率很低,而别人的算法就不存在该问题,而且没有if分支判断,避免CPU指令Cache miss,方便并行化
总之,该算法求解1个数,类似于辗转相除法求最大公约数,都是基于某种数学原理,性能自然比愚蠢的枚举不知道高到哪里去了
该算法的原理,我晚上思考了下,觉得是对【判断一个整数是否为2的幂】算法的扩展应用
【判断一个整数是否为2的幂】算法
bool power_of_2(int x)
{
if (0 == (x & x-1))
return true;
else
return false;
}
该算法的思路是,如果整数i为2的幂,则其二进制表示必然只有1位为1,假设是 第N位为1,则 0到N-1位必然为0,且N+1到31位(假设是32位CPU)全零
令j = i-1,则j的二进制表示就是第N位为0,0到N-1位全1,N+1到31位还是全零,
因为减法需要从高位借位,对于2的幂整数,能借位的只有唯一的那个1,所以N位必然变为0
又因为减数为1,所以0到N-1位必然全为1
这样i和j的【按位与】运算,必然等于0
将上述思路拓展
对于任意一个整数,都可以看成一系列幂的和,比如
520,就是512+8
1111,就是1024+64+16+4+2+1
当对该整数进行【减一】和【按位与】操作时,最低等级的幂先变为零
然后再次执行【减一】和【按位与】操作,次低等级的幂也变为零
以此类推,每次循环都将一个非零幂项清除(为零的幂项不发生借位,所以自动忽略)
最后变为全零,循环退出