Fibonacci数列问题

两种方法实现Fibonacci数列。考虑性能对比。

方法1:迭代(考虑合成效益法则等问题)

方法2:保存上一个值和当前值,用空间换时间,循环算法复杂度O(n)

方法3: 矩阵乘法计算 复杂度O(logn)

运行结果如下:

使用迭代方法计算: 89
使用循环方法计算89
fib[0]: 34
fib[1]: 55
fib[2]: 34
fib[3]: 21
fib[4]: 13
fib[5]: 8
fib[6]: 5
fib[7]: 3
fib[8]: 2
fib[9]: 1
fib[10]: 1
请按任意键继续. . .

注意,使用递归法进行计算时,可以看到进行了很多重复运算,所以当n=45时,递归算法需要很长时间才能计算出结果。而迭代计算时间复杂度只有O(n),可以很快计算的得出结果。

PS:O(n)  VS O(lgn)

从此图可以看出复杂度为O(n)和O(logn)的程序性能实际上会差很多,并且数据量越大,差距会越大。1000个数据使用logn方法只需要10次即可,但是O(n)算法需要1000次。

将一个0.11mm厚的纸折叠25次,得到的厚度是一个富士山的高度。所以指数增长会是非常恐怖的增长速度。

矩阵乘法

我们将数列写成:

Fibonacci[0] = 0,Fibonacci[1] = 1

Fibonacci[n] = Fibonacci[n-1] + Fibonacci[n-2] (n >= 2)

可以将它写成矩阵乘法形式:

将右边连续的展开就得到:

下面就是要用O(log(n))的算法计算:

显然用二分法来求,结合一些面向对象的概念,C++代码如下:

class Matrix

{

public:

long matr[2][2];

Matrix(const Matrix&rhs);

Matrix(long a, long b, long c, long d);

Matrix& operator=(const Matrix&);

friend Matrix operator*(const Matrix& lhs, const Matrix& rhs)

{

Matrix ret(0,0,0,0);

ret.matr[0][0] = lhs.matr[0][0]*rhs.matr[0][0] + lhs.matr[0][1]*rhs.matr[1][0];

ret.matr[0][1] = lhs.matr[0][0]*rhs.matr[0][1] + lhs.matr[0][1]*rhs.matr[1][1];

ret.matr[1][0] = lhs.matr[1][0]*rhs.matr[0][0] + lhs.matr[1][1]*rhs.matr[1][0];

ret.matr[1][1] = lhs.matr[1][0]*rhs.matr[0][1] + lhs.matr[1][1]*rhs.matr[1][1];

return ret;

}

};

Matrix::Matrix(long a, long b, long c, long d)

{

this->matr[0][0] = a;

this->matr[0][1] = b;

this->matr[1][0] = c;

this->matr[1][1] = d;

}

Matrix::Matrix(const Matrix &rhs)

{

this->matr[0][0] = rhs.matr[0][0];

this->matr[0][1] = rhs.matr[0][1];

this->matr[1][0] = rhs.matr[1][0];

this->matr[1][1] = rhs.matr[1][1];

}

Matrix& Matrix::operator =(const Matrix &rhs)

{

this->matr[0][0] = rhs.matr[0][0];

this->matr[0][1] = rhs.matr[0][1];

this->matr[1][0] = rhs.matr[1][0];

this->matr[1][1] = rhs.matr[1][1];

return *this;

}

Matrix power(const Matrix& m, int n)

{

if (n == 1)

return m;

if (n%2 == 0)

return power(m*m, n/2);

else

return power(m*m, n/2) * m;

}

long fib4 (int n)

{

Matrix matrix0(1, 1, 1, 0);

matrix0 = power(matrix0, n-1);

return matrix0.matr[0][0];

}

这时程序的效率为Olog(N) 

 

公式解法:

O1的时间就能求得到F(n)了:

 

注意:其中[x]表示取距离x最近的整数。

C++写的代码如下:

long fib5(int n)

{

     double z = sqrt(5.0);

     double x = (1 + z)/2;

     double y = (1 - z)/2;

     return (pow(x, n) - pow(y, n))/z + 0.5;

}

这个与数学库实现开方和乘方本身效率有关的,我想应该还是在O(log(n))的效率。

 

总结:

上面给出了5中求解斐波那契数列的方法,用测试程序主函数如下:

int main()

{

     cout << fib1(45) << endl;

     cout << fib2(45) << endl;

     cout << fib3(45) << endl;

     cout << fib4(45) << endl;

cout << fib5(45) << endl;

     return 0;

}

函数fib1会等待好久,其它的都能很快得出结果,并且相同为:1134903170。

而后面两种只有在n = 1000000000的时候会显示出优势。由于我的程序都没有涉及到高精度,所以要是求大数据的话,可以通过取模来获得结果的后4位来测试效率与正确性。

