斐波那契数列的最优算法(O(logN))
来源:互联网 发布:记账软件 编辑:程序博客网 时间:2024/05/22 05:48
相信大家都对斐波那契数列已经相当的熟悉了,最多两分钟就可以写出来以下时间复杂度为O(N)的代码:
//递归实现long long fib(int n){if (n =1 || n== 2){return 1;}return (fib(n - 2) + fib(n - 1));}
或者是这样的时间复杂度为O(N),空间复杂度为O(1):
//优化一:时间复杂度为O(N)long long fib(int n){long long* fibarry = new long long[n + 1];fibarry[0] = 0;fibarry[1] = 1;for (int i = 2; i <= n; i++){fibarry[i] = fibarry[i - 1] + fibarry[i - 2];}long long ret = fibarry[n];delete fibarry;return ret;}//优化二:时间复杂度O(N) 空间复杂度O(1)long long fib(int n){long long fibarry[3] = { 0, 1, 0 };for (int i = 2; i <= n; i++){fibarry[2] = fibarry[0] + fibarry[1];fibarry[0] = fibarry[1];fibarry[1] = fibarry[2];}return fibarry[2];}
等等这些都是不错算法,都可以实现斐波那契数列的求解,但是今天我们讨论的是斐波那契数列的最优算法(当然博主水平就这样,各位亲有什么更加优的算法可以和博主分享)。这个算法的时间复杂度是O(logN)。
斐波那契数列的递推公式是:f(n)=f(n-1)+f(n-2);
在线性代数中,类似于斐波那契数列这种递推式称为二阶递推式。我们可以用f(n)=af(n-1)+bf(n-2)将二阶递推式一般化。只要符合这种二阶递推式的算法,都可以将算法的时间复杂度降为O(logN)。当然,三阶,四阶....都可以,只要得到递推公式的n阶矩阵即可。如下:
f(n)=af(n-1)+bf(n-2)+......
(f(n),f(n-1))=(f(n-1),f(n-2))*matrix;(matrix是一个矩阵,几阶递推式就是几阶的矩阵,在这里是二阶的矩阵,斐波那契数列属于二阶)
或许这样看起来还不够直观,我们带入一个例子看一下:
f(3)=f(2)+f(1)->(f(3),f(2))=(f(2),f(1))*(matrix)^1;
f(4)=f(3)+f(2)->(f(4),f(3))=(f(3),f(2))*(matrix)^1=(f(2),f(1))*(matrix)^2;
.....
f(n)=(f(2),f(1))*(matrix)^n-2;
根据这些公式可以算出二阶的matrix={{1,1},{1,0}};
算出了matrix后只差怎样算出matrix^n-2这个难题了。时间复杂度的降低,在于降低求矩阵的n次方的时间复杂度。其实这个问题也不难,我们先来看一个数怎样能快速算出num^n,
例:10^68,我们通常是10*10乘上68次,这样时间效率为O(N),我们要用O(logN)方法算:
68的二进制序列为:1000100
10^68=10^64*10^4,也就是取出68二进制序列为1的位,其他忽略。这样我们只算了7次(二进制序列的长度)就可以算出10^68,效率就达到了O(logN)。(最优化算法的关键所在)
现在大家知道了算num^n的优化算法,matrix^n也是一样的道理。不懂的可以去查阅一下线性代数的矩阵乘法。
有了上面的基础,我们不难写出以下的代码:
//优化三 时间复杂度为O(logN)static const long long int Mod = 1000000007;//防止结果溢出static const int row = 2;static const int col = 2;struct DoubleArry//用来聚合二维数组{long _arry[row][col];DoubleArry(){for (int i = 0; i < row; ++i)for (int j = 0; j < col; ++j)_arry[i][j] = 0;}DoubleArry(long matrix[][col]){for (int i = 0; i < row; ++i)for (int j = 0; j < col; ++j)_arry[i][j] = matrix[i][j];}DoubleArry(const DoubleArry& d){for (int i = 0; i < row; ++i)for (int j = 0; j < col; ++j)_arry[i][j] = d._arry[i][j];}DoubleArry& operator=(DoubleArry d){swap(d._arry, _arry);return *this;}};
//两个矩阵相乘DoubleArry matrixMul(DoubleArry l, DoubleArry r){DoubleArry ret;for (int i = 0; i<row; ++i)for (int j = 0; j<col; ++j){for (int k = 0; k<row; ++k){ret._arry[i][j] += ((l._arry[i][k] %= Mod)*(r._arry[k][j] %= Mod)) % Mod;}}return ret;}
//矩阵的n次方DoubleArry GetmatrixPower(DoubleArry matrix, int n){DoubleArry ret;//DoubleArry ret = new DoubleArry(cellmtrix);for (int i = 0; i<col; ++i)//先把ret设为单位矩阵,相当于整数中的1{ret._arry[i][i] = 1;}DoubleArry tmp = matrix;for (; n != 0; n >>= 1){if ((n & 1) == 1)//只拿出二进制序列中为1的位来乘{ret = matrixMul(ret, tmp);}tmp = matrixMul(tmp, tmp);}return ret;}long fib(int n) {if (n<1)return 0;if (n == 1 || n == 2)return 1;long matrix[][col] = { { 1, 1 }, { 1, 0 } };DoubleArry ret = GetmatrixPower(matrix, n - 2);return ((ret._arry[0][0] + ret._arry[1][0]) % Mod;}
在写代码的过程中一开始博主是没有定义DoubleArry结构体的。用的是一个二维数组,但是编译一直错误,提示应使用(....)初始化聚合对象,一直搞不懂是什么情况,上网搜了一下网友的解释,发现是对于non-aggregates(非聚合对象),不能使用初始化列表。只有聚合对象才可以这样使用。所以把二维数组改为一个结构体来聚合起来,然后就成功了。
还有其他的办法就是使用指针,但是略麻烦,也不直观。有兴趣可以试一下。
本文出自 “稻草阳光” 博客,请务必保留此出处http://helloleex.blog.51cto.com/10728491/1769253
- 斐波那契数列的最优算法(O(logN))
- 51nod 1242 斐波那契数列的第N项(O(logn)求递推式)
- 求斐波那契数列O(logn算法)
- 【难】【数学】斐波那契数列的O(logn)解法
- O(logN) 计算经典斐波那契数列的某个数
- 斐波那契数列的最优算法(golang代码)
- 斐波那契数列 logn
- 递归的logN的优化(菲波那契数列,青蛙上台阶问题,母牛问题)!!!!
- 斐波那契logn算法
- 斐波那契数列最优解
- 斐波那契数列的应用(算法实现)
- O(logn)求Fibonacci数列[算法]
- O(logn)求Fibonacci数列[算法]
- O(logn)求Fibonacci数列[算法]
- 面试算法(八)斐波那契数列
- 【C】斐波那契数列(递归算法)
- 算法随练(斐波那契数列)
- 【算法】斐波那契数列--C++源代码(VS2015)
- 大数运算之字符串模拟
- 操作系统之银行家算法
- 数据结构之堆(Heap)的实现
- 找出N个数据中的最大的K个数据---堆排序
- 优先级队列
- 斐波那契数列的最优算法(O(logN))
- TableView 实现京东购物车功能
- 在网页上显示当前日期
- 类模板的分离编译
- 处理哈希冲突的线性探测法
- 模拟实现英汉字典(使用key/value形式的哈希表)
- 实现哈希桶(空间利用率较高的哈希表)
- [hadoop]hadoop2.x(七)
- 复习二进制位的一些操作