求二进制数中1的个数
来源:互联网 发布:nginx 入门指南 pdf 编辑:程序博客网 时间:2024/06/11 18:27
问题描述 任意给定一个32位无符号整数n,求n的二进制表示中1的个数,比如n = 5(0101)时,返回2,n = 15(1111)时,返回4
平行算法
int BitCount4(unsigned int n) { n = (n & 0x55555555) + ((n >> 1) & 0x55555555) ; n = (n & 0x33333333) + ((n >> 2) & 0x33333333) ; n = (n & 0x0f0f0f0f) + ((n >> 4) & 0x0f0f0f0f) ; n = (n & 0x00ff00ff) + ((n >> 8) & 0x00ff00ff) ; n = (n & 0x0000ffff) + ((n >> 16) & 0x0000ffff) ; return n ; }
速度不一定最快,但是想法绝对巧妙。 说一下其中奥妙,其实很简单,先将n写成二进制形式,然后相邻位相加,重复这个过程,直到只剩下一位。
以217(11011001)为例,有图有真相,下面的图足以说明一切了。217的二进制表示中有5个1
完美法
int BitCount5(unsigned int n) { unsigned int tmp = n - ((n >> 1) & 033333333333) - ((n >> 2) & 011111111111); return ((tmp + (tmp >> 3)) & 030707070707) % 63;}
最喜欢这个,代码太简洁啦,只是有个取模运算,可能速度上慢一些。区区两行代码,就能计算出1的个数,到底有何奥妙呢?为了解释的清楚一点,我尽量多说几句。
第一行代码的作用
先说明一点,以0开头的是8进制数,以0x开头的是十六进制数,上面代码中使用了三个8进制数。
将n的二进制表示写出来,然后每3bit分成一组,求出每一组中1的个数,再表示成二进制的形式。比如n = 50,其二进制表示为110010,分组后是110和010,这两组中1的个数本别是2和3。2对应010,3对应011,所以第一行代码结束后,tmp = 010011,具体是怎么实现的呢?由于每组3bit,所以这3bit对应的十进制数都能表示为2^2 * a + 2^1 * b + c的形式,也就是4a + 2b + c的形式,这里a,b,c的值为0或1,如果为0表示对应的二进制位上是0,如果为1表示对应的二进制位上是1,所以a + b + c的值也就是4a + 2b + c的二进制数中1的个数了。举个例子,十进制数6(0110)= 4 * 1 + 2 * 1 + 0,这里a = 1, b = 1, c = 0, a + b + c = 2,所以6的二进制表示中有两个1。现在的问题是,如何得到a + b + c呢?注意位运算中,右移一位相当于除2,就利用这个性质!
4a + 2b + c 右移一位等于2a + b
4a + 2b + c 右移量位等于a
然后做减法
4a + 2b + c –(2a + b) – a = a + b + c,这就是第一行代码所作的事,明白了吧。
第二行代码的作用
在第一行的基础上,将tmp中相邻的两组中1的个数累加,由于累加到过程中有些组被重复加了一次,所以要舍弃这些多加的部分,这就是&030707070707的作用,又由于最终结果可能大于63,所以要取模。
需要注意的是,经过第一行代码后,从右侧起,每相邻的3bit只有四种可能,即000, 001, 010, 011,为啥呢?因为每3bit中1的个数最多为3。所以下面的加法中不存在进位的问题,因为3 + 3 = 6,不足8,不会产生进位。
tmp + (tmp >> 3)-这句就是是相邻组相加,注意会产生重复相加的部分,比如tmp = 659 = 001 010 010 011时,tmp >> 3 = 000 001 010 010,相加得
001 010010 011
000 001010 010
---------------------
001 011 100101
001 + 101 = 1 + 5 = 6,所以659的二进制表示中有6个1
注意我们想要的只是第二组和最后一组(绿色部分),而第一组和第三组(红色部分)属于重复相加的部分,要消除掉,这就是&030707070707所完成的任务(每隔三位删除三位),最后为什么还要%63呢?因为上面相当于每次计算相连的6bit中1的个数,最多是111111 = 77(八进制)= 63(十进制),所以最后要对63取模。
位标志法
struct _byte { unsigned a:1; unsigned b:1; unsigned c:1; unsigned d:1; unsigned e:1; unsigned f:1; unsigned g:1; unsigned h:1; }; long get_bit_count( unsigned char b ) { struct _byte *by = (struct _byte*)&b; return (by->a+by->b+by->c+by->d+by->e+by->f+by->g+by->h); }
- 求二进制数中1的个数
- 求二进制数中1的个数
- 求二进制数中1的个数
- 求二进制数中1的个数
- 求二进制数中1的个数
- 求二进制数中1的个数
- 求二进制数中1的个数
- 求二进制数中1的个数
- 求二进制数中 1 的个数
- 求二进制数中1的个数
- 求二进制数中1的个数
- 求二进制数中1的个数
- 求二进制数中1的个数
- 求二进制数中1的个数
- 求二进制数中1的个数
- “求二进制数中1的个数”
- 求二进制数中1的个数
- 求二进制数中1的个数
- IOS 理解iPhone项目的BaseSDK和DeploymentTarget含义
- UNIX IO小结
- C++ 调试技巧
- 字符转义
- Android开发教程之--sql语句
- 求二进制数中1的个数
- Android 4.0 开机自动启动
- 修复Linux系统中Firefox Backspace不能后退
- mysql数据库中表属性的操作
- 命令行关闭进程
- jQuery中设置form表单中action值的方法
- 《在MFC中使用OpenCV》例程编译问题解决方法
- TADOQuery通過存儲過程傳遞參數的一種方式
- 分布式Web服务器架构