原文發表在個人博客主頁,轉載請註明出處python
我一直是一個比較喜歡算法的人,以爲算法真的是至關美妙和神奇!!!趁春節有時間看看算法書,體會思想和技術沉澱下來的美妙,今天看到了統計二進制中1的個數這個本來很簡單的題目,以前也看過,不過此次看書加深刻思考以後發現裏面的水仍是很深的,特別是用python的程序猿更應該明白,閒話少說,開始正題。程序員
實現一個函數,輸入一個整數,輸出該數二進制表示中1的個數算法
拿到這個題目,全部人確定立馬有了思路,幾個關鍵詞立馬在腦海中顯現:「循環」,「右移」,「與」。而後很快寫出了函數,隨便輸入幾個數驗證,沒問題。。。可是這個題目須要注意的首先是整數,包括正整數和負整數,其次在python中,數據位數是一個比較模糊的概念,在程序中基本不存在,由於越位以後他會自動將int轉爲爲long類型,因此對python程序員來講,須要提早搞明白整數的位數,或者在python語言中調用C語言,下面來列舉其中的集中解法。api
技巧:python中的左移和右移與其餘C/C++等的定義和結果都是不同的,你們能夠自行作實驗,python中的定義:右移n位定義爲除以pow(2,n),左移n位定義爲乘以pow(2,n),並且沒有溢出(移除的int會升級爲long類型)函數
上面題目分析的時候說過每一個人看見題目心中都會涌現出最naive可是不對的解法(不論用什麼語言):編碼
def count(num): cnt = 0 while num: if num & 1 == 1: cnt += 1 num = num >> 1 return cnt
爲何說他不對呢,你們能夠輸入一個負整數看看,程序會陷入死循環,由於在其餘語言中,負數在計算機中是用補碼錶示的,最高位是1,在右移的過程當中,高位都是用1來填補的,因此while num這個條件一直爲真;在python中,根據右移的定義就能夠自行推斷出來。既然如今右移num不行,那咱們能夠左移1,在32的整數中,最多左移32位,1就會變爲零,因此這能夠做爲判斷條件,這在C語言中能夠寫出和上面相似的代碼,可是在python中,咱們一塊兒能夠左移下去(到虛擬內存大小的位數),因此這裏我用到了python中的庫ctypes,在python中使用C語言,代碼以下:code
from ctypes import * def count(num): cnt = 0 flag = 1 while c_int(flag).value: if c_int(num & flag).value: cnt += 1 flag = flag << 1 return cnt
上面的代碼不論輸入正數負數仍是零,均可以獲得正確的答案,可是對於全部的整數都須要循環32次才能獲得結果,繼續改進。看一個簡單的例子,整數12的二進制表示爲1100,將其減一變爲1011,將獲得的結果和原樹進行按位與,獲得1000,因此發現規律沒有?把一個整數減去1以後再和原來的整數作按位與,獲得的結果至關因而把整數的二進制表示中最右邊的一個1變成0,按照這個規律進行遍歷,則函數的循環次數爲二進制中一的個數次。代碼以下:blog
from ctypes import * def count(num): cnt = 0 while c_int(num).value: cnt += 1 num = (num -1) & num return cnt
技巧:把一個整數減去1以後再和原來的整數作按位與,獲得的結果至關因而把整數的二進制表示中最右邊的一個1變成0內存
以上都是各類語言通用的方法,只不過是以python做爲例子而已,那麼在python中語言中能夠利用他的庫函數很容易的解決這個問題,代碼以下:get
def num_of_one(num): ''' count the num of "one" in num n bin():convert the num to binary string :param num: num num :return: the num of "one" in num ''' if num >= 0: nbin = bin(num) return nbin.count('1') else: num = abs(num) nbin = bin(num-1) return 32 - nbin.count('1')
上面的代碼是爲了更加詳細的區分正數和負數,固然利用python的一些特性,簡化代碼以下:
def num_of_one(num): nbin = bin(n & 0xffffffff) return nbin.count('1')
技巧:對於二進制來講,先減一後取反和先去反後加一結果同樣
博主John Rambo在我博客下面提供了另一種方法,這個方法也是十分巧妙,先用一個列表存儲下來0到15的二進制中1的個數,在計算的時候每四位進行查表,代碼以下:
#Add up the number of 1 bits in every 4 bits. #number of 1 bits in 0x0 to 0xF. counts = [0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4] def num_of_one(num): result = 0 for i in range(0,32,4): result += counts[num >> i & 0xf] return result
技巧:在python中,負數和0xffffffff按位與以後變成一個無符號數,二進制表示爲編碼形式
網友PowerShell免費軟件提供了一種方法,也比較巧妙:
$num=Read-Host -Prompt "請輸入一個整數(能夠是負數)" ([System.Convert]::ToString($num,2)).replace("0","").length #一句話的事兒
本篇blog中的題目對於不少程序員來講一點也不陌生,可是要完美的解決這些問題仍是須要一些思考的,同時技巧也是必不可少的,算法如此美妙,計算機的世界使人神往~