快速统计二进制中1的个数(分析篇)
2016-08-16 15:12
169 查看
今天做了一道题,发现n&=(n-1)这个式子很好奇,然后试着算了一遍发现它竟然能够快速统计二进制1的个数,特此拿来分享一下。
首先,分析一下该式子,先可以简化为
n=n&(n-1);
我们先做一个实例,
我们先试着求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)这个比较有代表性
同样进行&操作,
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结束
}另一种写法:
首先,分析一下该式子,先可以简化为
n=n&(n-1);
我们先做一个实例,
n | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
十进制 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
二进制 | 0001 | 0010 | 0011 | 0100 | 0101 | 0110 | 0111 | 1000 |
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)这个比较有代表性
n | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
十进制 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
二进制 | 0001 | 0010 | 0011 | 0100 | 0101 | 0110 | 0111 | 1000 |
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++; }
相关文章推荐
- 天纵智能软件快速开发同比环比统计分析插件
- 天纵智能软件快速开发平台仪表盘类统计分析插件
- 快速统计一个数的二进制下1的个数
- 统计分析类目下mysql快速查询应用
- 天纵智能软件快速开发中国地图统计分析插件
- 天纵智能软件快速开发一般统计分析插件
- FreeBSD-4.7下安装网络流量统计分析工具MRTG
- 发现一款日志分析工具AWStats,能取代网站统计程序
- 网站流量的数据统计和分析
- Web服务器日志统计分析完全解决方案
- 统计分析与数据挖掘所涉及的应用领域探讨
- OPENH323中呼叫建立过程的跟踪与分析(七)----非快速隧道方式之主叫端
- 网站流量统计与网站访问分析
- 网站流量的数据统计和分析
- Web服务器日志统计分析完全解决方案
- 从“分析”的角度谈OLAP、数据挖掘、统计分析三者之间的区别和联系
- OPENH323中呼叫建立过程的跟踪与分析(三)----快速启动方式之主叫端
- OPENH323中呼叫建立过程的跟踪与分析(五)----快速隧道方式之主叫端
- 96626访问统计分析系统V1.0正式投入使用!
- OPENH323中呼叫建立过程的跟踪与分析(六)----快速隧道方式之被叫端