斐波那契數列的具體實現方式并不難,但是利用python的諸多特性它會有許多便利的實現;便利一方面是對于大規模計算時候的運行時間短,一方面是占用內存少。同時我在實現過程中遇到的一些小問題。
計時
為了計算代碼遞歸運行的時間,所以順便介紹一下
我選擇使用time.process_time()方法
該方法能夠返回系統和CPU運行當前進程的時間,不過有個問題就是所返回時間的參考點(大概是指類似計時開始的時間)是未知的,因此返回的時間并不能作為一個真正的參考依據,只有兩個不同進程的process_time()返回值之間的差值,才是有意義的。
差值是有意義的,那么對于我的使用已經足以了。
import time
t = time.process_time()
遞歸深度
以下是我自己寫的兩種方式獲得斐波那契數列的某個值,肯定不會是最優的,但已經能夠反映迭代與遞歸之間的區別了。
import time
class Fibo:
def get_certain_rec(self, n): # 遞歸獲取斐波那契數列第n+1個值
if n == 0:
return 0
elif n == 1:
return 1
else:
return self.get_certain_rec(n - 1) + self.get_certain_rec(n - 2)
@staticmethod
def get_certain(n): # 迭代方式
fibo_0 = 0
fibo_1 = 1
if n == 0:
return 0
elif n == 1:
return 1
else:
for i in range(2, n+1):
fibo_n = fibo_0 + fibo_1
fibo_0, fibo_1 = fibo_1, fibo_n
return fibo_n
t = time.process_time()
f = Fibo()
f.get_certain(1000)
print(t)
>>>0.0625
t = time.process_time()
f = Fibo()
f.get_certain_rec(1000)
print(t)
>>>RecursionError: maximum recursion depth exceeded in comparison
面對大規模問題的時候,遞歸方法直接被拋出遞歸深度達到最大,或許能夠通過改變默認遞歸深度解決該問題,但遠不如使用迭代方法來處理。
并且,我們要知道,凡是能夠寫成遞歸的方法,就一定也有迭代的解決方法。
t = time.process_time()
f = Fibo()
f.get_certain(100)
print(t)
>>>0.046875
t = time.process_time()
f = Fibo()
f.get_certain_rec(100)
print(t)
>>>
# 雖然沒出問題。。。。不過跑了很久都還沒出結果
規模還沒有特別大,遞歸就已經跑不動了;而迭代法的時間幾乎沒有變化,可能是常數級的運行時間。
個人認為,用遞歸的思想思考如何解決問題,用迭代的方法來實際解決問題。
修改遞歸深度
import sys
sys.setrecursionlimit(1000000000)
yield
直接print一個生成器返回的是<generator object <genexpr> at 0x00000255F11DA200>,它在內存中的位置。
需要使用next()方法才能夠獲取生成器的值;
生成器generator也是一個可迭代對象,可以被for循環調用;
yield只能在函數中使用;
yield是定義生成器的方法之一;定義的函數中若有yield語句,那么它就是一個generator;
調用yield b會使得函數中斷并返回b;循環的話則會在yield處中斷并返回一個值,再次執行時從上次調用yield的地方繼續進程。
定制類
來看看廖老師對于斐波那契數列的寫法
廖雪峰的教程,關于定制類
class Fib(object):
def __init__(self):
self.a, self.b = 0, 1 # 初始化兩個計數器a,b
def __iter__(self):
return self # 實例本身就是迭代對象,故返回自己
def __next__(self):
self.a, self.b = self.b, self.a + self.b # 計算下一個值
if self.a > 100000: # 退出循環的條件
raise StopIteration()
return self.a # 返回下一個值
# 使用了諸如__iter__、__next__等實現了類似iterable的方法
這些以雙下劃線開頭和結尾的特殊類方法,是會被python的函數調用的,因此定義這些特殊方法,可以實現我們對于一些方法的定制,比如為print函數輸出的內容添加一種固定格式。
Method xxx may be 'static'
這是用pycharm寫代碼時候它提示的一個小問題,認為該方法是個靜態方法。
當你在類中定義一個與類的屬性無關的方法的時候,便可以認為是在定義一個靜態方法。
@staticmethod
def```
# 這就定義了一個靜態方法
除此之外還有一個類定義@classmethod
其作用在于使得該方法能夠直接被方法調用而無需創建一個實例,不過不經常被使用。
最后附上自己寫的斐波那契數列的獲得方法
class Fibo:
@staticmethod
def get_all(n): # 用于獲取斐波那契數列前n+1個值,也可以通過列表獲取任意值
seq = list()
for i in range(n+1):
if len(seq) == 0:
seq.append(0)
elif len(seq) == 1:
seq.append(1)
else:
seq.append(seq[i-1] + seq[i-2])
return seq
def get_certain_rec(self, n): # 遞歸獲取斐波那契數列第n+1個值
if n == 0:
return 0
elif n == 1:
return 1
else:
return self.get_certain_rec(n - 1) + self.get_certain_rec(n - 2)
@staticmethod
def get_certain(n): # 迭代方式
fibo_0 = 0
fibo_1 = 1
if n == 0:
return 0
elif n == 1:
return 1
else:
for i in range(2, n+1):
fibo_n = fibo_0 + fibo_1
fibo_0, fibo_1 = fibo_1, fibo_n
return fibo_n