当前位置:编程学习 > C/C++ >>

编程之美2.1——求二进制树中1的个数

问题:
对一个4字节的无符号整形变量,求其二进制表示中“1”的个数,要求算法的执行效率尽可能高。
 
解法一(位遍历法):使用位操作,移位后来判断是否有1存在,利用v&0x01和v>>=1。
 
解法二(1遍历法):在每次判断中只与1的个数进行判断,利用v&=v-1。
 
解法三(二分累加法):依次连续两位相加,连续四位相加,连续八位相加。以上操作得到4个八位数,每个八位数的值表示8个数位之和(也就是这8位中1的个数),然后将每个八位数与紧接着前面的八位数相加,仍然得到4个八位数,但每个八位数的值表示16个数位之和(也就是这16位中1的个数),最后将每个八位数与前面再前面的一个八位数相加,最后取第一个八位数中前6位的值(这个值最多能表示63,大于1的最多个数32)。
   1:  int Count(unsigned x) {  
   2:     x = x - ((x >> 1) & 0x55555555);    // 也可改为(x + (x >> 1)) & 0x55555555;   
   3:     x = (x +(x >> 2))& 0x33333333;   
   4:     x = (x + (x >> 4)) & 0x0F0F0F0F;   
   5:     x = x + (x >> 8);   
   6:     x = x + (x >> 16);   
   7:     return x & 0x0000003F;   
   8:  }
将第2行的x看成16个连续的2个二进制数位ab,a和b分别表示1个二进制数,0x55555555在每连续的2个二进制数位上都是01,则ab-(ab>>1)&01=ab-a=2*a+b-a=a+b表示x的连续2个二进制数位之和。
将第3行的x看成8个连续的4个二进制数位ab,a和b分别表示2个二进制数,且值分别是x的对应2个二进制数位之和,0x33333333在每连续的4个二进制数位上都是0011,则ab&0011+(ab>>2)&0011=b+a=a+b表示x的连续4个二进制数位之和。
将第4行的x看成4个连续的8个二进制数位ab,a和b分别表示4个二进制数,且值分别是x的对应4个二进制数位之和,0x0F0F0F0F在每连续的8个二进制数位上都是00001111,则ab&00001111+(ab>>2)&00001111=b+a=a+b表示x的连续8个二进制数位之和。
 将第5行的x看成4个连续的8个二进制数位abcd,a,b,c和d分别表示8个二进制数,且值分别是x的对应8个二进制数位之和,x+(x>>8)=abcd+abc=a(a+b)(b+c)(c+d)。继续第6行,x+(x>>16)=a(a+b)(b+c)(c+d)+a(a+b)=a(a+b)(a+b+c)(a+b+c+d)。最后8个二进制数(a+b+c+d)就是x的32个二进制数位之和。www.zzzyk.com
 
解法四(累加取余法):首先将连续三位相加,再将连续的6位相加,得到5个数(每个数是连续的6个二进制数位之和)+1个数(剩余的2个二进制数位之和),最后取余得到32个二进制数位之和。

1: int BitCount(unsigned int u)
    {
2:       unsigned int uCount;
3:       uCount = u - ((u >> 1) & 033333333333) - ((u >> 2) & 011111111111);
4:       return ((uCount + (uCount >> 3)) & 030707070707) % 63;
    }
将第2行的u看成11个连续的3个二进制数位abc,a,b和c分别表示1个二进制数,033333333333在每连续的3个二进制数位上都是011,&上它会去掉这3个二进制数位上的最高位,011111111111在每连续的3个二进制数位上都是001,&上它只保留这3个二进制数位上的最低位,在这里主要是保证前面的数位在右移后不会对该3个二进制数位的加减产生影响,则uCount在连续的3个二进制上的运算是abc-ab-a=4a+2b+c-2a-b-a=a+b+c,表示u的该3个二进制数位之和。
将第3行的uCount看成6个连续的6个二进制数位ab,a和b分别表示3个二进制数,且值分别是u的对应3个二进制数位之和,030707070707在每连续的6个二进制数位上都是000111,则ab & 000111+(ab>> 3) & 000111= b+a。在对结果取余之前,该结果可看成6个连续的6个二进制数位abcdef,a,b,c,d,e和f分别表示连续的6个二进制数,且其值是u的对应该6个二进制数位之和,该结果=f+64e+64^2d+64^3c+64^4b+64^5a,取余63后(乘法对取余运算有分配律(64*64*d)%63=((64%63)*(64%63)*(d%63))%63=d%63),得a+b+c+d+f,即u的32个二进制数位之和,且a+b+c+d+f<=32<63,取余操作对最后结果无影响,但如果是64位的数据就会有影响。
 

作者:linyunzju
补充:软件开发 , C++ ,
CopyRight © 2012 站长网 编程知识问答 www.zzzyk.com All Rights Reserved
部份技术文章来自网络,