斐波那契数列的几种编程实现及一般推广
斐波那契数列是一列规律很简单、明显的数列,它的第0项是0,第1项是1,第2项是1,依此类推,之后每一项是之前两数的和。首几个数是:
0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765, 10946 ……(OEIS A000045)
编程实现
实现它最容易想到的方法,可以设一个数组,首两项是0和1,从n=2项起,每一项是之前两项之和,循环依次赋值,这里代码略去。下面介绍另几种实现方法。
用递归方法实现:
static long getItemRecursive(int index) { if (index < 1) return 0; if (index == 1) return 1; return getItemRecursive(index - 1) + getItemRecursive(index - 2); }
这种实现方式最直观,但会很耗时,若方法名为fib,当index为5时,fib(5)的计算过程如下:
- fib(5)
- fib(4) + fib(3)
- (fib(3) + fib(2)) + (fib(2) + fib(1))
- ((fib(2) + fib(1)) + (fib(1) + fib(0))) + ((fib(1) + fib(0)) + fib(1))
- (((fib(1) + fib(0)) + fib(1)) + (fib(1) + fib(0))) + ((fib(1) + fib(0)) + fib(1))
由上面可以看出,这种算法对于相似的子问题进行了重复的计算,因此不是一种高效的算法。实际上,该算法的运算时间是指数级增长的。
另外两种递归实现:
static long getItem2(int index) { return getItemRecursive2(0, 1, 0, index); } static long getItemRecursive2(int curr, int next, int currIndex, int index) { if (currIndex == index) { return curr; } else { return getItemRecursive2(next, curr + next, currIndex + 1, index); } }
static void getItemRecursive1(out long a2, out long a1, int index) { if (index <= 1) { a2 = 1; a1 = 0; } else { long m2, m1; getItemRecursive1(out m2, out m1, index - 1); a1 = m2; a2 = m2 + m1; } }
利用动态规划:
static long getItem(int index) { long n0 = 0, n1 = 1; if (index < 1) return n0; if (index == 1) return n1; long sn; for (int i = 2; i <= index; i++) { sn = n0 + n1; n0 = n1; n1 = sn; //或者以下方法 //n1 = n0 + n1; //n0 = n1 - n0; //或者以下方法 //n0 = n1 ^ (n0 + n1); //n1 = n1 ^ n0; //n0 = n1 ^ n0; } return n1; }
利用矩阵乘法、快速幂的实现:
这种方式当计算较大项(index大于65535)时,所花费的时间要比前面的方法花费的时间少至少一个数量级。
原理如下:
实现代码:
class FibonacciCalculator { struct FibonacciMatrixMultiple { public BigInteger a11; public BigInteger a12; public BigInteger a21; public BigInteger a22; public FibonacciMatrixMultiple(BigInteger p_a11, BigInteger p_a12, BigInteger p_a21, BigInteger p_a22) { this.a11 = p_a11; this.a12 = p_a12; this.a21 = p_a21; this.a22 = p_a22; } public static FibonacciMatrixMultiple operator *(FibonacciMatrixMultiple mat1, FibonacciMatrixMultiple mat2) { return new FibonacciMatrixMultiple( mat1.a11 * mat2.a11 + mat1.a12 * mat2.a21, mat1.a11 * mat2.a12 + mat1.a12 * mat2.a22, mat1.a21 * mat2.a11 + mat1.a22 * mat2.a21, mat1.a21 * mat2.a12 + mat1.a22 * mat2.a22 ); } } struct FibonacciMatrix { public BigInteger a11; public BigInteger a21; public FibonacciMatrix(BigInteger p_a11, BigInteger p_a21) { this.a11 = p_a11; this.a21 = p_a21; } public static FibonacciMatrix operator *(FibonacciMatrixMultiple mat1, FibonacciMatrix mat2) { return new FibonacciMatrix( mat1.a11 * mat2.a11 + mat1.a12 * mat2.a21, mat1.a21 * mat2.a11 + mat1.a22 * mat2.a21 ); } } private static FibonacciMatrix getFibonacciMatrix(int n) { FibonacciMatrix resultMatix = new FibonacciMatrix(1, 1); FibonacciMatrixMultiple multiple = new FibonacciMatrixMultiple(1, 1, 1, 0); while (n > 0) { if ((n & 1) == 1) resultMatix = multiple * resultMatix; n >>= 1; if (n > 0) multiple *= multiple; } return resultMatix; } public static BigInteger GetFibonacci(int index) { if (index < 1) return 0; if (index == 1) return 1; return getFibonacciMatrix(index - 2).a11; } }
JAVA实现:
1 class FibonacciCalculator { 2 static class FibonacciMatrixMultiple { 3 public BigInteger a11; 4 public BigInteger a12; 5 public BigInteger a21; 6 public BigInteger a22; 7 8 public FibonacciMatrixMultiple(BigInteger p_a11, BigInteger p_a12, 9 BigInteger p_a21, BigInteger p_a22) { 10 this.a11 = p_a11; 11 this.a12 = p_a12; 12 this.a21 = p_a21; 13 this.a22 = p_a22; 14 } 15 } 16 17 public static FibonacciMatrixMultiple Multiply( 18 FibonacciMatrixMultiple mat1, FibonacciMatrixMultiple mat2) { 19 return new FibonacciMatrixMultiple(mat1.a11.multiply(mat2.a11).add( 20 mat1.a12.multiply(mat2.a21)), mat1.a11.multiply(mat2.a12).add( 21 mat1.a12.multiply(mat2.a22)), mat1.a21.multiply(mat2.a11).add( 22 mat1.a22.multiply(mat2.a21)), mat1.a21.multiply(mat2.a12).add( 23 mat1.a22.multiply(mat2.a22))); 24 } 25 26 static class FibonacciMatrix { 27 public BigInteger a11; 28 public BigInteger a21; 29 30 public FibonacciMatrix(BigInteger p_a11, BigInteger p_a21) { 31 this.a11 = p_a11; 32 this.a21 = p_a21; 33 } 34 } 35 36 public static FibonacciMatrix Multiply2(FibonacciMatrixMultiple mat1, 37 FibonacciMatrix mat2) { 38 return new FibonacciMatrix(mat1.a11.multiply(mat2.a11).add( 39 mat1.a12.multiply(mat2.a21)), mat1.a21.multiply(mat2.a11).add( 40 mat1.a22.multiply(mat2.a21))); 41 } 42 43 private static FibonacciMatrix getFibonacciMatrix(int n) { 44 FibonacciMatrix resultMatrix = new FibonacciMatrix( 45 BigInteger.valueOf(1), BigInteger.valueOf(1)); 46 FibonacciMatrixMultiple multiple = new FibonacciMatrixMultiple( 47 BigInteger.valueOf(1), BigInteger.valueOf(1), 48 BigInteger.valueOf(1), BigInteger.valueOf(0)); 49 while (n > 0) { 50 if ((n & 1) == 1) 51 resultMatrix = Multiply2(multiple, resultMatrix); 52 n >>= 1; 53 if (n > 0) 54 multiple = Multiply(multiple, multiple); 55 } 56 return resultMatrix; 57 } 58 59 public static BigInteger GetFibonacci(int index) { 60 if (index < 1) 61 return BigInteger.valueOf(0); 62 if (index == 1) 63 return BigInteger.valueOf(1); 64 return getFibonacciMatrix(index - 2).a11; 65 } 66 }
通项公式
斐波那契数列有通项公式(推导见下方):
令人惊奇的是,公式中的an值是以无理数的幂表示的,然而所得的结果完全是整数。
不难看出,数列随着项数n的增加,前后项之比值会愈来愈趋近于黄金比例。
推广
斐波那契—卢卡斯数列
卢卡斯数列1、3、4、7、11、18…,也具有斐波那契数列同样的性质。(我们可称之为斐波那契—卢卡斯递推:从第三项开始,每一项都等于前两项之和f(n) = f(n-1)+ f(n-2)。
卢卡斯数列的通项公式为 f(n)=[(1+√5)/2]^n+[(1-√5)/2]^n
这两个数列还有一种特殊的联系(如下表所示),F(n)*L(n)=F(2n),及L(n)=F(n-1)+F(n+1)
n | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | … |
斐波那契数列F(n) | 1 | 1 | 2 | 3 | 5 | 8 | 13 | 21 | 34 | 55 | … |
卢卡斯数列L(n) | 1 | 3 | 4 | 7 | 11 | 18 | 29 | 47 | 76 | 123 | … |
F(n)*L(n) | 1 | 3 | 8 | 21 | 55 | 144 | 377 | 987 | 2584 | 6765 | … |
类似的数列还有无限多个,我们称之为斐波那契—卢卡斯数列。
如1,4,5,9,14,23…,因为1,4开头,可记作F[1,4],斐波那契数列就是F[1,1],卢卡斯数列就是F[1,3],斐波那契—卢卡斯数列就是F[a,b]。
斐波那契—卢卡斯数列之间的广泛联系
①任意两个或两个以上斐波那契—卢卡斯数列之和或差仍然是斐波那契—卢卡斯数列。
如:F[1,4]n+F[1,3]n=F[2,7]n,F[1,4]n-F[1,3]n=F[0,1]n=F[1,1](n-1)
n | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | … |
F[1,4]n | 1 | 4 | 5 | 9 | 14 | 23 | 37 | 60 | 97 | 157 | … |
F[1,3]n | 1 | 3 | 4 | 7 | 11 | 18 | 29 | 47 | 76 | 123 | … |
F[1,4]n-F[1,3]n | 0 | 1 | 1 | 2 | 3 | 5 | 8 | 13 | 21 | 34 | … |
F[1,4]n+F[1,3]n | 2 | 7 | 9 | 16 | 25 | 41 | 66 | 107 | 173 | 280 | … |
②任何一个斐波那契—卢卡斯数列都可以由斐波那契数列的有限项之和获得,如
n | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | … |
F[1,1](n) | 1 | 1 | 2 | 3 | 5 | 8 | 13 | 21 | 34 | 55 | … |
F[1,1](n-1) | 0 | 1 | 1 | 2 | 3 | 5 | 8 | 13 | 21 | 34 | … |
F[1,1](n-1) | 0 | 1 | 1 | 2 | 3 | 5 | 8 | 13 | 21 | 34 | … |
F[1,3]n | 1 | 3 | 4 | 7 | 11 | 18 | 29 | 47 | 76 | 123 | … |
黄金特征与孪生斐波那契—卢卡斯数列
斐波那契—卢卡斯数列的另一个共同性质:中间项的平方数与前后两项之积的差的绝对值是一个恒值,
斐波那契数列:|1*1-1*2|=|2*2-1*3|=|3*3-2*5|=|5*5-3*8|=|8*8-5*13|=…=1
卢卡斯数列:|3*3-1*4|=|4*4-3*7|=…=5
F[1,4]数列:|4*4-1*5|=11
F[2,5]数列:|5*5-2*7|=11
F[2,7]数列:|7*7-2*9|=31
斐波那契数列这个值是1最小,也就是前后项之比接近黄金比例最快,我们称为黄金特征,黄金特征1的数列只有斐波那契数列,是独生数列。卢卡斯数列的黄金特征是5,也是独生数列。前两项互质的独生数列只有斐波那契数列和卢卡斯数列这两个数列。
而F[1,4]与F[2,5]的黄金特征都是11,是孪生数列。F[2,7]也有孪生数列:F[3,8]。其他前两项互质的斐波那契—卢卡斯数列都是孪生数列,称为孪生斐波那契—卢卡斯数列。
广义斐波那契数列
斐波那契数列的黄金特征1,还让我们联想到佩尔数列:1,2,5,12,29,…,也有|2*2-1*5|=|5*5-2*12|=…=1(该类数列的这种特征值称为勾股特征)。
佩尔数列Pn的递推规则:P1=1,P2=2,Pn=P(n-2)+2P(n-1).
据此类推到所有根据前两项导出第三项的通用规则:f(n) = f(n-1) * p + f(n-2) * q,称为广义斐波那契数列。
当p=1,q=1时,我们得到斐波那契—卢卡斯数列。
当p=2,q=1时,我们得到佩尔—勾股弦数(跟边长为整数的直角三角形有关的数列集合)。
当p=2,q=-1时,我们得到等差数列。其中f(1)=1,f(2)=2时,我们得到自然数列1,2,3,4…。自然数列的特征就是每个数的平方与前后两数之积的差为1(等差数列的这种差值称为自然特征)。
具有类似黄金特征、勾股特征、自然特征的广义斐波那契数列p=±1。
当f1=1,f2=2,p=2,q=1时,我们得到等比数列1,2,4,8,16……
通项公式推导
对于广义斐波那契数列,有
设
化简得:
比较系数可得:
当p2 + 4q > 0,解得:
令
有
...①
令
有
...②
联立①、②式,
①式等式两边同乘β1得:
②式等式两边同乘β2得:
两式相减得:
斐波那契数列的特征为:a1=1, a2=1, p=1, q=1,代入上式可得通项公式为:
卢卡斯数列的特征为:a1=1, a2=3, p=1, q=1,代入上式可得通项公式为:
当p2 + 4q = 0,解得:
令
有
……
将上述n-1个式子两边分别乘以1, , , ... ,
……
再相加,得:
自然数数列的特征为:a1=1, a2=2, p=2, q=-1,代入上式可得通项公式为:
an=n
性质
一些有关斐波那契数列的Online Judge的题目参见:http://www.cnblogs.com/Knuth/archive/2009/09/04/1559951.html
参见
http://zh.wikipedia.org/wiki/斐波那契数列
http://baike.baidu.com/view/816.htm
http://science.scileaf.com/library/763
http://bbs.tianya.cn/post-666-20190-1.shtml
http://www.hytc.cn/xsjl/szh/lec5.pdf