另外斐波那契数列在实际工作中应该用的很少,尤其是当数据n很大的时候(例如:1000000000),所以综合考虑基本普通的非递归O(n)方法就很好了,没有必要用矩阵乘法。

在思考算法复杂度时,这种感觉是最重要的。例如要操作的数据有1000万条,如果能选择对数算法,那么只需几十次计算就可以了。相反,如果选错了算法,使用O(n2)或O(2n)的算法实现的话,写出的程序即使只有几百条数据,也要浪费相当多资源。

#include <iostream>
#include <vector>
#include <string>
#include <cmath>
#include <fstream>

using namespace std;

class Matrix
{
public:
       long matr[2][2];

       Matrix(const Matrix&rhs);
       Matrix(long a, long b, long c, long d);
       Matrix& operator=(const Matrix&);
       friend Matrix operator*(const Matrix& lhs, const Matrix& rhs)
       {
              Matrix ret(0,0,0,0);
              ret.matr[0][0] = lhs.matr[0][0]*rhs.matr[0][0] + lhs.matr[0][1]*rhs.matr[1][0];
              ret.matr[0][1] = lhs.matr[0][0]*rhs.matr[0][1] + lhs.matr[0][1]*rhs.matr[1][1];
              ret.matr[1][0] = lhs.matr[1][0]*rhs.matr[0][0] + lhs.matr[1][1]*rhs.matr[1][0];
              ret.matr[1][1] = lhs.matr[1][0]*rhs.matr[0][1] + lhs.matr[1][1]*rhs.matr[1][1];
              return ret;
       }
};

Matrix::Matrix(long a, long b, long c, long d)
{
       this->matr[0][0] = a;
       this->matr[0][1] = b;
       this->matr[1][0] = c;
       this->matr[1][1] = d;
}

Matrix::Matrix(const Matrix &rhs)
{
       this->matr[0][0] = rhs.matr[0][0];
       this->matr[0][1] = rhs.matr[0][1];
       this->matr[1][0] = rhs.matr[1][0];
       this->matr[1][1] = rhs.matr[1][1];
}

Matrix& Matrix::operator =(const Matrix &rhs)
{
       this->matr[0][0] = rhs.matr[0][0];
       this->matr[0][1] = rhs.matr[0][1];
       this->matr[1][0] = rhs.matr[1][0];
       this->matr[1][1] = rhs.matr[1][1];
       return *this;
}

Matrix power(const Matrix& m, int n)
{
       if (n == 1)
              return m;
       if (n%2 == 0)
              return power(m*m, n/2);
       else
              return power(m*m, n/2) * m;
}

//普通递归
long fib1(int n)
{
              if (n <= 2)
              {
                     return 1;
              }
              else
              {
                     return fib1(n-1) + fib1(n-2);
              }
}
/*上面的效率分析代码
long fib1(int n, int* arr)
{
              arr[n]++;
              if (n <= 1)
              {
                     return 1;
              }
              else
              {
                     return fib1(n-1, arr) + fib1(n-2, arr);
              }
}
*/

long fib(int n, long a, long b, int count)
{
       if (count == n)
              return b;
       return fib(n, b, a+b, ++count);
}
//一叉递归
long fib2(int n)
{
       return fib(n, 0, 1, 1);
}

//非递归方法O(n)
long fib3 (int n)
{
       long x = 0, y = 1;
       for (int j = 1; j < n; j++)
       {
              y = x + y;
              x = y - x;
       }
       return y;
}

//矩阵乘法O(log(n))
long fib4 (int n)
{
       Matrix matrix0(1, 1, 1, 0);
       matrix0 = power(matrix0, n-1);
       return matrix0.matr[0][0];
}

//公式法O(1)
long fib5(int n)
{
       double z = sqrt(5.0);
       double x = (1 + z)/2;
       double y = (1 - z)/2;
       return (pow(x, n) - pow(y, n))/z + 0.5;
}

int main()
{
       //n = 45时候fib1()很慢
       int n = 10;
       cout << fib1(n) << endl;
       cout << fib2(n) << endl;
       cout << fib3(n) << endl;
       cout << fib4(n) << endl;
       cout << fib5(n) << endl;
       return 0;
}
时间: 2024-10-28 21:20:48

Fibonacci数列问题的相关文章

Python中的函数递归思想,以及对比迭代和递归解决Fibonacci数列

什么是递归?简单的说就是:函数自身调用自身. "普通程序员用迭代,天才程序员用递归" 虽然递归 在运行时会不断出栈压栈,调用底层的寄存器,造成空间上的占用以及时间上的缓慢, 但在一些算法上面仍然是递归很实用 但需要注意的是: #递归是自己调用自己 很消耗时间,还会有消耗空间的危险,所以递归递归一定要知道"归去来兮" #所谓"归去来兮"就是指递归的两个原则: #1.调用了函数自身 #2.设置了自身正确的返回值 (必须有一个正确的返回停止条件,不能无

蓝桥杯算法训练 java算法 Fibonacci数列

