# dynamic programming version def make_change(amount, coins) result = Array.new(amount + 1, 0) result[0] = 1 coins.each do |coin| (coin..amount).each do |higher_amount| remainder = higher_amount - coin result[higher_amount] += result[remainder] end end result[amount] end # recursive version: def make_change2(target, coins = [25, 10, 5, 1]) return [] if target == 0 return nil if coins.none? { |coin| coin <= target } coins = coins.sort.reverse best_change = nil coins.each_with_index do |coin, index| # can't use this coin, it's too big next if coin > target # use this coin remainder = target - coin # Find the best way to make change with the remainder (recursive # call). Why `coins.drop(index)`? This is an optimization. Because # we want to avoid double counting; imagine two ways to make # change for 6 cents: # (1) first use a nickel, then a penny # (2) first use a penny, then a nickel # To avoid double counting, we should require that we use *larger # coins first*. This is what `coins.drop(index)` enforces; if we # use a smaller coin, we can never go back to using larger coins # later. best_remainder = make_change2(remainder, coins.drop(index)) # We may not be able to make the remaining amount of change (e.g., # if coins doesn't have a 1cent piece), in which case we shouldn't # use this coin. next if best_remainder.nil? # Otherwise, the best way to make the change **using this coin**, # is the best way to make the remainder, plus this one coin. this_change = [coin] + best_remainder # Is this better than anything we've seen so far? best_change = this_changeif best_change.nil? || this_change.count < best_change.count end best_change end