斐波那契数列的具体实现方式并不难,但是利用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