本来以为就是非常简单的一道题,想着递归大法来着,结果n=37时直接超过时间限制了o(╥﹏╥)o
题目
解法
一、动态规划法
- 滚动数组思想
class Solution:
def fib(self, n: int) -> int:
MOD = 10 ** 9 + 7
if n < 2:
return n
p, q, r = 0, 0, 1
for i in range(2, n + 1):
p = q
q = r
r = (p + q) % MOD
return r
- 该方法时间复杂度是 O ( n ) O(n) O(n),但空间复杂度是 O ( 1 ) O(1) O(1)
二、矩阵法
- 发现 F ( n ) = F ( n − 1 ) + F ( n − 2 ) F(n)=F(n-1)+F(n-2) F(n)=F(n−1)+F(n−2)可以用矩阵表示成
- [ 1 1 1 0 ] [ F ( n ) F ( n − 1 ) ] = [ F ( n ) + F ( n − 1 ) F ( n ) ] = [ F ( n + 1 ) F ( n ) ] \left[\begin{array}{ll} 1 & 1 \\ 1 & 0 \end{array}\right]\left[\begin{array}{c} F(n) \\ F(n-1) \end{array}\right]=\left[\begin{array}{c} F(n)+F(n-1) \\ F(n) \end{array}\right]=\left[\begin{array}{c} F(n+1) \\ F(n) \end{array}\right] [1110][F(n)F(n−1)]=[F(n)+F(n−1)F(n)]=[F(n+1)F(n)]
- 有 [ F ( n + 1 ) F ( n ) ] = [ 1 1 1 0 ] n [ F ( 1 ) F ( 0 ) ] \left[\begin{array}{c} F(n+1) \\ F(n) \end{array}\right]=\left[\begin{array}{ll} 1 & 1 \\ 1 & 0 \end{array}\right]^{n}\left[\begin{array}{l} F(1) \\ F(0) \end{array}\right] [F(n+1)F(n)]=[1110]n[F(1)F(0)]
- 令 M = [ 1 1 1 0 ] M=\left[\begin{array}{ll} 1 & 1 \\ 1 & 0 \end{array}\right] M=[1110]
- 然后这里的问题就转化成了快速求矩阵乘积,我看官方的方法好像也就是普通的行×列这样的,得自己写个函数
- 函数1: 2 × 2 2\times 2 2×2矩阵乘法
- 函数2:求矩阵的n次方
- 哦哦哦!!我才懂,原来奥秘在算 M n M^n Mn的时候,不要一个一个的乘,如果n是偶数,比如说8这种,那我只要计算 M 2 → M 4 → M 8 M^2\rightarrow M^4\rightarrow M^8 M2→M4→M8即可,也就是 log 2 8 = 3 \log_2 8=3 log28=3次即可
代码
class Solution:
def fib(self, n: int) -> int:
MOD = 10 ** 9 + 7
if n < 2:
return n
def multiply(a: List[List[int]], b: List[List[int]]) -> List[List[int]]:
c = [[0, 0], [0, 0]]
for i in range(2):
for j in range(2):
c[i][j] = (a[i][0] * b[0][j] + a[i][1] * b[1][j]) % MOD
return c
def matrix_pow(a: List[List[int]], n: int) -> List[List[int]]:
ret = [[1, 0], [0, 1]]
while n > 0:
if n & 1:
ret = multiply(ret, a)
n >>= 1
a = multiply(a, a)
return ret
res = matrix_pow([[1, 1], [1, 0]], n - 1)
return res[0][0]
-这个 n >>= 1
是位运算,之前没见过,开了篇博客一起学习一下(位运算与集合运算)
- 话说这个
n&1 = 1
也是位运算,就是判断n奇偶性的,等价于n%2 =1
- 时间复杂度是 O ( log n ) O(\log n) O(logn),空间复杂度是 O ( 1 ) O(1) O(1)
三、递归法
- 原理: 把 f ( n ) f(n) f(n) 问题的计算拆分成 f ( n − 1 ) f(n−1) f(n−1) 和 f ( n − 2 ) f(n-2) f(n−2) 两个子问题的计算,并递归,以 f ( 0 ) f(0) f(0) 和 f ( 1 ) f(1) f(1) 为终止条件。
- 缺点: 大量重复的递归计算,例如
f
(
n
)
f(n)
f(n)和
f
(
n
−
1
)
f(n - 1)
f(n−1)两者向下递归需要 各自计算
f
(
n
−
2
)
f(n−2)
f(n−2) 的值。
- !!是的!!现在才反应过来,单独算确实有很多重复计算
- !!是的!!现在才反应过来,单独算确实有很多重复计算