斐波那契数列
斐波那契数列(意大利语:Successione di Fibonacci),又译为费波拿契数列、费波那西数列、斐波那契数列、黄金分割数列。
在数学上,费波那契数列是以递归的方法来定义:
F[0] = 0
F[1] = 1
F[n] = F[n − 1] + F[n − 2] (n≧2)
用文字来说,就是费波那契数列由0和1开始,之后的费波那契系数就是由之前的两数相加而得出。首几个费波那契系数是:0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233……
特别指出:0不是第一项,而是第零项。
好了,以上的摘自wiki,现在我就用python写一下求取数列第n项的方法:
- 按数学定义的递归方法:
1 |
|
可以把参数n看成是问题的规模,不难看出,计算F[n]
的时间代价(考虑求假发操作的次数)大致等于F[n-1]
和F[n-2]
的时间代价之和,也可以基本时间代价是指数增长的。而且每次调用一个函数的时候,系统都会重新开辟一个与这个函数相对应的栈帧。如果递归调用的深度比较大,栈帧会开辟很多,一来是浪费空间,二来性能也必然会下降很多(有很多读写内存操作)。
所以有没有什么可以优化的地方呢?可以比较明显看出,其实每次计算的时候有很多重复计算,所以如果我们可以将已经计算的参数记录下来,以供后面使用,这样就会节省部分时间。
1 | from functools import wraps |
这里再展开说一下关于装饰器调用递归函数,这里的用法直接调用就可以了,如果是下面计算时间的,就必须要再封装一次,而上面的装饰器是不需要的,你明白为什么要这样么?
1 |
|
- 循环迭代
由于我们看到,后一项是前两项的叠加,所以我们可以考虑用循环实现。
1 |
|
明显看出,使用循环,只在一个函数栈空间里,不会开辟更多的空间,所以使用循环,性能要远远好于递归。
- 矩阵运算
对于F[n] = F[n − 1] + F[n − 2] (n≧2)这样的递推公式,我们可以考虑构建一个二维的递归公式:
可以写出一直递推到F[1],F[0]的公式,前面是矩阵的次幂形式:
1 |
|
- 尾递归形式
这个是从知乎上学到的知识点,链接 https://zhuanlan.zhihu.com/p/24305359
1 |
|
循坏改成尾递归的通用技巧,那就是把循环的写法中,需要使用变量的地方,都改成递归参数。
几种写法写完了,可以拿去测一下时间了,看看哪个快一些呢。
1 | #2**32=4294967296 |