斐波那契数列
定义: F 0 = 0 , F 1 = 1 , F n = F n − 1 + F n − 2 F_0 = 0,\quad F_1 = 1, \quad F_n = F_{n-1} + F_{n-2} F0=0,F1=1,Fn=Fn−1+Fn−2
则 F n F_n Fn所组成的数列叫做斐波那契数列。
更为详细的定义见百度百科:斐波那契数列
该数列的前几项如下:
0
,
1
,
1
,
2
,
3
,
5
,
8
,
13
,
21
,
34
,
55
,
89
,
.
.
.
0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, ...
0,1,1,2,3,5,8,13,21,34,55,89,...
很显然,第
n
n
n个斐波那契数可以用这个递推公式以线性时间复杂度
O
(
n
)
O(n)
O(n)递推求解出来。
C++代码:
#include <iostream>
using namespace std;
const int mod = 1e9 + 7;
const int N = 1e6 + 5;
int f[N];
int main()
{
f[0] = 0; f[1] = 1;
int n;
cin >> n;
for(int i = 2; i <= n; i ++ ) {
f[i] = (f[i-1] + f[i-2]) % mod; //要注意取模,防止数字过大溢出
}
cout << f[n] << endl;
return 0;
}
这种方法很简单,也很容易理解。但我们不难发现,当 n n n 比较大的时候,求出 F n F_n Fn 的时间代价就比较大了。并且也会占用极大地内存,因为数列中的每一个数都需要占4个字节。那么有没有更好的方法去求斐波那契数列呢?
矩阵形式
\quad
斐波那契数列的递推可以用矩阵乘法的形式表达:
[
F
n
−
1
F
n
]
=
[
F
n
−
2
F
n
−
1
]
⋅
[
0
1
1
1
]
\begin{bmatrix} F_{n-1} &F_n \end{bmatrix} =\begin{bmatrix} F_{n-2} &F_{n-1} \end{bmatrix} \cdot\begin{bmatrix} 0 &1 \\ 1 &1 \\ \end{bmatrix}
[Fn−1Fn]=[Fn−2Fn−1]⋅[0111]
\quad
设
P
=
[
0
1
1
1
]
P = \begin{bmatrix}0 &1\\1 &1 \end{bmatrix}
P=[0111],我们得到
[
F
n
F
n
+
1
]
=
[
F
0
F
1
]
⋅
P
n
\begin{bmatrix}F_n &F_{n+1}\end{bmatrix}=\begin{bmatrix}F_0 &F_1\end{bmatrix}\cdot P^{n}
[FnFn+1]=[F0F1]⋅Pn
\quad
于是我们可以用矩阵乘法在
Θ
(
log
n
)
\Theta(\log n)
Θ(logn)的时间内计算斐波那契数列。
快速倍增法
\quad
使用上面的方法我们可以得到以下等式:
F
2
k
=
F
k
(
2
F
k
+
1
−
F
k
)
F
2
k
+
1
=
F
k
+
1
2
+
F
k
2
\begin{aligned} F_{2k}=F_{k}(2F_{k+1}-F_{k}) \\ F_{2k+1}=F_{k+1}^{2}+F_{k}^{2} \end{aligned}
F2k=Fk(2Fk+1−Fk)F2k+1=Fk+12+Fk2
\quad
于是可以通过这样的方法快速计算两个相邻的斐波那契数(常数比矩乘小)。代码如下,返回值是一个二元组
(
F
n
,
F
n
+
1
)
(F_n, F_{n+1})
(Fn,Fn+1) 。
C++代码实现如下:
pair<int, int> fib(int n)
{
if(n == 0) {
return {0, 1};
}
pair<int, int> p = fib(n >> 1);
int c = p.first * (2 * p.second - p.first);
int d = p.first * p.first + p.second * p.second;
if (n & 1)
return {d, c + d};
else
return {c, d};
}
矩阵法
- 对矩阵形式的简单证明:
设转换矩阵为
P
P
P ,则有:
[
F
n
−
2
F
n
−
1
]
⋅
P
=
[
F
n
−
1
F
n
]
\begin{bmatrix} F_{n-2} &F_{n-1} \end{bmatrix}\cdot P=\begin{bmatrix} F_{n-1} &F_{n} \end{bmatrix}
[Fn−2Fn−1]⋅P=[Fn−1Fn]
由矩阵相乘的性质可知
P
P
P 为
2
×
2
2\times 2
2×2 的矩阵,设
P
=
(
a
b
c
d
)
P=\begin{pmatrix} a &b \\ c &d \end{pmatrix}
P=(acbd),
那么 [ F n − 2 F n − 1 ] ⋅ ( a b c d ) = [ F n − 1 F n − 1 + F n − 2 ] \begin{bmatrix} F_{n-2} &F_{n-1} \end{bmatrix}\cdot\begin{pmatrix}a &b \\ c &d \end{pmatrix}=\begin{bmatrix} F_{n-1} &F_{n-1}+F_{n-2} \end{bmatrix} [Fn−2Fn−1]⋅(acbd)=[Fn−1Fn−1+Fn−2]
则: { a ⋅ F n − 2 + c ⋅ F n − 1 = F n − 1 b ⋅ F n − 2 + d ⋅ F n − 1 = F n − 1 + F n − 2 \begin{cases}a\cdot F_{n-2}+c\cdot F_{n-1}=F_{n-1}\\b\cdot F_{n-2}+d\cdot F_{n-1}=F_{n-1}+F_{n-2} \end{cases} {a⋅Fn−2+c⋅Fn−1=Fn−1b⋅Fn−2+d⋅Fn−1=Fn−1+Fn−2
解得: { a = 0 b = 1 c = 1 d = 1 \begin{cases}a=0\\b=1\\c=1\\d=1 \end{cases} ⎩ ⎨ ⎧a=0b=1c=1d=1
∴ P = ( 0 1 1 1 ) \therefore P=\begin{pmatrix}0 &1\\1 &1 \end{pmatrix} ∴P=(0111),由此得证。
那么我们可以用矩阵快速幂快速求出 F n 、 F n + 1 F_{n}、F_{n+1} Fn、Fn+1 的值。
快速幂的时间复杂度是 O ( log n ) O(\log n) O(logn) ,所以矩阵法求斐波那契数列时间复杂度是 Θ ( log n ) \Theta(\log n) Θ(logn) 。
C++代码实现如下:
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
constexpr int mod = 1e9 + 7;
struct matrix {
ll cell[2][2];
matrix(ll a = 0, ll b = 0, ll c = 0, ll d = 0) {
cell[0][0] = a;
cell[0][1] = b;
cell[1][0] = c;
cell[1][1] = d;
}
}one(0, 1, 1, 1);
matrix mul(matrix a, matrix b) {
matrix r;
for(int i = 0; i < 2; i ++)
for(int j = 0; j < 2; j ++)
for(int k = 0; k < 2; k ++) {
r.cell[i][j] += 1LL * a.cell[i][k] * b.cell[k][j] % mod; //防止整型溢出
r.cell[i][j] = r.cell[i][j] % mod;
}
return r;
}
matrix qpow(ll k) {
matrix r(0, 1, 0, 0);
matrix cm(0, 1, 1, 1);
while(k) {
if(k & 1) r = mul(r, cm);
cm = mul(cm, cm);
k >>= 1;
}
return r;
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
ll n;
cin >> n;
matrix t = qpow(n);
cout << t.cell[0][0] << endl;
return 0;
}
本文章全部内容均为原创,如需转载请注明出处