……續(xù)上回Fibonacci數(shù)列高效解法大全及時(shí)間復(fù)雜度分析 連載【2】
6.? 非尾遞歸的實(shí)用化方案
如之前所說密似,斐波那契數(shù)列的典型遞歸解法時(shí)間復(fù)雜度為O(1.618 ^ n)吗垮,耗時(shí)指數(shù)式快速上升,沒有實(shí)用價(jià)值
仔細(xì)看遞歸調(diào)用過程會(huì)發(fā)現(xiàn)织堂,Calculate_Fibonacci_sequence(n-1) + Calculate_Fibonacci_sequence(n-2)叠艳,這二元遞歸過程會(huì)有很多重復(fù)性中間結(jié)果
那么在遞歸過程中把得到中間結(jié)果用字典保存起來,每當(dāng)一次遞歸調(diào)用都查字典看是否為相同參數(shù)的調(diào)用易阳,如果是就直接返回字典里保存的值
這樣算法時(shí)間復(fù)雜度就降為O(n)附较。通過一個(gè)緩存技巧,讓時(shí)間復(fù)雜度降到可用程度
開始寫出這個(gè)遞歸緩存裝飾器潦俺。Python里裝飾器應(yīng)用非常普遍拒课,在很多情境下方便的模塊化級(jí)聯(lián)
from functools import wraps
def recursive_function_cache(func: function) -> function_value:
? ? '用于緩存遞歸函數(shù)的裝飾器代碼。使用方法:在定義函數(shù)的前一行寫裝飾器“@recursive_function_cache”'
? ? cache = dict()
? ? @wraps(func)
? ? def wrapper(*args, **kwargs):
? ? ? ? parameters = (repr(args), repr(kwargs))
? ? ? ? if parameters not in cache:
? ? ? ? ? ? cache.update({parameters : func(*args, **kwargs)})
? ? ? ? return dict.get(cache, parameters)? #返回緩存的函數(shù)值
? ? return wrapper
讓我們來試一下效果事示,算第100項(xiàng)
>>> print('Fibonacci number= ', Fibonacci_sequence_02(100))
Fibonacci number= 354224848179261915075
原來卡住的情況沒有了早像,秒出結(jié)果
好,再試一下算第1200項(xiàng)
>>> print('Fibonacci number= ', Fibonacci_sequence_02(1200))
RecursionError: maximum recursion depth exceeded while calling a Python object
報(bào)棧溢出錯(cuò)誤
又是一個(gè)對(duì)于實(shí)用化的障礙肖爵。Python默認(rèn)設(shè)置的椔校空間不大,只有1000劝堪。Python官方不建議大家用遞歸冀自,盡量寫成迭代循環(huán)的揉稚。并且官方理念是默認(rèn)設(shè)不大的棧空間凡纳,如果程序有遞歸過深的問題就能在運(yùn)行時(shí)早暴露窃植,早改寫
不過有時(shí)遞歸寫法確實(shí)簡(jiǎn)潔清晰,在對(duì)效率沒有要求時(shí)還是可以用的
那么對(duì)遞歸過程出現(xiàn)溢出錯(cuò)誤荐糜,一個(gè)解決方案就是巷怜,動(dòng)態(tài)增加設(shè)置的限制值
可能會(huì)想到還是用裝飾器這個(gè)很優(yōu)雅的寫法來實(shí)施
然而,當(dāng)考慮還要在遞歸函數(shù)運(yùn)行結(jié)束后恢復(fù)recursionlimit原值
裝飾器就無法單獨(dú)簡(jiǎn)單完成這個(gè)目標(biāo)了(各位可有什么好想法暴氏,歡迎在下方留言探討^_^)
我用函數(shù)參數(shù)傳入方式來實(shí)現(xiàn)這個(gè)解決方案
import sys
def dynamic_increase_recursion_limit(func_str: string) -> eval:
? ? '加載可能溢出的遞歸函數(shù)延塑,動(dòng)態(tài)增加棧空間限制值答渔。使用方法:dynamic_increase_recursion_limit("函數(shù)(參數(shù))")'
? ? Increased_limit = 1000
? ? previous_recursion_limit = sys.getrecursionlimit()
? ? while True:?
? ? ? ? try:
? ? ? ? ? ? result = eval(func_str)
? ? ? ? ? ? sys.setrecursionlimit(previous_recursion_limit)
? ? ? ? ? ? return result
? ? ? ? except RecursionError:
? ? ? ? ? ? sys.setrecursionlimit(sys.getrecursionlimit() + Increased_limit)
來試一下效果
>>> print('Fibonacci number= ', dynamic_regulation_recursion_limit('Fibonacci_sequence_02(1200)'))
Fibonacci
number=?
54877108839480000051413673948383714443800519309123592724494953427039811201064341234954387521525390615504949092187441218246679104731442473022013980160407007017175697317900483275246652938800
運(yùn)行時(shí)間測(cè)時(shí)同前例关带,Total time: 0.012642秒
問題解決
讓沒有實(shí)用價(jià)值的斐波那契數(shù)列遞歸解法變成可用的遞歸解法
未完待續(xù)……