Making Change

From CodeCodex

Given a set of available denominations of coins, how many different ways can one make up a specified amount?

Python[edit]

This is a good illustration of how easy it is to generate all possible permutations or combinations of something using a generator function.

#!/usr/bin/python
#+
# This script computes the number of different ways that combinations
# of a given set of coin denominations can add up to a specified amount.
#
# Call this script as follows:
#
#     make_change --coins=denomination,denomination... [--total] amount ...
#
# where amount is one or more amounts (in integer cents) to compute change
# for, denomination is the list of available coin denominations, and
# --total means to only list the total number of ways to make change,
# not show the details of the combinations themselves.
#
# The details of the combinations are displayed on standard output as
# follows:
#
#     amount => [n1 * denom1, n2 * denom2 ... ]
#
# where the n are the integer numbers of each coin, and the denom are
# the corresponding coin denomination, to make the total add up to amount.
#-

import sys
import getopt

def CoinsCombination(Coins, Amount) :
    """generator function which yields all possible combinations of
    Coins adding up to Amount. Assumes Coins is sorted by increasing
    value."""
    if len(Coins) == 0 :
        if Amount == 0 :
            yield ()
        else :
            return
        #end if
    else :
        Coeff = 0
        while True :
            for Combi in CoinsCombination(Coins[1:], Amount) :
                yield (Coeff,) + Combi
            #end for
            if Coins[0] > Amount :
                break
            Coeff += 1
            Amount -= Coins[0]
        #end while
    #end if
#end CoinsCombination

(Opts, Args) = getopt.getopt \
  (
    sys.argv[1:],
    "",
    ["coins=", "total"]
  )
Coins = None
TotalOnly = False
for Keyword, Value in Opts :
    if Keyword == "--coins" :
        Coins = []
        for Denom in Value.split(",") :
            Coin = int(Denom)
            if Coin <= 0 :
                raise getopt.error("bad --coins denomination \"%s\"" % Denom)
            #end if
            Coins.append(Coin)
        #end for
        if len(Coins) == 0 :
            raise getopt.error("empty --coins specification")
        #end if
        Coins.sort()
    elif Keyword == "--total" :
        TotalOnly = True
    #end if
#end for
if Coins == None :
    raise getopt.error("missing --coins specification")
#end if
if len(Args) == 0 :
    raise getopt.error("missing amounts")
#end if
for Amount in Args :
    Amount = int(Amount)
    NrWays = 0
    for Combi in CoinsCombination(Coins, Amount) :
        NrWays += 1
        if not TotalOnly :
            print "%u => [%s]" % \
                    (Amount,
                     ", ".join(str(a) + " * " + str(b) for a, b in zip(Combi, Coins)))
        #end if
    #end for
    print "%u nr ways = %u" % (Amount, NrWays)
#end for

Invoking the above script as, for example

make_change --coins=10,20,50 100

will list all the ways of making change for a dollar using current New Zealand coin denominations—there are just 10 such ways.