位运算n&=(n-1)快速统计二进制1的个数

看到一篇博客,发现n&=(n-1)竟然能够快速统计二进制1的个数,经过博主同意特此拿来分享一下。


首先,分析一下该式子,先可以简化为

n=n&(n-1);
 
 

我们先做一个实例,

n12345678
十进制12345678
二进制00010010001101000101011001111000

我们先试着求7中二进制1的个数k

     0111

&  0110

-------------

    0110(6)

然后对其结果继续进行上述操作

    0110

& 0101

-------------

    0100(4)    

继续上述操作

    0100

&  0011

-------------

    0000

结束操作。

至此,我们可以发现,每次&操作后,0111(7)的二进制数中最右边的1都被消除1个,

第一次&操作后,结果为0110

第二次&操作后,结果为0100

第三次&操作后,结果为0000

而k恰好与&操作的次数是相同的。那么我们可以猜测这两者必然存在直接关联关系。

不过,当二进制数中1的位置不连续,中间有若干个0这个公式也能“跳过”0,直接统计1的个数吗?

下面我们在举个实例37(0010 0101)这个比较有代表性

n12345678
十进制12345678
二进制00010010001101000101011001111000
同样进行&操作,

     100101

&   100100(36)

----------------------

     100100(36)    

继续重复上述操作

     100100

&   100011

--------------------

     100000(32) 

继续重复上述操作

     100000

&   011111

--------------------

     000000

结束。

到这里,我们会得出一个结论,

1的位置无非就三种情况,头和中间、尾部

a.在尾部的情况

    当末尾是1的时候(奇数),与前一个数(偶数)进行&操作后,结果必为0,末尾的1消除

b.在头部的情况

    当1只在头部的时候,其余位上都为0.类似8(1000),4(0100),与前一个数(全1,7(0111),3(0011))进行&操作时,结果必为0.

c.在中间的情况

     无非也是上述两种情况的递归。保证末尾位为0,因为之前已经处理过尾部的情况了。

     比如,尾部是*0010结尾,*0100结尾,*1010结尾,*11010结尾。他们对应的前一个数分别是:*0001,*0011,*1001,*11001。(*代表左边还有若干个0和1)

     0010

&   0001

-------------

    0000

结束。

     0100

&   0011

-------------

     0000

结束。

     1010

&   1001

--------------

     1000(消除最右边的1,下一步进行第二种情况的处理)

结束。

      11010

&    11001

---------------

      11000(消除最右边的1,下一步进行第一种情况的处理)

结束。


--------------------------------分割线-------------------------------------

到这里,我们就可以看出,每次在最右的1设置一个flag的话,

当它(i)与它前一位(i-1)进行&操作时,对flag左边的1是没有影响的,每次得到的结果,就会将flag位置及右边所有的数置为0.

例如:11010&11001 ==11000(24)

那么,结束条件是什么呢?

      那就是当&操作后的结果为0,循环结束。


好啦,分析就到这里。下面附上源代码供看官们欣赏哈~


 
 
  1. for ( int count = 0; n; ++count)
  2. {
  3. n &= (n -1) ; //每次消除最右边的1,当n为0结束
  4. }
另一种写法:

 
 
  1. count= 0
  2. while(k){
  3. k=k&(k -1);
  4. count++;
  5. }









评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值