题目
- 原文:
Write a method to generate the nth Fibonacci number. - 译文:
写一个函数来产生第n个斐波那契数。
分析
斐波那契数列想必大家都很熟悉啦,递归方法和非递归方法很常规,这里就不详细介绍了,代码详见fib1()和fib2()。
今天主要来介绍下一种新鲜学到的矩阵算法,以及其中用到的做幂运算的速度更快的方式:
此时fn就等于矩阵的(n-2)次方后矩阵中第一行两列数相加。
在做m^n这样的幂运算时,常规方法我们是这样的:
ll pow(ll m, ll n){
ll res = 1;
for(ll i=0; i<n; ++i)
res *= m;
return res;
}
这样的时间复杂度为O(n),而下面这种利用位运算的方法则可以把速度提升到O(logn):
ll pow1(ll m, ll n){
ll res = 1;
while(n > 0){
if(n&1) res *= m;
m *= m;
n >>= 1;
}
return res;
}
这是什么原理呢?
一个十进制的数字总是可以以二进制的方式表示,比如11写成二进制就是1011,即8+2+1。那么m^11就可以写成m的(1+2+8)次方,也即m的1次方乘上m的2次方乘上m的8次方。这样就把11这个数字依次右移与1做按位与运算,如果对应位为1就乘以相应的m的相应次方。
整数求幂如此,矩阵亦然,具体见代码fib3()。
代码
#include<iostream>
using namespace std;
typedef long long ll;
//recursion version
ll fib1(int n) {
if(n <= 0) {
return 0;
}
else if(n == 1 || n == 2) {
return 1;
}
else {
return fib1(n-1) + fib1(n-2);
}
}
//unrecursion version
ll fib2(int n) {
if(n <= 0) {
return 0;
}
else if(n == 1 || n == 2) {
return 1;
}
else {
ll a = 1, b = 1;
while(n-- > 2) {
ll c = a + b;
a = b;
b = c;
}
return b;
}
}
mul(ll c[2][2], ll b[2][2], ll a[2][2]) {
ll t[4];
t[0] = b[0][0] * a[0][0] + b[0][1] * a[1][0];
t[1] = b[0][0] * a[0][1] + b[0][1] * a[1][1];
t[2] = b[1][0] * a[0][0] + b[1][1] * a[1][0];
t[3] = b[1][0] * a[0][1] + b[1][1] * a[1][1];
c[0][0] = t[0];
c[0][1] = t[1];
c[1][0] = t[2];
c[1][1] = t[3];
}
pow(ll b[2][2], ll a[2][2], int n){
while(n > 0) {
if(n & 1) {
mul(b, b, a);
}
mul(a, a, a);
n >>= 1;
}
}
//matrix version
ll fib3(int n) {
if(n <= 0) {
return 0;
}
else if(n == 1 || n == 2) {
return 1;
}
else {
ll a[2][2] = {{1,1}, {1,0}};
ll b[2][2] = {{1,1}, {1,0}};
pow(b, a, n-3);
return b[0][0] + b[0][1];
}
}
int main() {
ll a = fib1(5);
ll b = fib2(5);
ll c = fib3(5);
cout << a << " " << b << " " << c;
}
输出结果