1. 程式人生 > >求二進位制中的1/0的個數

求二進位制中的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'的個數,閱讀了幾篇文章,在這裡整理一下:

演算法如下:

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是為了遮蔽"組間""串擾",即遮蔽掉從左邊組的低位移動過來的數。

以上,這個演算法分析我還沒有看得太懂,先標記一下,下次再看~~~