This problem is same as asked in here.
Given a list of coins, their values (c1, c2, c3, ... cj, ...), and the total sum i. Find the minimum number of coins the sum
Here is a fun way to do it. A bit of a hack, but that is why it is fun.
import math
def find_change(coins, value):
coins = sorted(coins, reverse=True)
coin_dict = {}
for c in coins:
if value % c == 0:
coin_dict[c] = value / c
return coin_dict
else:
coin_dict[c] = math.trunc(value/ float(c))
value -= (c * coin_dict[c])
coins = [1, 5, 10, 25]
answer = find_change(coins, 69)
print answer
[OUT]: {25: 2, 10: 1, 5: 1, 1: 4}
below is the same solution with annotations with edge case protection
import math
def find_change(coins, value):
'''
:param coins: List of the value of each coin [25, 10, 5, 1]
:param value: the value you want to find the change for ie; 69 cents
:return: a change dictionary where the key is the coin, and the value is how many times it is used in finding the minimum change
'''
change_dict = {} # CREATE OUR CHANGE DICT, THIS IS A DICT OF THE COINS WE ARE RETURNING, A COIN PURSE
coins = sorted(coins, reverse=True) # SORT COINS SO WE START LOOP WITH BIGGEST COIN VALUE
for c in coins:
for d in coins: # THIS LOOP WAS ADDED BY A SMART STUDENT: IE IN THE CASE OF IF THERE IS A 7cent COIN AND YOU ARE LOOKING FOR CHANGE FOR 14 CENTS, WITHOUT THIS D FOR LOOP IT WILL RETURN 10: 1, 1: 4
if (d != 1) & (value % d == 0):
change_dict[d] = value / d
return change_dict
if value % c == 0: # IF THE VALUE DIVIDED BY THE COIN HAS NO REMAINDER, # ie, if there is no remainder, all the neccessary change has been given # PLACE THE NUMBER OF TIMES THAT COIN IS USED IN THE change_dict # YOU ARE FINISHED NOW RETURN THE change_dict
change_dict[c] = value / c
return change_dict
else:
change_dict[c] = math.trunc(value/ float(c)) # PLACE THAT NUMBER INTO OUR coin_dict # DIVIDE THE VALUE BY THE COIN, THEN GET JUST THE WHOLE NUMBER # IE 69 / 25.0 = 2.76 # math.trunc(2.76) == 2 # AND THAT IS HOW MANY TIMES IT WILL EVENLY GO INTO THE VALUE,
amount = (c * change_dict[c]) # NOW TAKE THE NUMBER OF COINS YOU HAVE IN YOUR UPDATE THE VALUE BY SUBTRACTING THE c * TIME NUMBER OF TIMES IT WAS USED # AMOUNT IS HOW MUCH CHANGE HAS BEEN PUT INTO THE CHANGE DICT ON THIS LOOP # FOR THE CASE OF 69, YOU GIVE 2 25CENT COINS, SO 2 * 25 = 50, 19 = 69 - 50
value = value - amount # NOW, UPDATE YOUR VALUE, SO THE NEXT TIME IT GOES INTO THIS LOOP, IT WILL BE LOOKING FOR THE MIN CHANGE FOR 19 CENTS...
coins = [1, 5, 10, 25]
answer = find_change(coins, 69)
print answer
[OUT]: {25: 2, 10: 1, 5: 1, 1: 4}
edge_case_coins = [1, 7, 10, 25]
edge_case_answer = find_change(coins, 14)
print edge_case_answer
[OUT]: {7: 2}
Here's a recursive and very inefficient implementation of the algorithm for making change, where V
is a list of coins and C
the target amount of money:
def min_change(V, C):
def min_coins(i, aC):
if aC == 0:
return 0
elif i == -1 or aC < 0:
return float('inf')
else:
return min(min_coins(i-1, aC), 1 + min_coins(i, aC-V[i]))
return min_coins(len(V)-1, C)
And this is the dynamic programming version of the same algorithm:
def min_change(V, C):
m, n = len(V)+1, C+1
table = [[0] * n for x in xrange(m)]
for j in xrange(1, n):
table[0][j] = float('inf')
for i in xrange(1, m):
for j in xrange(1, n):
aC = table[i][j - V[i-1]] if j - V[i-1] >= 0 else float('inf')
table[i][j] = min(table[i-1][j], 1 + aC)
return table[m-1][n-1]
This is a great algorithms question, but to be honest I don't think your implementation is correct or it could be that I don't understand the input/output of your function, for that I apologize.
heres a modified version of your implementation.
def C(i, coins, cdict = None):
if cdict == None:
cdict = {}
if i <= 0:
cdict[i] = 0
return cdict[i]
elif i in cdict:
return cdict[i]
elif i in coins:
cdict[i] = 1
return cdict[i]
else:
min = 0
for cj in coins:
result = C(i - cj, coins)
if result != 0:
if min == 0 or (result + 1) < min:
min = 1 + result
cdict[i] = min
return cdict[i]
This is my attempt at solving a similar problem, but this time returning a list of coins. I initially started with a recursive algorithm, which accepts a sum and a list of coins, which may return either a list with the minimun number of coins or None if no such configuration could be found.
def get_min_coin_configuration(sum = None, coins = None):
if sum in coins: # if sum in coins, nothing to do but return.
return [sum]
elif max(coins) > sum: # if the largest coin is greater then the sum, there's nothing we can do.
return None
else: # check for each coin, keep track of the minimun configuration, then return it.
min_length = None
min_configuration = None
for coin in coins:
results = get_min_coin_configuration(sum = sum - coin, coins = coins)
if results != None:
if min_length == None or (1 + len(results)) < len(min_configuration):
min_configuration = [coin] + results
min_length = len(min_configuration)
return min_configuration
ok now lets see if we can improve it, by using dynamic programming (I just call it caching).
def get_min_coin_configuration(sum = None, coins = None, cache = None):
if cache == None: # this is quite crucial if its in the definition its presistent ...
cache = {}
if sum in cache:
return cache[sum]
elif sum in coins: # if sum in coins, nothing to do but return.
cache[sum] = [sum]
return cache[sum]
elif max(coins) > sum: # if the largest coin is greater then the sum, there's nothing we can do.
cache[sum] = None
return cache[sum]
else: # check for each coin, keep track of the minimun configuration, then return it.
min_length = None
min_configuration = None
for coin in coins:
results = get_min_coin_configuration(sum = sum - coin, coins = coins, cache = cache)
if results != None:
if min_length == None or (1 + len(results)) < len(min_configuration):
min_configuration = [coin] + results
min_length = len(min_configuration)
cache[sum] = min_configuration
return cache[sum]
now lets run some tests.
assert all([ get_min_coin_configuration(**test[0]) == test[1] for test in
[({'sum':25, 'coins':[1, 5, 10]}, [5, 10, 10]),
({'sum':153, 'coins':[1, 5, 10, 50]}, [1, 1, 1, 50, 50, 50]),
({'sum':100, 'coins':[1, 5, 10, 25]}, [25, 25, 25, 25]),
({'sum':123, 'coins':[5, 10, 25]}, None),
({'sum':100, 'coins':[1,5,25,100]}, [100])] ])
granted this tests aren't robust enough, you can also do this.
import random
random_sum = random.randint(10**3, 10**4)
result = get_min_coin_configuration(sum = random_sum, coins = random.sample(range(10**3), 200))
assert sum(result) == random_sum
its possible that the no such combination of coins equals our random_sum but I believe its rather unlikely ...
Im sure there are better implementation out there, I tried to emphasize readability more than performance. good luck.
Updated
the previous code had a minor bug its suppose to check for min coin not the max, re-wrote the algorithm with pep8 compliance and returns []
when no combination could be found instead of None
.
def get_min_coin_configuration(total_sum, coins, cache=None): # shadowing python built-ins is frowned upon.
# assert(all(c > 0 for c in coins)) Assuming all coins are > 0
if cache is None: # initialize cache.
cache = {}
if total_sum in cache: # check cache, for previously discovered solution.
return cache[total_sum]
elif total_sum in coins: # check if total_sum is one of the coins.
cache[total_sum] = [total_sum]
return [total_sum]
elif min(coins) > total_sum: # check feasibility, if min(coins) > total_sum
cache[total_sum] = [] # no combination of coins will yield solution as per our assumption (all +).
return []
else:
min_configuration = [] # default solution if none found.
for coin in coins: # iterate over all coins, check which one will yield the smallest combination.
results = get_min_coin_configuration(total_sum - coin, coins, cache=cache) # recursively search.
if results and (not min_configuration or (1 + len(results)) < len(min_configuration)): # check if better.
min_configuration = [coin] + results
cache[total_sum] = min_configuration # save this solution, for future calculations.
return cache[total_sum]
assert all([ get_min_coin_configuration(**test[0]) == test[1] for test in
[({'total_sum':25, 'coins':[1, 5, 10]}, [5, 10, 10]),
({'total_sum':153, 'coins':[1, 5, 10, 50]}, [1, 1, 1, 50, 50, 50]),
({'total_sum':100, 'coins':[1, 5, 10, 25]}, [25, 25, 25, 25]),
({'total_sum':123, 'coins':[5, 10, 25]}, []),
({'total_sum':100, 'coins':[1,5,25,100]}, [100])] ])
Here's one that uses a while loop. The algorithm is pretty simple. You use the biggest coin first to pay towards the money. If you know you will go over you switch to the next smaller coin and repeat until the money is 0. The advantage with this code is that while the worst case runtime is higher (I think it's m*n (m is size of list while n is iterations in while), the code is a lot simpler.
I assumed that there is no 0 valued coin and there will always be a coin of value 1. When there isn't a value of 1, the function will give an answer of the best number of coins under the price.
def find_change(coins, money):
coins = sorted(coins, reverse=True)
coincount = 0
for coin in coins:
while money >= coin:
money = money - coin
coincount += 1
return coincount
I tried to think of a corner case where this would not work (it would overflow with any list that has a 0 valued coin) but couldn't think of one.
Like the comment says, you need to return a big enough value when i < 0
, so that it won't get selected by your min
like this:
cdict = {}
def C(i, coins):
if i == 0:
return 0
if i < 0:
return 1e100 # Return infinity in ideally
if i in cdict:
return cdict[i]
else:
answer = 1 + min([C(i - cj, coins) for cj in coins])
cdict[i] = answer
return answer
now whenever the function returns 1e100, it means solution is not possible.
for example:
$ python2 coins.py 13555 1 5 9
1507 coins
$ python2 coins.py 139 1 5 9
19 coins
$ python2 coins.py 139 5 9
19 coins
$ python2 coins.py 13977 5 9
1553 coins
$ python2 coins.py 13977 9
1553 coins
$ python2 coins.py 139772 9
1e+100 coins
with usage:
python2 coins.py <amount> <coin1> <coin2> ...