Subset Sum of Coins for Exact Change
by Isai Damier

#=======================================================================
# Author: Isai Damier
# Title: Subset Sum of Coins for Exact Change
# Project: geekviewpoint
# Package: algorithms
#
# Statement:
#   Given a set of coins, find the shortest sequence of coins that
#   adds up to a given total t.
#
# Time Complexity: O(n*t)
#    where n is the number of coins and t is the total to make change for.
#
# Sample Input: coin=[1,5,10,25]; t=167
# Sample Output: [1, 1, 5, 10, 25, 25, 25, 25, 25, 25]
#
# Dynamic Programming Strategy:
#
#   Our approach is to compute the number of coins in each subtotal
#   amount of money from 0 to t, inclusive. The technique is to
#   add coins to a previously found subtotal so to get the next
#   subtotal, until we solve for the minimum number of coins in t.
#   After that, we simply walk the list of subtotals to get the
#   exact change (you'll see).
#
# 0] Create an array N of size t+1:
#     The indices/keys of the array represent different subtotals.
#     And the element/value at each index represents the number of
#     coins that adds up to that given subtotal:
#     N[subtotal] = number of coins
# 1] Initialize N to infinity:
#     Before any computation is performed, assume/pretend that each
#     subtotal takes an infinite number of coins to compute. We know
#     that's not true, but that's what we have to prove.
# 2] Set N at index 0 to 0:
#     Recall that for the N array, each index represents a subtotal
#     and the element at that index represents the number of coins
#     that add up to that subtotal. Therefore, we can set N[0] to 0
#     because we know for certain that it takes 0 coins to get 0
#     amount of money (this is the anchor/base case).
#
# 3] For subtotals ranging from 1 to t, inclusive, do:
#     For each coin:
#       If coin <= subtotal and N[subtotal-coin]+1 < subtotal, then:
#         N[subtotal] = N[subtotal-coin]+1
#
#   Step 3 is known as the relaxation step. We started with an extreme
#   and unrealistic assumption (i.e. that it takes infinite number of
#   coins to make change for the money t), then we keep relaxing
#   our assumptions until we get to the truth.
#
# EXAMPLE: You should really skip the following manual example and go
#   straight to the code. Then, if you need help seeing how the code
#   is computing the number of coins for each subtotal, come back here.
#
#   For t=10 and coins = [1,3,5], the final version of N is:
#
#   N[0] = 0; //base case             : 0 coins for 0 cent
#   N[1] = N[0]+coins[0] = 0+1 = 1    : 1 coin for 1 cent   [1]
#   N[2] = N[1]+coins[0] = 1+1 = 2    : 2 coins for 2 cents [1,1]
#   N[3] = coins[1] = 1               : 1 coin for 3 cents  [3]
#   N[4] = N[1]+coins[1] = 1+1 = 2    : 2 coins for 4 cents [3,1]
#   N[5] = coins[2] = 1               : 1 coin for 5 cents [5]
#   N[6] = N[5]+coins[0] = 1+1 = 2    : 2 coins for 6 cents [5,1]
#   N[7] = N[6]+coins[0] = 2+1 = 3    : 3 coins for 7 cents [5,1,1]
#   N[8] = N[5]+coins[2] = 1+1 = 2    : 2 coins for 8 cents [5,3]
#   N[9] = N[8]+coins[0] = 2+1 = 3    : 3 coins for 9 cents [5,1,3]
#   N[10] = N[5]+coins[2] = 1+1 = 2   : 2 coins for 10 cents [5,5]
#=======================================================================
 
 # return makeExactChange(numberOfCoinsPerSubtotal(t, coins), t);
def subsetSumOfCoins( coins, t ) :
  C = numberOfCoinsPerSubtotal( t, coins )
  return makeExactChange( C, t )


#======================================================================
# Compute the number of coins in each subtotal amount of money
# from 0 to t.
#======================================================================
from sys import maxint
def numberOfCoinsPerSubtotal( t, coins ):
  C = [maxint] * ( t + 1 )
  C[0] = 0 # base case
  for coin in coins:
    for subtotal in range( 1, t + 1 ):
      if coin <= subtotal and C[subtotal] > C[subtotal - coin] + 1:
        C[subtotal] = C[subtotal - coin] + 1
  return C


#======================================================================
# Now that we know how many coins it takes to get each subtotal
# amount of money up to t, we can track the denomination of each coin:
# keep subtracting the next subtotal that takes fewer coins than t.
# For instance in the manual example above where t=10 and
# coins = [1,3,5]; since t N[t=10] takes 2 coins, the next subtotal
# that takes fewer coins than N[10] is N[5], and the next subtotal
# that takes fewer coins than N[5] is N[0]. Hence the denominations
# for N[10] are 10-5 and 5-0: [5,5].
#======================================================================
def makeExactChange( C, t ):
  result = []
  while 0 < C[t]:
    big = t
    while C[big] <= C[t]:
      t -= 1
    result.append( big - t )

  return result;
import unittest
from dynamic_programming import SubsetSum as algorithm

class Test( unittest.TestCase ):

  def testSubsetSumOfCoins( self ):
    coin = [1, 5, 10, 25]
    t = 167
    expResult = [1, 1, 5, 10, 25, 25, 25, 25, 25, 25]
    result = algorithm.subsetSumOfCoins( coin, t )
    self.assertEquals( expResult, result )