1. 程式人生 > >使用分治法得到一個數中位元為1的個數

使用分治法得到一個數中位元為1的個數

 

有這麼一個問題, 給定一個數(假定32位), 如何得到這個數轉為二進位制後1的個數?

 

解:

X=(x & 0x55555555)+((x>>1)&0x55555555)

X=(x & 0x33333333)+((x>>2)&0x33333333)

X=(x & 0x0F0F0F0F)+((x>>4)& 0x0F0F0F0F)

X=(x & 0x0000FFFF)+((x>>1)& 0x0000FFFF)

 

 

原理圖:

  

 將原始問題(統計32個位元中1的個數),分解為統計16個位元中1的個數,在分解為統計8個位元中1的個數……, 最後統計兩個位元中1的個數。最後求和。

 

分析:

首先請看左上角:

 

 

這兩個位元中只有一個位元為1,於是將結果填寫到下面的格子中。這一行以此類推。

接下來看第二行第三行:

 

 

紅色表示我的上面兩個位元中只有一個位元為1,青黃色表示我的上面有兩個位元為1,將兩個數相加得到0011,代表我上面的4個位元中有3個位元為1.

.

.

.

以此類推, 得到最後的數為 0B010111 = 23

 

 

那麼現在的問題是, 如何統計1的個數然後相加呢?

 

首先第一行, 0x55555555 = 0B01010101010101010101010101010101

與x與的結果就將偶數位的1取了出來, x>>1與0x55555555相與,就將奇數位的1取了出來。這兩個數相加, 就將每兩位中的1的個數取了出來,放到了下一行的對應的位中。

請注意,由於只右移了一位,因此只是統計每兩位中的1的個數 。 可以使用1111&0101測試,很容易發現問題。

0x33333333 = 00110011001100110011001100110011

與x與的結果就所有第2^n和(2^n)-1的數取出來。與x>>2相與,就是將剩餘的數取出來。,這些數就是位元為1個數的和。同樣可以使用 1111&0011測試。

.

.

.

.

依次類推,最終得到的就是位元為1的個數。

 

 

 

統計值為1的位元數有很多方法,這裡只是一種較為簡單的分治法。還有很多精妙絕倫的方法。

 

 

參考文獻:

<<演算法心得(Hacker’s Delight)>>                      --機械工業出版社