02_Python學習筆記之統計整數二進位制中1的個數

AstroBoy發表於2019-02-28

題目描述:

光棍們對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:

相關文章