Scala中硬币更改的StackOverflowError?

时间:2013-04-07 06:03:28

标签: scala

我正在为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的东西:(我的逻辑出了什么问题?

6 个答案:

答案 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)

来自https://github.com/pathikrit/scalgos/blob/9e99f73b4241f42cc40a1fd890e72dbeda2df54f/src/main/scala/com/github/pathikrit/scalgos/DynamicProgramming.scala#L44的想法

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)
}