MIT HAKMEM算法分析(bitCount算法)

MIT HAKMEM算法分析

問題需求:計算32位整型數中的’1’的個數

思路分析:

1. 整型數 i 的數值,實際上就是各位乘以權重——也就是一個以2爲底的多項式:

i=A020+A121+A222+...

因此,要求1的位數,實際上只要將各位消權:i = A0+A1+A2+... 所得的係數和就是’1’的個數。

2.對任何自然數n的N次冪,用n-1取模得數爲1,

證明如下:
若:

nk1%(n1)=1

成立

則:

nk%(n1)=((n1)nk1+nk1)%(n1)=0+nk1%(n1)=1

也成立

n(11)%(n1)=1

故對任意非負整數N,

nN%(n1)=1

3.因此,對一個係數爲Ai 的以n爲底的多項式P(N), P(N)%(n-1) = (sum(A_i)) % (n-1)

註釋: (a + b) % p = (a % p + b % p) %p (a * b) % p = (a % p * b % p) % p 如果能保證sum(Ai)
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);  
}  
010101010101爲8進制

但MIT HAKMEM最終的算法要比上面的代碼更加簡單一些。

爲什麼說上面的式子中不能先把(ti>>k)都先提取出來相加,然後再進行&運算呢?

因爲用&(000001)進行MASK後,產生的有效位只有1位,只要6位數中的’1’個數超過1位,那麼在”先加”的過程中,得數就會從最低位中向上溢出。

但是我們注意到,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)

C代碼:

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組。並且已經提取了”最大公因式”。然而這仍然不是最終的MIT HAKMEM算法,不過已經非常接近了,看看代碼吧。

MIT HAKMEM算法:

C代碼:

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

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

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

    return (tmp%63);  
}  

又減少了一組+, >>, &運算。被優化的是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是爲了屏蔽”組間”“串擾”,即屏蔽掉從左邊組的低位移動過來的數。

總結

第一次自己寫算法分析,感覺收穫還是蠻大的。

看算法的時候都是看這個算法是如何工作的,寫的過程則是去模擬發現者的思路,看這個算法是如何被推導出來的,把思路反過來又重新梳理了一遍。

最大的感慨是數學對於算法的重要性。這個算法最開始的思路就是多項式消權,並且貫穿了整個算法推導和優化的過程。而第二步的必要條件則是對取模和冪運算關係的瞭解。優化的第一步用到了“提取公因式”思想,第二步則迴歸到了多項式的基本運算。

其中除了取模和冪運算的知識不算太基礎以外,其他無一例外都是初中以內的數學知識與思想。特別是對2進制數本質的理解,所有人都清楚如何求得一個二進制整型數的數值,但是卻很少有人對其多項式本質始終保持着清晰的認識。

就是這些小得不能再小,基礎得不能再基礎的地方,決定了我們的水平。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章