斐波那契數列:1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, ...
由列昂納多·斐波那契(Leonardoda Fibonacci)以兔子繁殖爲例子而引入,故又稱爲「兔子數列」。python
這個數列從第3項開始,每一項都等於前兩項之和。
若是設F(n)爲該數列的第n項(n∈N*),那麼這句話能夠寫成以下形式::F(n)=F(n-1)+F(n-2)app
經過這個公式,容易想到第一個遞歸版本函數
def f(n): """遞歸版本 1""" return 1 if n <= 2 else f(n - 1) + f(n - 2)
遞歸版本 1 簡單明瞭,可是存在不少冗餘運算,致使運行效果堪憂。spa
def f(n): """遞歸版本 2""" cache = {1: 1, 2: 1} s = lambda n: cache.get(n) or cache.setdefault(n, s(n - 1) + s(n - 2)) return s(n)
遞歸版本 2 經過字典避免了冗餘元算,可是存在大量函數調用的開銷。code
def f(n, a=1, b=1): """遞歸版本 3""" return a if n <= 1 else f(n - 1, b, a + b)
遞歸版本 3 使用傳參及默認參數,減小冗餘元算的同時也減小了函數調用。blog
有遞歸版本,怎麼少的了迭代版本遞歸
def f(n): """迭代版本 1""" lst = [1, 1] for i in range(2, n): lst.append(lst[i - 2] + lst[i - 1]) return lst[-1]
迭代版本 1 經過一個列表存儲了每次運算的結果,也很直觀ip
def f(n): """迭代版本 2""" dct = {1: 1, 2: 1} for i in range(3, n + 1): dct[i] = dct[i - 1] + dct[i - 2] return dct[n]
迭代版本 2 和迭代版本 1 相似,使用字典而不是列表存儲,佔用空間更大ci
def f(n): """迭代版本 3""" a, b = 1, 1 for _ in range(n - 2): a, b = b, a + b return b
迭代版本 3 較前面2個迭代版本,經過交換技巧,節省了空間,效率有了提高get
這個數列有通項公式,因此還能夠來個公式版本
def f(n): """公式版本""" sqf = math.sqrt(5) return int(sqf / 5 * (math.pow(((1 + sqf) / 2), n) - math.pow(((1 - sqf) / 2), n)))
python遞歸版本從新設置遞歸層數限制
import sys sys.setrecursionlimit(1000000)
公式版本n較大時會引起溢出
OverflowError: math range error
又想了個遞歸版本,利用了python中一個使人詬病的寫法(可變類型做爲默認參數)提高效率
def f(n, cache={1: 1, 2: 1}): """遞歸版本徹底體""" return cache.get(n) or cache.setdefault(n, f(n - 2, cache) + f(n - 1, cache))
n=1000 時,千次執行時間(s)