递归斐波那契记忆化

14

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

我已经成功地编写了没有数组和记忆化的程序,现在我正在尝试实现它,并且我陷入了困境。我不确定如何构建它。我已经谷歌搜索并浏览了一些书籍,但没有找到太多帮助我解决如何实现解决方案的信息。

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);
  
 

}

}
上面的代码有误,我的斐波那契方法末尾是主要问题。我不知道如何将递归地将数字添加到数组的正确部分。

你知道在循环中从一开始设置值比使用递归要快得多。如果这只是作业,我才会使用递归。实际上,用这种方式计算可以表示的最大数字是如此之快,以至于不需要记住值。也就是说,仅仅将结果绘制到屏幕上就需要更长的时间。 - Peter Lawrey
我很希望能够使用递归来解决这个问题。也许有一种教我们如何使用递归的方法。 - Eogcloud
5
顺便提一句,这个术语应该是“记忆化”而不是“记忆化”。 - Miserable Variable
15个回答

26

在字典中,你需要区分已经计算过的数字和未计算过的数字,而目前你没有做到:你总是重新计算这些数字。

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
3
@Eogcloud说道:特殊情况是必要的,因为通用情况下的代码无法计算fib(0)和fib(1)(因为fib(-2)和fib(-1)未定义!)。你可以用if (n < 2) { return n; }替换特殊情况以避免数组查找。 - Joachim Sauer

13
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; 

}

与大多数上述解决方案相似,但使用Map代替。


使用Map肯定是可行的;然而,我会尽量避免在代码中添加不必要的复杂性。包含整数元素的数组可以被视为从索引到相关整数的映射。 - Adam Bak
我更喜欢他的方法 - Anthony Moon Beam Toorie

6

使用备忘录编写程序以打印前 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 + ", ");
    }
}

4
我相信你忘记了实际在字典中查找内容。
更改
else
    return dictionary[n] = fibonacci(n-1) + fibonacci(n-2);

to

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

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

它可以正常工作(我亲自测试过 :))


4

这是我的递归斐波那契记忆化实现。使用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

2
这是一个利用记忆化概念的完整类:

这里是一个完全成熟的类,它利用了记忆化概念:

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;

每次递归函数调用时。


1
这是另一种使用静态值数组来实现递归斐波那契(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/

1
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;

}

0

使用 System; 使用 System.Collections.Generic;

命名空间 Fibonacci { public class FibonacciSeries {

        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;
    }

}

}


0
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]));
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接