zoukankan      html  css  js  c++  java
  • 斐波那契数列的几种编程实现及一般推广

    斐波那契数列是一列规律很简单、明显的数列,它的第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)的计算过程如下:

    1. fib(5)
    2. fib(4) + fib(3)
    3. (fib(3) + fib(2)) + (fib(2) + fib(1))
    4. ((fib(2) + fib(1)) + (fib(1) + fib(0))) + ((fib(1) + fib(0)) + fib(1))
    5. (((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 }
    View Code

    通项公式

    斐波那契数列有通项公式(推导见下方):

    令人惊奇的是,公式中的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……

    通项公式推导

    对于广义斐波那契数列F_n=p cdot F_{n-1}+q cdot F_{n-2},有

    a_n=pa_{n-1}+qa_{n-2}

    a_n +alpha a_{n-1}=eta (a_{n-1}+alpha a_{n-2})

    化简得:

    a_n=(eta -alpha) a_{n-1}+ alphaeta a_{n-2}

    比较系数可得:

     egin{cases} eta-alpha=p \ alphaeta=q end{cases}

    当p2 + 4q  > 0,解得:

     egin{cases} alpha_1=dfrac{-p+sqrt{p^2+4q}}{2} \ eta_1=dfrac{p+sqrt{p^2+4q}}{2} end{cases}

     egin{cases} alpha_2=dfrac{-p-sqrt{p^2+4q}}{2} \ eta_2=dfrac{p-sqrt{p^2+4q}}{2} end{cases}

    T_n=a_n+alpha_1a_{n-1}

    T_n=eta_1T_{n-1}

    T_n={eta_1}^{n-2}T_2

    a_n+dfrac{-p+sqrt{p^2+4q}}{2}a_{n-1}=left(dfrac{p+sqrt{p^2+4q}}{2}
ight)^{n-2}left(a_2+dfrac{-p+sqrt{p^2+4q}}{2}a_1
ight) ...①

    S_n=a_n+alpha_2a_{n-1}

    S_n=eta_2S_{n-1}

    S_n={eta_2}^{n-2}S_2

    a_n+dfrac{-p-sqrt{p^2+4q}}{2}a_{n-1}=left(dfrac{p-sqrt{p^2+4q}}{2}
ight)^{n-2}left(a_2+dfrac{-p-sqrt{p^2+4q}}{2}a_1
ight) ...②

    联立①、②式,

    ①式等式两边同乘β1得:

    dfrac{p+sqrt{p^2+4q}}{2}a_n+2qa_{n-1}=left(dfrac{p+sqrt{p^2+4q}}{2}
ight)^{n-1}left(a_2+dfrac{-p+sqrt{p^2+4q}}{2}a_1
ight)

    ②式等式两边同乘β2得:

    dfrac{p-sqrt{p^2+4q}}{2}a_n+2qa_{n-1}=left(dfrac{p-sqrt{p^2+4q}}{2}
ight)^{n-1}left(a_2+dfrac{-p-sqrt{p^2+4q}}{2}a_1
ight)

    两式相减得:

    sqrt{p^2+4q}a_n=left(dfrac{p+sqrt{p^2+4q}}{2}
ight)^{n-1}left(a_2+dfrac{-p+sqrt{p^2+4q}}{2}a_1
ight)-left(dfrac{p-sqrt{p^2+4q}}{2}
ight)^{n-1}left(a_2+dfrac{-p-sqrt{p^2+4q}}{2}a_1
ight)

    a_n=dfrac{left(dfrac{p+sqrt{p^2+4q}}{2}
ight)^{n-1}left(a_2+dfrac{-p+sqrt{p^2+4q}}{2}a_1
ight)-left(dfrac{p-sqrt{p^2+4q}}{2}
ight)^{n-1}left(a_2+dfrac{-p-sqrt{p^2+4q}}{2}a_1
ight)}{sqrt{p^2+4q}}

    斐波那契数列的特征为:a1=1, a2=1, p=1, q=1,代入上式可得通项公式为:

    a_{n}=frac{sqrt{5}}{5} cdot left[left(frac{1 + sqrt{5}}{2}
ight)^{n} - left(frac{1 - sqrt{5}}{2}
ight)^{n}
ight]

    卢卡斯数列的特征为:a1=1, a2=3, p=1, q=1,代入上式可得通项公式为:

    a_{n}=left(frac{1+sqrt{5}}{2}
ight)^{n}+left(frac{1-sqrt{5}}{2}
ight)^{n}

    当p2 + 4q = 0,解得:

    egin{cases}alpha=-dfrac{p}{2} \ eta=dfrac{p}{2}end{cases}

    T_n=a_n+alpha a_{n-1}

    T_n=eta T_{n-1}

    T_n=eta^{n-2}T_2

    a_n-frac{p}{2}a_{n-1}=left(frac{p}{2}
ight)^{n-2}left(a_2-frac{p}{2}a_1
ight)

    a_{n-1}-frac{p}{2}a_{n-2}=left(frac{p}{2}
ight)^{n-3}left(a_2-frac{p}{2}a_1
ight)

    ……

    a_2-frac{p}{2}a_1=left(frac{p}{2}
ight)^0left(a_2-frac{p}{2}a_1
ight)

    将上述n-1个式子两边分别乘以1, frac{p}{2}left(frac{p}{2}
ight)^2, ... , left(frac{p}{2}
ight)^{n-2}

    a_n-frac{p}{2}a_{n-1}=left(frac{p}{2}
ight)^{n-2}left(a_2-frac{p}{2}a_1
ight)

    frac{p}{2}a_{n-1}-left(frac{p}{2}
ight)^2a_{n-2}=left(frac{p}{2}
ight)^{n-2}left(a_2-frac{p}{2}a_1
ight)

    left(frac{p}{2}
ight)^2a_{n-2}-left(frac{p}{2}
ight)^3a_{n-3}=left(frac{p}{2}
ight)^{n-2}left(a_2-frac{p}{2}a_1
ight)

    ……

    left(frac{p}{2}
ight)^{n-2}a_2-left(frac{p}{2}
ight)^{n-1}a_1=left(frac{p}{2}
ight)^{n-2}left(a_2-frac{p}{2}a_1
ight)

    再相加,得:

    a_n-left(frac{p}{2}
ight)^{n-1}a_1=left(frac{p}{2}
ight)^{n-2}left(a_2-frac{p}{2}a_1
ight)

    a_n=left(n-1
ight)left(frac{p}{2}
ight)^{n-2}a_2-left(n-2
ight)left(frac{p}{2}
ight)^{n-1}a_1

    自然数数列的特征为:a1=1, a2=2, p=2, q=-1,代入上式可得通项公式为:

    an=n

    性质

    • (f(n))^2=f(n-1)*f(n+1)+(-1)^(n+1)  (n≥2)
    • (f(m+n))^2-(f(m-n))^2=f(2m)*f(2n)  (m>n≥1)
    • f(n-1)*f(n+2)-f(n)*f(n+1)=(-1)^n  (n≥2)

    一些有关斐波那契数列的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

  • 相关阅读:
    【Spring】Spring中的Bean
    【Spring】Spring中的Bean
    【Spring】Spring中的Bean
    【Spring】Spring中的Bean
    简单的订阅发布模式
    setTimeout,setInterval原理
    按圆形轨迹排列元素
    平行四边形导航,背景颜色渐变动画(不支持IE6/7/8)
    html,body的关系
    判断移动端js代码
  • 原文地址:https://www.cnblogs.com/scotcn/p/Fibonacci_Number.html
Copyright © 2011-2022 走看看