天天看點

python 找質數的個數_盤一盤 Python 系列特别篇 All 和 Any

python 找質數的個數_盤一盤 Python 系列特别篇 All 和 Any

本文含  3758  字, 9  圖表截屏 建議閱讀 10  分鐘

本文是 Python 系列的特别篇的第十四篇

  • 特别篇 1 - PyEcharts TreeMap
  • 特别篇 2 - 面向對象程式設計
  • 特别篇 3 - 兩大利「器」
  • 特别篇 4 - 裝飾器
  • 特别篇 5 - Sklearn 0.22
  • 特别篇 6 - Jupyter Notebook
  • 特别篇 7 - 格式化字元串
  • 特别篇 8 - 正規表達式
  • 特别篇 9 - 正規表達式實戰
  • 特别篇 10 - 錯誤類型
  • 特别篇 11 - 異常處理
  • 特别篇 12 - Collection
  • 特别篇 13 - Matplotlib Animation
  • 特别篇 14 - All 和 Any

今天星期五,不燒腦了,寫一個簡單的例子以飨大家。例子雖然簡單,但還是有些東西可以學習的。

故事背景:判斷一個正整數是不是質數。

邏輯很簡單,對于一個數 n,隻有從 2 到 n 做個循環,來檢查 n 是不是被每個數能整除,如果是,那麼 n 不是質數;如果不是,n 是質數。簡單明了,代碼如下。

def is_prime(n):    for i in range(2, n):        if n % i == 0:            return False    return True
           

檢驗結果沒問題。

print( is_prime(6) )print( is_prime(7) )
           
False
           

如果你就此滿足,就太不 Pythonic 了。你看這上面 for + if + 兩個 return 不覺得醜嗎?反正我覺得醜。是以我準備用 Python 裡面的 all() 函數來實作,先看看 all() 函數怎麼用,用 help(all) 來檢視。

help(all)
           
Help 
           

從上可知,all() 函數的參數是一個可疊代對象(iterable),像清單、元組、字典等等都是可疊代對象。不清楚的同學請參考【兩大利器】此貼。

當所有元素都為 True 時,傳回 True;隻要有一個元素為 False,傳回 False。

來我們驗證幾組,結果沒問題。

