用矩阵计算斐波那契数列
思路来自于知乎文章
我们都知道斐波那契数列存在递推关系
def fibo(n):
if n <= 2:
return 1
return fibo(n-1) + fibo(n-2)
但是要这么写,计算
def fibo(n):
cur = 1
prev = 1
if n <= 2:
return cur
for i in range(2, n):
res = cur + prev
prev = cur
cur = res
return cur
但是如果我们执意要用递归呢?如果Python支持的话,还有尾递归这个选项。
def fibo(n):
def __fibo(n, prev, cur):
if n <= 2:
return cur
return __fibo(n-1, cur, cur+prev)
return __fibo(n, 1, 1)
尾递归的形式很像迭代的写法,只不过,将循环演化成了对自身的调用,cur
和 prev
这两个值放在了递归调用的参数上。 除了以上三种常规写法,还有使用矩阵进行运算的,类似动态规划的算法。 我们可以把递推关系推广到矩阵形式
容易得到
那么
矩阵形式的递推公式就是
如果我们从头开始算起,那么递推公式可以表示成
其中numpy
来在 python
中处理矩阵的乘法
import numpy as np
def fibo(n):
matrix = np.array([[1, 1], [1, 0]])
origin = np.array([[1], [0]])
for i in range(n):
origin = np.dot(matrix, origin)
return origin[0][0]
矩阵的乘法,可以使用快速幂算法来减小时间复杂度 快速幂可以把复杂度将为
因此我们可以根据它来写出Python递归函数计算
def fast_pow(a, n):
if n <= 0:
return 1
if n % 2 == 0:
return fast_pow(a, n/2) * fast_pow(a, n/2)
return a * fast_pow(a, (n-1)/2) * fast_pow(a, (n-1)/2)
进一步考虑迭代的做法,
也就是说,如果用二进制表示
代码如下
def fast_pow(a, n):
res = 1
while n > 0:
if n & 1 != 0:
res = res * a
a *= a
n = n // 2
return res
尾递归的代码也可以根据迭代的思路写出来
def fast_pow(a, n):
def __fast_pow(res, a, n):
if n <= 0:
return res
if n & 1 == 0:
return __fast_pow(res, a*a, n>>1)
return __fast_pow(res*a, a*a, n>>1)
return __fast_pow(1, a, n)
不过以上都是针对整数的快速幂的应用,那么该如何用快速幂来求解矩阵呢?并没有太大的区别,只不过将参与运算的整数和运算符号替换成矩阵的就可以了。
python
代码如下
import numpy as np
def fast_pow(origin_matrix, n):
def __fast_pow(res, matrix, n):
if n <= 0:
return res
if n & 1 == 0:
return __fast_pow(res, np.dot(matrix, matrix), n >> 1)
return __fast_pow(np.dot(res, matrix), np.dot(matrix, matrix), n >> 1)
unit_matrix = np.array([[1, 0], [0, 1]])
return __fast_pow(unit_matrix, origin_matrix, n)
def fibo(n):
matrix = np.array([[1, 1], [1, 0]])
origin = np.array([[1], [0]])
matrix = fast_pow(matrix, n-1)
origin = np.dot(matrix, origin)
return origin[0][0]
这里使用的尾递归的方式来计算快速幂,写的稍微好看一点
def fast_pow(times):
def __fast_pow(init, el, n):
def u_fast_pow(res, el, n):
if n <= 0:
return res
if n & 1 == 0:
return u_fast_pow(res, times(el, el), n >> 1)
return u_fast_pow(times(res, el), times(el, el), n >> 1)
return u_fast_pow(init, el, n)
return __fast_pow
def fibo(n):
matrix = np.array([[1, 1], [1, 0]])
origin = np.array([[1], [0]])
unit_matrix = np.array([[1, 0], [0, 1]])
matrix = fast_pow(np.dot)(unit_matrix, matrix, n-1)
origin = np.dot(matrix, origin)
return origin[0][0]