第2章 數字之魅 求二進位制中1的個數

2021-09-07 00:14:50 字數 3228 閱讀 9506

對於乙個位元組(8bit)的變數,求其二進位制表示中「1」的個數,要求演算法的執行效率盡可能地高。

可以舉乙個八位的二進位制例子來進行分析。對於二進位制操作,我們知道,除以乙個2,原來的數字將會減少乙個0。如果除的過程中有餘,那麼就表示當前位置有乙個1。

以10 100 010為例;

第一次除以2時,商為1 010 001,余為0。

第二次除以2時,商為101 000,余為1。

因此,可以考慮利用整型資料除法的特點,通過相除和判斷餘數的值來進行分析。於是有了如下的**:

1

package

chapter2shuzizhimei.erjinzhicount1;

2/**

3* 求二進位制數中1的個數

4* 【方法一】5*

@author

dell6*

7*/8public

class

count1

21 x /= 2;22}

23return

count;24}

2526

public

static

void

main(string args)

30 }

程式執行結果如下:

二進位制中1的個數為:4

前面的**看起來比較複雜。我們知道,向右移位操作同樣也可以達到相除的目的。唯一不同之處在於,移位之後如何來判斷是否有1存在。對於這個問題,再來看看乙個八位的數字:10 100 001。

在向右移位的過程中,我們會把最後一位直接丟棄。因此,需要判斷最後一位是否為1,而"與"操作可以達到目的。可以把這個八位的數字與00000001進行"與"操作。如果結果為1,則表示當前八位數的最後一位為1,否則為0。**如下:

1

package

chapter2shuzizhimei.erjinzhicount1;

2/**

3* 求二進位制數中1的個數

4* 【方法二】採用位操作5*

@author

dell6*

7*/8public

class

count2

21return

count;22}

2324

public

static

void

main(string args)

28 }

程式執行結果如下:

二進位制中1的個數為:4

位操作比除、餘操作的效率高了很多。但是,即使採用位操作,時間複雜度仍為o(log2v),log2v為二進位制數的位數。那麼,還能不能再降低一些複雜度呢?如果有辦法讓演算法的複雜度只與"1"的個數有關,複雜度不就能進一步降低了嗎?

同樣用10 100 001來舉例。如果只考慮和1的個數相關,那麼,我們是否能夠在每次判斷中,僅與1來進行判斷呢?

為了簡化這個問題,我們考慮只有乙個1的情況。例如:01 000 000。

如何判斷給定的二進位制數裡面有且僅有乙個1呢?可以通過判斷這個數是否是2的整數次冪來實現。另外,如果只和這乙個"1"進行判斷,如何設計操作呢?我們知道的是,如果進行這個操作,結果為0或為1,就可以得到結論。

如果希望操作後的結果為0,01 000 000可以和00 111 111進行"與"操作。

這樣,要進行的操作就是 01 000 000 &(01 000 000 - 00 000 001)= 01 000 000 &00 111 111 = 0。

因此就有了解法三的**:

1

package

chapter2shuzizhimei.erjinzhicount1;

2/**

3* 求二進位制數中1的個數

4* 【方法三】只考慮1的個數減少時間複雜度5*

@author

dell6*

7*/8public

class

count3

21return

count;22}

2324

public

static

void

main(string args)

28 }

程式執行結果如下:

二進位制中1的個數為:4

【解法四】使用分支操作

解法三的複雜度降低到o(m),其中m是v中1的個數,可能會有人已經很滿足了,只用計算1的位數,這樣應該夠快了吧。然而我們說既然只有八位資料,索性直接把0~255的情況都羅列出來,並使用分支操作,可以得到答案,**如下:

1

int count(int

x) 2

30return

num;

31 }

解法四看似很直接,但實際執行效率可能會低於解法二和解法三,因為分支語句的執**況要看具體位元組的值,如果a =0,那自然在第1個case就得出了答案,但是如果a =255,則要在最後乙個case才得出答案,即在進行了255次比較操作之後!

看來,解法四不可取!但是解法四提供了乙個思路,就是採用空間換時間的方法,羅列並直接給出值。如果需要快速地得到結果,可以利用空間或利用已知結論。這就好比已經知道計算1+2+ … +n的公式,在程式實現中就可以利用公式得到結論。

演算法中不需要進行任何的比較便可直接返回答案,這個解法在時間複雜度上應該能夠讓人高山仰止了。

**如下:

1

int counttable[256] = 2;

2021

int count(int

v)22

這是個典型的空間換時間的演算法,把0~255中"1"的個數直接儲存在陣列中,v作為陣列的下標,counttable[v]就是v中"1"的個數。演算法的時間複雜度僅為o(1)。

在乙個需要頻繁使用這個演算法的應用中,通過"空間換時間"來獲取高的時間效率是乙個常用的方法,具體的演算法還應針對不同應用進行優化。

1. 如果變數是32位的dword,你會使用上述的哪乙個演算法,或者改進哪乙個演算法?

2. 另乙個相關的問題,給定兩個正整數(二進位制形式表示)a和b,問把a變為b需要改變多少位(bit)?也就是說,整數a 和b 的二進位制表示中有多少位是不同的?

這個問題其實就是比問題1多了乙個步驟,只要先算出a和b的異或結果,然後求這個異或值中1的個數就行了。

二進位制 二進位制中1的個數

題目 請實現乙個函式,輸入乙個整數,輸出該數二進位制表示中 1 的個數。例如,把 9 表示成二進位制是 1001,有 2 位是 1。因此,如果輸入 9,則該函式輸出 2。示例 1 輸入 00000000000000000000000000001011 輸出 3 解釋 輸入的二進位制串 0000000...

求二進位制中1的個數

在 程式設計之美 一書中有一節提到如何求乙個位元組的無符號整型變數二進位制表示中中1的個數,主要提到了四種方法。下面簡單介紹一下 1.求餘法 在將十進位制數轉換為二進位制數時,採用除2取餘法。將每次除2得到的餘數儲存起來逆序輸出便是該十進位制整數的二進位制表示。因此可以採用這種方法去統計1的個數。i...

求二進位制中1的個數

解法有很多種 以乙個位元組無符號位元組變數作為例子 解法一 求餘法 在將十進位制數轉換為二進位制數時,採用除2取餘法。將每次除2得到的餘數儲存起來逆序輸出便是該十進位制整數的二進位制表示。因此可以採用這種方法去統計1的個數。public int count byte n return sum 解法二...