解决硬币找零暴力解决方案

时间:2020-05-30 19:58:12

标签: python recursion

我开始写和理解硬币找零问题,无法直觉,所以我开始写蛮力解决方案。在转向记忆化之前,我想了解蛮力解决方案。

coins = [2, 3, 7]
change = 12

def coin_change(c):
    print(c)
    if c <= 0:
        return 0
    else:
        for i in coins:
            if c - i >= 0:
                coin_change(c - i)

coin_change(change)

它会打印出一些更改,但我不知道如何将每个路径存储在数组中。

我还想了解如何使用递归来跟踪路径。我可以考虑在coin_change中添加一个额外的参数,但是也许有另一种方法。

我对它的复杂性感到困惑。它的每一步都有很多投币电话,但是许多在线资源都提到它是2 n

编辑:请注意,硬币的供应是无限的,答案是4

[2, 2, 2, 2, 2, 2]
[3, 3, 3, 3]
[2, 2, 2, 3, 3]
[2, 3, 7]

3 个答案:

答案 0 :(得分:1)

了解硬币找零问题:

假设您要使用最小数量的硬币来计算找零的问题,那么这里的关键思想是将问题视为在每个步骤中做出选择-在这种情况下,这是“接下来要分配的硬币” '

您可以将问题分解为coin_change(score) = 1 + min{coin_change(score - c1), coin_change(score - c2), ...},其中c1, c2...是您拥有的硬币。

跟踪路径

这很简单。无需返回解决方案(最小硬币组合),只需返回所有可能性(所有硬币组合)。因此,当您对(score-c1)进行递归调用时,您将获得所有组成(score-c1)的硬币组合,而只需将c1添加到它们中即可。

代码

coins = [2, 3, 7]
change = 12

def coin_change(c):
  if c == 0:
    return [[]]     # the only combo possible is no coins
  if c < 0:
    return []      # no combos possible
  else:
    all_combos = []
    for i in coins:
      recursive_result = coin_change(c-i)
      for combo in recursive_result:
        combo.append(i)
      all_combos.extend(recursive_result)

    return all_combos

result = coin_change(change)
for combo in result:
  print(combo)

注意:这将为您提供所有排列。如果顺序无关紧要,则可以使用集删除重复项

编辑:在注释之后,这是删除重复项的代码

coins = [2, 3, 7]
change = 12

def removeDuplicates(combos):
  filtered = set()
  for combo in combos:
    combo.sort()
    filtered.add(tuple(combo))
  return [list(i) for i in filtered]

def coin_change(c):
  if c == 0:
    return [[]]     # the only combo possible is no coins
  if c < 0:
    return []      # no combos possible
  else:
    all_combos = []
    for i in coins:
      recursive_result = coin_change(c-i)
      for combo in recursive_result:
        combo.append(i)
      all_combos.extend(recursive_result)

    return removeDuplicates(all_combos)

result = coin_change(change)
for combo in result:
  print(combo)

答案 1 :(得分:1)

这里是获取硬币所有路径的示例。通常,在编写递归函数时,您希望1)具有退出递归的条件,以及2)编写如何扩展上一个递归步骤。

coins  = [2, 3, 7]
change = 12

def coin_change_paths(paths):
    if all(sum(path)>=change for path in paths):
        return paths
    new_paths = []
    for path in paths:
        if sum(path)<change:
            new_paths.extend(path+[c] for c in coins)
        else:
            new_paths.append(path)
    return coin_change_paths(new_paths)

paths = coin_change_paths([[2],[3],[7]])

这不考虑重复。为此,您应该进行排序,然后进行重复处理:

paths = [sorted(p) for p in paths]
temp = []
for p in paths:
    if p not in temp:
        temp.append(p)
paths = temp

您仍然必须检查哪些有效。

paths = [p for p in paths if sum(p)==change]
print(paths)

尝试查看是否可以将它们组合在一起并简化代码。

答案 2 :(得分:1)

另一种可能的方法是对yieldyield from使用递归生成器函数。这样就无需将发现的组合保存在列表中,然后必须将其返回:

coins = [2, 3, 7]
change = 12
def coin_change(c = []):
  if sum(c) == change:
     yield tuple(sorted(c))
  else:
     for i in coins:
        if sum(c+[i]) <= change:
           yield from coin_change(c+[i])

print(list(map(list, set(coin_change()))))

输出:

[[3, 3, 3, 3], [2, 2, 2, 3, 3], [2, 3, 7], [2, 2, 2, 2, 2, 2]]