簡體   English   中英

遞歸斐波那契記憶

[英]Recursive Fibonacci memoization

我正在為大學編程 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);
  
 

}

}

以上不正確,我的fib方法的結尾是主要問題。 我不知道如何讓它遞歸地將數字添加到數組的正確部分。

您需要區分字典中已經計算的數字和未計算的數字,而您目前沒有這樣做:您總是重新計算數字。

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;
}
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 代替。

使用 Memoization 打印前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 + ", ");
    }
}

這是我對遞歸斐波那契記憶化的實現。 使用 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

我相信你忘記在你的字典里查資料了。

改變

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

它工作得很好(我自己測試過:)

這是一個充分利用記憶概念的成熟類

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;

在每次遞歸函數調用時。

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;

}

這是使用靜態值數組對遞歸 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/

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

在 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]

或者在循環中使用該函數。

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

這是我的實現。

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

可能太舊了,但這是我的 swift 解決方案

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

    }


}

使用系統; 使用 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;
    }

}

}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM