在上一篇文章中(《迭代器案例1:zip》中,已經使用過了Python標準庫itertools,本文將繼續探討這個標準庫中的幾個有意思的函數,從而理解迭代器的應用。python
數學中的「組合」與「排列」,若是用程序來實現,使用Python中的itertools模塊的函數,是很是簡單實用的。單純來說排列組合問題,沒有什麼意思,能夠看一個經典的問題。算法
題目:假設有這樣一些RMB:3張20元;5張10元;2張5元;5張1元。問:如何組合出100元。編程
首先,能夠用一個列表,把已知的RMB表示出來。bash
rmb = [20, 20, 20, 10, 10, 10, 10, 10, 5, 5, 1, 1, 1, 1, 1]
複製代碼
剛纔這個問題,是一個組合問題,好比,能夠這麼思考:先找出一張20的,而後逐個嘗試,其它各類組合可能,直到找到和爲100爲止。這樣就獲得了一種組合方式。app
Python標準庫中的itertools就爲咱們提供了這樣一個函數,實現這種組合。機器學習
>>> import itertools
>>> com = itertools.combinations([1,2,3], 2)
>>> list(com)
[(1, 2), (1, 3), (2, 3)]
複製代碼
itertools.combinations([1,2,3], 2)
中的第一個參數是待組合的對象,第二個參數是組合結果中的元素個數。若是把這個函數用於rmb
這個列表,就是這樣的結果:函數
>>> list(itertools.combinations(rmb, 3))
[(20, 20, 20), (20, 20, 10), (20, 20, 10), (20, 20, 10), (20, 20, 10), (20, 20, 10), (20, 20, 5), (20, 20, 5), (20, 20, 1), (20, 20, 1), (20, 20, 1), (20, 20, 1), (20, 20, 1), (20, 20, 10), (20, 20, 10), (20, 20, 10), (20, 20, 10), (20, 20, 10), (20, 20, 5), (20, 20, 5), (20, 20, 1), (20, 20, 1), (20, 20, 1), (20, 20, 1), (20, 20, 1), (20, 10, 10), (20, 10, 10), (20, 10, 10), (20, 10, 10), (20, 10, 5), (20, 10, 5), (20, 10, 1), (20, 10, 1), (20, 10, 1), (20, 10, 1), (20, 10, 1), (20, 10, 10), (20, 10, 10), (20, 10, 10), (20, 10, 5), (20, 10, 5), (20, 10, 1), (20, 10, 1), (20, 10, 1), (20, 10, 1), (20, 10, 1), (20, 10, 10), (20, 10, 10), (20, 10, 5), (20, 10, 5), (20, 10, 1), (20, 10, 1), (20, 10, 1), (20, 10, 1), (20, 10, 1), (20, 10, 10), (20, 10, 5), (20, 10, 5), (20, 10, 1), (20, 10, 1), (20, 10, 1), (20, 10, 1), (20, 10, 1), (20, 10, 5), (20, 10, 5), (20, 10, 1), (20, 10, 1), (20, 10, 1), (20, 10, 1), (20, 10, 1), (20, 5, 5), (20, 5, 1), (20, 5, 1), (20, 5, 1), (20, 5, 1), (20, 5, 1), (20, 5, 1), (20, 5, 1), (20, 5, 1), (20, 5, 1), (20, 5, 1), (20, 1, 1), (20, 1, 1), (20, 1, 1), (20, 1, 1), (20, 1, 1), (20, 1, 1), (20, 1, 1), (20, 1, 1), (20, 1, 1), (20, 1, 1), (20, 20, 10), (20, 20, 10), (20, 20, 10), (20, 20, 10), (20, 20, 10), (20, 20, 5), (20, 20, 5), (20, 20, 1), (20, 20, 1), (20, 20, 1), (20, 20, 1), (20, 20, 1), (20, 10, 10), (20, 10, 10), (20, 10, 10), (20, 10, 10), (20, 10, 5), (20, 10, 5), (20, 10, 1), (20, 10, 1), (20, 10, 1), (20, 10, 1), (20, 10, 1), (20, 10, 10), (20, 10, 10), (20, 10, 10), (20, 10, 5), (20, 10, 5), (20, 10, 1), (20, 10, 1), (20, 10, 1), (20, 10, 1), (20, 10, 1), (20, 10, 10), (20, 10, 10), (20, 10, 5), (20, 10, 5), (20, 10, 1), (20, 10, 1), (20, 10, 1), (20, 10, 1), (20, 10, 1), (20, 10, 10), (20, 10, 5), (20, 10, 5), (20, 10, 1), (20, 10, 1), (20, 10, 1), (20, 10, 1), (20, 10, 1), (20, 10, 5), (20, 10, 5), (20, 10, 1), (20, 10, 1), (20, 10, 1), (20, 10, 1), (20, 10, 1), (20, 5, 5), (20, 5, 1), (20, 5, 1), (20, 5, 1), (20, 5, 1), (20, 5, 1), (20, 5, 1), (20, 5, 1), (20, 5, 1), (20, 5, 1), (20, 5, 1), (20, 1, 1), (20, 1, 1), (20, 1, 1), (20, 1, 1), (20, 1, 1), (20, 1, 1), (20, 1, 1), (20, 1, 1), (20, 1, 1), (20, 1, 1), (20, 10, 10), (20, 10, 10), (20, 10, 10), (20, 10, 10), (20, 10, 5), (20, 10, 5), (20, 10, 1), (20, 10, 1), (20, 10, 1), (20, 10, 1), (20, 10, 1), (20, 10, 10), (20, 10, 10), (20, 10, 10), (20, 10, 5), (20, 10, 5), (20, 10, 1), (20, 10, 1), (20, 10, 1), (20, 10, 1), (20, 10, 1), (20, 10, 10), (20, 10, 10), (20, 10, 5), (20, 10, 5), (20, 10, 1), (20, 10, 1), (20, 10, 1), (20, 10, 1), (20, 10, 1), (20, 10, 10), (20, 10, 5), (20, 10, 5), (20, 10, 1), (20, 10, 1), (20, 10, 1), (20, 10, 1), (20, 10, 1), (20, 10, 5), (20, 10, 5), (20, 10, 1), (20, 10, 1), (20, 10, 1), (20, 10, 1), (20, 10, 1), (20, 5, 5), (20, 5, 1), (20, 5, 1), (20, 5, 1), (20, 5, 1), (20, 5, 1), (20, 5, 1), (20, 5, 1), (20, 5, 1), (20, 5, 1), (20, 5, 1), (20, 1, 1), (20, 1, 1), (20, 1, 1), (20, 1, 1), (20, 1, 1), (20, 1, 1), (20, 1, 1), (20, 1, 1), (20, 1, 1), (20, 1, 1), (10, 10, 10), (10, 10, 10), (10, 10, 10), (10, 10, 5), (10, 10, 5), (10, 10, 1), (10, 10, 1), (10, 10, 1), (10, 10, 1), (10, 10, 1), (10, 10, 10), (10, 10, 10), (10, 10, 5), (10, 10, 5), (10, 10, 1), (10, 10, 1), (10, 10, 1), (10, 10, 1), (10, 10, 1), (10, 10, 10), (10, 10, 5), (10, 10, 5), (10, 10, 1), (10, 10, 1), (10, 10, 1), (10, 10, 1), (10, 10, 1), (10, 10, 5), (10, 10, 5), (10, 10, 1), (10, 10, 1), (10, 10, 1), (10, 10, 1), (10, 10, 1), (10, 5, 5), (10, 5, 1), (10, 5, 1), (10, 5, 1), (10, 5, 1), (10, 5, 1), (10, 5, 1), (10, 5, 1), (10, 5, 1), (10, 5, 1), (10, 5, 1), (10, 1, 1), (10, 1, 1), (10, 1, 1), (10, 1, 1), (10, 1, 1), (10, 1, 1), (10, 1, 1), (10, 1, 1), (10, 1, 1), (10, 1, 1), (10, 10, 10), (10, 10, 10), (10, 10, 5), (10, 10, 5), (10, 10, 1), (10, 10, 1), (10, 10, 1), (10, 10, 1), (10, 10, 1), (10, 10, 10), (10, 10, 5), (10, 10, 5), (10, 10, 1), (10, 10, 1), (10, 10, 1), (10, 10, 1), (10, 10, 1), (10, 10, 5), (10, 10, 5), (10, 10, 1), (10, 10, 1), (10, 10, 1), (10, 10, 1), (10, 10, 1), (10, 5, 5), (10, 5, 1), (10, 5, 1), (10, 5, 1), (10, 5, 1), (10, 5, 1), (10, 5, 1), (10, 5, 1), (10, 5, 1), (10, 5, 1), (10, 5, 1), (10, 1, 1), (10, 1, 1), (10, 1, 1), (10, 1, 1), (10, 1, 1), (10, 1, 1), (10, 1, 1), (10, 1, 1), (10, 1, 1), (10, 1, 1), (10, 10, 10), (10, 10, 5), (10, 10, 5), (10, 10, 1), (10, 10, 1), (10, 10, 1), (10, 10, 1), (10, 10, 1), (10, 10, 5), (10, 10, 5), (10, 10, 1), (10, 10, 1), (10, 10, 1), (10, 10, 1), (10, 10, 1), (10, 5, 5), (10, 5, 1), (10, 5, 1), (10, 5, 1), (10, 5, 1), (10, 5, 1), (10, 5, 1), (10, 5, 1), (10, 5, 1), (10, 5, 1), (10, 5, 1), (10, 1, 1), (10, 1, 1), (10, 1, 1), (10, 1, 1), (10, 1, 1), (10, 1, 1), (10, 1, 1), (10, 1, 1), (10, 1, 1), (10, 1, 1), (10, 10, 5), (10, 10, 5), (10, 10, 1), (10, 10, 1), (10, 10, 1), (10, 10, 1), (10, 10, 1), (10, 5, 5), (10, 5, 1), (10, 5, 1), (10, 5, 1), (10, 5, 1), (10, 5, 1), (10, 5, 1), (10, 5, 1), (10, 5, 1), (10, 5, 1), (10, 5, 1), (10, 1, 1), (10, 1, 1), (10, 1, 1), (10, 1, 1), (10, 1, 1), (10, 1, 1), (10, 1, 1), (10, 1, 1), (10, 1, 1), (10, 1, 1), (10, 5, 5), (10, 5, 1), (10, 5, 1), (10, 5, 1), (10, 5, 1), (10, 5, 1), (10, 5, 1), (10, 5, 1), (10, 5, 1), (10, 5, 1), (10, 5, 1), (10, 1, 1), (10, 1, 1), (10, 1, 1), (10, 1, 1), (10, 1, 1), (10, 1, 1), (10, 1, 1), (10, 1, 1), (10, 1, 1), (10, 1, 1), (5, 5, 1), (5, 5, 1), (5, 5, 1), (5, 5, 1), (5, 5, 1), (5, 1, 1), (5, 1, 1), (5, 1, 1), (5, 1, 1), (5, 1, 1), (5, 1, 1), (5, 1, 1), (5, 1, 1), (5, 1, 1), (5, 1, 1), (5, 1, 1), (5, 1, 1), (5, 1, 1), (5, 1, 1), (5, 1, 1), (5, 1, 1), (5, 1, 1), (5, 1, 1), (5, 1, 1), (5, 1, 1), (1, 1, 1), (1, 1, 1), (1, 1, 1), (1, 1, 1), (1, 1, 1), (1, 1, 1), (1, 1, 1), (1, 1, 1), (1, 1, 1), (1, 1, 1)]
複製代碼
而後,就從這些組合中挑出那些可以符合某些條件的,就是本問題的解了。不過,上面僅僅以組合3個元素爲例。實際上多是從1開始一直到rmb列表的長度爲止。固然,咱們一眼就能夠看出來,只取得1個、2個,不管怎麼組合,也達不到100。或者說,先從咱們的分析角度出發考慮,最小的可能組合應該是(20, 20, 20, 10, 10, 10, 10),即至少要有7張鈔票才能組合出100元。post
>>> makes_100 = []
>>> for n in range(7, len(rmb)+1):
... for comb in itertools.combinations(rmb, n):
... if sum(comb) == 100:
... makes_100.append(comb)
...
複製代碼
注意,這時候獲得的makes_100
,並非最終結果。由於裏面有不少重複的,之因此這樣,是由於rmb
中的元素就有重複的。因此,還要進一步去重。學習
>>> set(makes_100)
{(20, 20, 20, 10, 10, 10, 5, 5), (20, 20, 20, 10, 10, 10, 5, 1, 1, 1, 1, 1), (20, 20, 10, 10, 10, 10, 10, 5, 5), (20, 20, 10, 10, 10, 10, 10, 5, 1, 1, 1, 1, 1), (20, 20, 20, 10, 10, 10, 10)}
複製代碼
獲得了最終結果,能夠有5中組合方案,實現100元的目標。測試
按照Python的編程習慣,上面的程序能夠寫成:
>>> makes_100 = [comb for n in range(7, len(rmb)+1) for comb in itertools.combinations(rmb, n) if sum(comb)==100]
>>> set(makes_100)
{(20, 20, 20, 10, 10, 10, 5, 5), (20, 20, 20, 10, 10, 10, 5, 1, 1, 1, 1, 1), (20, 20, 10, 10, 10, 10, 10, 5, 5), (20, 20, 10, 10, 10, 10, 10, 5, 1, 1, 1, 1, 1), (20, 20, 20, 10, 10, 10, 10)}
複製代碼
是否是顯示出了Python的簡潔。
這個問題還能夠這樣變化一下:
題目:有若干張面額爲50、20、十、五、1元的RMB,問有多少種方式,可以組合出100元。
問題變化了,所用函數也要變化。由於combinations()
的做用是從已知數據集中選擇若干個元素組合,可是,並不會實現如本題中所要求的。例如問題中最直接的一種組合就是100個1元的。而這種組合是combinations
函數沒法提供的。
itertools中還有另一個函數,它能幫助咱們解決這個問題。
>>> list(itertools.combinations_with_replacement([1, 2], 2))
[(1, 1), (1, 2), (2, 2)]
>>> list(itertools.combinations([1,2], 2)) #注意比較二者的結果
[(1, 2)]
複製代碼
從上述操做中,就能夠看出combinations_with_replacement
與combinations
的區別了。combinations_with_replacement
就是用來解決這個問題的函數。
>>> rmbs = [50, 20, 10, 5, 1]
>>> makes_100_2 = []
>>> for n in range(2, 101):
... for comb in itertools.combinations_with_replacement(rmbs, n):
... if sum(comb) == 100:
... makes_100_2.append(comb)
...
#開始執行上面的程序,須要一段時間,你能夠此時完成以下活動:喝水、上廁所、想一想人生。
>>> len(makes_100_2)
343
複製代碼
儘管上面的程序比較耗費時間,畢竟可以用暴力的方式解決了上述問題——須要執行96,560,645此組合。
對於「錢幣組合」問題,是算法中的一個經典問題,在這裏咱們其實使用了「暴力窮舉」的方法,不算是最好的方法,只是演示itertools中的兩個函數的使用方法。
在itertools中,除了提供上述實現組合的函數以外,還提供了實現排列功能的函數:permutations()
>>> import itertools
>>> list(itertools.permutations(['a', 'b', 'c']))
[('a', 'b', 'c'), ('a', 'c', 'b'), ('b', 'a', 'c'), ('b', 'c', 'a'), ('c', 'a', 'b'), ('c', 'b', 'a')]
複製代碼
itertools
模塊中,還提供了生成包含無限項的迭代器對象——注意,雖說包含了無限項,可是由於它們並無被讀入內存,因此不用擔憂。這就是迭代器的優點。
從數學的角度來講,奇數和偶數,都有無限項。那麼,在Python中如何表示這樣的集合?這就要建立一個迭代器——其實下面所建立是生成器,生成器也是迭代器。
>>> def evens():
... n = 0
... while True:
... yield n
... n += 2
...
複製代碼
這個函數,就可以生成偶數集合,它實際上是返回了一個生成器對象——《跟老齊學Python:輕鬆入門》中有對生成器的詳細講述。
>>> e = evens() #①
>>> list(next(e) for _ in range(6)) #②
[0, 2, 4, 6, 8, 10]
複製代碼
①獲得了偶數集合的生成器對象,在②中,經過循環語句,生成6個偶數,這時候,這些偶數才被讀入內存。
一樣的道理,奇數的生成器函數,也是相似的。
>>> def odds():
... n = 1
... while True:
... yield n
... n += 2
...
複製代碼
在itertools中,還提供了另一個函數,實現對某種特定規律的數的集合的迭代器對象的建立。
例如天然數:
>>> counter = itertools.count()
>>> list(next(counter) for _ in range(9))
[0, 1, 2, 3, 4, 5, 6, 7, 8]
複製代碼
跟前面偶數和奇數集合的生成器對象相似,這裏獲得的counter
對象也是隻有在讀取數值的時候,才根據設定的個數將相應的天然數讀入內存——0是否是天然數?這裏姑且認爲是吧。
若是使用help()
查看這個函數的幫助信息,能夠看到這種表述:
count(start=0, step=1) --> count object
在counter = itertools.count()
中沒有設置參數step
的值,意味着採用了默認的step=1
,而且另一個參數start=0
。從這兩個參數的設置可知,咱們能夠設置從某個數值開始直到無限大,且步驟爲某個值的天然數集合。那麼前面演示過的偶數和奇數集合,就有另一種方式實現了。
#偶數的迭代器
>>> evens = itertools.count(step=2)
>>> list(next(evens) for _ in range(9))
[0, 2, 4, 6, 8, 10, 12, 14, 16]
#奇數的迭代器
>>> odds = itertools.count(start=1, step=2)
>>> list(next(odds) for _ in range(9))
[1, 3, 5, 7, 9, 11, 13, 15, 17]
複製代碼
其實,不只能夠針對天然數,還能夠針對浮點數進行操做。注意:要Python3.1以後的版本。
>>> counter_float = itertools.count(start=0.5, step=0.25)
>>> list(next(counter_float) for _ in range(9))
[0.5, 0.75, 1.0, 1.25, 1.5, 1.75, 2.0, 2.25, 2.5]
複製代碼
負數也能夠。
>>> counter_negative = itertools.count(start=-1, step=-0.3)
>>> list(next(counter_negative) for _ in range(9))
[-1, -1.3, -1.6, -1.9000000000000001, -2.2, -2.5, -2.8, -3.0999999999999996, -3.3999999999999995]
複製代碼
上面的結果中出現了一些特別的浮點數,這是什麼緣由——這個問題我早就在《跟老齊學Python:輕鬆入門》一書中講得很清楚了,欲知詳情,請參考。
經過上述操做,itertools.count()
和range()
的區別也很是明顯了。在針對天然數部分,二者功能相似,而且range
也返回迭代器對象。可是,range
返回的對象都是在有限範圍以內,count
則是無限的。另外的差異在於,count
不只能夠處理天然數範圍的數字,還能處理負數、浮點數。
前面提到了生成器,雖說有無限個數的集合有不少,而且前面列舉了幾個,多數經過itertools.count
能夠實現,對於它不能實現的,還能夠寫一個生成器(迭代器)函數來實現。下面這個例子,是在這時候必定要舉出來的,那就是編寫斐波那契數列的生成器函數——關於斐波那契數列的其它更多寫法,推薦閱讀拙做《跟老齊學Python:輕鬆入門》。
>>> def fibs():
... a, b = 0, 1
... while True:
... yield a
... a, b = b, a + b
...
複製代碼
無限多項的斐波那契數列,就能夠經過這個生成器函數得到。
>>> f = fibs()
>>> list(next(f) for _ in range(9))
[0, 1, 1, 2, 3, 5, 8, 13, 21]
複製代碼
獲得了9項斐波那契數列。
對於無限項的集合,要取出其部分有限個數的數字,除了用上面的循環方式next(f) for _ in range(9)
以外,還可使用itertools中的一個函數。例如:
>>> counter = itertools.count()
>>> list(itertools.islice(counter, 9))
[0, 1, 2, 3, 4, 5, 6, 7, 8]
複製代碼
itertools真不辜負它的名字。
在數學中,遞歸或者遞推關係( recurrence relation)在數列中常見,好比等差數列、等比數列等,都是遞歸關係式。從這點上來看,前面使用itertools.count
獲得的數列,都是一種遞歸關係的數列。
前面列舉的斐波那契數列數列,其實也是一些具備遞歸關係的數。或者說,也能夠經過「遞歸」實現斐波那契數列函數。
不過,還有一種遞歸關係的數列,不能用itertools.count
獲得,那就是全部的數字都是同樣的,好比都是由1組成的,或者是由某幾個數字組成,好比都是由1,3,5組成。
對此,itertools模塊提供了別的函數。
>>> ones = itertools.repeat(1)
>>> list(itertools.islice(ones, 9))
[1, 1, 1, 1, 1, 1, 1, 1, 1]
複製代碼
itertools.repeat
函數專門生成由某個數字組成的無限長數列。注意:其參數除了數字以外,還能夠是其它對象,好比字符串、列表等。
固然,建立有限的也能夠,那就增長一個標識元素個數的參數。
>>> five_ones = itertools.repeat(1, 5)
>>> [i for i in five_ones]
[1, 1, 1, 1, 1]
複製代碼
另一種狀況,則可使用itertools.cycle
函數實現。先不用看示例演示,看函數名稱,就能猜想到結果應該是什麼樣子的——[1,3,5,1,3,5,...],應該如此,不然就不能是cycle了。
>>> otf = itertools.cycle([1, 3, 5])
>>> list(itertools.islice(otf, 9))
[1, 3, 5, 1, 3, 5, 1, 3, 5, 1, 3, 5, 1, 3, 5]
複製代碼
上面的演示中,只取出數列的前15項看看,果真不出所料。
以上演示的都是遞推關係中的特例,下面要用數學形式說明一下遞推關係中的所謂一階和二階遞推關係。
數列中相鄰兩項符合此式子的,就是一階遞歸關係數列。
還有另一種遞推關係,它表示的是某一項和相鄰的兩項之間的關係,好比前面的斐波那契數列,第三項等於前兩項和。這種關係的通項式能夠表示爲:
對於此通項式,若是P=Q=1,且h=0,那麼它就稱爲了斐波那契數列。
若是根據上述通項式,可知前面用count
、repeat
、cycle
等所實現的數列,都是特殊數列。有沒有比它們更通常的呢?
必須有。
>>> list(range(9))
[0, 1, 2, 3, 4, 5, 6, 7, 8]
>>> list(itertools.accumulate(range(9)))
[0, 1, 3, 6, 10, 15, 21, 28, 36]
複製代碼
注意,上面實現的不是斐波那契數列,而是按照下圖所示方式獲得了一個新的數列。
看到這個函數,你應該想起來另一個函數,若是沒有想起來,說明你的基礎知識尚有發展的空間。
>>> import functools
>>> functools.reduce(lambda x,y :x+y, range(9))
36
複製代碼
與functools.reduce
相比,itertools.accumulate
返回的不是一個數,而是數列,若是嚴格地說,是由數列元素做爲潛在對象的迭代器對象。
已經看到了itertools.accumulate(range(9))
的結果了。而對於accumulate()
而言,其完整的參數形式是這樣的:
accumulate(iterable[, func]) --> accumulate object
默認狀況,後面的func是加法函數。因此,像下面的方式與前述示例是同樣的。
>>> import operator
>>> list(itertools.accumulate(range(9), operator.add))
[0, 1, 3, 6, 10, 15, 21, 28, 36]
複製代碼
根據這個原理,就能夠對不少數列進行操做,依據函數關係獲得另一個數列了。下面依次列舉若干示例。
>>> list(itertools.accumulate([9, 21, 17, 5, 11, 12, 2, 6], min))
[9, 9, 9, 5, 5, 5, 2, 2]
>>> list(itertools.accumulate([1, 2, 3, 4, 5], lambda x, y: (x + y) / 2))
[1, 1.5, 2.25, 3.125, 4.0625]
複製代碼
還記得前面的一階遞歸關係式嗎?若是寫一個函數,實現那個關係式?
>>> def first_order(p, h, initial_val):
... return itertools.accumulate(itertools.repeat(initial_val), lambda a, _: p*a + h)
...
複製代碼
這個函數就是一階遞歸通項式,經過它可以獲得一階遞歸的數列。
在函數中綜合運用了學習過的兩個函數。下面測試一下。
>>> evens = first_order(p=1, h=2, initial_val=0) #獲得偶數數列
>>> list(itertools.islice(evens, 5))
[0, 2, 4, 6, 8]
#能被3整除的數
>>> count_by_threes = first_order(p=1, h=3, initial_val=0)
>>> list(itertools.islice(count_by_threes, 5))
[0, 3, 6, 9, 12]
#由1,-1組成的數列
>>> alternating_ones = first_order(p=-1, h=0, initial_val=1)
>>> list(itertools.islice(alternating_ones, 5))
[1, -1, 1, -1, 1]
#都是1組成的數列
>>> all_ones = first_order(p=1, h=0, initial_val=1)
>>> list(itertools.islice(all_ones, 5))
[1, 1, 1, 1, 1]
複製代碼
經過上面的示例不難看出,當獲得了一階遞推關係式通項以後,全部一階遞推數列,均可以通吃了。
厲害。
一樣,也能夠寫出二階遞歸關係的通項函數。
>>> def second_order(p, q, h, initial_val):
... itermediate = itertools.accumulate(
itertools.repeat(initial_val),
lambda a, _:(a[1], p*a[1] + q*a[0] + h))
... return map(lambda x: x[0], itermediate)
...
複製代碼
二階遞歸關係通項函數貌似複雜。由於二階遞推通項式中,初始值是前兩項,因此,若是直接實現數學函數值,返回的是兩個值,就不得不使用map
函數,把其中的值一個一個取出來。
寫好以後,用它來生成斐波那契數列。
>>> fibs = second_order(p=1, q=1, h=0, initial_val=(0, 1))
>>> list(itertools.islice(fibs, 9))
[0, 1, 1, 2, 3, 5, 8, 13, 21]
複製代碼
對照前面斐波那契數列數列。感覺到二階遞推關係的通項函數魅力了吧。
典型的二階遞推關係除了斐波那契數列以外,還有佩爾數(Pell number)和盧卡斯數(Lucas number)。
Pell number:
Lucas Number:
下面就用上面的二階遞推關係通項實現這兩個數列。
>>> pell = second_order(p=2, q=1, h=0, initial_val=(0, 1))
>>> list(itertools.islice(pell, 6))
[0, 1, 2, 5, 12, 29]
>>> lucas = second_order(p=1, q=1, h=0, initial_val=(2, 1))
>>> list(itertools.islice(lucas, 6))
[2, 1, 3, 4, 7, 11]
複製代碼
迭代器的確是個好東西,itertools還有更多好內容。
系列圖書: