我正在为Scala中的Coin (change) problem编写一个递归函数。
我的实现打破了StackOverflowError,我无法弄清楚它为什么会发生。
Exception in thread "main" java.lang.StackOverflowError
at scala.collection.immutable.$colon$colon.tail(List.scala:358)
at scala.collection.immutable.$colon$colon.tail(List.scala:356)
at recfun.Main$.recurs$1(Main.scala:58) // repeat this line over and over
这是我的电话:
println(countChange(20, List(1,5,10)))
这是我的定义:
def countChange(money: Int, coins: List[Int]): Int = {
def recurs(money: Int, coins: List[Int], combos: Int): Int =
{
if (coins.isEmpty)
combos
else if (money==0)
combos + 1
else
recurs(money,coins.tail,combos+1) + recurs(money-coins.head,coins,combos+1)
}
recurs(money, coins, 0)
}
编辑:我刚刚在mix中添加了else if语句:
else if(money<0)
combos
它摆脱了错误,但我的输出是1500的东西:(我的逻辑出了什么问题?
答案 0 :(得分:17)
以下是基于您的代码的正确解决方案:
def countChange(money: Int, coins: List[Int]): Int = {
def recurs(m: Int, cs: List[Int], cnt: Int): Int =
if(m < 0) cnt //Not a change, keep cnt
else if(cs.isEmpty) {
if(m == 0) cnt + 1 else cnt // plus cnt if find a change
}
else recurs(m, cs.tail, cnt) + recurs(m-cs.head, cs, cnt)
recurs(money, coins, 0)
}
无论如何,有一个简短的解决方案(但效率不高,你可以缓存中间结果以提高效率。)
def countChange(m: Int, cs: List[Int]): Int = cs match {
case Nil => if(m == 0) 1 else 0
case c::rs => (0 to m/c) map (k => countChange(m-k*c,rs)) sum
}
答案 1 :(得分:16)
the accepted answer中的第一个解决方案有一个冗余的最后一个参数as noted by Paaro所以我想摆脱它。第二个解决方案使用map
我想避免,因为它在第1周或Scala课程中没有涵盖,我认为你正在服用。此外,正如作者正确指出的那样,第二种解决方案会慢一些,除非它使用一些记忆。最后,Paaro's solution似乎有一个不必要的嵌套函数。
所以这就是我最终的结果:
def countChange(money: Int, coins: List[Int]): Int =
if (money < 0)
0
else if (coins.isEmpty)
if (money == 0) 1 else 0
else
countChange(money, coins.tail) + countChange(money - coins.head, coins)
正如你所看到的,这里不需要括号。
我想知道是否可以进一步简化。
答案 2 :(得分:1)
@Eastsun解决方案很好但是当money = 0时它失败,因为它返回1而不是0,但你可以轻松修复它:
def countChange(money: Int, coins: List[Int]): Int = {
def recurs(m: Int, cs: List[Int], cnt: Int): Int =
if(m < 0) cnt //Not a change, keep cnt
else if(cs.isEmpty) {
if(m == 0) cnt + 1 else cnt // plus cnt if find a change
}
else recurs(m, cs.tail, cnt) + recurs(m-cs.head, cs, cnt)
if(money>0) recurs(money, coins, 0) else 0
}
答案 3 :(得分:1)
可以省略cnt参数,实际上,它从未累积过。递归函数总是返回0或1,因此优化的算法将是:
def countChange(money: Int, coins: List[Int]): Int = {
def recurs(m: Int, cs: List[Int]): Int =
if(m < 0) 0 //Not a change, return 0
else if(cs.isEmpty) {
if(m == 0) 1 else 0 // 1 if change found, otherwise 0
}
else recurs(m, cs.tail) + recurs(m-cs.head, cs)
if(money>0) recurs(money, coins) else 0
}
答案 4 :(得分:1)
这是一种在递归方法中减少大量重新计算的DP方法
object DP {
implicit val possibleCoins = List(1, 5, 10, 25, 100)
import collection.mutable.Map
def countChange(amount: Int)(implicit possibleCoins: List[Int]) = {
val min = Map((1 to amount).map (_->Int.MaxValue): _*)
min(0) = 0
for {
i <- 1 to amount
coin <- possibleCoins
if coin <= i && min(i - coin) + 1 < min(i)
} min(i) = min(i-coin) + 1
min(amount)
}
def main(args: Array[String]) = println(countChange(97))
}
请参阅DP from novice to advanced了解算法
答案 5 :(得分:0)
case class Memo[K,I,O](f: I => O)(implicit i2k:I=>K ) extends (I => O) {
import scala.collection.mutable.{Map => Dict}
val cache = Dict.empty[K, O]
override def apply(x: I) = cache getOrElseUpdate (x, f(x))
}
def coinchange(s: List[Int], t: Int) = {
type DP = Memo[ (Int, Int), (List[Int], Int),Seq[Seq[Int]]]
implicit def encode(key: (List[Int], Int)):(Int,Int) = (key._1.length, key._2)
lazy val f: DP = Memo {
case (Nil, 0) => Seq(Nil)
case (Nil, _) => Nil
case (_, x) if x< 0 => Nil
case (a :: as, x) => f(a::as, x - a).map(_ :+ a) ++ f(as, x)
}
f(s, t)
}