题目
大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项。
n<=39
解析
预备知识
斐波那契数列(Fibonacci sequence),又称黄金分割数列、因数学家列昂纳多·斐波那契(Leonardoda Fibonacci)以兔子繁殖为例子而引入,故又称为“兔子数列”,指的是这样一个数列:1、1、2、3、5、8、13、21、34、……
通项公式为:
思路一
就是按照通项公式写出递归形式即可。
/**
* 普通递归形式
* @param n
* @return
*/
public static int Fibonacci2(int n) {
if(n <= 1) {
return n;
}
return Fibonacci2(n - 1) + Fibonacci2(n - 2);
}
思路二
上述递归存在一个问题,会重复计算相同子问题的解。
比如:
f(10) = f(9) + f(8)
f(9) = f(8) + f(7)
f(8) = f(7) + f(6)
f(7) = f(6) + f(5)
我们在计算f(10)的时候需要继续f(8),而在f(9)也要再计算f(8),下面的计算也是,存在大量的重复的计算,复杂度只奔指数级别。这时我们可以把已计算过的子问题的结果记录下来,下次再碰到相同的子问题,直接取值即可。
static int[] fibo = new int[45];
/**
* 记忆化搜索
* @param n
* @return
*/
public static int Fibonacci3(int n) {
if(n <= 1) {
return n;
}
if(fibo[n] != 0) {
return fibo[n];
}
fibo[n] = Fibonacci3(n - 1) + Fibonacci3(n - 2);
return fibo[n];
}
思路三
但记忆化搜索本身还是存在栈溢出的可能,递归的本质就是函数不断调用自身,即函数栈不断进行入栈操作,在递归深度达到一定程度时,必然会超过栈的内存限制,造成栈溢出。所以化递归为循环。
/**
* 循环形式
* @param n
* @return
*/
public static int Fibonacci1(int n) {
int first = 0;
int second = 1;
if(n == 0) {
return first;
}
if(n == 1) {
return second;
}
int result = 0;
for(int i = 2; i <= n; i++) {
result = first + second;
first = second;
second = result;
}
return result;
}
思路四
利用矩阵快速幂来做,复杂度可以达到O(logn)。
矩阵快速幂的原理与普通数的快速幂一样,不清楚可以自行看看快速幂的原理,还是挺简单,利用指数二进制表示来分解乘法。减少了乘法次数。
我们发现f(n) = f(n - 1) + f(n - 2)。对应的矩阵形式为:
化简上式可得:
代码如下:
/**
* 基于矩阵快速幂的解法
* 新颖,快速,牛逼
* @param n
* @return
*/
public static int Fibonacci4(int n) {
if(n == 0) {
return 0;
}
if(n == 1 || n == 2) {
return 1;
}
int[][] base = {{1, 1}, {1, 0}};
int[][] res = fastPowMatrix(base, n - 2);
return res[0][0] + res[1][0];
}
/**
* 矩阵快速幂
* @param base
* @param index
* @return
*/
public static int[][] fastPowMatrix(int[][] base, int index) {
int[][] res = new int[base.length][base[0].length];
for(int i = 0; i < base.length; i++) {
res[i][i] = 1;
}
int[][] t = base;
while(index != 0) {
if((index & 1) == 1) {
res = multipleMatix(res, t);
}
t = multipleMatix(t, t);
index >>= 1;
}
return res;
}
/**
* 矩阵相乘
* @param left
* @param right
* @return
*/
public static int[][] multipleMatix(int[][] left, int[][] right) {
int[][] res = new int[left.length][left[0].length];
for(int i = 0; i < left.length; i++) {
for(int j = 0; j < right.length; j++) {
for(int k = 0; k < left[0].length; k++) {
res[i][j] += left[i][k] * right[k][j];
}
}
}
return res;
}