网上看到的一篇很不错的总结,这里收藏下,顺便记录下自己的理解。
原文:http://blog.csdn.net/xinpo66/article/details/8599306
原文主要内容(红色字体为自己的理解或对原文修改过的地方):
求一个unsigned int 数的二进制表示中有多少个1? 这是一道面试题可以用以下的一些方案。
第一种是很容易想到的采用循环的方式并且与1进行位与运算,具体代码如下。
这两种做法很相像,却别就是在是对nBitMask进行左移还是对nValue进行右移。 当然了以上的两个方法存在一个问题:不管如何这个函数肯定要循环32次(对于32平台来说)。
那又没有更好的方法?当然有,请看下面:
假如使用参数12345(二进制是11000000111001)调用该函数,该函数的执行情况如下:
第一次进入循环
0 < 11000000111001 11000000111001 &= (11000000111001 - 1) 之后 nValue 的值 是 11000000111000
nBitNum 的值是1
经过本次循环之后11000000111001 变成了 11000000111000 比之前少了一个1
第二次进入循环
0 < 11000000111000 11000000111000 &= (11000000111000 - 1) 之后 nValue 的值 是 11000000110000
nBitNum 的值是2
经过本次循环之后11000000111000 变成了 11000000110000 比之前少了一个1
第三次进入循环 0 < 11000000110000 11000000110000 &= (11000000110000 - 1) 之后 nValue 的值 是 11000000100000
nBitNum 的值是3
经过本次循环之后11000000110000 变成了 11000000100000 比之前少了一个1
经过以上3次循环情况的说明,我相信你一定看出了些什么吧。nValue &=(nValue - 1),这句 代码实际上就是把nValue 的某位及其以后的所有位都变成0,
当nValue最后变成0的时候循环结束, 且nBitNum 记录的就是1的个数。
这里也可以这样理解,nValue & (nValue-1) 实际上是将nValue的二进制表达式中,最右边的1变为0,
例如:
nValue=13,二进制为00001101
则nValue-1=12,对应的二进制为00001100
13 & 12 即 00001101 & 00001100 结果为 00001100 (最右边的1变为了0)
这样每次循环都会减少一个1,直到nValue值为0
上面的做法已经很不错了,但是作为程序员的你是否会有疑问,"还有其他的方法吗?"。 有,当然有!请看下面的代码:
假如你是第一次看到这些代码,你是否能看明白?呵呵,本人第一次看到这些代码的时候看了好久才感觉 好像有点明白。
下面我就以一个例子来说明上面的代码是如何做到的。
假如参数是0xffffffff。
第一行代码: nValue = ((0xaaaaaaaa & nValue)>>1) + (0x55555555 & nValue);
a的二进制表示是:1010
5的二进制表示是:0101
0xffffffff 与 0xaaaaaaaa 进行与运算之后是0x10101010101010101010101010101010 然后再进行右移操作后是0x01010101010101010101010101010101
0x55555555 & nValue 进行与运算之后是0x01010101010101010101010101010101
然后0x01010101010101010101010101010101 + 0x01010101010101010101010101010101 得到0x10101010101010101010101010101010
我们把得到的结果分成16组0x10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 每组的10单独来看是不是十进制的2
我们0x01010101010101010101010101010101和0x01010101010101010101010101010101这两个数也按照上面的方法分成 16个组
0x01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01
0x01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01
那么这两个数的每一个组都是01 那么两个01 里面有几个1,是不是2个。这是2是不是二进制的10,
然后16个10组合起来是不是 0x10101010101010101010101010101010
第二行代码:
nValue = ((0xcccccccc & nValue)>>2) + (0x33333333 & nValue);
此时nValue是0x10101010101010101010101010101010
c的二进制表示是:1100
3的二进制表示是:0011
0xcccccccc 与 nValue) 进行与运算之后是0x10001000100010001000100010001000 然后再进行右移操作后是0x00100010001000100010001000100010
0x33333333 & nValue 进行与运算之后是0x00100010001000100010001000100010
然后0x00100010001000100010001000100010 + 0x00100010001000100010001000100010 得到0x01000100010001000100010001000100
我们把得到的结果分成8组0x0100 0100 0100 0100 0100 0100 0100 0100
每组的0100单独来看是不是十进制的4 总共有多少个4?是不是8个,8×4=32。
以下的代码:
nValue = ((0xf0f0f0f0 & nValue)>>4) + (0x0f0f0f0f & nValue);
nValue = ((0xff00ff00 & nValue)>>8) + (0x00ff00ff & nValue);
nValue = ((0xffff0000 & nValue)>>16) + (0x0000ffff & nValue);
请自己按照上面的方法做一遍,就会发现规律:第一次32位数分成32组,第二次分成16组,第三次分成8,第四次分成4,第五次分成2组。
如果还是不明白请多看看,然后多选择几个参数进行试验,多试几次肯定会明白的。
这里追加下我的理解:
假如一个unsigned int 的二进制表示只有2个bit位,即范围为 0~3,此时求nValue二进制包含几个1就变的简单了:
nCount = ( (nValue & 2) >> 1 )+ (nValue & 1)
2的二进制表示为 10
(nValue & 2)>> 1 表示nValue二进制中第一个(后面不加说明一律是从左边开始算起)bit位为1的个数,1或者0
nValue & 1 表示nValue二进制中第二个bit位为1的个数,1或0
这样两者相加就得到了1的总个数。
现在我们在进一步探索,假如unsigned int 的二进制表示扩展到4个bit位,
依照刚才的思想我们先对其进行分组,分为两组,前两个bit位一组,后两个bit位一组,0000,只要分别求出两组中1的个数,然后相加便到的了1的总个数。
假设nValue = 11 对应的二进制为 1011
我们先计算每组中第一个bit位为1的个数,让 1011 & 1010 ,是不是感觉很眼熟,对了1010就是上面原文中提到的a,
得到值为 1010 ,然后右移1位 1010 >> 1 ,得到结果 0101,此时对结果要分组理解,即:
第一对 01 表示原数据第一组中第一个bit位为1的个数,1个
第二对 01 表示原数据第二组中第一个bit位为1的个数,1个
这个结果 0101 先留着待会用
现在我们来计算每组中第二个bit位为1的个数,让 1011 & 0101 ,这里的0101就是上面原文中提到的5,
得到结果为 0001,此时对结果也要分组理解, 即:
第一对 00 表示原数据中第一组中第二个bit位为1的个数, 0个
第二对 01 表示原数据中第二组中第二个bit位为1的个数,1个
到这里已经看出原数据中1的个数了,1+1+0+1 = 3,但我们还需要继续推敲计算来得到这个结果3
首先将上面两次得到的结果进行相加,0101 + 0001 相加值为 0110 ,其中前两位 01 值为1,后两位 10 值为2,
由此可以想到1+2=3,如何得到这个结果呢?
让 0110 & 1100 ,1100就是上面原文中提到的c
得到结果 0100,然后 0100 >> 2,到的 0001,十进制值为1,此时1+2=3 中的1已经得到了,然后我们来计算那个2:
让 0110 & 0011,0011就是上面原文中提到的3
得到结果 0010,十进制值为2,现在1和2都有了 0001 + 0010 = 0011 ,结果就出来了0011十进制值为3。
如果unsigned int 的二进制bit位有8个、16个、32个、64个....,都可以按照此方法推导。
现在结合原文中的描述是不是就更好理解了。
再次向原创者致谢!
原文:http://www.cnblogs.com/weiquxiong/p/3548086.html