当前位置: 首页 > 知识库问答 >
问题:

与递归基本情况作斗争

闻人吕恭
2023-03-14

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

你会得到不同面额的硬币和总金额。写一个函数来计算你需要的最少数量的硬币来组成这个数量。如果这些硬币的任何组合都不能弥补这个金额,返回-1。

例1:币=[1,2,5],金额=113 (11 = 5 5 1)

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

注意:你可以假设每种硬币的数量是无限的。

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

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

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

Sample input:
Coins: [2]
Amount: 3

My output: 0
Correct output: -1 

代码:

def coin_change(coins, amount)
    coin_count(coins, amount, coins.min)
end

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

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

    min
end

共有3个答案

邹修真
2023-03-14

这个问题已经有了一个非常好的答案,它向你展示了如何准确地解决你的问题,但是我想指出你的算法实际上发生了什么,这样你就知道为什么它不适合你。

你的第一个问题是

coin_count(coins, amount - coins[i], min)

应该是

coin_count(coins, amount - coins[i], coins.min)

你不是传递最小的硬币,而是传递你的最小值,你已经设置为无穷大,这使得这个声明检查金额是否小于最小的硬币:

return -1 if amount < min_coin

实际上,检查金额是否小于无穷大,这意味着您的硬币计数总是返回-1。这导致了第二个问题:

1 + coin_count(coins, amount - coins[i], min)
#1 + -1 = 0

在递归编程中使用-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

我将您的错误号从-1改为无穷大,这本质上使您的算法忽略了无效排列,因为它们总是由您的用户进行排序。min()。在这种情况下,函数返回无穷大的唯一方法是它是返回的最小数,这仅在没有有效置换时发生。然后在最少的硬币中,我将其设置为检查无穷大,并返回-1。

哦,顺便说一句,在ruby中有一种更简单的循环方式:

coins.each do |coin|
  with_coin = 1 + coin_count(coins, amount - coin, coins.min) if amount - coin >= 0
  min = [min, with_coin].min
end
张德佑
2023-03-14

你可以这样做。

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)

我从我的答案中count_ways。

两个

萧宏远
2023-03-14

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

你这里有两个基本情况

  1. 如果amount状态变量为零,则我们已完成计数,因此返回count

否则我们有两个递归案例

  1. 哎呀:amount小于0,意味着减去的最后一枚硬币(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

检查你的理解

作为练习,修改最少的_硬币以输出组成答案的硬币数组

# for example
fewest_coins 199, [1, 5, 10, 25, 50]
# => [50, 50, 50, 25, 10, 10, 1, 1, 1, 1]

 类似资料:
  • 我试图理解这段代码,它返回传递给它的的所有可能组合: 在这里,我尝试了这个样本输入: 在这里,我似乎无法理解递归最终将如何停止并返回,因为函数肯定没有任何指针在列表中移动,在每次调用时,当它满足基本情况时,它返回。根据我的说法,它将调用函数无限,并且永远不会自行停止。或者可能是我错过了什么? 另一方面,take 在完成递归调用后返回返回所有计算后,仅返回的前 个元素。那么,实际上递归如何满足这里的

  • 我想澄清一下O(N)函数。我正在使用SICP。 考虑书中生成伪代码递归过程的阶乘函数: 我不知道如何测量步数。也就是说,我不知道“步骤”是如何定义的,所以我使用书中的语句来定义步骤: 因此,我们可以计算n!通过计算(n-1)!将结果乘以n。 我想这就是他们所说的一步。对于一个具体的例子,如果我们跟踪(阶乘5), 阶乘(1)=1=1步(基本情况-恒定时间) 阶乘(2)=2*阶乘(1)=2步 阶乘(3

  • 我写了一个到达基本情况的方法(我可以告诉你,因为它打印了print语句),但是它会循环返回null(在方法的结尾)。为什么我的方法没有在基本情况下停止? 编辑:此外,如果一个对象不存在于我的BST中,它不会返回null。我得到了一个空指针异常,这是不应该发生的,因为或语句

  • 问题内容: 我在上面直接写了上面的内容,因此可能无法编译,但认为可以。 任何人都可以从存储的角度来简短地解释它的工作原理吗?它通过计算5 (5-1)开始,然后依次下降到4 (4-1)然后是3 *(3-1).....直到达到1,它将只返回1,对吗?抱歉,我太粗略了,我只想知道这是如何工作的 谢谢 但随着工作的进行,它将获得各个阶段的值 5 (5-1)4 (4-1)… … … 这些如何存储然后取回,或

  • 我有一个迷宫,我必须用递归来解。迷宫必须在找到开放路径的地方放置一个X(我的代码就是这样做的)。它必须这样做,直到使用递归调用到达出口为止(我的代码就是这样做的,下面描述的除外)。它还必须在到达死胡同的地方放置一个O,将操作系统拉回到“正确”路径,然后沿着新路径继续求解(我的代码就是这样做的)。 然而,一旦到达迷宫的末端,它就必须求解一个新的迷宫(原始迷宫,转置)。我的问题如下: 一旦我到达迷宫的

  • 问题内容: 我在使用Java中的基本递归问题时遇到了很多麻烦;任何指针都很棒。 “写一种静态递归方法来打印出几何序列的第n个项:2、6、18、54。” 据我所知,我应该在代码中的某处递归地将某物乘以3,但我一直在努力寻找方法。我知道我需要终止声明,但是何时发生?我需要帮手方法吗? 问题答案: 一个递归函数是一个函数,它的实现引用自身。以下是一些有趣的示例: 解决问题的方法: 编辑 : 上面的类使用