【问题标题】:Recursive Fibonacci memoization递归斐波那契记忆
【发布时间】:2011-12-14 02:14:06
【问题描述】:

我需要一些帮助,我正在为我在大学的编程 II 课程编写程序。该问题要求使用递归计算斐波那契数列。必须将计算出的斐波那契数存储在一个数组中,以停止不必要的重复计算并缩短计算时间。

我设法让程序在没有数组和记忆的情况下工作,现在我正在尝试实现它,但我被卡住了。我不确定如何构建它。我在 Google 上搜索并浏览了一些书籍,但没有找到太多可以帮助我解决如何实施解决方案的内容。

import javax.swing.JOptionPane;
public class question2
{
static int count = 0;
static int [] dictionary;

public static void main(String[] args)
{

int answer;
int num = Integer.parseInt(javax.swing.JOptionPane.showInputDialog("Enter n:"));

javax.swing.JOptionPane.showMessageDialog(null, 
        "About to calculate fibonacci(" + num + ")");

//giving the array "n" elements
dictionary= new int [num];

if (dictionary.length>=0)
dictionary[0]= 0;

if (dictionary.length>=1)
dictionary[0]= 0;
dictionary[1]= 1;


//method call
answer = fibonacci(num);

//output
JOptionPane.showMessageDialog(null,"Fibonacci("+num+") is "+answer+" (took "+count+" calls)");
}



  static int fibonacci(int n)
  {
count++;

// Only defined for n >= 0
if (n < 0) {
  System.out.println("ERROR: fibonacci sequence not defined for negative numbers.");
  System.exit(1);
}

// Base cases: f(0) is 0, f(1) is 1
// Other cases: f(n) = f(n-1) + f(n-2)/
if (n == 0) 
{
  return dictionary[0];
}

else if (n == 1) 
{
  return dictionary[1];
}

else
return dictionary[n] = fibonacci(n-1) + fibonacci(n-2);
  
 

}

}

以上不正确,我的fib方法结束是主要问题。我不知道如何让它递归地将数字添加到数组的正确部分。

【问题讨论】:

  • 您知道从一开始就在循环中设置值比使用递归要快得多。如果这是家庭作业并且您必须这样做,我只会使用递归。事实上,通过这种方式计算你可以表示的最大数字是如此之快,它可能不需要记住值。也就是说,在屏幕上绘制结果需要更长的时间。
  • 我会多么喜欢那个......不过,使用递归是特定于问题的。我猜想某种方式可以教我们它是如何工作的。
  • 顺便说一句,这个词是memoization,不是记忆。

标签: java math recursion fibonacci


【解决方案1】:

我相信你忘记在你的字典中查找内容了。

改变

else
    return dictionary[n] = fibonacci(n-1) + fibonacci(n-2);

else {
    if (dictionary[n] > 0)
        return dictionary[n];

    return dictionary[n] = fibonacci(n - 1) + fibonacci(n - 2);
}

它工作得很好(我自己测试过:)

【讨论】:

    【解决方案2】:

    您需要区分字典中已计算的数字和未计算的数字,而您目前不这样做:您总是重新计算数字。

    if (n == 0) 
    {
      // special case because fib(0) is 0
      return dictionary[0];
    }
    else 
    {
      int f = dictionary[n];
      if (f == 0) {
        // number wasn't calculated yet.
        f = fibonacci(n-1) + fibonacci(n-2);
        dictionary[n] = f;
      }
      return f;
    }
    

    【讨论】:

    • 谢谢你,我看了一个小时,无法确定我做错了什么或如何解决它。我在 Main 方法中定义了 fib(1) 和 fib(0) 是否真的需要特殊情况?
    • @Eogcloud:特殊情况是必要的,因为 fib(0) 和 fib(1) 不能用一般情况下的代码计算(如 fib(-2) 和 fib(-1 ) 未定义!)。您可以将特殊情况替换为 if (n &lt; 2) { return n; } 以避免数组查找。
    【解决方案3】:
    int F(int Num){
    int i =0;
    int* A = NULL;
    if(Num > 0)
    {
     A = (int*) malloc(Num * sizeof(int));
    }
    else
     return Num;
    
    for(;i<Num;i++)
     A[i] = -1;
    
    return F_M(Num, &A);
    
    
    }
    
    int F_M(int Num, int** Ap){
    int Num1 = 0;
    int Num2 = 0;
    
    if((*Ap)[Num - 1] < 0)
    {
      Num1 = F_M(Num - 1, Ap);
      (*Ap)[Num -1] = Num1;
      printf("Num1:%d\n",Num1);
    }
    else
      Num1 = (*Ap)[Num - 1];
    
    if((*Ap)[Num - 2] < 0)
    {
      Num2 = F_M(Num - 2, Ap);
      (*Ap)[Num -2] = Num2;
      printf("Num2:%d\n",Num2);
    }
    else
      Num2 = (*Ap)[Num - 2];
    
    if(0 == Num || 1 == Num)
    {
     (*Ap)[Num] = Num;
     return Num;
    }
    else{
    //  return ((*Ap)[Num - 2] > 0?(*Ap)[Num - 2] = F_M(Num -2, Ap): (*Ap)[Num - 2]  ) +     ((*Ap)[Num - 1] > 0?(*Ap)[Num - 1] = F_M(Num -1, Ap): (*Ap)[Num - 1]  );
      return (Num1 + Num2);
    }
    
    }
    
    int main(int argc, char** argv){
    int Num = 0;
    if(argc>1){
    sscanf(argv[1], "%d", &Num);
    }
    
    printf("F(%d) = %d", Num, F(Num));
    
    return 0;
    
    }
    

    【讨论】:

      【解决方案4】:

      使用记忆化打印第一个n斐波那契数字的程序。

      int[] dictionary; 
      // Get Fibonacci with Memoization
      public int getFibWithMem(int n) {
          if (dictionary == null) {
              dictionary = new int[n];
          }
      
          if (dictionary[n - 1] == 0) {
              if (n <= 2) {
                  dictionary[n - 1] = n - 1;
              } else {
                  dictionary[n - 1] = getFibWithMem(n - 1) + getFibWithMem(n - 2);
              }
          }
      
          return dictionary[n - 1];
      }
      
      public void printFibonacci()
      {
          for (int curr : dictionary) {
              System.out.print("F[" + i++ + "]:" + curr + ", ");
          }
      }
      

      【讨论】:

        【解决方案5】:

        这是使用静态值数组来处理递归 fibonacci() 方法的记忆化的另一种方法 -

        public static long fibArray[]=new long[50];\\Keep it as large as you need
        
        public static long fibonacci(long n){
        long fibValue=0;
        if(n==0 ){
            return 0;
        }else if(n==1){
            return 1;
        }else if(fibArray[(int)n]!=0){
            return fibArray[(int)n];    
        }
        else{
            fibValue=fibonacci(n-1)+fibonacci(n-2);
            fibArray[(int) n]=fibValue;
            return fibValue;
        }
        }
        

        请注意,此方法使用全局(类级别)静态数组 fibArray[]。要查看带有解释的整个代码,您还可以查看以下内容 - http://www.javabrahman.com/gen-java-programs/recursive-fibonacci-in-java-with-memoization/

        【讨论】:

          【解决方案6】:

          这是我对递归斐波那契记忆的实现。使用 BigInteger 和 ArrayList 可以计算第 100 个甚至更大的项。我尝试了第 1000 条,结果在几毫秒内返回,代码如下:

              private static List<BigInteger> dict = new ArrayList<BigInteger>();
              public static void printFebonachiRecursion (int num){
              if (num==1){
                  printFebonachiRecursion(num-1);
                  System.out.printf("Term %d: %d%n",num,1);
                  dict.add(BigInteger.ONE);
              }
              else if (num==0){
                  System.out.printf("Term %d: %d%n",num,0);
                  dict.add(BigInteger.ZERO);
              }
              else {
              printFebonachiRecursion(num-1);
              dict.add(dict.get(num-2).add(dict.get(num-1)));
              System.out.printf("Term %d: %d%n",num,dict.get(num));
              }
          }
          

          输出示例

          printFebonachiRecursion(100);
          
          Term 0: 0
          Term 1: 1
          Term 2: 1
          Term 3: 2
          ...
          Term 98: 135301852344706746049
          Term 99: 218922995834555169026
          Term 100: 354224848179261915075
          

          【讨论】:

            【解决方案7】:
            public static int fib(int n, Map<Integer,Integer> map){
            
                if(n ==0){
                    return 0;
                }
            
                if(n ==1){
                    return 1;
                }
            
                if(map.containsKey(n)){
                    return map.get(n);
                }
            
                Integer fibForN = fib(n-1,map) + fib(n-2,map);
                map.put(n, fibForN);
            
                return fibForN; 
            
            }
            

            与上述大多数解决方案类似,但改用地图。

            【讨论】:

            • 使用地图绝对有效;但是,我会尽量避免在代码中添加不必要的复杂性。包含整数作为元素的数组可以被认为是从索引到相关整数的映射。
            • 我更喜欢他的方法
            【解决方案8】:

            这是一个利用 memoization 概念的成熟类

            import java.util.HashMap;
            import java.util.Map;
            
            public class Fibonacci {
            
                public static Fibonacci getInstance() {
                    return new Fibonacci();
                }
            
                public int fib(int n) {
                    HashMap<Integer, Integer> memoizedMap = new HashMap<>();
            
                    memoizedMap.put(0, 0);
                    memoizedMap.put(1, 1);
            
                    return fib(n, memoizedMap);
                }
            
                private int fib(int n, Map<Integer, Integer> map) {
                    if (map.containsKey(n))
                        return map.get(n);
            
                    int fibFromN = fib(n - 1, map) + fib(n - 2, map);
            
                    // MEMOIZE the computed value
                    map.put(n, fibFromN);
            
                    return fibFromN;
                }
            }
            

            注意

            memoizedMap.put(0, 0);
            memoizedMap.put(1, 1);
            

            用于消除以下检查的必要性

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

            在每次递归函数调用时。

            【讨论】:

              【解决方案9】:
              #include <stdio.h>
              long int A[100]={1,1};
              long int fib(int n){
                  if (A[n])
                  {
                      return A[n];
                  }
                  else
                  {
                       return A[n]=fib(n-1)+fib(n-2);
                  }
              }
              int main(){
                  printf("%ld",fib(30));
              }
              

              【讨论】:

                【解决方案10】:

                这是我的实现。

                private static int F(int N, int[] A) {
                    if ((N == 0) || (N == 1)) return N;
                    if (A[N] != 0) return A[N];
                
                    if ((A[N - 1] != 0) && (A[N - 2] != 0)) {
                        A[N] = A[N - 1] + A[N - 2];
                        return A[N];
                    }
                
                    if (A[N-2] != 0) {
                        A[N] = A[N - 2] + F(N - 1, A);
                        return A[N];
                    }
                    if (A[N-1] != 0) {
                        A[N] = A[N - 1] + F(N - 2, A);
                        return A[N];
                    }
                    A[N] = F(N-1, A) + F(N-2, A);
                    return A[N];
                }
                

                【讨论】:

                • 我绝对不会调用方法“F”或参数 N 或 A,其他尝试阅读它的人不太清楚。
                • F 是斐波那契,N 是自然数 > 0 的标准数学约定。A 是一个数组。除非没有数学或 CS 背景,否则这些选择并非不合理。
                【解决方案11】:
                import java.util.HashMap;
                import java.util.Map;
                
                public class FibonacciSequence {
                
                    public static int fibonacci(int n, Map<Integer, Integer> memo) {
                        if (n < 2) {
                            return n;
                        }
                        if (!memo.containsKey(n)) {
                            memo.put(n, fibonacci(n - 1, memo) + fibonacci(n - 2, memo));
                        }
                        return memo.get(n);
                    }
                
                    public static int fibonacci(int n, int[] memo) {
                        if (n < 2) {
                            return n;
                        }
                        if (memo[n - 1] != 0) {
                            return memo[n - 1];
                        }
                        return memo[n - 1] = fibonacci(n - 1, memo) + fibonacci(n - 2, memo);
                    }
                
                    public static void main(String[] s) {
                        int n = 10;
                
                        System.out.println("f(n) = " + fibonacci(n, new HashMap<Integer, Integer>()));
                        System.out.println("f(n) = " + fibonacci(n, new int[n]));
                    }
                }
                

                【讨论】:

                  【解决方案12】:

                  可能太旧了,但这是我的快速解决方案

                  class Recursion {
                      func fibonacci(_ input: Int) {
                          var dictioner: [Int: Int] = [:]
                          dictioner[0] = 0
                          dictioner[1] = 1
                         print(fibonacciCal(input, dictioner: &dictioner))
                      }
                  
                      func fibonacciCal(_ input: Int, dictioner: inout [Int: Int]) -> Int {
                          if let va = dictioner[input]{
                              return va
                          } else {
                              let firstPart = fibonacciCal(input-1, dictioner: &dictioner)
                  
                              let secondPart = fibonacciCal(input-2, dictioner: &dictioner)
                  
                              if dictioner[input] == nil {
                                  dictioner[input] = firstPart+secondPart
                              }
                  
                              return firstPart+secondPart
                          }
                      }
                  }
                  
                  // 0,1,1,2,3,5,8
                  class TestRecursion {
                      func testRecursion () {
                          let t = Recursion()
                          t.fibonacci(3)
                      }
                  }
                  

                  【讨论】:

                    【解决方案13】:
                    public class FiboSeries {
                    
                        // first two terms of Fibonacci
                        int x1 = 0;
                        int x2 = 1;
                        long xn; // nth number in Fibo series
                        long[] array; // an array for implementing memoization
                    
                        // print the Nth number of Fibonacci - logic is f(n) = f(n-1) + f(n-2)
                    
                        long fibo(int n) {
                    
                            // initialize the array having n elements if it does not exist already
                            if (array == null) {
                    
                                array = new long[n + 1];
                    
                            }
                    
                            // Fetch the memoized value from the array instead of recursion
                            // for instance, fibo(3) will be calculated just once and stored inside this
                            // array for next call
                            if (array[n] != 0)
                    
                            {
                                xn = array[n];
                                return xn;
                            }
                    
                            // value of fibo(1)
                            if (n == 1) {
                                xn = x1;
                    
                            }
                    
                            // value of fibo(2)
                            if (n == 2) {
                                xn = x2;
                    
                            }
                    
                            // value of Fibo(n) using non linear recursion
                            if (n > 2) {
                    
                                xn = fibo(n - 1) + fibo(n - 2);
                            }
                    
                            // before returning the value - store it at nth position of an array
                            // However, before saving the value into array, check if the position is already 
                            //full or not
                    
                            if (array[n] == 0) {
                    
                                array[n] = xn;
                            }
                    
                            return xn;
                    
                        }
                    
                        public static void main(String[] args) {
                    
                            FiboSeries f = new FiboSeries();
                    
                            int n = 50;
                            long number = f.fibo(n);
                    
                            System.out.println(number);
                    
                        }
                    
                    
                    }
                    

                    【讨论】:

                    • 虽然此代码可以解决问题,including an explanation 说明如何以及为什么解决问题将真正有助于提高您的帖子质量,并可能导致更多的赞成票。请记住,您正在为将来的读者回答问题,而不仅仅是现在提出问题的人。请edit您的回答添加解释并说明适用的限制和假设。
                    【解决方案14】:

                    在 Swift 5.3 中

                    这是一个使用记忆的非常快速的方法。 首先我初始化我的缓存字典。

                    var cache = [Int:Int]()
                    

                    然后我创建我的斐波那契数生成器。由于它是一个递归函数,因此每次调用该函数理论上都会再次计算整个斐波那契数列,直到所请求的数字。这就是我们使用缓存来加速递归函数的原因:

                    func fibonacci(_ number: Int) -> Int {
                        // if the value is in the dictionary I just return it
                        if let value = cache[number] { return value }
                    
                        // Otherwise I calculate it recursively. 
                        // Every recursion will check the cache again, 
                        // this is why memoisation is faster!
                        let newValue = number < 2 ? number : fibonacci(number - 1) + fibonacci(number - 2)
                        cache[number] = newValue
                        return newValue
                    }
                    

                    我可以将我的序列保存在这样的数组中:

                    var numbers = Array(0..<10).map(fibonacci) //[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
                    

                    或者在循环中使用函数。

                    【讨论】:

                      【解决方案15】:

                      使用系统; 使用 System.Collections.Generic;

                      命名空间斐波那契 { 公共类斐波那契数列 {

                              static void Main(string[] args)
                          {
                              int n;
                              Dictionary<int, long> dict = new Dictionary<int, long>();
                              Console.WriteLine("ENTER NUMBER::");
                              n = Convert.ToInt32(Console.ReadLine());
                              for (int j = 0; j <= n; j++)
                              {
                                  Console.WriteLine(Fib(j, dict));
                              }
                          }
                      
                         
                      
                          public static long Fib(int n, Dictionary<int, long> dict)
                          {
                              if (n <= 1)
                                  return n;
                              if (dict.ContainsKey(n))
                                  return dict[n]; 
                             
                              var value = Fib(n - 1,dict) + Fib(n - 2,dict);
                              dict[n] = value;
                              return value;
                          }
                      
                      }
                      

                      }

                      【讨论】:

                        猜你喜欢
                        • 2021-11-17
                        • 2010-12-03
                        • 2019-06-04
                        • 2015-10-19
                        • 2014-04-02
                        • 2020-03-02
                        • 2019-07-03
                        • 2018-09-24
                        相关资源
                        最近更新 更多