问题描述 Fibonacci数列的递推公式为:Fn=Fn-1+Fn-2,其中F1=F2=1. 当n比较大时,Fn也非常大,现在我们想知道,Fn除以10007的余数是多少. 输入格式 输入包含一个整数n. 输出格式 输出一行,包含一个整数,表示Fn除以10007的余数. 说明:在本题中,答案是要求Fn除以10007的余数,因此我们只要能算出这个余数即可,而不需要先计算出Fn的准确值,再将计算的结果除以10007取余数,直接计算余数往往比先算出原数再取余简单. 样例输入 10 样例输出 55 样例输

Fibonacci数列(codevs 1250)

题目描述 Description 定义:f0=f1=1, fn=fn-1+fn-2(n>=2).{fi}称为Fibonacci数列. 输入n,求fn mod q.其中1<=q<=30000. 输入描述 Input Description 第一行一个数T(1<=T<=10000). 以下T行,每行两个数,n,q(n<=109, 1<=q<=30000) 输出描述 Output Description 文件包含T行,每行对应一个答案. 样例输入 Sample I

Fibonacci数列--矩阵乘法优化

Fibonacci数列 题目描述 定义:f0=f1=1, fn=fn-1+fn-2(n>=2).{fi}称为Fibonacci数列. 输入n,求fn mod q.其中1<=q<=30000. 输入描述 第一行一个数T(1<=T<=10000). 以下T行,每行两个数,n,q(n<=109, 1<=q<=30000) 输出描述 文件包含T行,每行对应一个答案. 样例输入 3 6 2 7 3 7 11 样例输出 1 0 10 数据范围及提示 1<=T<

Fibonacci数列

问题描述 Fibonacci数列的递推公式为:Fn=Fn-1+Fn-2,其中F1=F2=1. 当n比较大时,Fn也非常大,现在我们想知道,Fn除以10007的余数是多少. 输入格式 输入包含一个整数n. 输出格式 输出一行,包含一个整数,表示Fn除以10007的余数. 说明:在本题中,答案是要求Fn除以10007的余数,因此我们只要能算出这个余数即可,而不需要先计算出Fn的准确值,再将计算的结果除以10007取余数,直接计算余数往往比先算出原数再取余简单. 样例输入 10 样例输出 55 样例输

为什么Fibonacci数列相邻两项之比会趋于0.618

转帖: http://www.matrix67.com/blog/archives/5221 你或许熟知一个非常经典的结论: Fibonacci 数列 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, … (头两项都是 1 ,此后每一项都是前两项之和)的相邻两项之比将会越来越接近黄金比例 0.618 ,不信请看: 1 / 1 = 1.0000000... 1 / 2 = 0.50000000... 2 / 3 = 0.66666667... 3 / 5 = 0.60000000

tsinsen A1067. Fibonacci数列整除问题 dp

A1067. Fibonacci数列整除问题 时间限制:1.0s   内存限制:512.0MB 总提交次数:2796   AC次数:496   平均分:51.83 将本题分享到: 查看未格式化的试题   提交   试题讨论 问题描述 已知四个数:a,b,c,d,判断在第s个Fibonacci数到第t个Fibonacci数之间哪些数既不是a也不是b也不是c也不是d的倍数. 输入格式 第一行两个数,s,t,表示要判断第s个Fibonacci数到第t个Fibonacci数之间(包含第s个和第t个)的F

Fibonacci数列的性质

Fibonacci: 0, 1, 1, 2, 3, 5, 8, 13, .... F[0] = 0; 1: gcd(Fn, Fm) = F[gcd(n, m)]; 当n - m = 1 或 2时满足,可用数学归纳法证明: 2: 特征方程为 x^2 = x + 1, 类Fibonacci数列的特征方程为:ax^2 = bx + c; aF[n] = bF[n - 1] + cF[n - 2]; 3: (证明方法为补项和数学归纳法) f[0] + f[1] + ... + f[n] = f[n +

数据结构(1)—fibonacci数列的复杂度

开始第二遍复习数据结构,决定把一些有意思的题目做个简单的小结,第一个遇见的是这个经典的Fibonacci数列,题目要求是求这个数列的时间复杂度,对于这个数列,我也不作过多的介绍,下面是对数列的几种简单的实现 1.初始版 long fibonacci1(int n){ if(n==0) return 0; if(n==1) return 1; if(n>1){ return fibonacci1(n-1)+fibonacci1(n-2); } }//递归 这种写法是每一个初学者第一次接触到递归时都

Fibonacci数列小程序

问题分析:Fibonacci数列特征是前两项数均为1,从第三项起,前两项的和为第三项的数的数值用公式归纳起来为:f1=f2=1.f1=f1+f2.f2=f1+f2. 程序源代码: #include<stdio.h> #include<stdlib.h> main() { int m,n,i,j=1; long f1,f2; printf("\t\t\t    Fibonacci数列测试小程序\n\n"); /***可省略***/ printf("请输入