I had an interesting interview question that I'm having a hard time solving (I have 7 of the 10 permutations)
The question was
Find all possible permutations to make change given the following coins, 25¢, 10¢, 5¢. The answer MUST BE saved in a list, and MUST BE returned as a JSON string when the method is called
Furthermore, the requirements were that when printed, the solution MUST look like this
For instance, given an amount of 50¢, the solution should look like the following when printed out
25: 2, 10: 0, 5: 0
25: 1, 10: 1, 5: 3
25: 1, 10: 2, 5: 1
25: 1, 10: 0, 5: 5
25: 0, 10: 5, 5: 0
25: 0, 10: 4, 5: 2
25: 0, 10: 3, 5: 4
25: 0, 10: 2, 5: 6
25: 0, 10: 1, 5: 8
25: 0, 10: 0, 5: 10
Needless to say, after 2 hours (the time limit to the test) I was unable to finish. But, it got me wandering, if I could solve the problem. I've tried for the past 6 hours to get the result, but the best I can come up with is
1 => {25: 2, 10: 0, 5: 0}
2 => {25: 1, 10: 1, 5: 3}
3 => {25: 1, 10: 2, 5: 1}
4 => {25: 1, 10: 0, 5: 5}
5 => {25: 0, 10: 5, 5: 0}
6 => {25: 0, 10: 1, 5: 8}
7 => {25: 0, 10: 0, 5: 10}
Using this code
class ChangeMachine(object):
def __init__(self, amount, coins=[25, 10, 5]):
self.amount = amount
self.coins = coins
self.result = []
self.initial_way = {}
for coin in coins:
self.initial_way[coin] = 0
def getAllPermutations(self):
for index in xrange(0, len(self.coins)):
coin = self.coins[index]
self.changeFromSameCoin(self.amount, coin)
self.changeUsingOneCoin(self.amount, coin, self.coins[index + 1:])
def changeFromSameCoin(self, amount, coin):
"""loops through all the coins, finding the ones which can be divided
into the amount evenly
Args:
amount: int
coin: int
Returns:
None
"""
way = dict(self.initial_way)
if amount % coin == 0:
way[coin] = amount / coin
self.result.append(dict(way))
def changeUsingOneCoin(self, amount, initial_coin, coin_list):
"""Makes change using 1 large coin and the rest small coins
Args:
amount: int
initial_coin: int - the "large" denomination that is to be used once
coin_list: list - contains the remainder of the coins
"""
if amount <= initial_coin:
return
remainder = amount - initial_coin
init_way = dict(self.initial_way)
num_coins = len(coin_list)
coin_used = 0
outer_counter = 0
# keep track of the number of times the outer coins are used
# make it 1 because the outer coin has to be used at least once
# even if outer coin is > remainder, we are still trying to use
# it once
outer_coin_used = 1
# since the initial coin MUST BE used at least once, go ahead and
# create an initial dictionary that has the initial coin used
# once
init_way[initial_coin] = 1
while outer_counter < num_coins:
outer_coin = coin_list[outer_counter]
# initialize way on every loop
way = dict(init_way)
# subtract the current outer coin from the remainder. We do this
# because if the remainder is 0, then it means that only 1 of this
# coin and the initial coin are needed to make change
# If the remainder is negative, then, one of the larger coin and
# one of this coin, cannot make change
# The final reason is because if we make change with the other
# coins, we need to check if we double, triple, etc this coin
# that we can still make change.
# This helps us find all permutations
remainder -= (outer_coin * outer_coin_used)
if remainder < 0:
# move to next coin using the outer_counter
outer_counter += 1
# reset the remainder to initial - large coin
remainder = amount - initial_coin
# rest the times the coin was used to 1
outer_coin_used = 1
continue
way[outer_coin] += outer_coin_used
if remainder == 0:
# add the way we just found to our result list
self.result.append(dict(way))
# move to the next element in the list
outer_counter += 1
# reset the remainder, our way result set, and times the
# outer coin was used
remainder = amount - initial_coin
way = dict(init_way)
outer_coin_used = 0
continue
# so, if we got here, the outer coin reduced the remainder, but
# didn't get it to 0
for index in range(outer_counter + 1, num_coins):
# our goal here is to make change with as few of coins as
# possible
inner_coin = coin_list[index]
if remainder % inner_coin == 0:
way[inner_coin] = remainder / inner_coin
remainder = 0
break
if remainder - inner_coin < 0:
# this coin is too large, move onto the next coin
continue
# this coin goes into the remainder some odd number of times
# subtract it from our remainder and move onto the next coin
remainder /= inner_coin
way[inner_coin] += remainder
# end for index in range()
if remainder == 0:
# we found a way to make change, save it
self.result.append(dict(way))
# reset the remainder to initial - large coin
remainder = amount - initial_coin
# increment the outer coin used by 1, because we will try
# to decrement remainder by more than 1 outer coin
outer_coin_used += 1
# end while loop
return
# end def changeUsingOneCoin()
# end class
from pprint import pprint
def main(amount, coins=[25, 10, 5]):
result = []
amount = 50
coins = [25, 10, 5]
cm = ChangeMachine(amount, coins)
# cm.changeUsingOneCoin(amount, coins[0], coins[1:])
cm.getAllPermutations()
counter = 1
for record in cm.result:
print "{} => {}".format(counter, record)
counter += 1
return result
if __name__ == '__main__':
"""
Result MUST BE a list of dictionaries containing all possible answers
For Example: if main(50, [25, 10, 5]) should return
[
{25: 2},
{25: 1, 10: 2, 5: 1},
{25: 1, 10: 1, 5: 3},
{25: 1, 10: 0, 5: 5},
{25: 0, 10: 5, 5: 0},
{25: 0, 10: 4, 5: 2},
{25: 0, 10: 3, 5: 4},
{25: 0, 10: 2, 5: 6},
{25: 0, 10: 1, 5: 8},
{25: 0, 10: 0, 5: 10},
]
"""
result = main(50)
I know I'm not going to get the job. But, I really want to know the solution
How strict are they on the actual output (some companies can be pretty pedantic in that regard)? Here's a quick-shod solution with only a couple of nested loops that produces the 10 combinations:
from itertools import count
from pprint import pprint
from operator import itemgetter
results = []
target = 50
for q in count(0):
for d in count(0):
for n in count(0):
if n * 5 + d * 10 + q * 25 == target:
results.append({25: q, 10: d, 5: n})
if n * 5 + d * 10 + q * 25 > target:
break
if d * 10 + q * 25 > target:
break
if q * 25 > target:
break
results.sort(key = itemgetter(5))
results.sort(key = itemgetter(10), reverse = True)
results.sort(key = itemgetter(25), reverse = True)
pprint(results)
Produces:
[{5: 0, 10: 0, 25: 2},
{5: 1, 10: 2, 25: 1},
{5: 3, 10: 1, 25: 1},
{5: 5, 10: 0, 25: 1},
{5: 0, 10: 5, 25: 0},
{5: 2, 10: 4, 25: 0},
{5: 4, 10: 3, 25: 0},
{5: 6, 10: 2, 25: 0},
{5: 8, 10: 1, 25: 0},
{5: 10, 10: 0, 25: 0}]
The sort
calls are just to get the list into the same order they provided (but that seems absurd, imho).
This simpler code does it, except that it doesn't write the output as JSON.
I would be suspicious of an employer that calls these 'permutations'. :)
TOTAL = 50
for q in range(0, 1+50//25):
remainder_q = TOTAL - 25*q
for d in range(0, 1+remainder_q//10):
remainder_d = remainder_q - 10*d
for n in range(0, 1+remainder_d//5):
remainder_n = remainder_d - 5*d
if 25*q+10*d+5*n == 50:
print (q, d, n)
break
if 25*q+10*d+5*n > 50:
break
0 0 10
0 1 8
0 2 6
0 3 4
0 4 2
0 5 0
1 0 5
1 1 3
1 2 1
2 0 0
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.