
Problem Statement
Memoization is a powerful technique in programming, where functions avoid re-computing results for previously processed input by caching these results. The given task involves building a memoized version of a function, fn
, which should not re-compute results for the same inputs but instead, retrieve them from a cache.
Three specific functions can be memoized:
Sum Function: This function,
sum(a, b)
, computes the sum of two integers. Ifsum
has already been computed for a pair(a, b)
, retrieving it from a cache must consider(a, b)
and(b, a)
as different pairs unlessa = b
.Fibonacci Function: The
fib(n)
calculates then
-th Fibonacci number. For input values wheren <= 1
, it returns 1. For other values, it uses the recursive relationfib(n) = fib(n - 1) + fib(n - 2)
.Factorial Function: The
factorial(n)
computes the factorial ofn
, wherefactorial(n) = n * factorial(n - 1)
forn > 1
, and returns 1 ifn <= 1
.
Examples
Example 1
Input:
fnName = "sum" actions = ["call","call","getCallCount","call","getCallCount"] values = [[2,2],[2,2],[],[1,2],[]]
Output:
[4,4,1,3,2]
Explanation:
const sum = (a, b) => a + b; const memoizedSum = memoize(sum); memoizedSum(2, 2); // "call" - returns 4. sum() was called as (2, 2) was not seen before. memoizedSum(2, 2); // "call" - returns 4. However sum() was not called because the same inputs were seen before. "getCallCount" - total call count: 1 memoizedSum(1, 2); // "call" - returns 3. sum() was called as (1, 2) was not seen before. "getCallCount" - total call count: 2
Example 2
Input:
fnName = "factorial" actions = ["call","call","call","getCallCount","call","getCallCount"] values = [[2],[3],[2],[],[3],[]]
Output:
[2,6,2,2,6,2]
Explanation:
const factorial = (n) => (n <= 1) ? 1 : (n * factorial(n - 1)); const memoFactorial = memoize(factorial); memoFactorial(2); // "call" - returns 2. memoFactorial(3); // "call" - returns 6. memoFactorial(2); // "call" - returns 2. factorial was not called because 2 was seen before. "getCallCount" - total call count: 2 memoFactorial(3); // "call" - returns 6. factorial was not called because 3 was seen before. "getCallCount" - total call count: 2
Example 3
Input:
fnName = "fib" actions = ["call","getCallCount"] values = [[5],[]]
Output:
[8,1]
Explanation:
fib(5) = 8 // "call" "getCallCount" - total call count: 1
Constraints
0 <= a, b <= 10⁵
1 <= n <= 10
1 <= actions.length <= 10⁵
actions.length == values.length
actions[i]
is one of "call" or "getCallCount"fnName
is one of "sum", "factorial", or "fib"
Approach and Intuition
The problem showcases interactions with memoized functions via a simulated environment where a series of actions
and their respective values
are provided. The core principle is:
- Cache results: Store function results for each unique set of arguments.
- Unique cache key: For functions with multiple arguments (e.g.
sum(a, b)
), use an argument string such as"a,b"
to differentiate inputs properly. - Track function calls: Maintain a counter that only increments when an actual computation happens (i.e. when the result is not fetched from the cache).
Function Behavior:
Sum Function:
(a, b)
and(b, a)
are treated as different unlessa === b
.- Function call counter increases only for first-time calls for a unique argument pair.
Factorial Function:
- Caches results of
factorial(n)
. - Recursive calls within
factorial
also benefit from memoization, reducing the number of computations.
- Caches results of
Fib Function:
- Fibonacci is the most computationally expensive of the three without memoization.
- Memoization drastically reduces redundant computations by caching
fib(n)
results.
By applying these memoization principles consistently, the solution optimizes both time and resource usage across all three functions.
Solutions
- JavaScript
var cacheFunction = (func, cacheStore = {}) => (...params) => cacheStore[params.join()] ?? (cacheStore[params.join()] = func(...params))
This JavaScript solution provides a caching mechanism for functions to optimize performance, especially beneficial for expensive function calls. The cacheFunction
uses a higher-order function approach, which takes a function func
as an argument and returns a new function. This returned function checks if the function's results for the given parameters params
exist in cacheStore
. If the result exists, it returns the cached result, otherwise, it computes the result, stores it in cacheStore
, and then returns the new result.
Key components:
cacheFunction
accepts two parameters:func
(the function to be memoized) andcacheStore
(an object to store cached values, which defaults to an empty object if not provided).- The returned function utilizes the rest parameter
...params
to gather function arguments into an array. - It uses the nullish coalescing operator (
??
) to check the cache. If the cache doesn't have the value (undefined
ornull
), it evaluates the right-hand expression which callsfunc(...params)
and stores the result intocacheStore
using the joined parameter values as the key. - Cached results are retrieved by using the
.join()
method onparams
, converting the array of parameters into a string key.
Ensure to handle edge cases where array inputs might result in identical string keys for different input arrays. Also, this caching mechanism is best suited for functions with deterministic outputs for the same set of parameters.
No comments yet.