Ruby 与递归基本情况作斗争

Ruby 与递归基本情况作斗争,ruby,recursion,Ruby,Recursion,我花了一段时间研究以下算法: 您将获得不同面额的硬币,总金额为 金额。编写一个函数来计算最少数量的硬币 你需要弥补这个数额。如果那笔钱不能 由任意组合的硬币组成,返回-1 示例1:硬币=[1,2,5],金额=11返回3 11=5+5+1 示例2:硬币=[2],金额=3返回-1 注意:您可以假设每种类型的 硬币 这可能不是解决问题的最有效方法,但我想我可以通过尝试每一个硬币并每次尝试启动一个新函数来解决它,其中新函数调用具有更新的数量。这将为每枚硬币启动N个函数调用。。。但我稍后会处理这个问题 现

我花了一段时间研究以下算法:

您将获得不同面额的硬币,总金额为 金额。编写一个函数来计算最少数量的硬币 你需要弥补这个数额。如果那笔钱不能 由任意组合的硬币组成,返回-1

示例1:硬币=[1,2,5],金额=11返回3 11=5+5+1

示例2:硬币=[2],金额=3返回-1

注意:您可以假设每种类型的 硬币

这可能不是解决问题的最有效方法,但我想我可以通过尝试每一个硬币并每次尝试启动一个新函数来解决它,其中新函数调用具有更新的数量。这将为每枚硬币启动N个函数调用。。。但我稍后会处理这个问题

现在我正在处理以下问题:通常在进行递归调用时,我无法在基本情况下正确地编写代码。例如,在这个问题中,如果货币的数量不能由硬币的任何组合来弥补,我们必须返回-1。但是,我还需要计算最少的硬币数量。所以我想我应该取一个min变量,然后调用1+new\u func\u调用

但是,当这个新的_func_调用最终不起作用时,它会向上传递一个-1到递归调用堆栈,而递归调用堆栈的最小值为零。我不知道该如何调整——我尝试过用不同的方式改变我的代码,但可能我有一个概念上的问题。我知道为什么会这样,只是不知道如何处理

Sample input:
Coins: [2]
Amount: 3

My output: 0
Correct output: -1 
代码:

现在我正在处理以下问题:通常在进行递归调用时,我无法在基本情况下正确地编写代码。例如,在这个问题中,如果货币的数量不能由硬币的任何组合来弥补,我们必须返回-1。但是,我还需要计算最少的硬币数量

你这里有两个基本情况

如果amount状态变量为零,则我们已完成计数,因此返回计数 如果要计数的硬币X用完了,但仍然没有达到零,那么返回-1 否则我们有两种递归情况

oops:amount小于0表示减去的最后一枚硬币x太大–在不使用该硬币的情况下倒带并递归 默认情况下:计数时加1,从金额中减去硬币x,然后使用相同的硬币x进行递归 这项工作的唯一要求是首先按降序对硬币进行排序

好的,所有这些都很容易用Ruby编码,使用辅助辅助工具aux保存状态变量。请记住使用0计数初始化,并确保xs按降序排序。-注意排序只发生一次,而不是每次递归一次

def fewest_coins amount, xs
  def aux count, amount, (x,*xs)
    if amount.zero?
      count
    elsif x.nil?
      -1
    elsif amount < 0
      aux (count - 1), (amount + x), xs
    else
      aux (count + 1), (amount - x), [x, *xs]
    end
  end
  aux 0, amount, xs.sort { |a,b| b <=> a }
end

fewest_coins 11, [1, 5, 2]         # => 3
fewest_coins 2, [3]                # => -1
fewest_coins 100, [1, 25, 10, 5]   # => 4
现在我正在处理以下问题:通常在进行递归调用时,我无法在基本情况下正确地编写代码。例如,在这个问题中,如果货币的数量不能由硬币的任何组合来弥补,我们必须返回-1。但是,我还需要计算最少的硬币数量

你这里有两个基本情况

如果amount状态变量为零,则我们已完成计数,因此返回计数 如果要计数的硬币X用完了,但仍然没有达到零,那么返回-1 否则我们有两种递归情况

oops:amount小于0表示减去的最后一枚硬币x太大–在不使用该硬币的情况下倒带并递归 默认情况下:计数时加1,从金额中减去硬币x,然后使用相同的硬币x进行递归 这项工作的唯一要求是首先按降序对硬币进行排序

好的,所有这些都很容易用Ruby编码,使用辅助辅助工具aux保存状态变量。请记住使用0计数初始化,并确保xs按降序排序。-注意排序只发生一次,而不是每次递归一次

