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);
}
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.
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.
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).
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.
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.
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.
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).
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);
}
//...
};
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With