剑指offer——斐波那契数列 python 求解,使用数学原理再次降低时间复杂度
一、题目描述
写一个函数,输入n,求斐波那契数列的前n项
二、解题方法
(1)常规递归方法实现:
解题思路:因为每当求解f(n)的时候,我们需要知道f(n-1)和f(n-2),而在获取f(n-1)的时候,我们也需要获得f(n-2)和f(n-3)……如此以来,求解问题的步骤是不断地往下找构成此问题的子问题,而子问题又与上层问题性质相同。这个时候就可以使用递归的思想求解。
方法优缺点:代码简单,容易看懂。然而思考下面这个问题:当随着n的增加,这样的递归产生了多少的计算操作呢?
- 求解f(1)时,直接返回,计算量记为1
- 求解f(2)时,直接返回,计算量记为1
- 求解f(3)时,f(3)产生了f(2)和f(1)两个子问题,计算量我们记为3
- 求解f(4)的时候,f(4)产生了f(3)和f(2),此时f(3)继续递归求解,f(3)又产生了f(2)和f(1)两个子问题(整个过程计算了两次f(2),这也是为什么这个递归实现随着n的增大运行越来越慢的原因所在),此时计算总量为5
- 同理,f(5)将产生总共9次的计算量
当问题从f(n-1)扩大到f(n)时,增加了f(n-2)的计算量,这样计算量是以2的幂次数增加的,按照时间复杂度的记法,这种递归求解斐波那契数列的时间复杂度为O(2n)
代码实现:
def fib(n):
if n == 1 or n == 2:
return 1
else:
return fib(n-1) + fib(n-2)
# 输出前七项
if __name__ == '__main__':
for _ in range(7):
print(fib(_+1))
代码输出:
1
1
2
3
5
8
13
(2)往后推移法
解题思路:递归法思路是从上往下,每次计算时多计算了很多重复的步骤,所以我们可以想如何去掉那些重复的步骤,这样就可以完成优化。按照斐波那契最简单的定义,f(n)=f(n-1)+f(n-2),每次记录下来f(n-1)和f(n-2),计算f(n)时只需要往后推移一次,这样就实现了不会进行重复的计算步骤了。
此时解题的关键点成了记录a和b的位置
方法优缺点:简单明了,但是每次都得从头往后移动,不能具备像生成器一样,需要的时候往后移动的特性
代码实现:
def fib(n):
if n==1 or n==2:
return 1
else:
a,b = 1,1
for i in range(n-2):
a,b = b,a+b
return b
if __name__ == '__main__':
for _ in range(7):
print(fib(_+1))
(3)生成器法
解题思路:在方法(2)的基础上进行优化,插入生成器的特性,当需要用到的时候,生成下一个数
代码实现:
def fib(n):
if n==1 or n==2:
for _ in range(n):
yield 1
else:
for _ in range(2):
yield 1
a,b = 1,1
for _ in range(n-2):
a,b = b,a+b
yield b
if __name__ == '__main__':
for _ in fib(7):
print(_)
三、二分法+矩阵求解
解题思路:
当n-1为偶数的时候,可以求右边矩阵的(n-1)/2次幂
当n-1为奇数时,可以求右边矩阵的(n-2)/2次幂
如此递归下去,由于使用了二分的思想,讲算法的时间复杂度降低到了O(log n)
代码实现:
#矩阵乘法
def matrixMul(a, b):
c = [[None, None], [None, None]]
c[0][0] = b[0][0] * a[0][0] + b[0][1] * a[1][0]
c[0][1] = b[0][0] * a[0][1] + b[0][1] * a[1][1]
c[1][0] = b[1][0] * a[0][0] + b[1][1] * a[1][0]
c[1][1] = b[1][0] * a[0][1] + b[1][1] * a[1][1]
return c
#获取n次幂的矩阵
def getMatrix(n):
a = [[1, 1], [1, 0]]
if n == 1:
return a
if n == 2:
return matrixMul(a, a)
elif n % 2 == 1: # n为奇数
return matrixMul(matrixMul(getMatrix((n - 1) // 2), getMatrix((n - 1) // 2)), a)
else: # n为偶数
return matrixMul(getMatrix(n // 2), getMatrix(n // 2))
#获取斐波那契数列
def fib(n):
if n <= 2:
return 1
else:
return getMatrix(n - 1)[0][0]
if __name__ == '__main__':
for _ in range(9):
print(fib(_ + 1))
嘿嘿,I am very glateful that 你看到这里了哦~下回再见ヾ(o◕∀◕)ノヾ
Thx