递归求解斐波那契数列的问题
费波那契数列(意大利语:Successione di Fibonacci),又译费波拿契数、斐波那契数列、费氏数列、黄金分割数列。
-
(n≧2)
用文字来说,就是费波那契数列由0和1开始,之后的费波那契系数就由之前的两数相加。首几个费波那契系数是(OEIS A000045):
0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765, 10946,………………
特别指出:0不是第一项,而是第零项。
(以上来自维基百科)
求解Fibonacci数列用java的递归很简单:
public static long fibonacci(int n)
{
if(n == 0 || n == 1)
{
return 1;
}
return fibonacci(n – 1) + fibonacci(n – 2);
}
但用递归求解的效率几乎是无法容忍的,当n为50是,估计需要好几分钟,因为每递归调用一次fibonacci()函数,都会重新计算fibonacci(n – 1)和fibonacci(n – 2),这样有很多数都是重复计算的,一个比较简单的方法就是用一个数据结构将计算后的值缓存起来,
求解时先从缓存里面拿数据,没有的话再计算并将计算后的结果加到缓存里面,这样就避免了很多重复计算。下面是用HashMap将数据缓存的例子:
private static HashMap<Integer, Long> hashMap = new HashMap<Integer, Long>();
private static long value;
public static long effectFibonacci(int n)
{
if(n == 0 || n == 1)
{
return 1;
}
if(hashMap.containsKey(n))
{
return hashMap.get(n);
}
else
{
value = effectFibonacci(n – 1) + effectFibonacci(n – 2);
hashMap.put(n, value);
}
return value;
}
这个算法跟上一个的效率相比有了很大的提高,当n = 1500时,花费的时间大约是 3082077 microseconds,而用上一个递归直接算,不知何年才能得出结果。
还有一种求解Fibonacci问题的方法就是利用动态规划算法,动态规划算法是将一个较大的问题分解为若干相似子问题,求解子问题然后合并并得到原问题的解,动态规划算法适用于有重叠子问题和最优子结构性质的问题,重叠子问题即原问题可分解为若干的求解算法相似的子问题,最优子结构就是局部最优导致全局最优,即能够将子问题的解合并后得到原问题的解,这跟贪心算法相似。下面是用动态规划算法求解Fibonacci数列问题:
public static long bestFinabocci(int n)
{
long previous = 1, next = 1;
long tmp = 2;
for (int i = 2; i <= n; i++)
{
tmp = previous + next; //从2开始,每一个后面的数都是前面两个数之和
previous = next;
next = tmp;
}
return tmp;
}
跟上一个使用递归缓存求解,动态规划算法在效率上有了很大的提高,同样当 n = 1500是,递归缓存花费的时间大约是 3082077 microseconds,而动态规划花费的时间大约是 40410 microseconds。
由上面的实验来看,在递归的问题上,最好使用循环代替递归,不得不用到递归时,最好将数据缓存起来避免重复计算,不要直接调用递归函数。