斐波那契数列算法
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了斐波那契数列算法相关的知识,希望对你有一定的参考价值。
参考技术A 斐波那契数列,又称黄金分割数列,指的是这样一个数列:0、1、1、2、3、5、8、13、21、34、……在数学上,斐波纳契数列以如下被以递归的方法定义:F(0)=0,F(1)=1,F(n)=F(n-1)+F(n-2)(n≥2,n∈N*)在现代物理、准晶体结构、化学等领域,斐波纳契数列都有直接的应用,为此,美国数学会从1963起出版了以《斐波纳契数列季刊》为名的一份数学杂志,用于专门刊载这方面的研究成果。我们以求解f(10)作为例子来分析递归求解的过程。要求得f(10),需要求得f(9)和f(8)。同样,要求得f(9),要先求得f(8)和f(7)……我们用树形结构来表示这种依赖关系
在这棵树中有很多结点会重复的,而且重复的结点数会随着n的增大而急剧增加。这意味这计算量会随着n的增大而急剧增大。事实上,用递归方法计算的时间复杂度是以n的指数的方式递增的,时间复杂度约等于O(2^n)
空间复杂度分析
在方法一的基础上改进
其实改进的方法并不复杂。上述方法之所以慢是因为重复的计算太多,只要避免重复计算就行了。比如我们可以把已经得到的数列中间项保存起来,如果下次需要计算的时候我们先查找一下,如果前面已经计算过了就不用再次计算了。算法步骤如下
一目了然,O(1) 约等于O(1)
这个是我目前见过最流弊的方式,但是前提你得先会用公式
时间复杂度是O(logn),就问你流弊不流弊。在介绍这种方法之前,先介绍一个数学公式:
算法实验课-1:斐波那契数列
算法实验课-1:斐波那契数列
1.定义
斐波纳契数列(Fibonacci sequence)以递归的方法定义:F(0)=0,F(1)=1, F(n)=F(n-1)+F(n-2)(n>=2,n∈N*)
2.求斐波那契数列的算法
2.1递归算法
根据定义实现的算法, 简单易实现
int Fib_Recursive(int n)
{
if(n == 0)
return 0;
else if(n == 1)
return 1;
else if(n == 2)
return 1;
else
return Fib_Recursive(n-1)+Fib_Recursive(n-2);
}
当要求的第n位稍大时, 该算法计算量急剧增大, 用递归方法计算的时间复杂度是以n的指数的方式递增的, 时间复杂度为O(2^n).
2.2迭代算法
定义一个数组 Arr[3] = { 0, 1, 1 } , 当求n = 0 , 1 , 2 时,直接从数组中返回Arr[n].
在n >= 3时,对数组中的数据进行迭代循环来计算第n个斐波那契数列. 例如, 在求第3个斐波那契数时:
Arr[0] <- Arr[1]; // Arr[0] = 1
Arr[1] <- Arr[2]; // Arr[1] = 1
Arr[2] <- Arr[0] + Arr[1];
// Arr[2] = 2, 此时Arr[2]就是第三个斐波那契数
// 如果要求第4个, 就循环这种步骤两次
代码如下:
int Fib_Iteration(int n)
{
int FibArray[3] = {0,1,1};
if(n < 3)
return FibArray[n];
else
{
//从i= 3开始循环
for(int i = 3; i <= n; ++i)
{
FibArray[0] = FibArray[1];
FibArray[1] = FibArray[2];
FibArray[2] = FibArray[0] + FibArray[1];
}
return FibArray[2];
}
}
单层循环,时间复杂度为O(n)
2.3通项公式法
斐波那契数列的通项公式如下:
double Fib_Formula(int n)
{
double phi = (1 + sqrt(5))/2;
double Fib_n = pow(phi,n) / sqrt(5);
Fib_n = floor(Fib_n + 0.5);
return Fib_n;
}
//由于各个函数返回值是double型,所以最后取整会有小数部分的0
在我的机器上如果返回float类型的值,由于精度的差异,最后一个准确的值是第31个数.
2.4矩阵乘方计算
这个算法基于上述公式 , 每次计算只需要对整数进行操作 , 是一个计算斐波那契数列的高效方法.
当以上公式中的 n = 1 时 , 可以在矩阵中找到 F(0) , F(1) , F(2)的值
2.4.1迭代计算矩阵乘方
/* 矩阵乘方 一 */
unsigned int Fib_Matrix(int n)
{
//用二维数保存矩阵初始状态
unsigned int FibMatrix[2][2] = {{0,1},{1,1}};
if(n == 0)
return FibMatrix[0][0];
else if(n == 1)
return FibMatrix[0][1];
else
{
//数组arr保存每次进行矩阵相乘后的结果
unsigned int arr[2][2];
//循环计算矩阵乘方
for(int i = 2; i <= n; ++i)
{
arr[0][0] = FibMatrix[0][0] * 0 + FibMatrix[0][1] * 1;
arr[0][1] = FibMatrix[0][0] * 1 + FibMatrix[0][1] * 1;
arr[1][0] = FibMatrix[1][0] * 0 + FibMatrix[1][1] * 1;
arr[1][1] = FibMatrix[1][0] * 1 + FibMatrix[1 ][1] * 1;
//将arr中的结果赋给FibMatrix, 用于下一次矩阵乘法
for(int j = 0; j < 2; ++j)
{
for(int k = 0; k < 2; ++k)
FibMatrix[j][k] = arr[j][k];
}
}
return FibMatrix[0][1];
}
}
/* 此种方式在计算矩阵的n次幂 (n >= 2) 时,要进行 n-1 次循环运算
所以采用分治法计算矩阵的乘方
*/
2.4.2分治法计算矩阵乘方
要计算 a^n , 有以下两种情况
-
n 为偶数时 , 有:
再继续判断 n/2 的奇偶性; -
n 为奇数时 , 有:
再继续判断(n-1)/2 的奇偶性; -
直到判断到 n/2 或 (n-1)/2 等于1时 , 开始回溯计算矩阵乘方 , 这样求幂运算的复杂度为O(log n)
/* 矩阵乘方 二 */
//这里的递归return的 值 在运算中没有实际作用,
//传入的第一个参数 数组名 相当于传地址调用
//可以将矩阵乘方的结果带回上一层递归
//计算矩阵乘方
unsigned int Matrix_Pow(unsigned int arr[2][2], int n)
{
if(n == 1)
{
arr[0][0] = 0;
arr[0][1] = 1;
arr[1][0] = 1;
arr[1][1] = 1;
return 0;
}
else
{
//偶数情况
if(n % 2 == 0)
{
Matrix_Pow(arr, n/2);
//临时保存矩阵相乘结果
unsigned int temp[2][2];
//这里的计算过程应该再封装一下, 代码就不会那么恶心了,跟baba一样
temp[0][0] = arr[0][0] * arr[0][0] + arr[0][1] * arr[1][0];
temp[0][1] = arr[0][0] * arr[0][1] + arr[0][1] * arr[1][1];
temp[1][0] = arr[1][0] * arr[0][0] + arr[1][1] * arr[1][0];
temp[1][1] = arr[1][0] * arr[0][1] + arr[1][1] * arr[1][1];
//重新赋值给arr数组
for(int j = 0; j < 2; ++j)
{
for(int k = 0; k < 2; ++k)
arr[j][k] = temp[j][k];
}
return 1;
}
else
{
Matrix_Pow(arr, (n-1)/2);
unsigned int temp[2][2];
temp[0][0] = arr[0][0] * arr[0][0] + arr[0][1] * arr[1][0];
temp[0][1] = arr[0][0] * arr[0][1] + arr[0][1] * arr[1][1];
temp[1][0] = arr[1][0] * arr[0][0] + arr[1][1] * arr[1][0];
temp[1][1] = arr[1][0] * arr[0][1] + arr[1][1] * arr[1][1];
//奇数情况矩阵相乘后,还要再次与初始矩阵相乘
arr[0][0] = temp[0][0] * 0 + temp[0][1] * 1;
arr[0][1] = temp[0][0] * 1 + temp[0][1] * 1;
arr[1][0] = temp[1][0] * 0 + temp[1][1] * 1;
arr[1][1] = temp[1][0] * 1 + temp[1][1] * 1;
return 2;
}
}
}
//调用分支计算矩阵函数, 得到最终结果
unsigned int Fib_Matrix(int n)
{
//二维数组表示矩阵初始状态
unsigned int FibMatrix[2][2] = {{0,1},{1,1}};
if(n == 0)
return FibMatrix[0][0];
else if(n == 1)
return FibMatrix[0][1];
else
{
//将数组 arr 作为参数传入计算矩阵乘方的函数
unsigned int arr[2][2];
Matrix_Pow(arr,n);
return arr[0][1];
}
}
3.总结
整体来说这几种方法只要知道定义或者相关公式都还是比较好实现, 还有本次实验课的一些花里胡哨的要求就不记录了,还有就是矩阵那段乘法不封装是真的难看, 不过就这样吧
以上是关于斐波那契数列算法的主要内容,如果未能解决你的问题,请参考以下文章