编程之美求二进制数中1的个数扩展题
轉自:http://s.sousb.com/?p=253
編程之美2.1節中的擴展題第1題:如果變量是32位的Dword,則如何統計該二進制數中1的個數。
對于該題,原本的想法還是想采用書中解法三,也就是用統計1中個數的算法v&(v-1),該算法時間復雜度為該32二進制數中“1”的個數。后來,參見了書中鏈接里的解法,該解法甚妙,復雜度只有若干個位運算,與“1”的數目無關。由于下面這段程序寫的比較難懂,所以在這里解釋一下他的解法。
解法一:
view plaincopy to clipboardprint?我們以0000 0000 0000 0000 0000 0000 1011 0101為例:
第一行中,x>>1是右移一位,(x>>1) 為 0000 0000 0000 0000 0000 0000 0101 1010,該結果與0101 0101 0101 0101 0101 0101 0101 0101相與,等于 0000 0000 0000 0000 0000 0000 0101 0000,實際上,((x >> 1) & 0×55555555) 該步是想得到原數據x的偶數位的數據。(代碼是先右移,再得奇數位,這個等價于:先得偶數位,再右移)然后,x-((x >> 1) & 0×55555555) 則是 用原數據減去原數據的偶數位。結果是 0000 0000 0000 0000 0000 0000 0110 0101,其實,之所有要相減就是為了得到第一位與第二位1的個數的和,第三位與第四位1的個數的和,第五位與第六位1的個數的和,以此類推。那么,為什么要用相減的方法來得到相鄰兩位的和呢?原理是:相鄰兩位1的個數的和是:A-A/2 。原數據是A,右移相當于除2。比如,如果原數據是1(01),那么一半就是0,相減后就是1,所以有1個“1”。如果原數據是3(11),那么一半就是1,相減后就是2,所有總共有2個“1”。這樣,經過第一行的運算,我們得到每兩個相鄰位的“1”的總和。
第二行,類似的,是求將原數據第一位第二位的“1“的個數的和 與 第三位第四位的“1”的個數的和 相加。這樣,第二行執行后,我們可以得到每四位的“1”的個數的和
第三行,可以得到每八位的“1”的個數的和
第四行,可以得到每16位“1”的個數的和
第五行,可以得到32位數中所有1的個數的和。
第六行,由于32位的數據,1的個數最大也就32個,不可能超過2的6位方,所以只要取最后6位數據就是所有數據1的個數的和了。
這里用的是二分法,兩兩一組相加,之后四個四個一組相加,接著八個八個,最后就得到各位之和了。
解法二:
view plaincopy to clipboardprint?這個解法更妙。需要注意的是,033333333333和033333333333都是指 八進制的數。第一行第二行連起來看,這與解法一很類似,目的是為了得到第一位與第二位的“1”的個數和。注意,第31、32位中1的個數和在這一步中被統計好了。
第一行和第三行、第四行連起來看,目的是為了得到第一位與第三位的“1”的個數的和。然后,再與上步的結果加起來,就得到第一位、第二位、第三位的“1”的個數的和。所以,從第一行到第四行就是為了得到 每三位一組的“1”的個數的和。原理是:
相鄰三位的結果是:A-A/2-A/4. 算法中有兩次向移。比如,第一位第二位第三位是011, 則第一次移位后為01,相減后為10,再移位后為0,相減還是10,所以有2個“1”。再比如,第一位第二位第三位是101,則第一次移位后為10,相減后為011,再移位后為1,相減后是010,所以有2個“1”。第五行是求相鄰六位的1的個數第六行,比較難懂。在第五行執行完后,我們得到了七組數據,第32、31位為一組,第30-25為一組,……第6-第1為一組。所以可以寫成:x_0 + x_1 * 64 + x_2 * 64 * 64 + x_3 * 64 * 64* 64+ x_4 * 64 * 64* 64* 64+ x_5 * 64 * 64* 64* 64* 64+ x_6 * 64 * 64* 64* 64* 64* 64,這個數除以63的余數 肯定 與 x_0 +……+x_6 相等(因為32位的數據最多也就32個1)簡短解釋:首先是將二進制各位三個一組,求出每組中1的個數,然后相鄰兩組歸并,得到六個一組的1的個數,最后很巧妙的用除63取余得到了結果。
這個程序只需要十條左右指令,而且不訪存,速度很快。
總結
以上是生活随笔為你收集整理的编程之美求二进制数中1的个数扩展题的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 面试题整理17 输入一个字符串判断一个字
- 下一篇: 在git下搭建个人博客