Fibonacci 数列及其计算方法
2016-06-17 23:02
627 查看
Fibonacci 数列及其计算方法
斐波那契数列(Fibonacci sequence),又称黄金分割数列,这个数列最早是由印度数学家提出来的。不过更多的人学习到这个数列是因为意大利数学家列昂纳多·斐波那契(Leonardoda Fibonacci)和他的《Liber Abaci》一书。在这本书中,列昂纳多·斐波那契以兔子繁殖为例子引出了这个序列,因此这个序列又称为“兔子数列”。这个序列的前几项是这样的:
在数学上,斐波纳契数列以如下被以递归的方法定义:
Fibonacci 数列的通项公式
Fibonacci 数列除了递归形式之外,当然还可以写出通项公式。下面就来算算 。是典型的线性差分方程,可以用经典的待定系数法来解,当然也可以用 变换解。考虑到不是每个人都学过 变换,这里就给个最基本的待定系数法。假设 , 和 是两个待定系数,那么有:
化简一下,得到:
很显然, 有两个解:
那么 也满足 。
可以通过起始条件来确定。
这是一个简单的二元一次方程,计算后可以得到:
所以:
当 较大时,
所以 较大时,
有些没有学过差分方程理论的同学可能会问为什么假设 。简单的说,这是猜的。当然也不是无缘无故的猜测。我们知道 的差分方程是这样的:
我们再构造两个辅助的差分方程:
那么当起始条件相同时,明显有 。
都是等比数列,通项公式如下:
所以 。
所以我们猜测 ,其中 。
程序实现
斐波纳契数列的定义是递归形式的。自然,用递归函数最容易实现。uint64_t Fibonacci(unsigned char n) { if( n == 0 ) return 0; if( n == 1 ) return 1; return Fibonacci(n - 1) + Fibonacci(n - 2); }
这个代码虽然简单,但是计算复杂度却太高。当 稍微大一点时,计算时间就会非常长。
我们可以简单的算一下,当计算 时, 内部会调用 次自己。
我们用 表示 中多少次调用自己。那么就有递归表达式:
那么这个 具体是多少呢? 显而易见,当 时,。
我们又知道 ,所以递归算法的时间复杂度是 。。也就是说当 时,递归函数要反复调用自己 次左右,这个计算时间是不能忍受的。
如果将递归算法改为递推,时间复杂度会降低很多。
uint64_t Fibonacci(unsigned char n) { if( n == 0 ) return 0; if( n == 1 ) return 1; uint64_t fn, fnn = 1, fnnn = 0; for(int i = 2; i <= n; i ++) { fn = fnn + fnnn; fnn = fn; fnnn = fnn; } return fn; }
这个程序很简答,for 循环中计算了 次, 所以时间复杂度为 。
那么原来的递归算法就没有改进的余地了吗?递归算法之所以算的慢,是因为计算 时,对于小于 的数 ,重复计算了很多次。下面这幅图给出了计算 时的递归调用关系。
可以看到 都重复计算了好几遍。其实,只要将计算过的 保存下来,下次用时直接读取就好了,这样就省去了反复计算 的问题。下面是改进后的代码,时间复杂度降低为 :
uint64_t Fibonacci(unsigned char n) { static uint64_t fib[256] = {0, 1}; if(n == 0) return 0; if( fib != 0 ) return fib ; fib = Fibonacci(n - 1) + Fibonacci(n - 2); return fib ; }
这个代码还用到了 static 型局部数据变量的一个特性:没有指定值的元素自动初始化为 0。如果没有这个特性,我们的程序中就要写上 255 个 0 了。
除了上面给出的两种 算法之外,还有没有更快的计算 的方法呢? 答案是肯定的。
我们将递推表达式变变型就能得到:
因此,计算 就变成了计算一个矩阵的 次方的问题了。而这个问题是有快速算法的。
当 为偶数时, 所以只要计算出了 了之后再自乘一下就行了。
当 为 奇数时, 所以只要计算出了 了之后自乘一下在加一下自己就可以了。
也就是说当 时,只需要进行 次乘法计算就可以了。当 时,最多也就是进行 次乘法计算 和 次加法计算。所以算法复杂度是 。当 较大时,,所以当 较大时这样计算会快的多。这里就不写出这种算法的具体实现了,感兴趣的同学可以自己写写。
这里还有一个问题一直没有解决,就是我们的程序可以正确计算到 为多少。 我们知道一个 位的无符号整数可以表示的最大的整数是 。当 自然计算结果就是错误的了。 估计这个最大的 还要用到我们上面给出的通项公式的近似结果:
简单的计算可知:
所以:
所以 32 位无符号整数最大可以表示到 ,64 位无符号整数最大可以表示到 。
相关文章推荐
- Fibonacci 数列及其计算方法
- new、new()和new[]三者的区别
- Leetcode算法题分类解析:(一)总览
- D3制作基础图表学习总结(part1)
- e.KeyChar用到的键盘对应ASCII码值(转)
- (OK)(OK)(All in CLI) running two Android-x86 which connect to NS3(MANETs) via "ethernet bridge"
- react-native-http请求后navigator导航跳转
- 一眨眼已做开发十年
- 蓝牙开发(1)--软件端开发的理解
- springmvc+datagrid+json分页
- Django Hello World
- ssh整合常见问题总结
- java数据结构 堆与堆排序
- C#中的String.Format方法(转)
- Android NDK开发篇:Java与原生代码通信(异常处理)
- CMM软件成熟度
- 计算机网络基础知识
- 关于tableView的cell距离下面间距38的出现原因以及解决
- hdu-5587 Array(回溯)
- Android NDK开发篇:Java与原生代码通信(数据操作)