算法-求二进制数中1的个数

http://www.cnblogs.com/graphics/archive/2010/06/21/1752421.html

问题描述

任意给定一个32位无符号整数n,求n的二进制表示中1的个数,比如n = 5(0101)时,返回2,n = 15(1111)时,返回4

这也是一道比较经典的题目了,相信不少人面试的时候可能遇到过这道题吧,下面介绍了几种方法来实现这道题,相信很多人可能见过下面的算法,但我相信很少有人见到本文中所有的算法。如果您上头上有更好的算法,或者本文没有提到的算法,请不要吝惜您的代码,分享的时候,也是学习和交流的时候。

普通法

我总是习惯叫普通法,因为我实在找不到一个合适的名字来描述它,其实就是最简单的方法,有点程序基础的人都能想得到,那就是移位+计数,很简单,不多说了,直接上代码,这种方法的运算次数与输入n最高位1的位置有关,最多循环32次。

复制代码
  
  
int BitCount(unsigned int n) { unsigned int c = 0 ; // 计数器 while (n > 0 ) { if ((n & 1 ) == 1 ) // 当前位是1 ++ c ; // 计数器加1 n >>= 1 ; // 移位 } return c ; }

一个更精简的版本如下

复制代码
  
  
int BitCount1(unsigned int n) { unsigned int c = 0 ; // 计数器 for (c = 0 ; n; n >>= 1 ) // 循环移位 c += n & 1 ; // 如果当前位是1,则计数器加1 return c ; }
复制代码

快速法

这种方法速度比较快,其运算次数与输入n的大小无关,只与n中1的个数有关。如果n的二进制表示中有k个1,那么这个方法只需要循环k次即可。其原理是不断清除n的二进制表示中最右边的1,同时累加计数器,直至n为0,代码如下

复制代码
  
  
int BitCount2(unsigned int n) { unsigned int c = 0 ; for (c = 0 ; n; ++ c) { n &= (n - 1 ) ; // 清除最低位的1 } return c ; }
复制代码

查表法

动态建表

由于表示在程序运行时动态创建的,所以速度上肯定会慢一些,把这个版本放在这里,有两个原因

1. 介绍填表的方法,因为这个方法的确很巧妙。

2. 类型转换,这里不能使用传统的强制转换,而是先取地址再转换成对应的指针类型。也是常用的类型转换方法。

复制代码
  
  
int BitCount3(unsigned int n) { // 建表 unsigned char BitsSetTable256[ 256 ] = { 0 } ; // 初始化表 for ( int i = 0 ; i < 256 ; i ++ ) { BitsSetTable256[i] = (i & 1 ) + BitsSetTable256[i / 2 ]; } unsigned int c = 0 ; // 查表 unsigned char * p = (unsigned char * ) & n ; c = BitsSetTable256[p[ 0 ]] + BitsSetTable256[p[ 1 ]] + BitsSetTable256[p[ 2 ]] + BitsSetTable256[p[ 3 ]]; return c ; }
复制代码

静态表-4bit

原理和8-bit表相同,详见8-bit表的解释

复制代码
  
  
int BitCount4(unsigned int n) { unsigned int table[ 16 ] = { 0 , 1 , 1 , 2 , 1 , 2 , 2 , 3 , 1 , 2 , 2 , 3 , 2 , 3 , 3 , 4 } ; unsigned int count = 0 ; while (n) { count += table[n & 0xf ] ; n >>= 4 ; } return count ; }
复制代码

静态表-8bit

首先构造一个包含256个元素的表table,table[i]即i中1的个数,这里的i是[0-255]之间任意一个值。然后对于任意一个32bit无符号整数n,我们将其拆分成四个8bit,然后分别求出每个8bit中1的个数,再累加求和即可,这里用移位的方法,每次右移8位,并与0xff相与,取得最低位的8bit,累加后继续移位,如此往复,直到n为0。所以对于任意一个32位整数,需要查表4次。以十进制数2882400018为例,其对应的二进制数为10101011110011011110111100010010,对应的四次查表过程如下:红色表示当前8bit,绿色表示右移后高位补零。

第一次(n & 0xff)             10101011110011011110111100010010

第二次((n >> 8) & 0xff)  00000000101010111100110111101111

第三次((n >> 16) & 0xff00000000000000001010101111001101

第四次((n >> 24) & 0xff00000000000000000000000010101011

复制代码
  
  
int BitCount7(unsigned int n) { unsigned int table[ 256 ] = { 0 , 1 , 1 , 2 , 1 , 2 , 2 , 3 , 1 , 2 , 2 , 3 , 2 , 3 , 3 , 4 , 1 , 2 , 2 , 3 , 2 , 3 , 3 , 4 , 2 , 3 , 3 , 4 , 3 , 4 , 4 , 5 , 1 , 2 , 2 , 3 , 2 , 3 , 3 , 4 , 2 , 3 , 3 , 4 , 3 , 4 , 4 , 5 , 2 , 3 , 3 , 4 , 3 , 4 , 4 , 5 , 3 , 4 , 4 , 5 , 4 , 5 , 5 , 6 , 1 , 2 , 2 , 3 , 2 , 3 , 3 , 4 , 2 , 3 , 3 , 4 , 3 , 4 , 4 , 5 , 2 , 3 , 3 , 4 , 3 , 4 , 4 , 5 , 3 , 4 , 4 , 5 , 4 , 5 , 5 , 6 , 2 , 3 , 3 , 4 , 3 , 4 , 4 , 5 , 3 , 4 , 4 , 5 , 4 , 5 , 5 , 6 , 3 , 4 , 4 , 5 , 4 , 5 , 5 , 6 , 4 , 5 , 5 , 6 , 5 , 6 , 6 , 7 , 1 , 2 , 2 , 3 , 2 , 3 , 3 , 4 , 2 , 3 , 3 , 4 , 3 , 4 , 4 , 5 , 2 , 3 , 3 , 4 , 3 , 4 , 4 , 5 , 3 , 4 , 4 , 5 , 4 , 5 , 5 , 6 , 2 , 3 , 3 , 4 , 3 , 4 , 4 , 5 , 3 , 4 , 4 , 5 , 4 , 5 , 5 , 6 , 3 , 4 , 4 , 5 , 4 , 5 , 5 , 6 , 4 , 5 , 5 , 6 , 5 , 6 , 6 , 7 , 2 , 3 , 3 , 4 , 3 , 4 , 4 , 5 , 3 , 4 , 4 , 5 , 4 , 5 , 5 , 6 , 3 , 4 , 4 , 5 , 4 , 5 , 5 , 6 , 4 , 5 , 5 , 6 , 5 , 6 , 6 , 7 , 3 , 4 , 4 , 5 , 4 , 5 , 5 , 6 , 4 , 5 , 5 , 6 , 5 , 6 , 6 , 7 , 4 , 5 , 5 , 6 , 5 , 6 , 6 , 7 , 5 , 6 , 6 , 7 , 6 , 7 , 7 , 8 , }; return table[n & 0xff ] + table[(n >> 8 ) & 0xff ] + table[(n >> 16 ) & 0xff ] + table[(n >> 24 ) & 0xff ] ; }

平行算法

网上都这么叫,我也这么叫吧,不过话说回来,的确有平行的意味在里面,先看代码,稍后解释

复制代码
  
  
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 ; }
复制代码

完美法

复制代码
  
  
int BitCount5(unsigned int n) { unsigned int tmp = n - ((n >> 1 ) & 033333333333 ) - ((n >> 2 ) & 011111111111 ); return ((tmp + (tmp >> 3 )) & 030707070707 ) % 63 ; }

位标志法

感谢网友 gussing提供

复制代码
  
  
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); }
复制代码

指令法

感谢网友 Milo Yip提供

使用微软提供的指令,首先要确保你的CPU支持SSE4指令,用Everest和CPU-Z可以查看是否支持。

  
  
unsigned int n = 127 ; unsigned int bitCount = _mm_popcnt_u32(n) ;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值