【问题标题】:coin change algorithm in scala using recursionscala中使用递归的硬币兑换算法
【发布时间】:2012-09-19 17:33:05
【问题描述】:

我正在尝试使用递归在 Scala 中编写硬币找零问题。我写的代码如下。

def countChange(money: Int, coins: List[Int]): Int = {
  def ways(change: List[Int], size: Int, capacity: Int): Int = {
    if(capacity == 0) 1
    if((capacity < 0) || (size <= 0)) 0

    //println and readLine to check and control each recursive call.

    println("calling ways(",change, change.length-1, capacity,") + ways(",change,   change.length, capacity - change(change.length - 1),")")
    readLine()
    //

    ways(change, change.length-1, capacity) + ways(change, change.length, capacity - change(change.length - 1))
  }
  ways(coins, coins.length, money)
}

在运行代码时,它不会终止并继续调用第一个递归调用。我哪里错了?

【问题讨论】:

标签: scala scala-2.9


【解决方案1】:

漂亮又简单

def countChange(money: Int, coins: List[Int]): Int = {
  if(money == 0)
    1
  else if(money > 0 && !coins.isEmpty)
    countChange(money - coins.head, coins) + countChange(money, coins.tail)
  else
    0
}

【讨论】:

  • 您能否提供一些关于此解决方案的直觉?
  • 这里的想法是用完我们的 coins 并从当前的 money 数量中减去它。最终,金额将是 0、某个负数(意味着这种硬币组合失败)或某个正数(意味着我们仍然可以用我们目前拥有的硬币减去更多)。 countChange(money - coins.head, coins) 将用尽所有组合,从钱中减去第一个硬币,而countChange(money, coins.tail) 用尽所有其他硬币的组合。它们加在一起,因为 + 是逻辑 OR 运算符的同义词。
  • 解决方案很好。关于基本情况的一个问题:如果初始资金为 0 怎么办。难道没有 0 方法可以为 0 资金进行更改吗?
  • 想象你面前有一些硬币,你被要求用一大袋硬币来匹配。如果您面前有一分钱,那么只有一种方法可以匹配;用你包里的一分钱。接下来,如果你面前没有硬币怎么办?同样只有一种方式来表示它;不要取出包里的任何硬币。现在最后,如果你有负钱怎么办?嗯,你不能真正代表身体。所以有 0 种方法可以产生这个输出;在这种情况下,硬币袋完全无关紧要。
  • @rileyss 你可以通过显示 0 个硬币来代表 0 个钱。请记住,问题是“给定一定面额的硬币,我们可以用多少种方式来表示货币”。所以使用零个硬币仍然被认为是一种不同的方式。
【解决方案2】:

这是我的实现: 我已经测试过了,效果很好

def countChange(money: Int, coins: List[Int]): Int = {

  def count(capacity: Int, changes: List[Int]): Int = {
    if (capacity == 0)
      1
    else if (capacity < 0)
      0
    else if (changes.isEmpty && capacity >= 1)
      0
    else
      count(capacity, changes.tail) 
        + count(capacity - changes.head, changes)
  }

  count(money, coins.sortWith(_.compareTo(_) < 0))
}

【讨论】:

  • 不错!虽然,我认为更改名称(钱 -> 容量,硬币 -> 更改)会让人更难理解。
  • 真的需要排序吗?
  • 我对 scala 有一个新问题:编译器如何读取这一行:count(capacity, changes.tail) + count(capacity - changes.head, changes)?我知道递归是如何工作的,但是在理解编译器如何执行最后一行时遇到了问题
  • @moe 您可以选择观看 Martin Odersky 的本课程第一周的讲座class.coursera.org/progfun-004/lecture/4
【解决方案3】:

只是另一种解决方案

def countChange(amount: Int, coins: List[Int]): Int = coins match {
  case _ if amount == 0 => 1
  case h :: t if amount > 0 => countChange(amount - h, h :: t) + countChange(amount, t)
  case _ => 0
}

【讨论】:

    【解决方案4】:

    简单地声明一个值不会让 Scala 返回它;您要么需要明确的退货,要么必须是最后一项。因此:

    if (capacity == 0) return 1
    

    if (capacity == 0) 1
    else if (...)
    else { ... }
    

    【讨论】:

    • 你确定它有效吗?我仍然遇到与前面所述相同的问题
    • @user1050258 - 嗯,这不是唯一的问题 - 您还在不同的地方使用 change.length-1 而不是 size-1。您不会在解决方案中更新 change 本身! (提示:如果你确实更新了change,那么你就可以避免这个错误并且不需要size参数......)
    【解决方案5】:

    嘿,我只是认为最好不仅看到数量而且还看到它们的列表,所以把上面的例子放在上面:

    def moneyChanges(money: Int, coins: List[Int]) : Option[List[Seq[Int]]]= {
      var listOfChange=List[Seq[Int]]()
      def changeMoney(capacity: Int, changes: List[Int], listOfCoins: Option[Seq[Int]]): Int = {
        if (capacity == 0) {
          listOfChange = listOfCoins.get :: listOfChange
          1
        } else if (capacity < 0)
          0
        else if (changes.isEmpty && capacity >= 1)
          0
        else {
          changeMoney(capacity, changes.tail, listOfCoins) +
          changeMoney(capacity - changes.head, changes, 
          Some(changes.head +: listOfCoins.getOrElse(Seq())))
        }
      }
    
      changeMoney(money, coins.sortWith(_.compareTo(_) < 0), None)
      Some(listOfChange)
    }
    

    【讨论】:

      【解决方案6】:

      这是一种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

      【讨论】:

        【解决方案7】:

        下面的代码类似于上面的示例之一,除了我使用匹配大小写而不是 if else

        def countChange(money: Int, coins: List[Int]): Int = {
            def change(m: Int, coinList: List[Int], count: Int): Int =
              m match {
                case _ if m < 0 => count
                case _ if coinList.isEmpty => {
                  m match {
                    case 0 => count + 1
                    case _ => count
                  }
                }
                case _ => change(m, coinList.tail, count) + change(m - coinList.head, coinList, count)
              }
            change(money, coins, 0)
          }
        

        【讨论】:

          【解决方案8】:

          这是我的代码:它没有经过优化,但适用于所有测试用例。

          这个想法是从货币中减去列表中的第一个硬币,直到它变为 0。一旦变为 0,它将返回 1,这意味着一个解决方案是可能的。要添加来自不同递归的所有解决方案,我使用了foldLeft

          (使用foldLeft迭代列表,所以首先进入1,然后再次进入递归并迭代(1, 2)列表)

                              [4, (1, 2)].  
                       /(1 as cn)       \ (2 as cn)
                      [3, (1, 2)].                 [2, (2)]
                   /(-1)       \(-2)                \
                [2, (1, 2)].     [1, (2)].          [0, (2)]   
                 /.(-1)    \(-2) 
               [1, (1, 2)].   [0, (2)]
                /. (-1)  \(-2)
              [0, (1, 2)].  [-1, (2)]
          
          def countChange(money: Int, coins: List[Int]): Int = coins.foldLeft(0)((accum, cn) =>
            (money, cn) match {
              case (money, _) if money < 0 => 0
              case (0, _) => 1
              case (curr_money, curr_coin) =>
                val (before_curr_coin, after_curr_coin) = coins.span(_ != curr_coin)
                accum + countChange(curr_money - curr_coin, after_curr_coin)
            })
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2021-10-01
            • 2022-08-19
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多