python - 在python中递归实现 'minimum number of coins'

标签 python algorithm dynamic-programming

此问题与 here 中的问题相同.

给定硬币列表、它们的值(c1、c2、c3、... cj、...)以及总和 i。找到总和为 i 的最少硬币数量(我们可以根据需要使用任意数量的一种类型的硬币),或者报告无法以总和为 S 的方式选择硬币。

我昨天刚接触动态规划,我试着为它编写代码。

# Optimal substructure: C[i] = 1 + min_j(C[i-cj])
cdict = {}
def C(i, coins):

    if i <= 0:
        return 0

    if i in cdict:
        return cdict[i]
    else:
        answer = 1 + min([C(i - cj, coins) for cj in coins])
        cdict[i] = answer
        return answer

这里,C[i] 是金额“i”的最优解。可用的硬币是 {c1, c2, ... , cj, ...} 对于程序,我增加了递归限制以避免最大递归深度超出错误。但是,这个程序只给出正确的答案,当解决方案不可能时,它并不表示。

我的代码有什么问题以及如何更正它?

最佳答案

这是一个很好的算法问题,但老实说,我认为您的实现不正确,或者可能是我不理解您函数的输入/输出,对此我深表歉意。

这是您的实现的修改版本。

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]

这是我尝试解决类似问题的尝试,但这次返回的是硬币列表。我最初从一个递归算法开始,它接受一个总和和一个硬币列表,如果找不到这样的配置,它可能返回一个包含最小硬币数量的列表或 None。

def get_min_coin_configuration(sum = None, coins = None):
if sum in coins: # if sum in coins, nothing to do but return.
    return [sum]
elif max(coins) > sum: # if the largest coin is greater then the sum, there's nothing we can do.
    return None
else: # check for each coin, keep track of the minimun configuration, then return it.
    min_length = None
    min_configuration = None
    for coin in coins:
        results = get_min_coin_configuration(sum = sum - coin, coins = coins)
        if results != None:
            if min_length == None or (1 + len(results)) < len(min_configuration):
                min_configuration = [coin] + results
                min_length = len(min_configuration)
    return min_configuration

好的,现在让我们看看我们是否可以通过使用动态编程(我称之为缓存)来改进它。

def get_min_coin_configuration(sum = None, coins = None, cache = None):
if cache == None: # this is quite crucial if its in the definition its presistent ...
    cache = {}
if sum in cache:
    return cache[sum]
elif sum in coins: # if sum in coins, nothing to do but return.
    cache[sum] = [sum]
    return cache[sum]
elif max(coins) > sum: # if the largest coin is greater then the sum, there's nothing we can do.
    cache[sum] = None
    return cache[sum]
else: # check for each coin, keep track of the minimun configuration, then return it.
    min_length = None
    min_configuration = None
    for coin in coins:
        results = get_min_coin_configuration(sum = sum - coin, coins = coins, cache = cache)
        if results != None:
            if min_length == None or (1 + len(results)) < len(min_configuration):
                min_configuration = [coin] + results
                min_length = len(min_configuration)
    cache[sum] = min_configuration
    return cache[sum]

现在让我们运行一些测试。

assert all([ get_min_coin_configuration(**test[0]) == test[1] for test in
[({'sum':25,  'coins':[1, 5, 10]}, [5, 10, 10]),
 ({'sum':153, 'coins':[1, 5, 10, 50]}, [1, 1, 1, 50, 50, 50]),
 ({'sum':100, 'coins':[1, 5, 10, 25]}, [25, 25, 25, 25]),
 ({'sum':123, 'coins':[5, 10, 25]}, None),
 ({'sum':100, 'coins':[1,5,25,100]}, [100])] ])

如果此测试不够稳健,您也可以这样做。

import random
random_sum = random.randint(10**3, 10**4)
result = get_min_coin_configuration(sum = random_sum, coins = random.sample(range(10**3), 200))
assert sum(result) == random_sum

有可能没有这样的硬币组合等于我们的 random_sum 但我相信这不太可能......

我确信有更好的实现,我试图强调可读性而不是性能。 祝你好运。

已更新 之前的代码有一个小错误,它应该检查最小硬币而不是最大硬币,重新编写了符合 pep8 标准的算法,并在找不到组合而不是 None< 时返回 []/.

def get_min_coin_configuration(total_sum, coins, cache=None):  # shadowing python built-ins is frowned upon.
    # assert(all(c > 0 for c in coins)) Assuming all coins are > 0
    if cache is None:  # initialize cache.
        cache = {}
    if total_sum in cache:  # check cache, for previously discovered solution.
        return cache[total_sum]
    elif total_sum in coins:  # check if total_sum is one of the coins.
        cache[total_sum] = [total_sum]
        return [total_sum]
    elif min(coins) > total_sum:  # check feasibility, if min(coins) > total_sum
        cache[total_sum] = []     # no combination of coins will yield solution as per our assumption (all +).
        return []
    else:
        min_configuration = []  # default solution if none found.
        for coin in coins:  # iterate over all coins, check which one will yield the smallest combination.
            results = get_min_coin_configuration(total_sum - coin, coins, cache=cache)  # recursively search.
            if results and (not min_configuration or (1 + len(results)) < len(min_configuration)):  # check if better.
                min_configuration = [coin] + results
        cache[total_sum] = min_configuration  # save this solution, for future calculations.
    return cache[total_sum]



assert all([ get_min_coin_configuration(**test[0]) == test[1] for test in
             [({'total_sum':25,  'coins':[1, 5, 10]}, [5, 10, 10]),
              ({'total_sum':153, 'coins':[1, 5, 10, 50]}, [1, 1, 1, 50, 50, 50]),
              ({'total_sum':100, 'coins':[1, 5, 10, 25]}, [25, 25, 25, 25]),
              ({'total_sum':123, 'coins':[5, 10, 25]}, []),
              ({'total_sum':100, 'coins':[1,5,25,100]}, [100])] ])

关于python - 在python中递归实现 'minimum number of coins',我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10974206/

相关文章:

java - 二进制 watch 算法 (Java)

algorithm - 给定范围超过 M 的 N 个数字的排序列表,M>>N 找到或确定给定数字不存在。如果可能的话,恒定时间?

java - 这是什么算法?盒装/背包?

algorithm - 诊断动态规划与优先级队列的情况

python - 提高查询性能

python - 导入 scipy.stats 后 Ctrl-C 使 Python 崩溃

javascript - 将一系列值映射到另一个值

python - 太多的写操作

python - 类型错误 : can't convert expression to float

algorithm - 将给定的字谜转换为另一个字谜所需的最小交换次数