目录
1、方式一:使用数学公式直接实现(二分递归)第n个斐波那契数
一、斐波那契数列定义
关于斐波那契数列的定义引用百度百科:
斐波那契数列(Fibonacci sequence),又称黄金分割数列,因数学家莱昂纳多·斐波那契(Leonardoda Fibonacci)以兔子繁殖为例子而引入,故又称为“兔子数列”,指的是这样一个数列:0、1、1、2、3、5、8、13、21、34、……这个数列从第3项开始,每一项都等于前两项之和。在数学上,斐波那契数列以如下被以递推的方法定义:F(0)=0,F(1)=1, F(n)=F(n - 1)+F(n - 2)(n ≥ 2,n ∈ N*)
二、两种递归实现方式及分析
根据斐波那契数列数学定义:
F(0)=0,F(1)=1, F(n)=F(n - 1)+F(n - 2)(n ≥ 2,n ∈ N*)
如下两种使用递归计算斐波那契数列的方法(python3):
1、方式一:使用数学公式直接实现(二分递归)第n个斐波那契数
def fibonacci_1(n):
if n<=1:
return n
else:
return fibonacci_1(n-2)+fibonacci_1(n-1)
这种方式中,是基于数学定义直接实现,也是很简便的实现方法,但是,需要关注的问题是,这种方式也会直接导致方法的执行效率会非常低,会随着n越大,其效率也更低。计算第n个斐波那契数需要对该方法进行指数级别的调用。假设用Cn表示第n个斐波那契数需要调用fibonacci_1(n)的次数,有如下:
n=0 | C0=1 |
---|---|
n=1 | C1=1 |
n=2 | C2=C0+C1+1=3 |
n=3 | C3=C1+C2+1=5 |
n=4 | C4=C2+C3+1=9 |
n=5 | C5=C3+C4+1=15 |
n=6 | C6=C4+C5+1=25 |
n=7 | C7=C5+C6+1=41 |
n=8 | C8=C6+C7+1=67 |
n=9 | C9=C7+C8+1=109 |
如果一直按照这个方式进行下去,我们可以从上表中看出,n越大调用次数也越多,使得调用的总次数时n的指数级。
使用如下程序段计算50次(windows10 CPU 1C):
if __name__ == "__main__":
t1 = time.clock()
for i in range(50):
t11 = time.clock()
print("n={},fibonacci={}".format(i,fibonacci_1(i)))
t12 = time.clock()
print("cost time:{}".format(t12-t11))
t2 = time.clock()
print("sum time:{}".format(t2-t1))
截取了部分结果:n=31时,花费时间0.4s,但是当n=41时,需要时间52s,这样看来n越大,效率是很低的。
n=31,fibonacci=1346269
cost time:0.43121010000000004
n=32,fibonacci=2178309
cost time:0.714747
n=33,fibonacci=3524578
cost time:1.3300094999999998
n=34,fibonacci=5702887
cost time:1.7607852999999998
n=35,fibonacci=9227465
cost time:2.8848124000000004
n=36,fibonacci=14930352
cost time:4.900053
n=37,fibonacci=24157817
cost time:7.670326699999999
n=38,fibonacci=39088169
cost time:13.099861399999998
n=39,fibonacci=63245986
cost time:20.710187599999998
n=40,fibonacci=102334155
cost time:32.3978941
n=41,fibonacci=165580141
cost time:52.244939900000006
2、方式二:使用线性递归计算第n个斐波那契数(推荐方式)
def fibonacci_2(n):
if n<=1:
return (n,0)
else:
(a,b) = fibonacci_2(n-1)
return (a+b,a)
方式二每次只调用fibonacci_2方法一次,该方法每次返回一对连续的斐波拉契数列,而不是返回一个斐波那契数,在O(n)时间内完成整体计算。
同样计算50次,截取了部分结果如下:每次花费的时间都非常小,总共的时间也在1秒以内。
n=32,cost time:7.000000000000062e-06
n=33,cost time:7.299999999994811e-06
n=34,cost time:7.699999999999374e-06
n=35,cost time:7.900000000005125e-06
n=36,cost time:8.100000000003937e-06
n=37,cost time:8.099999999996998e-06
n=38,cost time:8.399999999998686e-06
n=39,cost time:8.500000000001562e-06
n=40,cost time:8.79999999999631e-06
n=41,cost time:8.899999999999186e-06
n=42,cost time:9.200000000000874e-06
n=43,cost time:9.500000000002562e-06
n=44,cost time:9.699999999994435e-06
n=45,cost time:9.80000000000425e-06
n=46,cost time:1.0200000000001874e-05
n=47,cost time:1.0599999999999499e-05
n=48,cost time:1.0700000000002374e-05
n=49,cost time:1.0999999999997123e-05
三、对递归的认识
使用递归时,就效率而言,效率低的递归和效率高的递归,就如同白天和黑夜,差别悬殊。对于一个实际问题,对于递归算法的设计显得尤为重要。另一个需要注意的问题是在递归误用,避免出现无限递归,会很快耗尽计算机资源,不仅仅是CPU的使用,也会因连续不断的调用创建需要的内存资源。
四、其他概念
1、线性递归
每个调用中至多执行调用一个新的递归调用
2、二路递归
一个递归函数执行调用两个递归函数时,称为二路递归
3、多重递归
一个递归函数执行调用三个或三个以上递归函数时,称为多重递归