Fibonacci數(shù)列高效解法大全及時(shí)間復(fù)雜度分析 連載【3】

在數(shù)學(xué)上,斐波那契數(shù)列是以遞歸的方法來定義

……續(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ù)……

Fibonacci數(shù)列高效解法大全及時(shí)間復(fù)雜度分析 連載【4】

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容