fibonacci序列的记忆化仍然比一般的递归解慢

mspsb9vt  于 2021-07-08  发布在  Java
关注(0)|答案(1)|浏览(268)

我注意到我的带记忆的递归斐波那契算法只适用于大于12的值。我和其他人比较了他是如何实现这个方法的,他还用这个方法传递了一个数组。我认为每次传递数组时,在重新调用方法时会占用太多内存,从而使程序变慢,但事实并非如此。不知何故,我仍然无法理解为什么我的外部数组没有比普通的递归斐波那契算法快。我想这可能是因为在我的if的许多条件,我检查它是慢,但我不确定。
如果可能的话,你可以看看我的代码,告诉我我做错了什么,或者在后台发生了什么。

public class Fibonacci2 {

    int memory[];

    public Fibonacci2(int f) {  
        if(memory==null){
            memory = new int[f+1];
            memory[0]=0;
            memory[1]=1;
            memory[2]=1;
        }   
    }

    public int recursive(int f){
        if(memory[f-1] != 0 && memory[f-2] != 0 && f>2){
            memory[f] = memory[f-1] + memory[f-2];
        }else if(memory[f-1] == 0 && memory[f-2] != 0 && f>2){
            memory[f] = recursive(f-1) + memory[f-2];
        }
        else if(f>2){
            memory[f] = recursive(f-1) + recursive(f-2);
        }
        return memory[f];
    }

}

public class Fibonacci1 {

    public Fibonacci1() {
    }

    public int recursive(int f){
        if(f > 2){
            return recursive(f-1)+recursive(f-2);
        }else{
            return 1;
        }
    }

}

public class Main {
    public static void main(String[] args) {

        int fibo = 12;
        Fibonacci1 fiboEx1 = new Fibonacci1();
        Fibonacci2 fiboEx2 = new Fibonacci2(fibo);

        int a = 0;
        long start = System.nanoTime();
        a = fiboEx1.recursive(fibo);
        long stop = System.nanoTime();
        System.out.println("fibo of " + fibo + " is " + a);
        System.out.println("Fibonacci time without memorization: "+(stop-start));

        int b = 0;
        start=System.nanoTime();
        b = fiboEx2.recursive(fibo);
        stop = System.nanoTime();
        System.out.println("fibo of " + fibo + " is " + b);
        System.out.println("Fibonacci time with memorization: "+(stop-start));
    }
}
u59ebvdq

u59ebvdq1#

我注意到我的递归fibonacci算法只适用于大于12的值。
我猜你的意思是写“值小于12”。不管怎样,这都不是真的:您的解决方案适用于大于12的值,直到结果类型溢出为止。
也就是说,您的代码存在多个问题。
不要测试 if(memory==null) 在构造函数中。这总是真的。 memory[f-1] != 0 && memory[f-2] != 0 && f>2 -你为什么要测试 f > 2 在这里?只有在开始测试时才有意义,以避免访问负数组元素。否则测试是多余的。
整个功能比必要的更复杂。分离访问记忆值的逻辑,使其更清晰、更简单、更不容易出错:

public int recursive(int f){
    if (f > 2 && memory[f - 2] == 0) {
        memory[f - 2] = recursive(f - 2);
    }
    if (f > 1 && memory[f - 1] == 0) {
        memory[f - 1] = recursive(f - 1);
    }
    memory[f] = memory[f - 2] + memory[f - 1];
    return memory[f];
}

  … 当然,你可以而且应该进一步简化:

public int recursive(int f){
    if (memory[f] == 0) {
        memory[f] = recursive(f - 2) + recursive(f - 1);
    }
    return memory[f];
}

这也一样。但是,不是每个函数调用都对记忆值执行多个冗余检查,而是简单地处理自己的值(即。 f 的),并要求自己的休息。
从根本上说,将它作为一个带有构造函数和附加函数的类是毫无意义的:该函数只能用于获取单个值的fibonacci数,此外,构造函数和函数调用中的fibonacci数必须相同(这很容易出错!)。例如,以下代码崩溃: new Fibonacci2(10).recursive(15) . 这也是: new Fibonacci2(1) . 好的代码不会允许这样的错误发生。
以下是一个不存在这些问题的解决方案:

class Fib {
    static int memory[];

    private static void resizeMemory(int newSize, int[] oldValues) {
        if (newSize < 3) newSize = 3;
        memory = new int[newSize];
        System.arraycopy(oldValues, 0, memory, 0, oldValues.length);
    }

    public static int fib(int n) {
        if (memory == null || memory.length <= n) {
            resizeMemory(n + 1, new int[] {0, 1, 1});
        }

        if (n == 0) return 0;
        if (memory[n] == 0) memory[n] = fib(n - 2) + fib(n - 1);
        return memory[n];
    }
}

但我还是不会像这样写一个“真正的”斐波那契实现 — 维护一个全局内存缓存会带来复杂性,而且没有真正的用途。这里有一个不使用缓存的高效实现。实际上,如果你计算很多斐波那契数,它的效率只会降低,即使这样,这也不可能成为瓶颈。
为了说明这一点,这里是这样的:

private static int fibImpl(int n, int a, int b) {
    if (n == 0) return a;
    if (n == 1) return b;
    return fibImpl(n - 1, b, a + b);
}

public static int fib(int n) {
    return fibImpl(n, 0, 1);
}

相关问题