all([1, 2, 3]all([1, 2, 0])all([1, 2, 'all'])all([[], 2, 'all'])
           
True
           

這時候需要了解一個知識點,看完就弄懂上面的結果了。

知識點 非布爾型變量也有相對性的布爾值,設該變量為 X

  • 當 X 是元素型變量(整型、浮點型)
    • 當 X 為 0, 0.0,

      bool(X) =

      False

    • 當 X 非零,

      bool(X) =

      True

  • 當 X 是容器型變量(字元串、清單、元組、字典、集合)
    • 當 X 為空,

      bool(X) =

      False

    • 當 X 不為空,

      bool(X) =

      True

代碼驗證:

print( type(0), bool(0), bool(3) )print( type(10.31), bool(0.00), bool(10.31) )print( type(''), bool( '' ), bool( 'python' ) )print( type(()), bool( () ), bool( (10,) ) )print( type([]), bool( [] ), bool( [1,2] ) )print( type({}), bool( {} ), bool( {'a':1, 'b':2} ) )print( type(set()), bool( set() ), bool( {1,2} ) )
           
class 'int'> False True
           

好,我們現在開始用 all() 函數來實作找質數,那麼最自然的就是先建立一個空清單,然後檢查 n 是否能被每個元素 i 整除,是的話 n 不是質數,是以清單附加 False 值,反之清單附加 True 值。

def is_prime(n):    divisible = []    for i in range(2, n):        if n % i == 0:            divisible.append(False)        else:            divisible.append(True)    return all(divisible)
           

結果雖然沒問題,但更醜了,這怎麼越改還越差了呢?

print( is_prime(11) )print( is_prime(12) )
           
True
           

來做點改進,直接在清單上附件表達式 n%i != 0,和上面的代碼等價。

def is_prime(n):    divisible = []    for i in range(2, n):        divisible.append(n % i != 0)    return all(divisible)
           

結果沒問題,但代碼還是醜陋冗長。

print( is_prime(13) )print( is_prime(14) )
           
True
           

Hello,這可是生成清單啊,那用清單解析式(list comprehension)啊!不清楚的同學請參考【盤一盤 Python 下篇】此貼第 5 章。

def is_prime(n):    divisible = \    [n % i != 0 for i in range(2, n)]    return all(divisible)
           

檢驗結果沒問題,而且代碼漂亮了。但是如果 n 很大又剛好不是質數,那麼按照判斷邏輯,我們隻要找到某個 i 使其可以整除 n 就已經可以判斷 n 不是質數了,但是寫成清單解析式我們還要把整個清單運作完!

print( is_prime(15) )print( is_prime(16) )
           
False
           

一個一個判斷,隻要到達某點就停,你想到了什麼?對,生成器(generator)!

生成器是按需求調用 (call-by-need) 的,你需要調用一個值,我就 yield 一個值,然後用 next() 更新内部狀态,等待你下次調用。這套流程也稱作惰性求值 (lazy evaluation),目的是最小化計算機要做的工作。在大規模資料時,一次性處理往往抵消而且不友善,而惰性求值解決了這個問題,它把計算的具體步驟延遲到了要實際用該資料的時候。

改下代碼,隻用把清單解析式的方括号 [] 改成生成器的小括号 (),就完事了。

def is_prime(n):    divisible = \    (n % i != 0 for i in range(2, n))    return all(divisible)
           

檢驗結果當然沒問題。

print( is_prime(17) )print( is_prime(18) )
           
True
           

那我們來看看兩者的運作效率吧,為了友善比較,定義了兩個函數:

  • is_prime_list_comprehension
  • is_prime_generator
def is_prime_list_comprehension(n):        return all([n % i != 0 for i in range(2, n)])
           
def is_prime_generator(n):        return all((n % i != 0 for i in range(2, n)))
           

例子 1:我找了個比較大的質數 131071,用 %timeit 魔法指令來顯示運作時間。

python 找質數的個數_盤一盤 Python 系列特别篇 All 和 Any

哈哈哈哈,傻眼了吧,生成器運作的時間 (13.6ms) 比清單解析式運作的時間 (13.1ms) 長。

再想想,當一個數的質數,兩個函數都要從頭運作到尾哦,13.6ms 和 13.1ms 可以看做一樣的。

例子 2:在找一個比較大的非質數,試試 8191*8191 = 67092481,它肯定不是質數,而且也沒那麼快找出來,因為 8191 是質數,你品一下。

8191*8191

67092481
           

再看兩者運作對比結果,引起極度舒适。

python 找質數的個數_盤一盤 Python 系列特别篇 All 和 Any

總結

你光講了 all 函數,那 any 函數呢?類比一下嘛!

all( [x1, x2, ..., xn] ) 是所有 xi 都為 True 傳回 True(all 的含義),反之傳回 False。

any( [x1, x2, ..., xn] ) 是隻要有一個 xi 為 True 就傳回 True(any 的含義),反之傳回 False。

是以,隻要你見到下圖左邊的代碼樣子,你就可以用 all() + 生成器。

python 找質數的個數_盤一盤 Python 系列特别篇 All 和 Any

隻要你見到下圖左邊的代碼樣子,你就可以用 any() + 生成器。

python 找質數的個數_盤一盤 Python 系列特别篇 All 和 Any

其實找質數也可以用 any() 函數來實作,代碼如下:

def is_prime_use_any(n):        return not any((n % i == 0 for i in range(2, n)))
           

注意 any() 函數前面加了一個 not,而且表達式從 n%i != 0 改成 n%i = 0。意思就是說如果 n 可以被任何(any 的含義)i 整除,傳回為 True,前面加個 not 就傳回為 False,那麼就不是質數。

我的天啊,繞不繞口?這代碼反不反人性?再回顧一下優雅 all() 函數實作的代碼吧。

def is_prime_use_all(n):        return all((n % i != 0 for i in range(2, n)))
           

說如果 n 不能被所有(all 的含義)i 整除,傳回為 True,那麼就是質數。

Yes, coding style is a progress!

Stay Tuned!

python 找質數的個數_盤一盤 Python 系列特别篇 All 和 Any

我的新書《快樂機器學習》

在新加坡終于有買了

掃碼進 Lazada 購買

新加坡全島包郵

python 找質數的個數_盤一盤 Python 系列特别篇 All 和 Any

我的新課《Python 基礎》

掃碼購買

零套路無前戲直接幹貨

上過的都說好

python 找質數的個數_盤一盤 Python 系列特别篇 All 和 Any