实现斐波那契数列的另一种方法

斐波那契数列最常见的表示方式就是递归表示:

Fib(0)Fib(1)Fib(n)=1=1=Fib(n−1)+Fib(n−2)
Fib(0)=1Fib(1)=1Fib(n)=Fib(n−1)+Fib(n−2)
我们在编程实现时也常用递归、迭代等方法,如果可以用一个等式来表示任意斐波那契数,会是怎么样的呢?上网时看到了AN INTEGER FORMULA FOR FIBONACCI NUMBERS这篇文章,觉得挺有意思的,整理记录一下。

常见实现方法

递归

def fib_recursive(n):
if n < 2: return 1
return fib_recursive(n-1)+fib_recursive(n-2)
1
2
3
迭代

def fib_iterative(n):
a, b = 1, 1
for x in range(n):
a, b = a + b, a
return b
1
2
3
4
5
矩阵幂

def fib_matrix(n):
m = numpy.matrix('1 1 ; 1 0') ** n
return m.item(0)
1
2
3
这个方法可以算是从迭代法衍生而来,所求的就是矩阵m的m11m11。
(an+1bn+1)=(an+bnan)=(1110)(anbn)=(1110)n+1(11)
(an+1bn+1)=(an+bnan)=(1110)(anbn)=(1110)n+1(11)
通项公式

def fib_phi(n):
phi = (1 + math.sqrt(5)) / 2.0
psi = (1 - math.sqrt(5)) / 2.0
return int((phi ** (n+1) - psi ** (n+1)) / math.sqrt(5))
1
2
3
4
生成函数实现

斐波那契数列生成函数

F(x)=1+x+2x2+3x3+5x4+8x5+...=ΣnFib(n)xn
F(x)=1+x+2x2+3x3+5x4+8x5+...=ΣnFib(n)xn
利用公式Fib(n)=Fib(n−1)+Fib(n−2)Fib(n)=Fib(n−1)+Fib(n−2)进行推导,n=n+2n=n+2,同乘xn+2xn+2:

ΣnFib(n+2)xn+2=ΣnFib(n+1)xn+2+ΣnFib(n)xn+2
ΣnFib(n+2)xn+2=ΣnFib(n+1)xn+2+ΣnFib(n)xn+2
由生成函数易知:

ΣnFib(n+1)xn+1ΣnFib(n+2)xn+2=x+2x2+3x3+5x4+8x5+...=2x2+3x3+5x4+8x5+...
ΣnFib(n+1)xn+1=x+2x2+3x3+5x4+8x5+...ΣnFib(n+2)xn+2=2x2+3x3+5x4+8x5+...
等式可化简为:

F(x)−x−1=x(F(x)−1)+x2F(x)
F(x)−x−1=x(F(x)−1)+x2F(x)

F(x)=11−x−x2
F(x)=11−x−x2
这个结果有个很好玩的特性,式子得到的值本身就是一个斐波那契数列!比如x=0.001x=0.001:

F(10−3)=1.001002003005008013021034055089144233377610988…
F(10−3)=1.001002003005008013021034055089144233377610988…
Amazing!原理也很好理解,与其生成函数有关,就相当于为每一个斐波那契数提供了一个3位空间,仔细观察就会发现当斐波那契数超过3位就会影响其前后数的准确性。所以如果我们设一个10−k10−k,使其满足第n个斐波那契数的长度,那么在这k位中就得到第n个斐波那契数。

实现

为了简便计算和方便程序,将值转换为整数(10kn10kn),并将模10换为模2(方便了很多,学到了)。

2knF(2−k)=2k(n+2)22k−2k−1
2knF(2−k)=2k(n+2)22k−2k−1
对于k值的选择,要保证斐波那契数“不溢出”,要满足Fib(n+1)<2kFib(n+1)<2k。对比斐波那契数列和2n2n的变化趋势,斐波那契数列的增长速度明显慢于指数增长,所以k=n+1k=n+1是安全的。想要得到第n个斐波那契数,可以通过取余(mod)得到。

Fib(n)≡2(n+1)nF(2−(n+1))mod2(n+1)≡2(n+1)(n+2)22n+2−2n+1−1mod2(n+1)≡4∗2n(n+3)4∗22n−2∗2n−1mod2∗2n
Fib(n)≡2(n+1)nF(2−(n+1))mod2(n+1)≡2(n+1)(n+2)22n+2−2n+1−1mod2(n+1)≡4∗2n(n+3)4∗22n−2∗2n−1mod2∗2n
这样的处理是为了便捷地使用python中的左右移位来实现2n2n变化。在不溢出的情况下,左移n位(<<)就相当于乘以2的n次方,右移n位(>>)相当于除以2的n次方[取商]。同时对于取余(mod)的处理真是让我学到了,居然可以将其转换为按位与(&)运算!(这样可以大大计算提升效率)mod(2<<n)→mod(2<<n)→&((2<<n)−1)((2<<n)−1)
代码实现:

def fib(n):
return (4 << n*(3+n)) // ((4 << 2*n) - (2 << n) - 1) & ((2 << n) - 1)
1
2
从效率出发,这种方法不如那些经典的方法。但是为”老生常谈”的斐波那契数列提供一个新的角度,多些新奇,也有些收获。在按位与(&)运算前我们得到了一个由整个斐波那契数列构成的整数,哈哈。
---------------------

posted @ 2019-07-14 07:37  李艳艳665  阅读(304)  评论(0编辑  收藏  举报