Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java Memoization of Recursive method

I am trying to create a memoized version of Factorial function. When I call factMemoized(4), it computes the factorial of 4 for the first time and stores it in a Map. When I call factMemoized(4) again, it now gives the stored result instead of recomputing it again. This works as expected. But, when I call factMemoized(3), it recomputes the value, eventhough it had computed fact(3) as part of computing fact(4). Is there any way to make sure that even the values computed as part of recursive calls will be stored in the map without adding the memoization function within the fact() function?

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);
}
like image 944
Tim Avatar asked Mar 25 '14 14:03

Tim


People also ask

Does recursion use memoization?

What is Memoization? Memoization is a way to potentially make functions that use recursion run faster. As I'll show in an example below, a recursive function might end up performing the same calculation with the same input multiple times. This means it could end up taking longer than the iterative alternative.

Would Memoizing the recursive solution improve the running time?

In the case of the factorial function, an algorithm only benefits from the optimization of memoization when a program makes repeated calls to the function during its execution. In some cases, however, memoization can save time even for a single call to a recursive function.

Is recursion with memoization vs dynamic programming?

Recursion and Dynamic ProgrammingDynamic programming is nothing but recursion with memoization i.e. calculating and storing values that can be later accessed to solve subproblems that occur again, hence making your code faster and reducing the time complexity (computing CPU cycles are reduced).

What is memoization in Java?

Memoization is a technique whereby we trade memory for execution speed. Suppose you have a function which. Is costly to execute. Always returns the same output for the same input. May be called many times with the same input.


1 Answers

The issue is that your Factorial function does not call recursively into the memoized version of the function.

To fix this, there are a few options.

  1. You could parameterize your Factorial function and give it reference to the Function it should call recursively. In the unmemoized case, this will be the function itself; in the memoized case, this will be the memoizing wrapper.

  2. You could implement memoization through extending the Factorial function class, overriding, rather than delegating to, the unmemoized apply(). This is difficult to do ad-hoc, but there are utilities out there to create subclasses dynamically (this is a common way of implementing AOP, for example).

  3. You could give the base function full knowledge of the memoization to start with.

Here's the gist of the first option:

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

  //...
};
like image 106
Mark Peters Avatar answered Sep 22 '22 09:09

Mark Peters