def fewest_coins amount, xs
  def aux count, amount, (x,*xs)
    if amount.zero?
      count
    elsif x.nil?
      -1
    elsif amount < 0
      aux (count - 1), (amount + x), xs
    else
      aux (count + 1), (amount - x), [x, *xs]
    end
  end
  aux 0, amount, xs.sort { |a,b| b <=> a }
end

fewest_coins 11, [1, 5, 2]         # => 3
fewest_coins 2, [3]                # => -1
fewest_coins 100, [1, 25, 10, 5]   # => 4

你可以这样做

def count_ways(cents, coins)
  if coins.size == 1
    return (cents % coins.first) == 0 ? [cents/coins.first] : nil
  end 
  coin, *remaining_coins = coins
  (0..cents/coin).each_with_object([]) { |n, arr|
    count_ways(cents-n*coin, remaining_coins).each { |a| arr << [n, *a] } }
end 

def fewest(cents, coins)
  count_ways(cents, coins)&.map(&:sum)&.min
end

fewest(11, [5,2,1])
  #=> 3
fewest(199, [25,10,5,1])
  #=> 13 (Let me guess: 7 quarters, 2 dimes, 4 pennies) 
fewest(2, [3]) 
  #=> nil

require 'time'

t = Time.now
fewest(2835, [25,10,5,1])
  #=> 114 
Time.now - t
  #=> 7.6961 (seconds)
我从我的回答中数了数


两个&’后接一个。是Ruby的,它是在Ruby v2.3.0中引入的。最早出现在Ruby v2.4.0中。

您可以按如下方式进行操作

def count_ways(cents, coins)
  if coins.size == 1
    return (cents % coins.first) == 0 ? [cents/coins.first] : nil
  end 
  coin, *remaining_coins = coins
  (0..cents/coin).each_with_object([]) { |n, arr|
    count_ways(cents-n*coin, remaining_coins).each { |a| arr << [n, *a] } }
end 

def fewest(cents, coins)
  count_ways(cents, coins)&.map(&:sum)&.min
end

fewest(11, [5,2,1])
  #=> 3
fewest(199, [25,10,5,1])
  #=> 13 (Let me guess: 7 quarters, 2 dimes, 4 pennies) 
fewest(2, [3]) 
  #=> nil

require 'time'

t = Time.now
fewest(2835, [25,10,5,1])
  #=> 114 
Time.now - t
  #=> 7.6961 (seconds)
我从我的回答中数了数


两个&’后接一个。是Ruby的,它是在Ruby v2.3.0中引入的。最早出现在Ruby v2.4.0中。

这个问题已经有了一个非常好的答案,可以准确地告诉您如何解决您的问题,但我想指出您的算法实际发生了什么,这样您就知道它为什么不适用于您了

你的第一个问题是

coin_count(coins, amount - coins[i], min)
应该是

coin_count(coins, amount - coins[i], coins.min)
你不是传递最小的硬币,而是传递你的最小值,你已经设置为无穷大,这使得这个声明检查金额是否小于最小的硬币:

return -1 if amount < min_coin
在递归编程中使用-1作为错误,令人沮丧的是-1是一个在其他方面有效的数字,并且是一个频繁的错误 逻辑错误导致逻辑问题。我会避免完全使用它,但如果您的提示或规范强制您返回它,我只会在最后一秒使用它。尝试:

def coin_change(coins, amount)
    result = coin_count(coins, amount, coins.min)
    return -1 if result == 1/0.0
    return result
end

def coin_count(coins, amount, min_coin)
    with_coin = min = 1.0/0
    return 0 if amount == 0
    return 1/0.0 if amount < min_coin
    i = 0

    while i < coins.length
        with_coin = 1 + coin_count(coins, amount - coins[i], coins.min) if amount - coins[i] >= 0
        min = [min, with_coin].min
        i += 1
    end

    min
end

这个问题已经有了一个很好的答案,它向你展示了如何解决你的问题,但是我想指出你的算法到底发生了什么,所以你知道为什么它对你不起作用

你的第一个问题是

coin_count(coins, amount - coins[i], min)
应该是

coin_count(coins, amount - coins[i], coins.min)
你不是传递最小的硬币,而是传递你的最小值,你已经设置为无穷大,这使得这个声明检查金额是否小于最小的硬币:

return -1 if amount < min_coin
在递归编程中使用-1作为错误,令人沮丧的是-1是一个在其他方面有效的数字,并且经常导致逻辑问题。我会避免完全使用它,但如果您的提示或规范强制您返回它,我只会在最后一秒使用它。尝试:

def coin_change(coins, amount)
    result = coin_count(coins, amount, coins.min)
    return -1 if result == 1/0.0
    return result
end

def coin_count(coins, amount, min_coin)
    with_coin = min = 1.0/0
    return 0 if amount == 0
    return 1/0.0 if amount < min_coin
    i = 0

    while i < coins.length
        with_coin = 1 + coin_count(coins, amount - coins[i], coins.min) if amount - coins[i] >= 0
        min = [min, with_coin].min
        i += 1
    end

    min
end

这肯定不是一个最聪明的方法,也不是性能最好的方法,可能需要花费大量的时间,但我在ruby中会这样做:

def get coins, amount
  coins = coins.sort
  max = amount / coins.first + 1
  (1..max).detect do |i|
    result = coins.repeated_permutation(i).detect do |e|
      e.reduce(:+) == amount
    end
    break result if result
  end || -1
end

get [1, 2, 5], 11
#⇒ [1, 5, 5]

get [2], 3
#⇒ -1

这肯定不是一个最聪明的方法,也不是性能最好的方法,可能需要花费大量的时间,但我在ruby中会这样做:

def get coins, amount
  coins = coins.sort
  max = amount / coins.first + 1
  (1..max).detect do |i|
    result = coins.repeated_permutation(i).detect do |e|
      e.reduce(:+) == amount
    end
    break result if result
  end || -1
end

get [1, 2, 5], 11
#⇒ [1, 5, 5]

get [2], 3
#⇒ -1


如果coin=min=1.0/0肯定不是一个好办法,1除以0得到Float::INFINITY。@mudasobwa这是故意的,因为我不想限制min。最初我有,如果min是无穷大,它永远不会被更新,所以返回-1。但这又回到了递归堆栈中向上传递-1的相同问题。如果coin=min=1.0/0绝对不是一种方法,1除以0得到Float::INFINITY。@mudasobwa这是故意的,因为我不想限制min。最初我有,如果min是无穷大,它永远不会被更新,所以返回-1。但这可以追溯到递归堆栈中向上传递-1的相同问题。看起来提问者实际上想要的是组成该数量所需的最小硬币数量,而不是硬币组合的总数。不过,令人耳目一新的功能强大的ruby代码值得称赞!我喜欢x的用法*xs@eiko啊,呜呜!我看到了著名的硬币问题,并假设它是一个组合!我以后会修改这个:哈哈,是的,我想我前几天在这里看到了组合硬币的问题?这是一个非常快速的编辑,如此重大的变化。好的@爱子很好。我经常发现询问者的帖子有太多问题,以至于我最终放弃了他们所有的代码。遗憾的是,我意识到这意味着我的答案并不总是说明如何帮助他们了解原始代码的错误。感谢您整理并提供了一个很好的答案。A+看起来提问者实际上想要的是组成金额所需的最小硬币数量,而不是硬币组合的总数。不过,令人耳目一新的功能强大的ruby代码值得称赞!我喜欢x的用法*xs@eiko啊,呜呜!我看到了著名的硬币问题,并假设它是一个组合!我以后会修改这个:哈哈,是的,我想我前几天在这里看到了组合硬币的问题?这是一个非常快速的编辑,如此重大的变化。好的@爱子很好。我经常发现询问者的帖子有太多问题,以至于我最终放弃了他们所有的代码。遗憾的是,我意识到这意味着我的答案并不总是说明如何帮助他们了解原始代码的错误。感谢您整理并提供了一个很好的答案。A+/我微笑着啜饮着tea@naomik,对于你和任何记得你以前的头像的人来说,微笑和喝茶的时候闭上嘴就行了。很好地使用%来减少一些iterations@naomik,你的aux方法比我的快多了。考虑一下我链接的问题。我刚注意到你在加拿大。我在维多利亚。哦,见鬼!我最初对这个问题的回答实际上是对这个问题的回答。多么奇怪的巧合啊/我微笑着啜饮tea@naomik,对于你和任何记得你以前的头像的人来说,微笑和喝茶的时候闭上嘴就行了。很好地使用%来减少一些iterations@naomik,你的aux方法比我的快多了。考虑一下我链接的问题。我刚注意到你在加拿大。我在维多利亚。哦,见鬼!我最初对这个问题的回答实际上是对这个问题的回答。多么奇怪的巧合啊!这对我来说太完美了。简单地将-1拉入原始方法并将其从递归代码中取出就解决了我的问题。我没想到!我很好奇为什么我的代码不起作用——但在他们的输入上,我比其他人投了更高的票!这对我来说太完美了。简单地将-1拉入原始方法并将其从递归代码中取出就解决了我的问题。我没想到!我很好奇为什么我的代码不起作用——但在他们的输入上,我比其他人投了更高的票!