在《计算机考研之数据结构:斐波那契数列专题(1)》中分析用递归方式描述的斐波那契数列的时间复杂度,还计算了斐波那契数列的通项。从而得知,如果用递归方式实现(如下代码):
long fib(long n){
if(n == 1 || n == 2) return 1;
else return fib(n-1) + fib(n-2);
}
其时间复杂度为指数级,不是一个有价值的算法。
实现斐波那契数列,通常作为我这里的一道面试题,多数来面试的给出的都是上面那个"没有价值"的实现方法。
对于斐波那契数列的实现,除了递归(最不提倡的)之外,还有其他方法。
1. 斐波那契数列的实现方法
以下用 Python 语言描述。
1.1 递归
python
# 递归
def fib_recu(n):
if n <= 1:
return n
else:
return fib_recu(n-1) + fib_recu(n-2)
print("fibonacci number:")
for i in range(10):
print(f"{fib_recu(i)}", end=" ")
# 输出:
fibonacci number:
0 1 1 2 3 5 8 13 21 34
递归方法,思路简单,但是"没有价值"。
1.2 循环
python
def fib_loop(n):
fibs = [0, 1]
if n <= 1:
return n
else:
for i in range(n-2):
fibs.append(fibs[-2] + fibs[-1])
return fibs
print(f"fibonacci number: {fib_loop(10)}")
# 输出:
fibonacci number: [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
循环方法,也称为递推法,即递增。随着数量的增大,执行速度会越来越慢。
1.3 迭代器对象
python
class Fibs:
def __init__(self, max):
self.max = max
self.a = 0
self.b = 1
def __iter__(self):
return self
def __next__(self):
fib = self.a
if fib > self.max:
raise StopIteration
self.a, self.b = self.b, self.a + self.b
return fib
fibs = Fibs(100000)
lst = [ fibs.__next__() for i in range(10)]
print(lst)
# 输出
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
1.4 生成器对象
python
def fibs():
prev, curr = 0, 1
while True:
yield prev
prev, curr = curr, prev + curr
import itertools
print(list(itertools.islice(fibs(), 10)))
# 输出
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
通过fibs()
得到了含有无限项斐波那契数的对象,只不过在上面只向内存中读入了10个。
1.5 矩阵
本方法来自《跟老齐学Python:数据分析》(电子工业出版社)
python
import numpy as np
def fib_matrix(n):
F = np.mat([[1,1], [1,0]])
F_m = pow(F, n)
return F_m[0, 0]
print("fibonacci numbers:")
for i in range(10):
print(fib_matrix(i), end=" ")
# 输出
fibonacci numbers:
1 1 2 3 5 8 13 21 34 55
2. 黄金分割
对斐波那契数列还可以做进一步的研究,以下纯属个人爱好。
如果用斐波那契数列中的后面的数字除以前面的数字,结果会如何?
python
def fib_iter():
prev, curr = 0, 1
while True:
yield prev
prev, curr = curr, prev + curr
import itertools
fibs = list(itertools.islice(fib_iter(), 100))
for i in range(2, 30):
print(f"{fibs[i+1]}/{fibs[i]}={fibs[i+1]/fibs[i]}")
# 输出
2/1=2.0
3/2=1.5
5/3=1.6666666666666667
8/5=1.6
13/8=1.625
21/13=1.6153846153846154
34/21=1.619047619047619
55/34=1.6176470588235294
89/55=1.6181818181818182
144/89=1.6179775280898876
233/144=1.6180555555555556
377/233=1.6180257510729614
610/377=1.6180371352785146
987/610=1.618032786885246
1597/987=1.618034447821682
2584/1597=1.6180338134001253
4181/2584=1.618034055727554
6765/4181=1.6180339631667064
10946/6765=1.6180339985218033
17711/10946=1.618033985017358
28657/17711=1.6180339901755971
46368/28657=1.618033988205325
75025/46368=1.618033988957902
121393/75025=1.6180339886704431
196418/121393=1.6180339887802426
317811/196418=1.618033988738303
514229/317811=1.6180339887543225
832040/514229=1.6180339887482036
从上面的输出结果发现: f n + 1 f n ≈ 1.618 ⋯ \frac{f_{n+1}}{f_n}\approx 1.618\cdots fnfn+1≈1.618⋯
这就是我们熟悉的黄金分割------黄金比例,准确值是 φ = 1 + 5 2 \varphi = \frac{1+\sqrt{5}}{2} φ=21+5 ,这是一个无理数,小数点后20位的近似值是: φ = 1.61803398874989484820 \varphi=1.61803398874989484820 φ=1.61803398874989484820 ,一般取 1.618 1.618 1.618 即可。
在历史上,伟大的天文学家开普勒,最早发现了这个结论。
在《计算机考研之数据结构:斐波那契数列专题(1)》已经计算得到了斐波那契数列通项表达式:
F k = 1 5 [ ( 1 + 5 2 ) k − ( 1 − 5 2 ) k ] , ( k = 0 , 1 , 2 , ⋯ ) F_k=\frac{1}{\sqrt{5}}\left[\left(\frac{1+\sqrt{5}}{2}\right)^{k}-\left(\frac{1-\sqrt{5}}{2}\right)^{k}\right] ,(k=0,1,2,\cdots) Fk=5 1 (21+5 )k−(21−5 )k ,(k=0,1,2,⋯)
将此结论对照上述结算结果,亦得有趣结果。
3. 斐波那契数列的有关性质
结合之前得到的斐波那契数列通项表达式,可知如下有关性质。
性质1: lim k → ∞ F k + 1 F k = 1 + 5 2 \lim_{k\to\infty}\frac{F_{k+1}}{F_k}=\frac{1+\sqrt{5}}{2} limk→∞FkFk+1=21+5
证明
对于 k ≥ 1 k\ge 1 k≥1 ,由(12)式得:
lim k → ∞ F k + 1 F k = lim k → ∞ ( 1 + 5 ) k + 1 − ( 1 − 5 ) k + 1 2 k + 1 5 ⋅ 2 k 5 ( 1 + 5 ) k − ( 1 − 5 ) k = 1 2 lim k → ∞ ( 1 + 5 ) k + 1 − ( 1 − 5 ) k + 1 ( 1 + 5 ) k − ( 1 − 5 ) k = 1 2 lim k → ∞ ( 1 + 5 ) − ( 1 − 5 1 + 5 ) k ( 1 − 5 ) 1 − ( 1 − 5 1 + 5 ) k = 1 + 5 2 \begin{split}\lim_{k\to\infty}\frac{F_{k+1}}{F_k}&=\lim_{k\to\infty}\frac{(1+\sqrt{5})^{k+1}-(1-\sqrt{5})^{k+1}}{2^{k+1}\sqrt{5}}\cdot\frac{2^k\sqrt{5}}{(1+\sqrt{5})^{k}-(1-\sqrt{5})^{k}}\\&=\frac{1}{2}\lim_{k\to\infty}\frac{(1+\sqrt{5})^{k+1}-(1-\sqrt{5})^{k+1}}{(1+\sqrt{5})^{k}-(1-\sqrt{5})^{k}}\\&=\frac{1}{2}\lim_{k\to\infty}\frac{(1+\sqrt{5})-\left(\frac{1-\sqrt{5}}{1+\sqrt{5}}\right)^k(1-\sqrt{5})}{1-\left(\frac{1-\sqrt{5}}{1+\sqrt{5}}\right)^k}\\&=\frac{1+\sqrt{5}}{2}\end{split} k→∞limFkFk+1=k→∞lim2k+15 (1+5 )k+1−(1−5 )k+1⋅(1+5 )k−(1−5 )k2k5 =21k→∞lim(1+5 )k−(1−5 )k(1+5 )k+1−(1−5 )k+1=21k→∞lim1−(1+5 1−5 )k(1+5 )−(1+5 1−5 )k(1−5 )=21+5
( ∵ ∣ 1 − 5 1 + 5 ∣ < 1 , lim k → ∞ ( 1 − 5 1 + 5 ) k = 0 \because \quad \begin{vmatrix}\frac{1-\sqrt{5}}{1+\sqrt{5}}\end{vmatrix}\lt 1, \quad \lim_{k\to\infty}\left(\frac{1-\sqrt{5}}{1+\sqrt{5}}\right)^k=0 ∵ 1+5 1−5 <1,limk→∞(1+5 1−5 )k=0 )
证毕。
所以,在前面的程序中,显示后项除以前项的结果趋近于黄金分割。
性质2: 幂矩阵 A k \pmb{A}^k Ak
因为 A = [ 1 1 1 0 ] \pmb{A}=\begin{bmatrix}1&1\\1&0\end{bmatrix} A=[1110] ,又因为斐波那契数列 F 0 = 0 , F 1 = 1 , F 2 = 1 F_0=0,F_1=1,F_2=1 F0=0,F1=1,F2=1 ,所以:
A = [ 1 1 1 0 ] = [ F 2 F 1 F 1 F 0 ] \pmb{A}=\begin{bmatrix}1&1\\1&0\end{bmatrix}=\begin{bmatrix}F_2&F_1\\F_1&F_0\end{bmatrix} A=[1110]=[F2F1F1F0]
A 2 = [ 1 1 1 0 ] [ F 2 F 1 F 1 F 0 ] = [ F 2 + F 1 F 1 + F 0 F 2 F 1 ] = [ F 3 F 2 F 2 F 1 ] \pmb{A}^2=\begin{bmatrix}1&1\\1&0\end{bmatrix}\begin{bmatrix}F_2&F_1\\F_1&F_0\end{bmatrix}=\begin{bmatrix}F_2+F_1&F_1+F_0\\F_2&F_1\end{bmatrix}=\begin{bmatrix}F_3&F_2\\F_2&F_1\end{bmatrix} A2=[1110][F2F1F1F0]=[F2+F1F2F1+F0F1]=[F3F2F2F1]
以此类推,可得:
A k = [ F k + 1 F k F k F k − 1 ] (1) \pmb{A}^k=\begin{bmatrix}F_{k+1}&F_k\\F_k&F_{k-1}\end{bmatrix}\tag{1} Ak=[Fk+1FkFkFk−1](1)
其中 A = [ 1 1 1 0 ] \pmb{A}=\begin{bmatrix}1&1\\1&0\end{bmatrix} A=[1110]
这就是矩阵法计算斐波那契数列的程序编写依据。
性质3: Cassini 等式
在《机器学习数学基础》第2章2.4节中曾经介绍过行列式的性质,下面应用其中一条: ∣ A B ∣ = ∣ A ∣ ∣ B ∣ |\pmb{AB}|=|\pmb{A}||\pmb{B}| ∣AB∣=∣A∣∣B∣ 。
由(1)可得:
∣ A k ∣ = ∣ F k + 1 F k F k F k − 1 ∣ = F k + 1 F k − 1 − F n 2 |\pmb{A}^k|=\begin{vmatrix}F_{k+1}&F_k\\F_k&F_{k-1}\end{vmatrix}=F_{k+1}F_{k-1}-F_n^2 ∣Ak∣= Fk+1FkFkFk−1 =Fk+1Fk−1−Fn2
又因为:
∣ A k ∣ = ∣ A ∣ k = ∣ 1 1 1 0 ∣ k = ( − 1 ) k |\pmb{A}^k|=|\pmb{A}|^k=\begin{vmatrix}1&1\\1&0\end{vmatrix}^k=(-1)^k ∣Ak∣=∣A∣k= 1110 k=(−1)k
所以:
F k + 1 F k − 1 − F n 2 = ( − 1 ) k (2) F_{k+1}F_{k-1}-F_n^2=(-1)^k\tag{2} Fk+1Fk−1−Fn2=(−1)k(2)
(2)式即为 Cassini 等式。表明了斐波那契数列的数之间的一种关系。如:
2 × 5 − 3 2 = 1 3 × 8 − 5 2 = − 1 5 × 13 − 8 2 = 1 8 × 21 − 1 3 2 = − 1 \begin{split}2\times5-3^2&=1\\3\times8-5^2&=-1\\5\times13-8^2&=1\\8\times21-13^2&=-1\end{split} 2×5−323×8−525×13−828×21−132=1=−1=1=−1
性质4: 前 n n n 项的和
设 F k F_k Fk 为斐波那契数列中的第 k k k 项,则:
F k + 2 = F k + 1 + F k , ( k = 0 , 1 , 2 , ⋯ ) F_{k+2} = F_{k+1} + F_k,(k=0,1,2,\cdots) Fk+2=Fk+1+Fk,(k=0,1,2,⋯)
根据斐波那契数列的特点,设定初始条件: F 0 = 0 , F 1 = 1 F_0=0, F_1=1 F0=0,F1=1 。
由此得:
F n = F n + 2 − F n + 1 F n − 1 = F n + 1 − F n F n − 2 = F n − F n − 1 ⋮ F 2 = F 4 − F 3 F 1 = F 3 − F 2 \begin{split}F_n &= F_{n+2}-F_{n+1}\\F_{n-1}&=F_{n+1}-F_n\\F_{n-2}&=F_{n}-F_{n-1}\\&\vdots\\F_2&=F_4-F_3\\F_1&=F_3-F_2\end{split} FnFn−1Fn−2F2F1=Fn+2−Fn+1=Fn+1−Fn=Fn−Fn−1⋮=F4−F3=F3−F2
上面各式等号左右各自相加,得:
F 1 + ⋯ + F n = ( F 3 + ⋯ + F n + 1 + F n + 2 ) − ( F 2 + F 3 + ⋯ + F n + 1 ) = F n + 2 − F 2 F_1+\cdots+F_n=(F_3+\cdots+F_{n+1}+F_{n+2})-(F_2+F_3+\cdots+F_{n+1})=F_{n+2}-F_2 F1+⋯+Fn=(F3+⋯+Fn+1+Fn+2)−(F2+F3+⋯+Fn+1)=Fn+2−F2
因为 F 2 = 1 F_{2}=1 F2=1 ,所以,前 n n n 项的和是:
∑ i = 1 n F i = F n + 2 − 1 (3) \sum_{i=1}^nF_i=F_{n+2}-1\tag{3} i=1∑nFi=Fn+2−1(3)
性质5: 每项的平方和
F n F n + 1 = F n ( F n + F n − 1 ) = F n 2 + F n − 1 F n = F n 2 + F n − 1 ( F n − 1 + F n − 2 ) = F n 2 + F n − 1 2 + F n − 2 F n − 1 = ⋯ = F n 2 + F n − 1 2 + ⋯ + F 2 2 + F 2 F 1 \begin{split}F_nF_{n+1}&=F_n(F_n+F_{n-1})\\&=F^2_n+F_{n-1}F_n\\&=F_n^2+F_{n-1}(F_{n-1}+F_{n-2})\\&=F_n^2+F_{n-1}^2+F_{n-2}F_{n-1}\\&=\cdots\\&=F_n^2+F_{n-1}^2+\cdots+F_2^2+F_2F_1\end{split} FnFn+1=Fn(Fn+Fn−1)=Fn2+Fn−1Fn=Fn2+Fn−1(Fn−1+Fn−2)=Fn2+Fn−12+Fn−2Fn−1=⋯=Fn2+Fn−12+⋯+F22+F2F1
又因为: F 2 = F 1 = 1 F_2=F_1=1 F2=F1=1 ,所以:
∑ i = 1 n F i 2 = F n F n + 1 (4) \sum_{i=1}^nF_i^2=F_nF_{n+1} \tag{4} i=1∑nFi2=FnFn+1(4)