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

转自 : https://www.cnblogs.com/graphics/archive/2010/06/21/1752421.html


 

一些简单的方法就不转了,转最后两个

平行算法

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

 

图中第一行,是数据 217的 二进制表现形式

图中第二行,是先按两两分组,然后再相加,即  

n = (n &0x55555555) + ((n >>1) &0x55555555)

其中第二个与操作,是为了清除移位操作时,高位分组侵入到低位分组的数据:

1 1 0 1 1 0 0 1

   1 1 0 1 1 0 0 1 

---------------------------------

 10   01   01   01

黄色部分被 "与" 操作清除,剩余部分相加,

然后继续分组,4 bit 一组 :

n = (n &0x33333333) + ((n >>2) &0x33333333) ;

 10   01   01   01

   10   01   01   01

----------------------------------

    0011       0010

最后,8 bit 一组:

n = (n &0x0f0f0f0f) + ((n >>4) &0x0f0f0f0f) ;

0011       0010

    0011       0010

-----------------------------------

               0101

 

即最后结果,

此例为8 bit, 如果是 16 bit 或则 32 bit,则继续执行 一到两次分组即可。

 


 

完美法

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

这是 8进制计算, 其中 十进制数63换算为8进制是 077,这里我准备分析十六进制的原理,8进制和十六进制是一样的。

#define BITCOUNT(x) (((BX_(x)+(BX_(x)>>4)) & 0x0F0F0F0F) % 255)
#define BX_(x) ((x) - (((x)>>1)&0x77777777) - (((x)>>2)&0x33333333) - (((x)>>3)&0x11111111))

32位的十六进制数,按4 bit 一组进行分组,对任意的 4bit 数 x, 都可以写成如下形式:

x = 2^3 * a + 2^2 * b + 2^1 * c + d

其中 a,b, c, d取值 0或者 1 ,也就是典型的8421 十六进制表达。于是 a+b+c+d的值,就是当前分组的1的个数。

展开来就是:

x = 8a + 4b + 2c + d

十六进制的右移操作,也可以理解为除 2 操作,

而 与操作,则是清除移位时,高位分组的低位 侵入到 低位分组的高位 数据

ex : 1010 1100 >> 1 = x101 0110

红色数据是侵入数据,可以通过 与操作清除

 

将 BX_(x) 展开,就是:

  (8a+4b+2c+d) - (8a+4b+2c+d) /2 - (8a+4b+2c+d) /4 - (8a+4b+2c+d) /8

=  (8a+4b+2c+d) - (4a+2b+c) - (2a+b) - (a)

= a+b+c+d

可见,结果就是求出了各个分组的1的个数。

 

下一步操作就是累加每个分组中 1的个数,与平行算法类似,错位相加,得到8bit中1的个数,由于与操作,所以结果的形式如下:

BITCOUNT(x) = 0x0a0b0c0d

余除 255后,就是1的累加值,怎么来的?

 

先把值展开 :

     d + c * 256 + b * 256^2 + a * 256 ^3 

=  d + c* (255+1) + b * (255+1)^2 + a * (255+1)^3

对每一项余除 255,

 d % 255 = d

c * (255+1) % 255 = c * 255 %255 + c %255 = 0 + c

另外, X *(255+1)^n 根据数学原理,展开后,除最后一项是1外,其他的项都是 255的倍数,255倍数余除 255,结果为0, 所以只会保留 X %255,也就是 X的值。

所以,[ d + c* (255+1) + b * (255+1)^2 + a * (255+1)^3  ] %255 = d + c + b + a, 也就是累加了每一组中 1的个数。

 

posted on 2024-07-21 21:27  longyue  阅读(18)  评论(0编辑  收藏  举报

导航