pythonrecursioncoin-change

How can I successfully call a recursion method in Python?


I read and reread about recursion. I've seen so many videos and Stack Overflow posts, but I just don't get it. I've been going round and round for three hours on this recursion solution (BTW, yes, I know it's a greedy/unsafe solution)

Example: given coins [10, 5, 1] cents, find the minimum number of each coin to total 18 cents. A solution is supposed to return the coin collection [10, 5, 1, 1, 1].

I can't get it to move past the first coin, 10 in the collection. What am I missing and why?

def r_change_money(total, denominations):
    def collect_coins(total, denominations):
        if total == 0:
            return False
        # fd (floor division)
        fd = total // denominations[0]
        addl_coins = [denominations[0]] * fd
        total -= fd * denominations[0]
        denominations.pop(0)
        return addl_coins

    # sort highest denom first
    denominations.sort(reverse=True)
    # holds coin collection
    coins = [] 
    coins += collect_coins(total, denominations)
    return coins

# [10, 5, 1, 1, 1]
print(r_change_money(18, [1, 10, 5]))

# [6, 1, 1] (although safe answer is [4, 4])
print(r_change_money(8, [6, 1, 4]))

At least this solution isn't giving me errors anymore, but I am getting:

[10]
[6]

Solution

  • "Recursion" is when a function calls ITSELF. Your code is not doing any recursion. You have r_change_money calling collect_coins. The key here is that you need collect_coins to call collect_coins, to process the rest of the list.

    For this to work, you need (a) to make sure the sequence WILL eventually finish, in this case by having the total go to 0, and (b) to provide a way to collect the answer. I do so here by passing the result list to the inner function.

    Here is your example, modified to work correctly:

    def r_change_money(total, denominations):
    
        def collect_coins(answer, total, denominations):
            if total == 0:
                return False
    
            coin = denominations[0]
            fd = total // coin
            answer.extend( [coin] * fd )
            total -= fd * coin
            collect_coins( answer, total, denominations[1:] )
    
        denominations.sort(reverse=True) # sort highest denom first
        coins = [] # holds coin collection
        collect_coins( coins, total, denominations )
        return coins
    
    print(r_change_money(18, [1, 10, 5]))
    print(r_change_money(8, [6, 1, 4]))
    

    Output:

    [10, 5, 1, 1, 1]
    [6, 1, 1]
    

    There are still problems with this code. It only works if there actually IS a combination that exactly meets the requirement. r_change_money(5, [2]) will fail.