Python:用filter函數求素數 (再理解generator)


目的:更熟悉應用generator。

參考:https://www.liaoxuefeng.com/wiki/1016959663602400/1017404530360000

 

素數定義:

素數:質數又稱素數。一個大於1的自然數,除了1和它自身外,不能被其他自然數整除的數叫做質數。

 

方法:

計算素數的一個方法是埃氏篩法

首先,列出從2開始的所有自然數,構造一個序列:

2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, ...

第二步,取序列的第一個數2,它一定是素數,然后用2把序列的2的倍數篩掉:

3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, ...

⚠️此時的序列是從3開始的奇數集合。

第三步,取新序列的第一個數3,它一定是素數,然后用3把序列的3的倍數篩掉:

5, 6, 7, 8910, 11, 12, 13, 141516, 17, 18, 19, 20, ...

第四步,取新序列的第一個數5,然后用5把序列的5的倍數篩掉:

7, 8910, 11, 12, 13, 141516, 17, 18, 19, 20, ...

后續,不斷篩下去,就可以得到所有的素數。

 

代碼:

2是素數,直接從一個奇數序列開始篩選:

# 生成一個奇數序列。從3開始
def _odd_iter():
    n = 1
    while True:
        n += 2
        yield n

 

 篩選函數:

  1. 由上面的分析可知,每次篩選,都除以序列的第一數。
  2. 條件是x % n > 0。x為序列中被判斷的數字,n為該序列的第一個數字。整除結果==0的都不是素樹。要從序列中刪除。
  3. 所以,判斷表達式是不固定的。定義一個函數,傳入參數n, 返回一個lambda表達式用於判斷。 
# 傳入參數n,生成不同的lambda表達式。
def _not_divisible(n):
    # 返回一個lambda表達式。用於判斷x,是不是素數。
    # 素數,只能被1和自身整除的自然數叫做素數。
    # 整除就是余數為0。
    # 如果被非自身數字整除,即余數為0,那自然不是素數,刪除掉。
    return lambda x: x % n > 0

 

主函數:

  1. 第一行代碼:yield 2,返回素數2。
  2. 第二行代碼:創建生成器,第一次運行,它產生奇數序列,這個序列會逐步被篩選,去掉非素數字。
  3. 循環代碼:
    1. n = next(it), 得到序列的第一個數字。
    2. yield n,  它是素數。
    3. 用filter()來篩選, it生成器中的數字。 因為it是可迭代的。並把新的序列賦值給it變量。

⚠️重新理解3。filter函數對生成器對象it進行篩選,其實就是代碼組合,把_not_divisible(n)返回的lambda表達式和it自身的代碼組合,形成新的對象<filter object>。

⚠️我的理解是,原先的_odd_iter()產生的算法不再用,而是使用增加了lambda條件判斷的新的算法,<filter object>。

⚠️這一步並不是真的篩選掉不符合的數字,_ood_iter()只是代表了生成序列數字的算法。只不過這個算法被新的算法替代了。

def primes():
    yield 2  #2是素數。
    it = _odd_iter() #創建一個<generator object _odd_iter>,代表奇數序列。
    while True:
        n = next(it) #返回奇數序列的一個數。
        yield n  #這個數是素數。所以返回。
        # 創建一個<filter object>, 代表新序列。
        it = filter(_not_divisible(n), it)

 

 

 

由於primes()所代表的算法生成的是一個無限序列,沒有退出機制,所以調用時需要設置一個退出循環的條件:

for n in primes():
    if n < 1000:
        print(n)
    else:
        break

⚠️primes本身是生成器函數, primes()就是一個生成器,它自身不儲存數據,只儲存生成數據的算法。 

總結;

由本例可知,generator其實就是算法,它不會一次性產生全部數據,而是根據調用的次數,逐步向內存寫入數據。

本例把一個生成器對象作為參數傳入了filter()函數,其實就是對這個生成器的代碼進行更新,即改進算法。最后返回一個新的迭代器。本例返回的是<filter object>。

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM