Java Memoization of Recursive方法

时间:2014-03-25 14:40:18

标签: java memoization

我正在尝试创建Factorial函数的memoized版本。当我调用factMemoized(4)时,它首次计算4的阶乘并将其存储在Map中。当我再次调用factMemoized(4)时,它现在提供存储的结果,而不是再次重新计算它。这按预期工作。但是,当我调用factMemoized(3)时,它会重新计算该值,尽管它已将事实(3)计算为计算事实(4)的一部分。是否有任何方法可以确保即使是作为递归调用的一部分计算的值也将存储在地图中而不在fact()函数中添加memoization函数?

import java.util.HashMap;
import java.util.Map;


public class MemoizeBetter {

public static <F, T> Function<F, T> memoize(final Function<F, T> inputFunction) {
    return new Function<F, T>() {
      // Holds previous results
      Map<F, T> memoization = new HashMap<F, T>();

      @Override
      public T apply(final F input) {
        // Check for previous results
        if (!memoization.containsKey(input)) {
          // None exists, so compute and store a new one

          memoization.put(input, inputFunction.apply(input));
        }else{
            System.out.println("Cache hit:"+input);
        }

        // At this point a result is guaranteed in the memoization
        return memoization.get(input);
      }
    };
  }

public static void main(String args[]){


final Function<Integer, Integer> fact = new Function<Integer, Integer>() {
      @Override
      public Integer apply(final Integer input) {
        System.out.println("Fact: " + input);
        if(input == 1)
            return 1;
        else return input * apply(input -1);

      }
    };

    final Function<Integer, Integer> factMemoized = MemoizeBetter.memoize(fact);

    System.out.println("Result:"+ factMemoized.apply(1));
    System.out.println("Result:"+factMemoized.apply(2));
    System.out.println("Result:"+factMemoized.apply(3));
    System.out.println("Result:"+factMemoized.apply(2));
    System.out.println("Result:"+factMemoized.apply(4));
    System.out.println("Result:"+factMemoized.apply(1));    }    
}

interface Function<F,T>{
    T apply(F input);
}

2 个答案:

答案 0 :(得分:2)

问题是你的因子函数不会递归调用函数的memoized版本。

要解决此问题,有几个选项。

  1. 您可以参数化您的Factorial函数,并提供它应该递归调用的Function的引用。在unmemoized案例中,这将是函数本身;在memoized案例中,这将是memoizing包装器。

  2. 您可以通过扩展 Factorial函数类来实现memoization,覆盖而不是委托给unmemoized apply()。这很难做到临时,但是有一些实用工具可以动态创建子类(例如,这是实现AOP的常用方法)。

  3. 您可以让基本功能完全了解备忘录。

  4. 这是第一个选项的要点:

    interface MemoizableFunction<I, O> extends Function<I, O> {
    
        //in apply, always recurse to the "recursive Function"
        O apply(I input);
    
        setRecursiveFunction(Function<? super I, ? extends O>);
    }
    
    final MemoizableFunction<Integer, Integer> fact = new MemoizableFunction<Integer, Integer>() {
    
      private Function<Integer, Integer> recursiveFunction = this;
    
      @Override
      public Integer apply(final Integer input) {
        System.out.println("Fact: " + input);
        if(input == 1)
            return 1;
        else return input * recursiveFunction.apply(input -1);
      }
    
      //...
    };
    

答案 1 :(得分:0)

解决此问题的另一种方法是使用数组来存储已计算的斐波纳契值。它的工作方式是,如果第n个位置的斐波纳契存在于数组的第n个索引处,则该值不会再次计算,只是从数组中选取。

但是,如果在第n个位置的数组中不存在该值,则计算出该值。下面给出了这种方法的代码fibonacci() -

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/