递归变换算法

时间:2012-09-20 20:17:08

标签: python algorithm recursion dynamic-programming coin-change

鉴于目标金额和硬币面额清单,我的代码应该找到达到目标金额所需的最少硬币。

示例:

  • C(78, [1, 5, 10, 25, 50]) = 6

    • 我们可以从3x 25 + 3x 1 制作78,因此需要6个硬币
  • C(48, [1, 7, 24, 42]) = 2

    • 48 = 2x 24 ,所以2个硬币就够了
  • C(35, [1, 3, 16, 30, 50]) = 3

    • 我们可以从2x 16 + 1x 3 制作35,所以3个硬币就足够了

我使用for循环创建了代码,但是如何使其递归?

def C(i, coins, cdict = None):
    if cdict == None:
        cdict = {}
    if i <= 0:
        cdict[i] = 0
        return cdict[i]
    elif i in cdict:
        return cdict[i]
    elif i in coins:
        cdict[i] = 1
        return cdict[i]
    else:
        min = 0
        for cj in coins:
            result = C(i - cj, coins)
            if result != 0:
                if min == 0 or (result + 1) < min:
                    min = 1 + result
        cdict[i] = min
        return cdict[i]

3 个答案:

答案 0 :(得分:19)

这是change-making问题。这是标准的递归解决方案,V是硬币列表,C是目标金额:

def min_change(V, C):
    def min_coins(i, aC):
        if aC == 0:
            return 0
        elif i == -1 or aC < 0:
            return float('inf')
        else:
            return min(min_coins(i-1, aC), 1 + min_coins(i, aC-V[i]))
    return min_coins(len(V)-1, C)

这是一个优化版本,使用dynamic programming

def min_change(V, C):
    m, n = len(V)+1, C+1
    table = [[0] * n for x in xrange(m)]
    for j in xrange(1, n):
        table[0][j] = float('inf')
    for i in xrange(1, m):
        for j in xrange(1, n):
            aC = table[i][j - V[i-1]] if j - V[i-1] >= 0 else float('inf')
            table[i][j] = min(table[i-1][j], 1 + aC)
    return table[m-1][n-1]

两种实现都将始终返回最佳解决方案,但第二种实现将对大型输入更快。请注意,其他答案中建议的贪婪算法仅为某些货币组合提供了最佳解决方案 - 例如,它适用于美国硬币。

答案 1 :(得分:2)

尝试使用贪心算法(最大的硬币)。申请总数后,从列表中删除硬币,然后从内部再次调用该功能。

答案 2 :(得分:0)

因此,寻找动态编程实现的最佳位置是:interactive python

然而,经过一些测试后,我发现它不是最快的解决方案。关于它的好处是,一次运行足以找到所需的所有变化值的值。

我最喜欢的仍然是使用缓存的递归:

from collections import defaultdict
from functools import lru_cache


@lru_cache(maxsize=1024)
def greedy(coin_list, change):
    if change in coin_list:
        return defaultdict(int, [(change, 1)])
    else:
        min_coins = change
        for c in [coin for coin in coin_list if coin < change]:
            result_min1 = greedy(coin_list, change - c)
            num_coins = 1 + sum(result_min1.values())
            if num_coins <= min_coins:
                min_coins = num_coins
                result = defaultdict(int, [(c, 1)])
                for c1, n in result_min1.items():
                    result[c1] += n
        return result