如何使用递归在最小零钱问题中输出实际的硬币组合

Posted

技术标签:

【中文标题】如何使用递归在最小零钱问题中输出实际的硬币组合【英文标题】:How to output the actual coin combination in the minimum change problem using recursion 【发布时间】:2020-02-24 05:44:27 【问题描述】:

给定硬币面额列表和目标值,我正在尝试创建一个递归函数,该函数将告诉我产生该值所需的最小硬币数量,然后显示哪些硬币我' d 需要。例如输入硬币 [1,5,10,25] 和目标 6,输出应该是“你需要 2 个硬币:[1,5]” 我写了一个函数,告诉我需要多少硬币,但我也想看看硬币组合是什么。

# USD - $1, $5, $10, etc.
currency = [1, 5, 10, 20, 50, 100]

# the amount to change - eg $6
amount = 6

cache = dict() 

def recursive_change(currency, amount):
  if amount in cache.keys() is not None:
    return cache[amount]

  # base case
  if amount == 0:
    return 0

  result = amount+1 # initially result is just some high number 
  #I figure amount + 1 is fine because we'd never use more coins than the value (assuming we can't have coins worth less than one)

  for coin in currency:
    if coin <= amount:
      result = min(result, recursive_change(currency, amount-coin) + 1)
  cache[amount]=result
  return result

这是我刚刚制作的另一个版本 - 我注意到我最初的解决方案没有很好地处理不可能的输入(例如,只使用五分钱和一角硬币赚 6 美分),所以现在它返回 -1 表示不可能的金额。虽然它有点丑,但会喜欢一些关于让它变得更好的意见。

def recursive_change(currency, amount):
  if amount in cache.keys():
    return cache[amount]

  # base case
  if amount == 0:
    return 0

  # If we can't make the amount with this coin, return -1
  if amount < 0:
    return -1

  result = -1

  for coin in currency:
    if coin <= amount:
      current_result = recursive_change(currency, amount-coin) 
      if current_result >= 0: 
        if result < 0:
          result = current_result 
        else: 
          result = min(current_result, result) 

  if result < 0:
    result = -1
  else:
    result = result + 1
  cache[amount]=result
  print(cache)
  return result

【问题讨论】:

@wim 感谢您的快速回复。我无法通过您提供的链接找到递归解决方案,我只看到迭代解决方案。 相关(DP方法)***.com/a/41812422/674039。迭代代码更适合这个问题,你有什么理由想在这里使用递归? 我正在学习,我觉得我应该能够使用迭代和递归来解决问题。我想如果我能做到这一点,我会觉得我对递归的理解要好得多——而且这种模式可能对其他事情有用,比如如果我想递归地找到一条最短路径,我可以跟踪那条路径是什么,而不仅仅是知道如何很久了。 【参考方案1】:

此解决方案使用递归来找到贪婪算法失败的理想解决方案,使用缓存因此它应该与 DP 迭代方法一样快,并输出实际的硬币组合。

# currency denominations
currency = [2,3,15,25]

# the amount to make change for - eg $30
amount = 30

# cache contains the best currency combos for each value: eg cache[7]=[5,1,1]
cache = dict()
def recursive_change(currency, amount):
  if amount in cache.keys():
    return []+cache[amount] # Had to do this [] thing so it passes a copy

  # base case
  if amount == 0:
    return []

  # If we can't make the amount with this coin, return -1
  if amount < 0:
    return [-1]

  best_combo = [-1]

  for coin in currency:
    current_result = recursive_change(currency, amount-coin)
    if current_result == [] or current_result[0] >= 0: 
      current_result.append(coin)  # if we picked a coin, we add it to the list
      if best_combo[0] < 0:
        best_combo = current_result 
        # if we found the first coin combo that works for the current problem, set best so we don't compare to -1 below
      else: 
        # if this isn't the first coin combo that works for the current problem, check to see if it's better than any of the other coin combos
        if len(current_result) < len(best_combo):
          best_combo = current_result

  cache[amount]=[]+best_combo # Had to do this [] thing so it passes a copy
  return best_combo

【讨论】:

【参考方案2】:

您的方法非常低效,尤其是对于大笔资金。您应该开始从最高面额而不是从最低面额开始生成总和。这是返回面额和硬币/钞票数量的更快、更简单的版本:

currency = [1, 5, 10, 20, 50, 100]

def recursive_change(currency, amount):
    for c in reversed(currency) :
        if amount >= c :
            return [(amount // c, c),] + recursive_change( currency, amount % c )
    return []

recursive_change( currency, 6 )
>>> [(1, 5), (1, 1)]

这意味着一枚5的硬币和一枚1的硬币。

更多测试结果:

>>> recursive_change( currency, 77 )
[(1, 50), (1, 20), (1, 5), (2, 1)]
>>> recursive_change( currency, 99 )
[(1, 50), (2, 20), (1, 5), (4, 1)]

【讨论】:

当我尝试运行它时,recursive_change(currency,6) 我得到了不同的结果。 [(1.2, 5)] @BobbyBattista 没有floats,请在任何地方使用int @BobbyBattista 好的,// 可能会为您正在使用的 python3 解决问题 @BobbyBattista 请尝试最新版本的代码 是的,明白了。 return [(int(amount / c), c),] + rc( currency, amount % c ) 工作。右括号前的逗号是什么?

以上是关于如何使用递归在最小零钱问题中输出实际的硬币组合的主要内容,如果未能解决你的问题,请参考以下文章

如何计算硬币数量的变化?

凑零钱问题的三种解法(Java)

第322题:零钱兑换

零钱兑换

322. 零钱兑换

322.零钱兑换