【问题标题】:Dynamic Programming Fibonacci Sequence动态规划斐波那契数列
【发布时间】:2017-04-17 00:25:40
【问题描述】:

我正在学习动态规划在斐波那契数列中的应用,并且有一个问题。以下是参考代码:

import java.math.BigInteger;
import java.util.Arrays;

public class FibonacciNumbersB {

    static BigInteger[] dp = new BigInteger[10000];

    public static void main(String[] args) {
        Arrays.fill(dp, BigInteger.ZERO);
        dp[0] = BigInteger.ONE;
        dp[1] = BigInteger.ONE;

        for(int i = 4; i < 9999; i++)
            System.out.println(fibRecurse(i).toString());
    }

    public static BigInteger fibRecurse(int N) {
        for(int i = 2; i < N; i++) {
            // For numerous calls to this function, this will save as it goes
            if(dp[i].equals(BigInteger.ZERO))
                dp[i] = dp[i - 1].add(dp[i - 2]);
        }

        return dp[N - 1];
    }
}

我有一个语句检查dp[i] 是否在fibRecurse 方法中等于0(尽管fibRecurse 不是递归的)。

检查dp[i] 是否已经计算过或者让dp[i] 等于前两个元素的总和是否更有效?

【问题讨论】:

  • 检查 null 或重新计算 dp[i] 是否更有效? @ElliottFrisch
  • 对您的代码进行基准测试。但这不是递归算法,你的记忆也无济于事。
  • 也许我不明白你的问题,但首先检查值会更有效,而不是总是重新计算 - 至少对于大量输入。这不就是动态规划的重点吗?
  • @Chris 哦,我明白了。感谢您的回复 - 这就是我想要的。
  • 没问题。动态编程与其他一些“算法类型”非常相似。我认为这有点像分而治之,但是您将“子问题”保存到解决方案中,目的是可能重用它们。希望能有所帮助

标签: java dynamic-programming fibonacci


【解决方案1】:

在执行此备忘录时,我更喜欢Map&lt;Integer, BigInteger&gt; 而不是使用固定的BigInteger[]。请注意,您当前的方法是不是 递归Map 可能被声明和初始化为

static Map<Integer, BigInteger> memo = new HashMap<>();
static {
    memo.put(0, BigInteger.ONE);
    memo.put(1, BigInteger.ONE);
}

然后检查当前n 是否存在于memo 中(如果存在,则返回它)- 否则,计算机并存储它。喜欢,

public static BigInteger fibRecurse(int n) {
    if (memo.containsKey(n)) {
        return memo.get(n);
    }
    BigInteger v = fibRecurse(n - 1).add(fibRecurse(n - 2));
    memo.put(n, v);
    return v;
}

没有memoization的版本会简单地省略memo like

public static BigInteger fibRecurseSlow(int n) {
    if (n == 0 || n == 1) return BigInteger.ONE;
    BigInteger v = fibRecurse(n - 1).add(fibRecurse(n - 2));
    return v;
}

我认为您可以从我选择的较慢的方法名称中推断

【讨论】:

【解决方案2】:
import java.math.BigInteger;
import java.util.Arrays;

public class FibonacciNumbersB {

    static BigInteger[] dp = new BigInteger[10000];

    public static void main(String[] args) {
        dp[0] = BigInteger.ONE;
        dp[1] = BigInteger.ONE;
        int N = 9999;
         fibRecurse(N);
        for(int i = 0; i < N; i++)
            System.out.println(dp[i].toString()) ;
    }

    public static void fibRecurse(int N) {
        for(int i = 2; i < N; i++) {

                dp[i] = dp[i - 1].add(dp[i - 2]);
        }
    }
}

【讨论】:

  • 这不比上面的方法慢吗?
  • 时间复杂度与@Elliott Frisch 的答案相同,即 O(n)
  • 但是对于多次调用,您必须重复计算,而 @Elliot Frisch 则不必。
  • 在函数 fibRecurse(N) 之后,我计算了从第 1 到第 N 的所有数据并将它们保存到数组中。
  • 你不会一遍又一遍地计算总和吗?
【解决方案3】:

求斐波那契数列的代码写起来很简单。让我们考虑使用递归代码求斐波那契数集。

import java.util.Scanner;

class fac{
  public static void main(String a[]){

    Scanner sc=new Scanner(System.in);
    System.out.print("Enter Your number :");
    int n=sc.nextInt();

    System.out.print(fibonacci(n));
}   

public static int fibonacci(int x){
    if(x<2){
        return 1;
    }
    else{
        return (fibonacci(x-1)+fibonacci(x-2));
    }
}

}

在这个阶段,有许多相同的子问题在一次又一次地计算。因此,在这种情况下,时间复杂度对于大输入会达到高度。由于这个原因,出现了动态编程技术......在动态编程中,维护了一个额外的表('lookUp'表)来存储先前计算的子问题的值。在计算下一个子问题的值之前,检查创建的表('lookUp'表)中特定子问题的答案是否可用。如果它在'lookUp table'中,则获取特定子问题的答案。如果它不在“lookUp”表中,则计算特定问题的值并存储“lookUp”表。这就是动态规划技术的含义。执行这种技术有两种方法。

1.Memoization - memoization 是一种自顶向下方式计算子问题值的技术。让我们考虑一下斐波那契数列的代码。

 import java.util.Scanner;

 class fab{
   public static void main(String a[]){

    Scanner sc=new Scanner(System.in);
    System.out.print("Enter Your number :");
    int n=sc.nextInt();

    int[] lookUp=new int[n];        
    int i;

    for(i=0;i<n;i++){
        lookUp[i]=-1;
    }
    fibonachi(n);   
}
public static void fibonachi(int x){
    if(lookUp[x]==-1){
        if(x<=1){
            lookUp[x]=x;
        }
        else{
            lookUp[x]=fibonachi(x-1)+fibonachi(x-2);
        }
    }
    System.out.print(lookUp[x]);
}   

}

2.Tabulation - 这个计算从下到上的方式进行。首先考虑基本情况并执行。然后使用前面的案例执行接下来的步骤。让我们考虑使用制表技术的斐波那契序列代码。

import java.util.Scanner;

class fac{
  public static void main(String a[]){

    Scanner sc=new Scanner(System.in);
    System.out.print("Enter Your number :");
    int n=sc.nextInt();

    int[] lookUp=new int[n];        
    int i;

    lookUp[0]=1; // store base case values in the 'lookUp' table
    lookUp[1]=1;

    for(i=2;i<n;i++){
        lookUp[i]=lookUp[i-1]+lookUp[i-2];
    }
    System.out.print(lookUp[n-1]);
  } 

}

在这种情况下,首先存储基本情况值。然后使用以前的值计算下一个值。在制表中,必须计算所有值,因为使用以前的值计算出新值。所以记忆比制表好。就是这样.我希望,你能明白。谢谢!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-03-16
    • 1970-01-01
    • 2020-07-16
    • 2012-09-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-08-07
    相关资源
    最近更新 更多