求二进制中的1/0的个数

设x为二进制数1111 1111 1111 1010,则求x中0的个数操作:

int countofZero(int x)
{
    int N = 0;
    while (x + 1)
    {
        N++;
        x |= (x + 1);
    }
    return N;
}

求x中1的个数操作:

int countOfOne(x)
{
    int N = 0;
    while(x)
    {
        N++;
        x &= (x - 1); 
    }
    return N;
}

昨天刷题的时候刷到MIT HAKMEM算法,用于求整型数中的'1'的个数,阅读了几篇文章,在这里整理一下:

(参考(侵删):https://blog.csdn.net/msquare/article/details/4536388

算法如下:

int bitcount(unsigned int n)  
{  
    unsigned int tmp;  

    tmp = n  
        - ((n >> 1) & 033333333333)  
        - ((n >> 2) & 011111111111);  

    tmp = (tmp + (tmp >> 3)) & 030707070707;  

    return (tmp%63);  
}  

分析:

1. 整型数 i 的数值,实际上就是各位乘以权重——也就是一个以2为底的多项式:

i = A0*2^0+A1*2^1+A2*2^2+... 因此,要求1的位数,实际上只要将各位消权:

i = A0+A1+A2+...所得的系数和就是'1'的个数。

2. 对任何自然数n的N次幂,用n-1取模得数为1。

3. 因此,对一个系数为{Ai}的以n为底的多项式P(N), P(N)%(n-1) = (sum({Ai})) % (n-1) 。

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

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)

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);  
}  

第一步优化:

6位数中最多只有6个'1',也就是000110,只需要3位有效位。上面的式子实际上是以1位为单位提取出'1'的个数再相加求和求出6位中'1'的总个数的,所以用的是&(000001)。如果以3位为单位算出'1'的个数再进行相加的话,那么就完全可以先加后MASK。算法如下:

tmp = (ti>>2)&(001001) + (ti>>1)&(001001) + ti&(001001)

(tmp + tmp>>3)&(000111)

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

注:代码中是使用8进制数进行MASK的,11位8进制数为33位2进制数,多出一位,因此第一位八进制数会把最高位舍去(7->3)以免超出int长度。

从第一个版本到第二个实际上是一个“提取公因式”的过程。用1组+, >>, &运算代替了3组。并且已经提取了"最大公因式"。

第二步优化:

又减少了一组+, >>, &运算。被优化的是3位2进制数“组”内的计算。再回到多项式,一个3位2进制数是4a+2b+c,我们想要求的是a
+b+c,n>>1的结果是2a+b,n>>2的结果是a。

于是: (4a+2b+c) - (2a+b) - (a) = a + b + c

中间的MASK是为了屏蔽"组间""串扰",即屏蔽掉从左边组的低位移动过来的数。

以上,这个算法分析我还没有看得太懂,先标记一下,下次再看~~~

猜你喜欢

转载自blog.csdn.net/ljytower/article/details/83178323
今日推荐