Memoize a Function (Count the Real Calls)
Problem
Write a function memoize(f) that returns a new function. The new function remembers the results, and calls f only for an argument it has not seen before. Use it to compute the Fibonacci number F(n) (F(0) = 0, F(1) = 1) with recursion, where the recursive calls also go through the memoized function. Print result: F(n) and calls: the number of times the real Fibonacci code ran.
Constraints
- 0 <= n <= 70
Examples
Input
10
Output
result: 55 calls: 11
Solutions
A memoizing wrapper is a function that takes a function and returns a function: a higher-order function. The calls count shows the effect: F(10) needs only 11 real calls (one for each value from 0 to 10) instead of 177. The recursion must call the wrapper, not the original function, or the cache is never used.
/*
* Problem : Memoized Fibonacci.
* Input : One integer n.
* Output : Two lines.
* Approach: A caching wrapper.
* Time : O(n)
* Space : O(n)
* Java : 8 or newer.
*/
import java.util.Scanner;
public class Main {
// Step 1: memoize takes a function and returns a new function with a cache (a map from the argument to the result).
static java.util.function.LongUnaryOperator memoize(java.util.function.LongUnaryOperator function, java.util.Map<Long, Long> cache) {
return argument -> {
Long saved = cache.get(argument);
if (saved != null) {
return saved;
}
long result = function.applyAsLong(argument);
cache.put(argument, result);
return result;
};
}
// Step 2: The counter of real calls, and the memoized Fibonacci function. The function must call itself through the memoized version.
static int calls = 0;
static java.util.function.LongUnaryOperator fibonacci;
// This method solves the task. It receives the input and returns the answer.
static String[] solve(long n) {
calls = 0;
java.util.Map<Long, Long> cache = new java.util.HashMap<>();
// Step 3: The real Fibonacci code counts each time it runs. Its recursive calls use the memoized function.
fibonacci = memoize(k -> {
calls++;
return k < 2 ? k : fibonacci.applyAsLong(k - 1) + fibonacci.applyAsLong(k - 2);
}, cache);
long result = fibonacci.applyAsLong(n);
return new String[] {"result: " + result, "calls: " + calls};
}
public static void main(String[] args) {
long n = new Scanner(System.in).nextLong();
System.out.println(String.join("\n", solve(n)));
}
}
Hints (1)
- What happens if the Fibonacci code calls itself directly?