BOP - 计算32位整型数中的1的个数

一、HAKMEM算法分析
1、整型数 i 的数值,实际上就是各位乘以权重——也就是一个以2为底的多项式:
    i = A0*2^0+A1*2^1+A2*2^2+...
    因此,要求1的位数,实际上只要将各位消权:
    A0+A1+A2+...  即系数和就是'1'的个数。

2、定理:对任何自然数n的N次幂,用n-1取模得数为1。
证明:
若 n^(k-1) % (n-1) = 1 成立
则 n^k % (n-1) = ((n-1)*n^(k-1) + n^(k-1)) % (n-1) = 0 + n^(k-1) % (n-1)  = 1 也成立
又有 n^(1-1) % (n-1) = 1
故对任意非负整数N, n^N %(n-1)=1

3、因此,对一个系数为{Ai}的以n为底的多项式P(N) = A0*n^N0+A1*n^N1+A2*n^N2+...

P(N)%(n-1) = (sum({Ai})) % (n-1) ;
如果能保证sum({Ai}) < (n-1),则 P(N)%(n-1) = (sum({Ai}))  
也就是说,此时只要用n-1对多项式取模,就可以完成消权,得到系数和。

于是,问题转化为,将以2为底的多项式转化为以n为底的多项式,其中n要足够大,使得n-1 > sum({Ai})恒成立。

32位整型数中Ai=0或1,sum({Ai})<=32。n-1 > 32 ,n需要大于33。

因此取n=2^6=64>33作为新多项式的底。

4、将32位二进制数的每6位作为一个单位,看作以64为底的多项式:

i = t0*64^0 + t1*64^1 + t2*64^2 + t3*64^3 + ...

各项的系数ti就是每6位2进制数的个数。

这样,只要通过运算,将各个单位中的6位数变为这6位中含有的'1'的个数,再用63取模,就可以得到所求的总的'1'的个数。

5、取其中任意一项的6位数ti进行考虑,最简单的方法显然是对每次对1位进行mask然后相加,即

(ti>>5)&(000001) + (ti&>>4)(000001) + (ti>>3)&(000001) + (ti>>2)&(000001) + (ti>>1)&(000001) + ti&(000001)

二、代码
1、由上面分析可以得到代码:
int  bitcount(unsigned  int  n)   
{   
    unsigned int  tmp;   
   
    tmp = (n &010101010101)   
     +((n>>1)&010101010101)   
     +((n>>2)&010101010101)   
     +((n>>3)&010101010101)   
     +((n>>4)&010101010101)   
     +((n>>5)&010101010101);   
   
    return  (tmp%63);   
} 

返回的就是1的个数。简单吧

2、化简一
上面是计算每六位中1的个数,得到上面原理分析的ti
但是,6位数中最多只有6个'1',也就是000110,只需要3位有效位,所以有些浪费
如果先得到每三位中1的个数,然后加一下也得到每六位1的个数,代码如下:
int  bitcount(unsigned  int  n)   
{   
    unsigned int  tmp;   
   
    tmp = (n &011111111111)   
     +((n>>1)&011111111111)   
     +((n>>2)&011111111111);   
        
    tmp = (tmp + (tmp>>3)) &03070707007;   
   
    return  (tmp%63);   
} 

这样又减少了几步!!

3、化简二
如何计算三位中1的个数,如三位abc,1的个数无非是计算 a+b+c
一个3位2进制数值是4a+2b+c
如果右移一位,变成2a+b;再右移一位,变成a
而(4a+2b+c) - (2a+b) - a = a+b+c  得到的就是我们想要的数
所以改代码如下:
int  bitcount(unsigned  int  n)   
{   
    unsigned int  tmp;   
   
    tmp = n                                       // 4a+2b+c
        - ((n >> 1) & 033333333333)    // 2a+b
        - ((n >> 2) & 011111111111);   // a
   
    tmp = (tmp + (tmp >> 3)) & 030707070707   
   
    return  (tmp%63);   
}  

这样又少了两步,这才是最最简单,复杂度最低的求1的个数的方法!!

转自: http://www.my1984.com.cn/article.asp?id=779
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值