I just started learning Python and practicing using material from a CS course from Berkeley. This problem is from that class. If someone could either show me how to find the solution or help fix my program, I would be very grateful.
The function I'm having trouble with is make_change; it takes a dictionary with each key being the value of the currency and the values being the amount of that currency you have. The function takes this dictionary and a value and returns the shortest list possible that can be constructed from the given dictionary that lists from lowest to highest the units of currency that add up to exactly the inputted amount. Look at the doc strings for more concrete examples.
When I run doc tests on this function, the actual results of the doc tests give are the correct numbers in the correct order for most of these tests, but many have lists within lists, and the doc tests for making change for 25 gives back None after enough recursive cycles.
Could someone explain to me why this program is giving me back None and how to get rid of the lists within the lists? I would be grateful for any help.
def make_change(amount, coins):
"""Return a list of coins that sum to amount, preferring the smallest
coins available and placing the smallest coins first in the returned
list. The coins argument is a dictionary with keys that are positive
integer denominations and values that are positive integer coin counts.
>>> make_change(2, {2: 1})
[2]
>>> make_change(2, {1: 2, 2: 1})
[1, 1]
>>> make_change(4, {1: 2, 2: 1})
[1, 1, 2]
>>> make_change(4, {2: 1}) == None
True
>>> coins = {2: 2, 3: 2, 4: 3, 5: 1}
>>> make_change(4, coins)
[2, 2]
>>> make_change(8, coins)
[2, 2, 4]
>>> make_change(25, coins)
[2, 3, 3, 4, 4, 4, 5]
>>> coins[8] = 1
>>> make_change(25, coins)
[2, 2, 4, 4, 5, 8]
"""
if len(coins) == 0:
return None
smallest = min(coins)
rest = remove_one(coins, smallest)
lst = []
if smallest == amount:
return [smallest]
elif amount - smallest >= smallest:
amount -= smallest
lst.extend([smallest] + [make_change(amount, rest)])
return lst
elif amount - smallest < smallest:
return [make_change(amount, rest)]
def remove_one(coins, coin):
"""Remove one coin from a dictionary of coins. Return a new dictionary,
leaving the original dictionary coins unchanged.
>>> coins = {2: 5, 3: 2, 6: 1}
>>> remove_one(coins, 2) == {2: 4, 3: 2, 6: 1}
True
>>> remove_one(coins, 6) == {2: 5, 3: 2}
True
>>> coins == {2: 5, 3: 2, 6: 1} # Unchanged
True
"""
copy = dict(coins)
count = copy.pop(coin) - 1
if count:
copy[coin] = count
return copy
The make_change
function returns either a list or None
. That implies two things: (a) you don't need to wrap it in another list; and (b) you need to check for None
before concatenating its return value to other lists. If we apply those points to a piece of your code, we get something like this:
change = make_change(amount, rest)
if change is None:
return None
else:
return [smallest] + change
The bigger problem (why the function returns None
for 25) is caused by your algorithm: it is greedy (consuming smallest coins no matter what) and thus will fail if it marches down an impossible path. You need to add some backtracking logic.
The most straight-forward way to do this is to use recursion. Suppose you have to make change for 100 using coins from the list [14, 7, 3, 1]. If the shortest list uses the 14 coin, it will have length one more than the shortest way to change 86 using the same coins. If it doesn't use the 14 coin, then it will simply be the shortest way to change 100 using coins from [7, 3, 1]. Recursion will take care of the backtracking for you.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.