Article Categories
- All Categories
-
Data Structure
-
Networking
-
RDBMS
-
Operating System
-
Java
-
MS Excel
-
iOS
-
HTML
-
CSS
-
Android
-
Python
-
C Programming
-
C++
-
C#
-
MongoDB
-
MySQL
-
Javascript
-
PHP
-
Economics & Finance
Explain Implement a Memoization Helper Function
Memoization is a programming technique that improves function performance by caching previously calculated results. When a function is called with the same arguments, instead of recalculating, it returns the cached result, significantly reducing execution time for expensive operations.
What is Memoization?
Memoization works by storing function results in a cache (usually an object or Map). When the function is called again with identical parameters, it checks the cache first. If the result exists, it returns the cached value; otherwise, it calculates the result and stores it for future use.
Basic Example: Slow Function Without Memoization
Let's start with a function that performs an expensive operation:
function slowAdd(a, b) {
// Simulate expensive computation
for (let i = 0; i < 100000; i++) {
// Time-consuming loop
}
return a + b;
}
console.time("First call");
console.log(slowAdd(5, 4));
console.timeEnd("First call");
console.time("Second call");
console.log(slowAdd(5, 4));
console.timeEnd("Second call");
9 First call: 2.156ms 9 Second call: 1.987ms
Both calls take similar time because the function recalculates the result each time.
Creating a Memoization Helper Function
Here's a generic memoization helper that can wrap any function:
function memoize(func) {
const cache = {};
return function(...args) {
// Create a unique key from arguments
const key = JSON.stringify(args);
// Check if result is already cached
if (cache[key]) {
console.log("Cache hit!");
return cache[key];
}
// Calculate and cache the result
console.log("Computing...");
const result = func.apply(this, args);
cache[key] = result;
return result;
};
}
function expensiveAdd(a, b) {
for (let i = 0; i < 1000000; i++) {}
return a + b;
}
const memoizedAdd = memoize(expensiveAdd);
console.time("First call");
console.log(memoizedAdd(10, 5));
console.timeEnd("First call");
console.time("Second call");
console.log(memoizedAdd(10, 5));
console.timeEnd("Second call");
console.time("Third call");
console.log(memoizedAdd(10, 5));
console.timeEnd("Third call");
Computing... 15 First call: 3.245ms Cache hit! 15 Second call: 0.123ms Cache hit! 15 Third call: 0.089ms
Real-World Example: Fibonacci Sequence
Fibonacci calculation is a perfect example where memoization provides dramatic performance improvements:
// Without memoization - exponential time complexity
function fibonacci(n) {
if (n <= 1) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
}
// With memoization - linear time complexity
const memoizedFibonacci = memoize(function(n) {
if (n <= 1) return n;
return memoizedFibonacci(n - 1) + memoizedFibonacci(n - 2);
});
console.time("Regular Fibonacci(35)");
console.log("Result:", fibonacci(35));
console.timeEnd("Regular Fibonacci(35)");
console.time("Memoized Fibonacci(35)");
console.log("Result:", memoizedFibonacci(35));
console.timeEnd("Memoized Fibonacci(35)");
Result: 9227465 Regular Fibonacci(35): 45.892ms Computing... Computing... Computing... Result: 9227465 Memoized Fibonacci(35): 0.234ms
Advanced Memoization with Custom Key Generation
For complex objects or specific use cases, you might need custom key generation:
function memoizeWithCustomKey(func, keyGenerator) {
const cache = new Map();
return function(...args) {
const key = keyGenerator ? keyGenerator(...args) : JSON.stringify(args);
if (cache.has(key)) {
return cache.get(key);
}
const result = func.apply(this, args);
cache.set(key, result);
return result;
};
}
// Custom key for user objects
const calculateUserScore = memoizeWithCustomKey(
function(user, multiplier) {
console.log(`Calculating score for ${user.name}`);
return user.points * multiplier;
},
(user, multiplier) => `${user.id}-${multiplier}` // Custom key
);
const user1 = { id: 1, name: "Alice", points: 100 };
const user2 = { id: 1, name: "Alice Updated", points: 100 }; // Same ID
console.log(calculateUserScore(user1, 2));
console.log(calculateUserScore(user2, 2)); // Uses cache despite name change
Calculating score for Alice 200 200
When to Use Memoization
| Good For | Avoid For |
|---|---|
| Pure functions (same input ? same output) | Functions with side effects |
| Expensive computations | Simple, fast operations |
| Recursive algorithms | Functions called once |
| Functions called repeatedly | Functions with large result sets |
Conclusion
Memoization is a powerful optimization technique that trades memory for speed by caching function results. It's particularly effective for expensive pure functions and recursive algorithms, providing dramatic performance improvements with minimal code changes.
