一、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、整型数 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、由上面分析可以得到代码:
这样又减少了几步!!
3、化简二
如何计算三位中1的个数,如三位abc,1的个数无非是计算 a+b+c
一个3位2进制数值是4a+2b+c
如果右移一位,变成2a+b;再右移一位,变成a
而(4a+2b+c) - (2a+b) - a = a+b+c 得到的就是我们想要的数
所以改代码如下:
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的个数,代码如下:
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);
}