Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion Sprint-2/improve_with_caches/fibonacci/fibonacci.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
_memo = {}

def fibonacci(n):
if n in _memo:
return _memo[n]
if n <= 1:
return n
return fibonacci(n - 1) + fibonacci(n - 2)

_memo[n] = fibonacci(n - 1) + fibonacci(n - 2)
return _memo[n]
29 changes: 19 additions & 10 deletions Sprint-2/improve_with_caches/making_change/making_change.py
Original file line number Diff line number Diff line change
@@ -1,32 +1,41 @@
from typing import List

from typing import List, Dict, Tuple

def ways_to_make_change(total: int) -> int:
"""
Given access to coins with the values 1, 2, 5, 10, 20, 50, 100, 200, returns a count of all of the ways to make the passed total value.

For instance, there are two ways to make a value of 3: with 3x 1 coins, or with 1x 1 coin and 1x 2 coin.
"""
return ways_to_make_change_helper(total, [200, 100, 50, 20, 10, 5, 2, 1])
coins = [200, 100, 50, 20, 10, 5, 2, 1]
memo: Dict[Tuple[int, int], int] = {}
return ways_to_make_change_helper(total, coins, memo)


def ways_to_make_change_helper(total: int, coins: List[int]) -> int:
def ways_to_make_change_helper(total: int, coins: List[int], memo: Dict[Tuple[int, int], int]) -> int:
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd probably document what the cache key here is in the docstring - it's not obvious what the two values in the tuple are (and it took me a minute to work out why len(coins) was a valid thing to include in the cache key at all).

"""
Helper function for ways_to_make_change to avoid exposing the coins parameter to callers.
Uses memoization to cache results for (total, coin_index).
"""
if total == 0 or len(coins) == 0:
return 0
if total == 0:
return 1
if total < 0 or not coins:
return 0

key = (total, len(coins))
if key in memo:
return memo[key]

ways = 0
for coin_index in range(len(coins)):
coin = coins[coin_index]
count_of_coin = 1
while coin * count_of_coin <= total:
total_from_coins = coin * count_of_coin
if total_from_coins == total:
remainder = total - coin * count_of_coin
if remainder == 0:
ways += 1
else:
intermediate = ways_to_make_change_helper(total - total_from_coins, coins=coins[coin_index+1:])
ways += intermediate
ways += ways_to_make_change_helper(remainder, coins[coin_index + 1:], memo)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall good approach with memoization, but I have one tiny request here:
What happens when we do slicing in Python? (i.e. do varOfTypeList[x:y])? Does it allocate additional memory, potentially a lot of? Can we avoid additional allocation of a lot of memory?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In Python, doing list[x:y] creates a new list containing the elements from index x to y-1. This requires allocating additional memory proportional to the slice length. we can use a module called intertools (itertools.islice), which returns a lazy iterator over that range instead of creating a new list to avoid excess memory allocation.

count_of_coin += 1

memo[key] = ways
return ways
Loading