題目描述:
光棍們對1總是那麼敏感,因此每年的11.11被戲稱為光棍節。小Py光棍幾十載,光棍自有光棍的快樂。讓我們勇敢地面對光棍的身份吧,現在就證明自己:給你一個整數a,數出a在二進位制表示下1的個數,並輸出。
例如:a=7
則輸出:3
分析:
看到這個題目,我們大多想到的是“左移”或者“右移”、“與”操作等,然後根據自己的想法很快寫出函式,並輸入7,10等整數驗證,但是這個題目中,我們需要注意,一個整數的範圍是多少,有多少位,是正整數還是負整數,在Python中,我們要知道,Python中似乎沒有資料位數的概念,資料位數與虛擬記憶體有關,在我們認為溢位之後,python會自動將int型別轉換為long型別。
方法1:左移&右移法
我們首先想到的一般都是右移,一直移動到數字為0即可,如下程式碼所示:
# -*- coding: utf-8 -*-
# @Time :2018/11/23 21:48
# @Author :AstroBoy
# @Site :
# @File :CalcBinaryNum_1.py
# @Software :PyCharm
def CalcBinaryNum(num):
cnt = 0;
while(num):
if (num & 1):
cnt += 1
num = num >> 1
return cnt
複製程式碼
我們輸入一個負數,發現系統很久沒有輸出結果,這是因為程式已經陷入了死迴圈,這是因為,在計算機程式語言中,負數在計算機中是用補碼錶示的,負數的補碼最高位為1,向右移動時,最高位一直用1來填充,所以while迴圈條件一直為真。既然右移的方法不可取,那麼我們就利用左移,在用32位表示的整數中,左移32位1就會變成0,這可以作為判斷條件,但是在python中左移並不會簡單的溢位,而是自動擴充套件位數。為了在python中使用c語言,可以利用python的庫庫ctypes,程式碼如下:
#!/usr/bin/python
# -*- coding: utf-8 -*-
# @Time :2018/11/26 21:27
# @Author :AstroBoy
# @Site :
# @File :CalcBinaryNum_Ctype.py
# @Software :PyCharm
from ctypes import *
def CalcBinaryNumByCtype(num):
count = 0
flag = 1
while(c_int(flag).value):
if (c_int(flag & num).value):
count += 1
flag = flag << 1
return count
print(CalcBinaryNumByCtype(5))
print(CalcBinaryNumByCtype(-1))
複製程式碼
方法2:大眾演算法
上述程式碼需要迴圈移位32次才能得到結果,我們可以利用N & (N -1)的方法,把整數的二進位制中最右邊的二進位制從1變為0
#!/usr/bin/python
# -*- coding: utf-8 -*-
# @Time :2018/11/28 20:18
# @Author :AstroBoy
# @Site :
# @File :CalcBinaryNumByCtype_2.py
# @Software :PyCharm
from ctypes import *
def CalcBinaryNumByCtype(num):
cnt = 0;
while(c_int(num).value):
cnt += 1
num = num & (num -1)
return cnt
print(CalcBinaryNumByCtype(5))
print(CalcBinaryNumByCtype(-1))
複製程式碼
方法3:庫函式方法
當前我們可以藉助Python特有的庫函式bin,來解決該問題
#!/usr/bin/python
# -*- coding: utf-8 -*-
# @Time :2018/11/28 20:25
# @Author :AstroBoy
# @Site :
# @File :CalcBinaryNumByBin_1.py
# @Software :PyCharm
#!/usr/bin/python
# -*- coding: utf-8 -*-
# @Time :2018/11/28 20:18
# @Author :AstroBoy
# @Site :
# @File :CalcBinaryNumByCtype_2.py
# @Software :PyCharm
def CalcBinaryNumByBin(num):
if (num >= 0):
nbin = bin(num)
return nbin.count(`1`)
else:
num = abs(num)
nbin = bin(num - 1)
return 32 - nbin.count(`1`)
print(CalcBinaryNumByBin(5))
print(CalcBinaryNumByBin(-1))
複製程式碼
當前還有更簡單的做法,利用python的特性:
#!/usr/bin/python
# -*- coding: utf-8 -*-
# @Time :2018/11/28 20:46
# @Author :AstroBoy
# @Site :
# @File :CalcBinaryNumByBin_2.py
# @Software :PyCharm
def CalcBinaryNumByBin(num):
nbin = bin(num & 0xffffffff)
return nbin.count(`1`)
print(CalcBinaryNumByBin(5))
print(CalcBinaryNumByBin(-1))
複製程式碼
在python中,負數與0xFFFFFFFF按位與,實際上按照語法,負數在做與操作之前會先把自己轉為計算機中的二進位制表示形式,然後與0xFFFFFFFF做與操作,也就變成了一個二進位制表示的無符號數
方法4:查表法
除以上方法外,還有查表的方法,先建立一個儲存0到15的二進位制中1的個數的列表,然後計算的時候每4位進行一次查表。
#!/usr/bin/python
# -*- coding: utf-8 -*-
# @Time :2018/11/28 21:04
# @Author :AstroBoy
# @Site :
# @File :CalcBinaryNumByList.py
# @Software :PyCharm
counts = [0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4]
def CalcBinaryNumByList(num):
result = 0
for i in range(0,32,4):
result += counts[(num >> i) & 0xf]
return result
print(CalcBinaryNumByList(5))
print(CalcBinaryNumByList(-1))
複製程式碼
當前還有其他優秀的演算法比如平行演算法、如果利用C或者C++語言還有位域的方法,不再一一列舉。
Reference: