Duh, I feel stupid right now. Below there is an overly complicated solution, which I'll preserve because it is a solution, after all. A simple solution would be this:
// Generate a pretty string val coinNames = List(("quarter", "quarters"), ("dime", "dimes"), ("nickel", "nickels"), ("penny", "pennies")) def coinsString = Function.tupled((quarters: Int, dimes: Int, nickels:Int, pennies: Int) => ( List(quarters, dimes, nickels, pennies) zip coinNames // join with names map (t => (if (t._1 != 1) (t._1, t._2._2) else (t._1, t._2._1))) // correct for number map (t => t._1 + " " + t._2) // qty name mkString " " )) def allCombinations(amount: Int) = (for{quarters <- 0 to (amount / 25) dimes <- 0 to ((amount - 25*quarters) / 10) nickels <- 0 to ((amount - 25*quarters - 10*dimes) / 5) } yield (quarters, dimes, nickels, amount - 25*quarters - 10*dimes - 5*nickels) ) map coinsString mkString "\n"
Here is the other solution. This solution is based on the observation that each coin is a multiple of the others, so they can be represented in terms of them.
// Just to make things a bit more readable, as these routines will access // arrays a lot val coinValues = List(25, 10, 5, 1) val coinNames = List(("quarter", "quarters"), ("dime", "dimes"), ("nickel", "nickels"), ("penny", "pennies")) val List(quarter, dime, nickel, penny) = coinValues.indices.toList // Find the combination that uses the least amount of coins def leastCoins(amount: Int): Array[Int] = ((List(amount) /: coinValues) {(list, coinValue) => val currentAmount = list.head val numberOfCoins = currentAmount / coinValue val remainingAmount = currentAmount % coinValue remainingAmount :: numberOfCoins :: list.tail }).tail.reverse.toArray // Helper function. Adjust a certain amount of coins by // adding or subtracting coins of each type; this could // be made to receive a list of adjustments, but for so // few types of coins, it's not worth it. def adjust(base: Array[Int], quarters: Int, dimes: Int, nickels: Int, pennies: Int): Array[Int] = Array(base(quarter) + quarters, base(dime) + dimes, base(nickel) + nickels, base(penny) + pennies) // We decrease the amount of quarters by one this way def decreaseQuarter(base: Array[Int]): Array[Int] = adjust(base, -1, +2, +1, 0) // Dimes are decreased this way def decreaseDime(base: Array[Int]): Array[Int] = adjust(base, 0, -1, +2, 0) // And here is how we decrease Nickels def decreaseNickel(base: Array[Int]): Array[Int] = adjust(base, 0, 0, -1, +5) // This will help us find the proper decrease function val decrease = Map(quarter -> decreaseQuarter _, dime -> decreaseDime _, nickel -> decreaseNickel _) // Given a base amount of coins of each type, and the type of coin, // we'll produce a list of coin amounts for each quantity of that particular // coin type, up to the "base" amount def coinSpan(base: Array[Int], whichCoin: Int) = (List(base) /: (0 until base(whichCoin)).toList) { (list, _) => decrease(whichCoin)(list.head) :: list } // Generate a pretty string def coinsString(base: Array[Int]) = ( base zip coinNames // join with names map (t => (if (t._1 != 1) (t._1, t._2._2) else (t._1, t._2._1))) // correct for number map (t => t._1 + " " + t._2) mkString " " ) // So, get a base amount, compute a list for all quarters variations of that base, // then, for each combination, compute all variations of dimes, and then repeat // for all variations of nickels. def allCombinations(amount: Int) = { val base = leastCoins(amount) val allQuarters = coinSpan(base, quarter) val allDimes = allQuarters flatMap (base => coinSpan(base, dime)) val allNickels = allDimes flatMap (base => coinSpan(base, nickel)) allNickels map coinsString mkString "\n" }
So, for 37 coins, for example:
scala> println(allCombinations(37)) 0 quarter 0 dimes 0 nickels 37 pennies 0 quarter 0 dimes 1 nickel 32 pennies 0 quarter 0 dimes 2 nickels 27 pennies 0 quarter 0 dimes 3 nickels 22 pennies 0 quarter 0 dimes 4 nickels 17 pennies 0 quarter 0 dimes 5 nickels 12 pennies 0 quarter 0 dimes 6 nickels 7 pennies 0 quarter 0 dimes 7 nickels 2 pennies 0 quarter 1 dime 0 nickels 27 pennies 0 quarter 1 dime 1 nickel 22 pennies 0 quarter 1 dime 2 nickels 17 pennies 0 quarter 1 dime 3 nickels 12 pennies 0 quarter 1 dime 4 nickels 7 pennies 0 quarter 1 dime 5 nickels 2 pennies 0 quarter 2 dimes 0 nickels 17 pennies 0 quarter 2 dimes 1 nickel 12 pennies 0 quarter 2 dimes 2 nickels 7 pennies 0 quarter 2 dimes 3 nickels 2 pennies 0 quarter 3 dimes 0 nickels 7 pennies 0 quarter 3 dimes 1 nickel 2 pennies 1 quarter 0 dimes 0 nickels 12 pennies 1 quarter 0 dimes 1 nickel 7 pennies 1 quarter 0 dimes 2 nickels 2 pennies 1 quarter 1 dime 0 nickels 2 pennies
code-golf=> stackoverflow.com/questions/tagged/code-golf