快速统计二进制中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,循环结束。


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

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









  • 25
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值