算法分析是我最喜欢的课程之一。一个精妙的算法,犹如一杯香浓的咖啡,让人意犹未尽。
算法代码,python。
今天分享的这个算法是生成斐波那契数列第N项。
斐波那契数列(Fibonacci sequence)
F
0
=
0
,
F
1
=
1
,
F
2
=
1
,
.
.
.
,
F
n
=
F
n
−
2
+
F
n
−
1
F_0=0, F_1 = 1, F_2 = 1, ... ,F_n = F_{n-2}+F_{n-1}
F0=0,F1=1,F2=1,...,Fn=Fn−2+Fn−1
写一个方法def fibonacci(n) 生成 F n F_n Fn项,输入参数n表示序列号,返回值是 F n F_n Fn的值:
直观朴素的算法:
def fibonacci(n):
if n == 0:
return 0
elif n == 1:
return 1
elif n > 1:
return fibonacci(n-2) + fibonacci(n-1)
print(fibonacci(7))
使用这个递归算法,效率如何呢?
当n = 0时,该方法调用次数是1
当n = 1时,该方法调用次数是1
当n = 2时,该方法调用次数是3
当n = 3时,该方法调用次数是5
当n = 4时,该方法调用次数是9
以此类推,
当n = 5时,该方法调用次数是15
当n = 6时,该方法调用次数是25
随着n的增长,该算法的复杂度呈指数级增长,这是很糟糕的情况,如果n值大一些,那就有可能超出编程语言的最大递归次数而无法获得有效结果。
有没有更高效的算法?
仔细观察上图,你会发现在计算过程中会出现许多重复调用,比如fibonacci(0)被重复调用了2次,fibonacci(1)被重复调用了3次。因此,我们可以把之前算过的结果存下来。
直观的顺序算法
def fibonacci(n):
f_n_1 = 0
f_n_2 = 1
if n == 0:
return f_n_1
elif n == 1:
return f_n_2
else:
for i in range(n-1):
f_n = f_n_2 + f_n_1
f_n_2 = f_n_1
f_n_1 = f_n
return f_n
print(fibonacci(7))
以上这个算法记录 f n − 2 f_{n-2} fn−2和 f n − 1 f_{n-1} fn−1的值,避免了重复运算,算法复杂度是线性的,效率较之前提升了不少。
那还有没有更高效的算法?
递归平法算法
有的,可以利用矩阵的n次方来计算斐波那契数列。
F
0
=
0
,
F
1
=
1
,
F
2
=
1
,
则
,
{
F
2
,
F
1
F
1
,
F
0
}
=
{
1
,
1
1
,
0
}
1
F_0 = 0, F_1=1, F_2 = 1, 则, \left\{ \begin{matrix} F_2, F_1 \\ F_1,F_0 \end{matrix} \right\} = \left\{ \begin{matrix} 1, 1\\ 1,0 \end{matrix} \right\}^1
F0=0,F1=1,F2=1,则,{F2,F1F1,F0}={1,11,0}1
假 设 , { F n − 1 , F n − 2 F n − 2 , F n − 3 } = { 1 , 1 1 , 0 } n − 2 假设,\left\{ \begin{matrix} F_{n-1}, F_{n-2} \\ F_{n-2}, F_{n-3} \\ \end{matrix} \right\} = \left\{ \begin{matrix} 1, 1\\ 1,0 \end{matrix} \right\}^{n-2} 假设,{Fn−1,Fn−2Fn−2,Fn−3}={1,11,0}n−2
{ F n − 1 , F n − 2 F n − 2 , F n − 3 } { 1 , 1 1 , 0 } = { F n − 1 + F n − 2 , F n − 1 F n − 2 + F n − 3 , F n − 2 } = { F n , F n − 1 F n − 1 , F n − 2 } \left\{ \begin{matrix} F_{n-1}, F_{n-2} \\ F_{n-2}, F_{n-3} \\ \end{matrix} \right\} \left\{ \begin{matrix} 1, 1\\ 1,0\\ \end{matrix} \right\} = \left\{ \begin{matrix} F_{n-1}+F_{n-2},F_{n-1} \\ F_{n-2}+F_{n-3},F_{n-2} \\ \end{matrix} \right\} = \left\{ \begin{matrix} F_n, F_{n-1}\\ F_{n-1}, F_{n-2} \end{matrix} \right\} {Fn−1,Fn−2Fn−2,Fn−3}{1,11,0}={Fn−1+Fn−2,Fn−1Fn−2+Fn−3,Fn−2}={Fn,Fn−1Fn−1,Fn−2}
由 此 , { F n , F n − 1 F n − 1 , F n − 2 } = { 1 , 1 1 , 0 } n − 1 由此,\left\{ \begin{matrix} F_n, F_{n-1} \\ F_{n-1}, F_{n-2} \end{matrix} \right\}= \left\{ \begin{matrix} 1, 1\\ 1,0 \end{matrix} \right\}^{n-1} 由此,{Fn,Fn−1Fn−1,Fn−2}={1,11,0}n−1
因此,我们可以通过计算 { 1 , 1 1 , 0 } n − 1 \left\{ \begin{matrix} 1, 1\\ 1,0 \end{matrix} \right\}^{n-1} {1,11,0}n−1来获得 F n F_n Fn的值。
计算 { 1 , 1 1 , 0 } n \left\{ \begin{matrix} 1, 1\\ 1,0 \end{matrix} \right\}^n {1,11,0}n则可以使用以下递归算法。
{ 1 , 1 1 , 0 } n = { { 1 , 1 1 , 0 } ( n − 1 ) / 2 . { 1 , 1 1 , 0 } ( n − 1 ) / 2 . . . n 是 奇 数 { 1 , 1 1 , 0 } n / 2 . { 1 , 1 1 , 0 } n / 2 . . . n 是 偶 数 \left\{ \begin{matrix} 1, 1\\ 1,0 \end{matrix} \right\}^n=\biggl\{^{\left\{ \begin{matrix} 1, 1\\ 1,0 \end{matrix} \right\}^{n/2}.\left\{ \begin{matrix} 1, 1\\ 1,0 \end{matrix} \right\}^{n/2} ...n是偶数} _{\left\{ \begin{matrix} 1, 1\\ 1,0 \end{matrix} \right\}^{(n-1)/2}.\left\{ \begin{matrix} 1, 1\\ 1,0 \end{matrix} \right\}^{(n-1)/2} ...n是奇数} {1,11,0}n={{1,11,0}(n−1)/2.{1,11,0}(n−1)/2...n是奇数{1,11,0}n/2.{1,11,0}n/2...n是偶数
def fibonacci(n):
if n == 0:
return 0
if n == 1:
return 1
else:
f_matrix = fibonacci_matrix(n)
return f_matrix[0][0]
def fibonacci_matrix(n):
if n < 2:
return -1
elif n == 2:
f_n = 1
f_n_1 = 1
f_n_2 = 0
elif n > 1 and n%2 == 0:
f = fibonacci_matrix(n/2)
f_n = f[0][0]* f[0][0] + f[0][1]*f[1][0]+f[0][0]* f[0][1] + f[0][1]*f[1][1]
f_n_2 = f[0][0]* f[0][1] + f[0][1]*f[1][1]
f_n_1 = f_n-f_n_2
else:
f = fibonacci_matrix((n+1)/2)
f_n = f[0][0]* f[0][0]+f[0][1]* f[1][0]
f_n_1 = f[0][0]* f[0][1]+f[0][1]*f[1][1]
f_n_2 = f_n - f_n_1
return [[f_n,f_n_1],[f_n_1,f_n_2]]
这个算法的复杂度时对数级的,比之前的算法又有改进。
我们可以来看一下,算法二和算法三的速度对比,分别是n=10000,n=50000,n=100000时的耗时,随着n的增长算法三的优势就显而易见了。
算法二
1st run - n=10000 :
0:00:00.004448
2nd run - n=50000:
0:00:00.038660
3rd run - n=100000:
0:00:00.164054
算法三
Recursive Squaring
1st run - n=10000 :
0:00:00.000158
2nd run - n=50000:
0:00:00.001296
3rd run - n=100000:
0:00:00.005507
今天的斐波那契数列算法就讲到这里。