functiils.lru_cache縮短遞歸時間

力扣上看到一道題:算法

假設你正在爬樓梯。須要 n 階你才能到達樓頂。編程

每次你能夠爬 1 或 2 個臺階。你有多少種不一樣的方法能夠爬到樓頂呢?緩存

注意:給定 n 是一個正整數。函數

 

  使用普通遞歸解決,超出時間限制:post

  添加lru_cache以後,縮短了時間性能

 

lru_cache的解釋以下:測試

 

如今你已經看到了如何本身實現一個memoization函數,我會告訴你,你可使用Python的functools.lru_cache裝飾器來得到相同的結果,以增長方便性。優化

我最喜歡Python的緣由之一就是它的語法的簡潔和美麗與它的哲學的美麗和簡單性並行不悖。Python被稱做「內置電池(batteries included)」,這意味着Python捆綁了大量經常使用的庫和模塊,這些只須要一個import聲明!spa

我發現functools.lru_cache是一個很好的例子。lru_cache裝飾器是Python標準庫實現的易於使用的記憶功能。一旦你認識到何時使用lru_cache,你只需幾行代碼就能夠快速加快你的應用程序。blog

咱們再來看看咱們的斐波那契數列示例。這一次,我會告訴你如何使用functools.lru_cache裝飾器添加記憶:

用functools.lru_cache實現Python的Memoization

請注意我給lru_cache傳遞的maxsize參數是同時來限制存儲在緩存中的項目數量。

我再一次使用該timeit模塊來運行一個簡單的基準測試,以便了解這種優化對性能的影響:

用functools.lru_cache實現Python的Memoization

您可能想知道,爲何咱們此次可以以更快的速度得到第一次運行的結果。第一次運行緩存不該該是 「凍結」的嗎?

不一樣的是,在這個例子中,我在函數定義的時候使用了@lru_cache裝飾器。這意味着此次遞歸調用fibonacci也在緩存中查找。

經過@lru_cache裝飾器裝飾fibonacci函數,我基本上把它變成了一個動態編程解決方案,每一個子問題只須要存儲一次子問題解決方案,並在下次嘗試解決相同問題時從緩存中查找結果。

這只是一個例子——但我相信你開始可以看到使用memoization裝飾器的美麗和強大,而且開始意識到實現一個動態算法可以帶來多大的好處。

 

爲何你應該喜歡 functools.lru_cache

通常來講,由functools.lru_cache實現的Python的memoization比咱們的專用memoize函數更全面,就像你在CPython源代碼中看到的同樣。

例如,它提供了一個方便的功能,容許您使用cache_info方法檢索緩存統計信息:

用functools.lru_cache實現Python的Memoization

再一次,正如你在CacheInfo輸出中看到的那樣,Python的lru_cache記住了遞歸調用fibonacci。當咱們查看memoized函數的緩存信息時,您會發現爲何它在第一次運行時比咱們的版本更快——緩存命中了34次。

正如我以前所暗示的,functools.lru_cache還容許您使用maxsize參數限制緩存結果的數量。經過設置maxsize=None你能夠強制緩存是無界的,我一般會反對這樣作。

還有一個typed布爾參數能夠設置爲True告訴緩存,不一樣類型的函數參數應該分開緩存。例如,fibonacci(35)和fibonacci(35.0)將被視爲產生大相徑庭結果的不一樣調用。

另外一個有用的功能是能夠隨時使用cache_clear方法重置結果緩存:

用functools.lru_cache實現Python的Memoization

若是您想了解更多關於使用lru_cache裝飾器的複雜信息,我建議您參考Python標準庫文檔。

總之,你永遠不須要推出本身的記憶功能。Python的內置方法lru_cache是易於使用的,更全面和通過測試的。

 

緩存注意事項——什麼是能夠被記憶的?

理想狀況下,您將要記憶肯定性的函數。

用functools.lru_cache實現Python的Memoization

這deterministic_adder是一個肯定性函數,由於它老是會爲相同的一對參數返回相同的結果。例如,若是您將2和3傳入該函數,它將始終返回5。

將此行爲與如下非肯定性函數進行比較:

用functools.lru_cache實現Python的Memoization

這個函數是不肯定的,由於它對於一個給定的輸入的輸出會根據星期幾而變化:若是你在星期一運行這個函數,緩存將在一週中的任何一天返回陳舊的數據。

通常來講,我發現任何更新記錄或返回隨時間變化的信息的函數對於記憶都是很差的選擇。

或者,正如Phil Karlton所說:

計算機科學只有兩件難事:緩存失效和命名事物。

——Phil Karlton

 

Python中的記憶:快速總結

在這篇Python教程中,您看到了memoization如何經過基於提供給它的參數緩存輸出來優化函數。

一旦你記憶一個函數,它將只爲你調用的每一組參數計算一次輸出。第一次調用以後的每次調用都將快速從緩存中檢索出來。

您看到了如何從頭開始編寫本身的memoization裝飾器,以及爲何在生產代碼中您可能想要用Python內置的lru_cache:

記憶是一種軟件優化技術,它根據參數存儲返回函數調用的結果。

若是你的代碼符合某個標準,memoization能夠是一個很好的方法來加快你的應用程序。

您能夠從Python標準庫中導入一個全面的memoization函數,functools模塊中的lru_cache。

相關文章
相關標籤/搜索