求一个INT32整数里面有多少个位是置1

看到这个问题我就笑,当年笔试的时候遇到过这个题目,附加的要求是不用循环。我想也没想就用了32个移位累加做了,还好是32位的,卷面也还有些位置。。。用循环的版本如下,我的非循环版本就是去掉for loop然后手写累计32次。。。这里就不写出来了。

unsigned  int  countBits(unsigned  int  x)
{
   
const   int  mask  =   1 ;
   unsigned 
int  result  =   0 ;
   
for ( short  si = 0 ; si < 32 ++ si)
      result 
+=  (x >> si) & mask;
   
return  result;
}

前段时间看到一个网站上列出了所有的bitwise的函数,才知道如何不用循环来做这个算法。真是感叹自己的不动脑筋。。。

 代码如下:

ContractedBlock.gif ExpandedBlockStart.gif Code
unsigned int countBits(unsigned int x)
{
   
// count bits of each 2-bit chunk
   x  = x - ((x >> 1& 0x55555555);
   
// count bits of each 4-bit chunk
   x  = (x & 0x33333333+ ((x >> 2& 0x33333333); 

   
// count bits of each 8-bit chunk
   x  = x + (x >> 4);
   
// mask out junk
   x &= 0xF0F0F0F;
   
// add all four 8-bit chunks
   return (x * 0x01010101>> 24;
}

咋一看,比较难理解,其实是用的分而治之的算法思想。

我们先看如果不是32位整形,而是2位,如何来做这道题。

无非4种组合,写个switch cases就搞定了。。。有没有更好的方法呢,如何通过计算(移位,加,减)得到最后的值。

00 ==》0个1 ==》00

01 ==》1个1 ==》01

10 ==》1个1 ==》01

11 ==》2个1 ==》10

 f(n) = n-(n>>1)&0x01

将32位整形分成2位为一组,则有16组,分别计算出1的个数,其算法是f(n)=n-(n>>1)&0x55555555

这样2位一组的计算完毕,可以看到,结果只有0,1,2。

下面以4位一组,组合的可能如下,这里是做加法:

00 和 00 ==》0个1 ==》0000

00 和 01 ==》1个1 ==》0001

00 和 10 ==》1个1 ==》0010

01 和 00 ==》1个1 ==》0010

01 和 01 ==》2个1 ==》0010

01 和 10 ==》3个1 ==》0011

10 和 00 ==》2个1 ==》0010

10 和 01 ==》3个1 ==》0011

10 和 10 ==》4个1 ==》0100

f(AABB) = AA+BB = (AABB>>2)&0011+AABB&0011

即算法是f(n) = n&0x33333333+(n>>2)&0x33333333

运用同样的方法计算4位一组的结果。f(n)=n&0x0f0f0f0f+(n>>4)&0x0f0f0f0f

最后计算8位一组的结果。f(n) = n&0x00ff00ff+(n>>8)&0x00ff00ff.

最最后计算16位一组的结果。f(n) = n&0x0000ffff + (n>>16)&0x0000ffff,即得到最后的结果。但是上面的代码可不是这样写的。

与0x01010101相乘有一个有趣的结果,如果我们有4个字节A,B,C,D。

A,B,C,D->A+B+C+D,B+C+D,C+D,D

显然,高位字节是我们需要的结果,于是f(n)=(n*0x01010101)>>24

Brian Kernighan and Dennis Ritchie的“The C Programming Language”一书中给出了更好更快的一个算法,不过是用循环做的。

不像其它算法,这个方法的性能很大程度上依赖于输入,当少于4个1时,比其他算法优越得多,都是1时最慢。

ContractedBlock.gif ExpandedBlockStart.gif countBitsLoop
 1 unsigned int countBitsLoop(unsigned int x)
 2 {
 3    unsigned int result = 0;
 4    // strip one set bit per iteration
 5    while (x != 0)
 6    {
 7       x &= x-1;
 8       result++;
 9    }
10    return result;
11 }

主要的技巧就是x&=x-1:

• 如果 x = 0, 则根本不进 while-loop
• 如果最右一位是1, 则最右一位的x − 1是0. 所有其他位都是一样的x &= x − 1 → x = x − 1.
• 如果最右边的位都是0, 则x看起来是: ...1000. 那么x − 1为: ...0111. x &= x-1的结果是: ...0000.
  总之, x &= x − 1 将所有非点的位清零, 点代表的位不变. 
总的来说,x &= x − 1 将最右边值为1的位变成0.

<<2009-11-2完>>

转载于:https://www.cnblogs.com/Kratos/archive/2009/11/02/1594536.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值