斐波那契数列

求斐波那契数列

题目

写一个函数,输入n,求斐波那契(Fibonacci)数列的第n项,斐波那契数列的定义如下:

f(n) = f(n-1) + f(n-2) n>1
f(0) = 0; f(1) = 1;

题目变形

一只青蛙一次可以跳上1级台阶,也可以跳上2级台阶。求解该青蛙跳上n级台阶总共有多少种跳法?

递归算法

long long Fibonacci(unsigned int n)
{
    if (n <= 0)
        return 0;
    if (n == 1)
        return 1;
    return Fibonacci (n-1) + Fibonacci(n-2);
}

存在大量的重复计算!!!

该算法计算量会随着n的增大而急剧增大,时间复杂度以n的指数的方式递增!!!

从下往上计算(实用的算法)

根据f(0)和f(1)计算f(2),再根据f(1)和f(2)计算f(3)……以此类推计算出第n项。

该算法的时间复杂度是O(n)!!!

long long Fibonacci(unsigned n)
{
    int result[2] = {0 , 1};
    if (n<2)
         return result[n];

    long long fibNMinusOne = 1;
    long long fibNMinusTwo = 0;
    long long fibN = 0;
    for (unsigned int i = 2; i <= n; i++)
    {
        fibN = fibNMinusOne + fibNMinuxTwo;
        fibNMinusTwo = fibNMinusOne;
        fibNMinusOne = fibN;
    }
    return fibN;
}

O(logn)但是不实用的算法

| f(n)   f(n-1)|= |1 1|n-1
|f(n-1)  f(n-2)|  |1 0|

采用分治算法,先求(n-1)/2次方,再平方以下即可。

求斐波那契数取模

斐波那契数列如下:

1,1,2,3,5,8,13,21,34......

其中:
f[n]=f[n-1]+f[n-2](n>=3),
f[1] = 1, f[2] = 1

对给定的下标n,求解a[n]%1997的值。

这个题目,主要是用到很关键的一个数学知识,求斐波那契数列的过程,可以转换为矩阵的连乘,矩阵的n次方算法又可以用分治的算法。

而且又有理论依据:

(n*m)%c=[(n%c)*(m%c)]%c
(n+m)%c=[(n%c)+(m%c)]%c 

所以过程中的结果可以随时取模,而不影响最终的结果。

斐波那契数列的矩阵连乘求法

我们知道我们若要简单计算f(n),有一种方法就是先保存a=f(0),b=f(1),然后每次设:

a'=b 
b'=a+b

并用新的a’和b’来继续这一运算。

如果大家熟悉利用“矩阵”这一工具的话,就知道,如果把a、b写成一个向量[a,b],完成上述操作相当于乘以矩阵

0 1 
1 1 

也就是说,如果我们要求第100个fibonacci数,只需要将矩阵
[0,1]乘上

0 1 
1 1 

的一百次方,再取出第一项。

因为我们知道,矩阵运算满足结合律,一次次右乘那个矩阵完全可以用乘上那个矩阵的N次方代替,更进一步,那个矩阵的N次方就是这样的形式:

f(n-1)  f(n) 
 f(n)  f(n+1)

而求矩阵的N次方,由于矩阵乘法满足结合律,所以我们可以用log(N)的算法求出——这个算法大家都会么?

一个是二分,一个是基于二进制的求幂。

二分的原理:要求矩阵的N次方A(N),设i=N/2

A(N)=A(i)*A(i)*A(1)  if N%2=1
A(N)=A(i)*A(i)       if N%2=0

基于二进制的原理:将N拆为二进制数,譬如13=1101那么 A^13= A^8 A^4 A^1 (这里^表示幂运算)。

也就是说,由A^1开始,自乘得到A^2,然后自乘得到A^4,如果N对应位为1,则将这个结果乘到目标上去。

这样的话,将所有乘法改为模乘,就可以得到一个较大Fibonacci数除以M的余数。

若不用递归,其实类似。