[Haskell-cafe] Re: Coin changing algorithm
Okasaki, C. DR EECS
Christopher.Okasaki at usma.edu
Thu Jul 14 14:58:09 EDT 2005
This is a classic dynamic programming problem. Dynamic programming is
easy to do in Haskell using recursive arrays. Instead of using
recursive arrays directly, however, I'll add a few helper functions that
make this kind of problem easier.
> import Array
>
> tabulate :: (Ix a) => (a,a) -> (a -> b) -> Array a b
> tabulate bounds f = array bounds [(i,f i) | i <- range bounds]
>
> dp :: (Ix a) => (a,a) -> ((a->b) -> a -> b) -> a -> b
> dp bounds f = (memo!) where
> memo = tabulate bounds (f (memo!))
'tabulate' just turns a function into an array. 'dp' is a memoizing Y
combinator. That is, you give the same kind of almost recursive
function you would give to the Y combinator, and it "ties the knot" for
you, but also interposes an array to hold the intermediate results.
(What do I mean by an "almost recursive function"? I mean that instead
of writing a recursive function "f = ...f...", you instead write f to
take what will end up being the recursive function as its first
argument. Then, whenever the f would normally call itself, it instead
calls this argument, as in "f rec = ...rec...".)
Now, here is the change algorithm, using esentially the same recursion
as Radu, except that I introduce an 'i' parameter, that is the index of
the largest denomination that you are allowed to use. (Using this
parameter makes it easier to store the results in an array than passing
around a list of the available coins.)
> change m n = dp bounds f (m,n,8) where
> bounds = ((0,0,0), (m,n,8))
> coins = listArray (1,8) [1,2,5,10,20,50,100,200]
> f rec (m,n,i)
> | m == 0 = [[]]
> | n == 0 || i == 0 = []
> | c > m = rec (m,n,i-1)
> | otherwise = map (c:) (rec (m-c,n-1,i)) ++ rec (m,n,i-1)
> where c = coins!i
In the f function, m is the amount of change to make, n is the number of
coins that can be used, and i is the highest kind of coin that you are
allowed to use.
You can do most traditional dynamic programming problems this way.
Enjoy,
Chris
More information about the Haskell-Cafe
mailing list