Memoization is a programming technique that involves caching the results of expensive function calls and reusing them when the same input occurs again. It is a form of dynamic programming that can be used to optimize recursive algorithms through caching the results of the function calls.
With memoization, when a function is called with a particular input, the function first checks if the result for that input is already stored in cache. If it is, then the function simply returns the cached result without having to execute the function again. However, if the result is not stored in cache, then the function computes the result and stores it in cache before returning it.
The most common implementation involves the use of a hash table to store the function inputs and their corresponding output from previous function calls.
Memoization becomes particularly useful when dealing with recursive algorithms, where the same function is called repeatedly with the same inputs. Without memoization, each function call would need to recomputed each time, leading to a lot of redundant computation time. However, with memoization, the results of the previous function calls are cached, reducing the time complexity of the algorithm from exponential to linear.
For example, consider the following implementation of the Fibonacci sequence using recursion:
def fib(n):
if n <= 1:
return n
else:
return fib(n-1) + fib(n-2)This implementation has a time complexity of O(2n), which means that the time taken to execute the function doubles for every increase in the input size. This is because each call to the function generates two more function calls.
However, we can use memoization to significantly reduce the time complexity of the function by caching the results of each function call. Hereβs an implementation of the memoized version:
cache = {}
def fib(n):
if n in cache:
return cache[n]
elif n <= 1:
return n
else:
result = fib(n-1) + fib(n-2)
cache[n] = result
return resultIn this implementation, we first check if the value of n already exists in cache. If it does, we simply return the cached result. If not, we compute the result and store it in cache for future use.
This implementation has a time complexity of O(n), since each function call is executed only once and subsequent function calls with the same input use the cached result. Therefore, memoization has reduced the time complexity of the function from exponential to linear, making it much more efficient.