-
定义
斐波那契(Fibonacci)数列,在形式上可表示为从0开始的一个正整数序列:
更正式一点的定义为:(摘自Cousera)
对于更深层次的探讨参见 维基百科:Fibonacci number
-
求解1:递归
看到Fibonacci的定义,最直观的解法是使用递归:
import java.math.BigInteger; public class Fibonacci1 { public static void main(String[] args) { //求解当n=40时的Fibonacci数 int n = 40; //输出为BigInteger类型 BigInteger result = null; long start = System.currentTimeMillis(); //求解Fibonacci result = fib(n); long end = System.currentTimeMillis(); System.out.println(String.format("RESUL::[%s], COST::[%s]ms", result, (end - start))); } private static BigInteger fib(int i) { if (i == 0) return BigInteger.valueOf(0); if (i == 1) return BigInteger.valueOf(1); return fib(i - 1).add(fib(i - 2)); } }
- 首先我们来验证算法的正确性:
按照维基百科上给出的前20项表格:
F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181 6765 给出验证代码:
package com.leetcode.problems; import java.math.BigInteger; public class Fibonacci1 { public static void main(String[] args) { for (int i = 0; i <= 20; i++) System.out.print(String.format("%s ", fib(i))); } private static BigInteger fib(int n) { if (n == 0) return BigInteger.valueOf(0); if (n == 1) return BigInteger.valueOf(1); return fib(n - 1).add(fib(n - 2)); } }
至此,可以断定算法是正确的。
- 然后再来看看它的效率:
当给定输入为 n = 35 时:
输出为9227465,用时 0.395 秒
当给定输入为 n = 40 时,我们来看看测试结果:
输出为102334155,用时 3.149 秒
当给定输入为 n = 50 时。。。计算的时间已经超出可接受的范围了。。。
可见这种直观的解法效率是极低的。
原因在于:当计算 n - 1 与 n - 2 时,它将分裂成两个分支:
即 n - 1 分裂为 n - 2,n - 3,而 n - 2 分裂为 n - 3,n - 4。不难看出,这里 n - 2,n - 3 被计算了两次。
-
求解2:缓存计算过的下标 n 所对应的Fibonacci数
根据上面的例子,不难想到,我们何不将每一次递归所遇到的重复计算都缓存起来,这样下次再遇到这个下标 n 时就不用重复计算了。
这里我们缓存前两次计算过的Fibonacci数 n-1 和 n-2,并将递归改为循环,代码如下:
private static BigInteger fastFib(int n) { BigInteger zero = BigInteger.valueOf(0L); if (n == 0) return zero; BigInteger one = BigInteger.valueOf(1L); if (n == 1) return one; BigInteger _2 = zero; BigInteger _1 = one; for (int i = 2; i <= n; i++) { BigInteger current = _2.add(_1); _2 = _1; _1 = current; } return _1; }
alright。。。大早晨的睡不着起来写了这篇博客,算是醒脑了。