West Midlands | 26 March SDC | Iswat Bello | Sprint 2 | Improve code with caches#202
West Midlands | 26 March SDC | Iswat Bello | Sprint 2 | Improve code with caches#202Iswanna wants to merge 4 commits into
Conversation
- Implement a dictionary-based cache (memo) to store previously calculated terms - Reduce time complexity from exponential O(2^n) to linear O(n) - Rename parameter 'n' to 'term_index' for better descriptive clarity - Add type hints and documentation for performance complexity
- Introduce 'memo' dictionary to cache results of recursive sub-problems - Use a state tuple (total, len(coins)) as a unique cache key - Refactor into a helper function to isolate recursive logic - Add a wrapper function to clear the cache and provide default denominations - Maintain legacy variable names to ensure code continuity
- Detail the transition from exponential O(2^n) to linear O(n) complexity - Explain the use of state-tracking tuples in the making_change algorithm - Summarize the space-vs-time trade-offs applied to Fibonacci and Coin Change - Document the refactoring into helper/wrapper patterns
nedssoft
left a comment
There was a problem hiding this comment.
Iswat — this is genuinely strong work, and your CHANGES_MADE.md shows you really understand what you did rather than just pattern-matching. Turning both of these from exponential down to linear with a hand-rolled cache, and being able to articulate the space-vs-time trade-off, is exactly the insight this exercise is chasing. 👏 A couple of touches I liked: renaming n → term_index so the Fibonacci function reads clearly, and choosing an immutable tuple for the Making Change key with a stated reason why.
One optional question inline — it's about the shape of your cache, not its correctness (your logic is sound and the whole test suite passes). You've nailed the task, so I'm marking this Complete. Lovely work. 🎉
(For your curiosity only: now that you've built memoisation by hand — which is the whole point of this exercise — it's worth peeking at Python's functools.cache / lru_cache. It's a one-line decorator that does exactly what your memo dict does. Something to explore later, nothing to change here.)
|
|
||
| def ways_to_make_change(total: int) -> int: | ||
| """Wrapper that matches the legacy test suite signature.""" | ||
| memo.clear() |
There was a problem hiding this comment.
You spotted something sharp here: because memo lives at module level and lingers between calls, you had to add memo.clear() so each fresh ways_to_make_change(...) doesn't reuse the previous call's cache. Nice catch — but needing to manually reset shared state is often a little hint worth listening to.
What do you think would happen if the memo dictionary instead lived inside ways_to_make_change — created fresh on each call — and the helper used that one (say, as a nested function, or by passing it down)? Would you still need to remember to .clear() it at all?
Your current version is correct — this is purely about making it harder to get wrong later. Something to turn over when you're curious.
Thank you so much for the feedback! I'm glad the renaming to term_index made the logic clearer to read. |
Learners, PR Template
Self checklist
Changelist
In this PR, I have optimised the Fibonacci and Making Change recursive algorithms by implementing manual memoisation. These changes transform functions that previously had exponential time complexity into efficient, linear-time operations.
A comprehensive breakdown of the logic, state-tracking strategies, and specific code changes is provided in the
CHANGES_MADE.mdfile included in this PR.Key Improvements
1. Fibonacci Sequence
memoto store and reuse results of previously calculated terms.term_indexand added type hints to clarify the function's purpose.2. Making Change Algorithm
(total, len(coins))to track sub-problems based on both the remaining amount and available denominations.Testing Done
All implementations were verified against the provided test suites:
fibonacci(200)now returns results instantly.test_9176(which produces a result in the quadrillions) completes in milliseconds, demonstrating the efficiency of the cache.Learning Reflection
These tasks served as a practical application of the Space-vs-Time trade-off. By "spending" memory (the
memodictionaries), I successfully "saved" massive amounts of CPU time, allowing the algorithms to scale to much larger inputs.