Java中的Fibonacci序列耗时太长?

时间:2015-03-24 14:17:48

标签: java fibonacci

我试图在Java中找到Fibonacci序列的总和,但是运行时间过长(或者假设是?)。这会在我使用40岁以上的整数时减慢。

注意:在50时,会返回一个负值,令我大吃一惊。

有什么建议吗?

public static void main(String[] args) {        
    //Find Fibonacci sequence
    int sum=getSum(50);
    System.out.println("Sum of Fibonacci Numbers is " + sum); 
}
static int getSum(int n){
    if (n==0) return 0;
    if (n==1 || n==2) return 1;
    else return getSum(n-1) + getSum(n-2);
}

6 个答案:

答案 0 :(得分:6)

对于n > 2getSum(n)的调用以递归方式调用自身两次。每个调用都可以进一步递归。方法调用的总数可以缩放为2^n2^50是一个非常大的数字。这种不良的缩放反映了这样一个事实:简单的递归方法最终不必要地重新计算相同的结果(例如fib(4))很多次,这就是为什么你的程序在你增加n时变慢的原因

超过数据类型int的限制后,您获得某一点后的负返回值。您可以使用更广泛的数据类型获得更大的限制,大概是long。如果这还不够,那么你需要去BigInteger之类的东西,而且会有很大的性能损失。

答案 1 :(得分:4)

如果要计算第50个Fibonacci数,则需要使用long而不是int。第50个斐波纳契数是12586269025并且超过了int的最大值(见http://www.maths.surrey.ac.uk/hosted-sites/R.Knott/Fibonacci/fibtable.html)。

非递归算法可能会更快,请参阅http://planet.jboss.org/post/fibonacci_sequence_with_and_without_recursion了解不同的实现。

答案 2 :(得分:2)

正如其他人已经说过的那样,你应该使用long来计算斐波纳契值,因为这个数字会很快变长。

如果您的表格优先级是性能,您可以使用以下公式:

http://mathurl.com/kcjrbja.png

http://mathurl.com/q4r3qyv.png

(取自线性代数讲座的想法,实际公式取自Wikipedia。)
这样你就可以得到恒定时间内的第n个斐波纳契数(取决于公式中第n个幂的计算)。


以下代码计算前93个数字的斐波那契序列,没有等待时间(在我的机器上):

private static final double SQRT_FIVE = Math.sqrt(5);
private static final double GOLDEN_RATIO = (1 + SQRT_FIVE) / 2;

public static void main(String[] args) {
    for(int i = 0; i <= 92; i++) {
        System.out.println("fib(" + i + ") = " + calculateFibonacci(i));
    }
}

public static long calculateFibonacci(int n) {
    double numerator = Math.pow(GOLDEN_RATIO, n) - Math.pow(1-GOLDEN_RATIO, n);
    double denominator = SQRT_FIVE;

    // This cast should in general work, as the result is always an integer. 
    // Floating point errors may occur!
    return (long)(numerator/denominator); 
}

从长度上的第94个数字开始不再足够,你需要使用BigInteger并拟合数学运算,因为double计算可能会产生如此大数字的计算错误。

答案 3 :(得分:1)

首先,使用long而不是int,以避免溢出。

其次,使用非递归算法,因为我认为在指数时间内存在递归算法。一个设计良好的非递归的将在线性时间内解决(我认为)。

非递归示例

static long getSum(int n){

    long[] fibonacci = new long[n];
    fibonacci[0] = 1;
    fibonacci[1] = 1;

    if (n==0) return 0;
    if (n==1 || n==2) return 1;

    for(int i = 2; i < n;i++){
        fibonacci[i] = fibonacci[i-1]+ finonacci[i-2];
    }
    return fibonacci[n-1];
}

我还没有对此进行测试,但它应该有效。

如果您打算经常调用此方法,将数组存储在方法之外可能是谨慎的,因此在执行此操作时这是一个简单的查找。这将为已经计算过至少一次的数字提供恒定的时间解决方案。以下是一个例子。

static long[] fibonacci= {1,1};
static long getSum(int n){

    if (n==0) return 0;
    if (n==1 || n==2) return 1;

    int old_length = fibonacci.length;

    if(fibonacci.length < (n-1)){
        fibonacci = Arrays.copyOf(fibonacci,n);
    }else{
        return fibonacci[n-1];
    }



    for(int i = old_length; i < n;i++){
        fibonacci[i] = fibonacci[i-1]+ finonacci[i-2];
    }
    return fibonacci[n-1];
}

同样,该示例未经测试,因此可能需要进行一些调试。

这是算法的线性时间实现,它使用恒定开销,而不是线性开销。

static long getSum(int n){

    long currentNum = 0;
    long previousNum = 1;
    long previousNum2 = 1;

    if (n==0) return 0;
    if (n==1 || n==2) return 1;

    for(int i = 2; i < n;i++){
        currentNum = previousNum+ previousNum2;
        previousNum2 = previousNum;
        previousNum = currentNum;
    }
    return currentNum;
}

答案 4 :(得分:0)

如果要保持递归方法不变,请在数组或映射中缓存计算结果。为n计算一个Fibonacci时,保存该结果。然后,在您的方法中,首先看看您是否有结果并返回,如果您这样做。否则,进行递归调用。这是一个例子:仍然使用递归并且它非常快:

public static Map<Long,Long> cache = null;

public static void main(String[] args) {
    cache = new HashMap<Long,Long>();
    cache.put(0L,0L);
    cache.put(1L,1L);
    cache.put(2L,1L);
    Long sum=getSum(50L);
    System.out.println("Sum of Fibonacci Numbers is " + sum); 

}

static Long getSum(Long n){
    if (cache.containsKey(n)) { return cache.get(n); }
    else {
        Long fib = getSum(n-1) + getSum(n-2);
        cache.put(n, fib);
        return fib;
    }
}

答案 5 :(得分:0)

递归解决方案不必一定要放慢速度。如果要使用这种尾部递归解决方案,则可以节省大量内存,并且仍然可以达到很高的速度(例如,Fib(10000)在我的计算机上以1.1秒的速度运行)。

此处 n 是要为其计算斐波那契数的序列号,而 f0 f1 是两个累加器,用于先前和当前的斐波那契数。

public class FibonacciRec {
    public static int fib(int n, int f0, int f1) {
        if (n == 0) {
            return f0;
        } else if (n == 1){
            return f1;
        } else {
            return fib(n-1, f1, f0+f1);
        }
    }

    public static void main(String[] args) {
        System.out.println(fib(10, 0, 1));
    }
}