I'm working on an algorithm to count the number of ways to build 100 cents given an infinite amount of pennies, dimes, nickels, and quarters.
I ended up coming with the above (which AFAIK works):
def count_ways(amount)
num_ways(amount, 0)
end
def num_ways(amount, index)
return 1 if amount == 0
return 0 if index >= COINS.length || amount < 0
num_ways(amount - COINS[index], index) +
num_ways(amount, index + 1)
end
Now, I'd like to memoize this algorithm. An effective way I've found to think about memoizing is to consider what inputs do we pass into this function repeatedly. In this case, I'd like to memoize the combination of amount & index parameters.
Typically when I have two parameters, I build a two D array as a way to memoize but here that makes a lot less sense. Consequently, how can you memoize off of these two parameters? Does it make sense to do something like this?
def count_ways(amount)
memo = Hash.new { |h, k| h[k] = [] }
num_ways(amount, 0, memo)
end
def num_ways(amount, index, memo)
return 1 if amount == 0
return 0 if index >= COINS.length || amount < 0
memo[amount - COINS[index], index] ||= num_ways(amount - COINS[index], index)
memo[amount, index + 1] ||= num_ways(amount, index + 1)
memo[amount - COINS[index], index] +
memo[amount, index + 1]
end
I believe there is no common way to implement memoization when you solve algo task. Because of speed. Way you have to choose depends on you algo, input data and so on. Few imperative rules:
h = {}; h[ [1,2] ]
= 3 will produce COINS.size * amount Arrays just for keysArray for contiguous data and Hash for gapped oneHash instead of Array when you can't predict data sizeArray with needed values when you can predict yourUsing that rules, memoization (just in your case, where COINS.size << amount and both datas are contiguous) might look like:
COINS = [25, 10, 5, 1]
def count_ways(amount)
# memo is just COINS.size + 1 Array instances
# a[0] = 1 instead of return 1 if amount == 0
memo = Array.new(COINS.size){ Array.new(amount).tap{ |a| a[0] = 1 } }
num_ways(amount, 0, memo)
end
def num_ways(amount, index, memo)
return 0 if index >= COINS.size || amount < 0
memo[index][amount] ||=
num_ways(amount - COINS[index], index, memo) +
num_ways(amount, index + 1, memo)
end
P.S. dynamic-programming tag is unnecessary :)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With