[SOLUTION][QUIZ] Making Change (#154)



There is my solution which I'm proud of ;-)

http://pastie.caboo.se/144083 (solution)
http://pastie.caboo.se/144089 (rspec)

def make_change(amount, coins=[25, 10, 5, 1])

# Does the change, recursively with a kind of
# alpha-beta pruning (whitout the beta)
# Best default value for alpha is +Infinity as
# alpha represents the size of the actual found
# solution, it can only decrease with the time.
def make_change_r(amount, coins, alpha)
# the coin with its solution
# that has to be the shortest one
best_coin = nil
solution = []

# The good coin exists (win!)
if coins.include? amount
best_coin = amount

# Only one coin stand (avoids a lot of recursion)
elsif coins.length == 1
coin = coins[0]
unless coin > amount or amount % coin != 0
# Do not construct a solution if this one
# is bigger than the allowed one (alpha).
size = amount/coin - 1
if size <= alpha
best_coin = coin
solution = [best_coin] * size
end
end

# No solution can be found (odd coins and even amount)
elsif amount % 2 === 1 and \
coins.select{|coin| coin % 2 != 0}.length === 0
# pass

# Alpha(-beta) pruning:
# Do not look to this subtree because another
# shorter solution has been found already.
elsif alpha > 1 and

coins.select{|c| c >= amount/alpha}.each do |coin|
# only give a subset of the coins, the bigger ones
# have been tested already.
found = make_change_r( \
amount-coin, \
coins.select {|c| c <= coin and c <= amount-coin}, \
alpha-1)

# Check if the solution (if there is any) is good enough
if not found.nil? and \
(solution.length === 0 or solution.length >
found.length)

best_coin = coin
solution = found
alpha = solution.length
end
end
end

return best_coin.nil? \
? best_coin \
: [best_coin] + solution
end

# Any money or coins to give back?
if not amount.nil? and amount > 0 and not coins.nil?

# make sure the coins are ready to be used
coins.sort!
coins.uniq!
coins.reverse!

infinity = 1.0/0
return make_change_r(amount, coins, infinity)
else
return []
end
end

And the RSpec I used to test it:

describe "make_change" do
it "should work with US money" do
make_change(39).should == [25, 10, 1, 1, 1, 1]
end

it "should work with alien money" do
make_change(14, [10, 7, 1]).should == [7, 7]
end

it "should work with non solvable solution" do
make_change(0.5).should == nil
end

it "should now when not to give money back" do
make_change(0).should == []
end

it "should agree with dh and Marcelo" do
make_change(1000001, [1000000, 1]).should == [1000000, 1]
make_change(10000001, [10000000, 1]).should == [10000000, 1]
make_change(100000001, [100000000, 1]).should == [100000000, 1]

make_change(1000001, [1000000, 2, 1]).should == [1000000, 1]
end

it "should not be naive (by James)" do
make_change(24, [10, 8, 2]).should == [8, 8, 8]

make_change(11, [10, 9, 2]).should == [9, 2]
end

it "should have a good pruning" do
make_change(19, [5, 2, 1]).should == [5, 5, 5, 2, 2]
make_change(39, [5, 2, 1]).should == [5, 5, 5, 5, 5, 5, 5, 2, 2]
end

it "should work with Swiss paper money" do
money = [1000,500,200,100,50,20,10,5,2,1]
make_change(1789, money).should == [1000,500,200,50,20,10,5,2,2]
end

it "should be nice to tho_mica_l" do
money = [97, 89, 83, 79, 73, 71, 67, 61, 59, 53, 47, 43, 41, 37,
31, 29, 23, 19, 17, 13, 11, 7, 5, 3]
make_change(101, money).should == [89, 7, 5]

make_change(4563, money).should == [97, 97, 97, 97, 97, 97, 97,
97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97,
97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97,
97, 97, 97, 89, 7, 5]
end

it "should fail with a combination trick (vsv)" do
make_change(2**10-1, (1..10).map{|n| 2**n}).should == nil
make_change(2**100-1, (1..100).map{|n| 2**n}).should == nil
end
end

with good performances ;-)

..........

Finished in 0.095362 seconds

10 examples, 0 failures

It's basically a min-max algorithm (min only) with alpha-beta pruning
(alpha only). An example with (24, [10, 8, 2])

24, [10, 8, 2], alpha=Infinity
24: tested coin 10 -> 14, [10, 8, 2], Infinity
14: tested coin 10 -> 4, [2], Infinity
4: one coin stands -> return [2, 2]
14: tested coin 8 -> 6, [2], alpha=2 (previous solution (at this
level) is [10, 2, 2] -> length - 1)
6: one coin stands and we need 3 coins which is bigger than
alpha, fail! -> return nil
24: tested coin 8 -> 16, [8, 2], alpha=3 (previous solution is [10,
10, 2, 2])
16: tested coin 8 -> 8, [8, 2], alpha=2
8: amount is a known coin, win! -> return [8]
16: tested coin 2 -> 14, [2], alpha=2
14: one coin stands and we need 7 coins which is bigger than
alpha, fail! -> return nil
24: tested coin 2 -> 22, [2], alpha=2 (previous solution is [8,8,8])
22: one coin stands but we need 11 coins which is far bigger than
alpha, fail! -> return nil

I had a lot of fun with this one coz it was my first play with rspec and
ruby -r profile to spot the weak parts.

Cheers,

-- Yoan

.



Relevant Pages

  • Re: How to calculate the beta value? What does it mean by a given beta value? .
    ... E.g., suppose I have one coin that is known to be fair, and one that is known to be biased towards Tails with the properties described below. ... Type I error can only occur if H0 is true, so the probabilities you need to sum to compute alpha are from the distribution that is conditional on H0 being true. ... Type II error can only occur if H0 is false, so to calculate beta, you must take the probabilities from the distribution that is conditional on H1 being true. ... here is the decision rule that minimizes the overall probability of error. ...
    (sci.stat.math)
  • Re: Simple binomial test question
    ... but making sure that you use the standard terminology ... trials (yes, 10 coin tosses), independent trials (yes, one coin toss ... The usual terminology here is P-value. ... Compare the P-value with the alpha you chose before, ...
    (sci.stat.math)
  • Re: Making Change (#154)
    ... it is not necessary to sort the input array of coins (when the input ... in every matrixcell report the integer part and the rest of the ... the number of coin, number_of_coins that will be used to compute the ... a "n,0" cell.value case, we have to now what amount we are processing, ...
    (comp.lang.ruby)
  • Re: Optimize lisp program
    ... (defstruct (coin (:type list)) ... and amount across minimal-amount ... (when (or (null old-amount) ... (setf (aref minimal-amount new-weight) ...
    (comp.lang.lisp)
  • Re: pay a motoring fine, get the bomb squad out
    ... know what the amount for 10Ps is ... There is nothing that determines that any amount of coin is legal, ... There is a limit as to what constitutes legal tender, ... If someone wants accept an offer of payment in chickens, ...
    (uk.transport)