求二进制中1的个数
来源:互联网 发布:手机淘宝评价管理 编辑:程序博客网 时间:2024/06/06 12:22
http://www.cnblogs.com/graphics/archive/2010/06/21/1752421.html
意给定一个32位无符号整数n,求n的二进制表示中1的个数,比如n = 5(0101)时,返回2,n = 15(1111)时,返回4
解法一:除余法
每次除二,看是否为奇数,是的话就累计加一,最后这个结果就是二进制表示中1的个数。
解法二:使用位运算
解法三:快速法
此法一张纸 一只笔 举例说明即可明白...
如果n的二进制表示中有k个1,那么这个方法只需要循环k次即可。
解法四:使用分支操作
switch(){
//....
}
此法写起来过于繁琐
另外 <<编程之美>> 上说法是错误的
下面红色方框使其错误的说法:
因为switch经过编译器优化后 本身就是一种查表机制 所以是不用比较255次的
关于switch语句原理:http://tieba.baidu.com/p/1842030715
解法一:除余法
每次除二,看是否为奇数,是的话就累计加一,最后这个结果就是二进制表示中1的个数。
解法二:使用位运算
解法三:快速法
此法一张纸 一只笔 举例说明即可明白...
如果n的二进制表示中有k个1,那么这个方法只需要循环k次即可。
解法四:使用分支操作
switch(){
//....
}
此法写起来过于繁琐
另外 <<编程之美>> 上说法是错误的
下面红色方框使其错误的说法:
因为switch经过编译器优化后 本身就是一种查表机制 所以是不用比较255次的
关于switch语句原理:http://tieba.baidu.com/p/1842030715
- 回复
- 1楼
- 2013-01-09 18:09
- 举报 |个人企业举报垃圾信息举报
- 本楼含有高级字体
解法五:查表法
⑴.静态表
①.简单静态表
int aryCountTable[256] = { 0, 1, 1, 2, 1, ..., 7, 7, 8 };
int CountBit(int v) {
return countTable[v];
}
这样只能查询8位的 16bit 32bit构建似乎就很麻烦了....
②.4bit-静态表法
③.静态表8bit
(2)动态表法
①.表的构建
填表的原理:
根据奇偶性来分析,对于任意一个正整数n
1.如果它是偶数,那么n的二进制中1的个数与n/2中1的个数是相同的,比如4和2的二进制中都有一个1,6和3的二进制中都有两个1。为啥?因为n是由n/2左移一位而来,而移位并不会增加1的个数。
2.如果n是奇数,那么n的二进制中1的个数是n/2中1的个数+1,比如7的二进制中有三个1,7/2 = 3的二进制中有两个1。为啥?因为当n是奇数时,n相当于n/2左移一位再加1。
②.查表
查表的原理就不解释了 自己看吧...
⑴.静态表
①.简单静态表
int aryCountTable[256] = { 0, 1, 1, 2, 1, ..., 7, 7, 8 };
int CountBit(int v) {
return countTable[v];
}
这样只能查询8位的 16bit 32bit构建似乎就很麻烦了....
②.4bit-静态表法
③.静态表8bit
(2)动态表法
①.表的构建
填表的原理:
根据奇偶性来分析,对于任意一个正整数n
1.如果它是偶数,那么n的二进制中1的个数与n/2中1的个数是相同的,比如4和2的二进制中都有一个1,6和3的二进制中都有两个1。为啥?因为n是由n/2左移一位而来,而移位并不会增加1的个数。
2.如果n是奇数,那么n的二进制中1的个数是n/2中1的个数+1,比如7的二进制中有三个1,7/2 = 3的二进制中有两个1。为啥?因为当n是奇数时,n相当于n/2左移一位再加1。
②.查表
查表的原理就不解释了 自己看吧...
回复
- 2楼
- 2013-01-09 20:47
- 举报 |个人企业举报垃圾信息举报
- 本楼含有高级字体
解法六:平行法
① n = (n & 0x55555555) + ((n >> 1) & 0x55555555);
此行代码 将n的每一位 两两相加
② n = (n & 0x33333333) + ((n >> 2) & 0x33333333);
同上 每2位一加
③.然后每4位一加 每8位一加 每16位一加...
最后的结果就是...二进制位的总个数
不得不说这个方法很巧妙
将二分法的思维充分的发挥出来了
① n = (n & 0x55555555) + ((n >> 1) & 0x55555555);
此行代码 将n的每一位 两两相加
② n = (n & 0x33333333) + ((n >> 2) & 0x33333333);
同上 每2位一加
③.然后每4位一加 每8位一加 每16位一加...
最后的结果就是...二进制位的总个数
不得不说这个方法很巧妙
将二分法的思维充分的发挥出来了
回复
- 3楼
- 2013-01-09 21:16
- 举报 |个人企业举报垃圾信息举报
- 本楼含有高级字体
解法七:HAKMEN算法
说明:
①.将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 010 010 011
000 001 010 010
---------------------
001 011 100 101
001 + 101 = 1 + 5 = 6,所以659的二进制表示中有6个1
注意我们想要的只是第二组和最后一组(绿色部分),而第一组和第三组(红色部分)属于重复相加的部分,要消除掉,这就是&030707070707所完成的任务(每隔三位删除三位),最后为什么还要%63呢?因为上面相当于每次计算相连的6bit中1的个数,最多是111111 = 77(八进制)= 63(十进制),所以最后要对63取模。
说明:
①.将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 010 010 011
000 001 010 010
---------------------
001 011 100 101
001 + 101 = 1 + 5 = 6,所以659的二进制表示中有6个1
注意我们想要的只是第二组和最后一组(绿色部分),而第一组和第三组(红色部分)属于重复相加的部分,要消除掉,这就是&030707070707所完成的任务(每隔三位删除三位),最后为什么还要%63呢?因为上面相当于每次计算相连的6bit中1的个数,最多是111111 = 77(八进制)= 63(十进制),所以最后要对63取模。
回复
- 4楼
- 2013-01-09 21:51
- 举报 |个人企业举报垃圾信息举报
- 本楼含有高级字体
解法八:位标记法
回复
- 5楼
- 2013-01-09 21:52
- 举报 |个人企业举报垃圾信息举报
- 本楼含有高级字体
解法九:指令法
使用微软提供的指令,首先要确保你的CPU支持SSE4指令,用Everest和CPU-Z可以查看是否支持。
unsigned int n = 127 ;
unsigned int bitCount = _mm_popcnt_u32(n) ;
References http://gurmeetsingh.wordpress.com/2008/08/05/fast-bit-counting-routines/
本贴摘抄于:
<<编程之美>>
http://blog.csdn.net/justpub/article/details/2292823
http://www.cnblogs.com/graphics/archive/2010/06/21/1752421.html
使用微软提供的指令,首先要确保你的CPU支持SSE4指令,用Everest和CPU-Z可以查看是否支持。
unsigned int n = 127 ;
unsigned int bitCount = _mm_popcnt_u32(n) ;
References http://gurmeetsingh.wordpress.com/2008/08/05/fast-bit-counting-routines/
本贴摘抄于:
<<编程之美>>
http://blog.csdn.net/justpub/article/details/2292823
http://www.cnblogs.com/graphics/archive/2010/06/21/1752421.html
0 0
- 求二进制数中1的个数
- 求二进制数中1的个数
- 求二进制数中1的个数
- 求二进制数中1的个数
- 求二进制数中1的个数
- 求二进制数中1的个数
- 求二进制数中1的个数
- 求二进制中1的个数
- 求二进制数中1的个数
- 求二进制数中 1 的个数
- 求二进制数中1的个数
- 求二进制数中1的个数
- 求二进制数中1的个数
- 求二进制数中1的个数
- 求二进制数中1的个数
- 求二进制数中1的个数
- “求二进制数中1的个数”
- 求二进制中1 的个数
- hdu1404
- Wins2003系统中Apache性能优化方法
- 如何导出树结构清晰的代码机构目录
- suse11 Linux系统下如何设置文件共享
- Oracle乱码
- 求二进制中1的个数
- Jump and Jump... HDU 5162
- CART分类与回归树的原理与实现
- Assembling Features Reading Features
- IOS中自动布局autoresizing
- 删除链表中的重复节点、剩余节点逆序输出
- CodeForces 514D R2D2 and Droid Army
- 编程之美3.3——类似——两个字符串的最长公共子序列(LCS)
- 【树形dp】Codeforces 238C World Eater Brothers