make_change算法:计算确切变化的最佳方法

时间:2015-08-01 01:50:34

标签: python dictionary

我刚刚开始学习Python并使用伯克利的CS课程中的材料进行练习。这个问题来自那个班级。如果有人可以告诉我如何找到解决方案或帮助修复我的程序,我将非常感激。

我遇到问题的功能是make_change;它需要一个字典,每个键都是货币的值,而值是你拥有的那个货币的数量。该函数接受该字典和一个值,并返回可以从给定字典构建的最短列表,该字典列出从最低到最高的货币单位,其恰好与输入的金额相加。查看doc字符串以获取更具体的示例。

当我对此函数运行doc测试时,doc测试的实际结果是大多数这些测试的正确顺序是正确的数字,但许多列表中有列表,而且更改的文档测试为25在经过足够的递归循环后返回None。

有人可以向我解释为什么这个程序给我回复无以及如何摆脱列表中的列表?我将不胜感激任何帮助。

def make_change(amount, coins):
    """Return a list of coins that sum to amount, preferring the smallest
    coins available and placing the smallest coins first in the returned 
    list.  The coins argument is a dictionary with keys that are positive 
    integer denominations and values that are positive integer coin counts.

    >>> make_change(2, {2: 1})
    [2]
    >>> make_change(2, {1: 2, 2: 1})
    [1, 1]
    >>> make_change(4, {1: 2, 2: 1})
    [1, 1, 2]
    >>> make_change(4, {2: 1}) == None
    True
    >>> coins = {2: 2, 3: 2, 4: 3, 5: 1}
    >>> make_change(4, coins)
    [2, 2]
    >>> make_change(8, coins)
    [2, 2, 4]
    >>> make_change(25, coins)
    [2, 3, 3, 4, 4, 4, 5]
    >>> coins[8] = 1
    >>> make_change(25, coins)
    [2, 2, 4, 4, 5, 8]
    """
    if len(coins) == 0:
        return None
    smallest = min(coins)
    rest = remove_one(coins, smallest)
    lst = []
    if smallest == amount:
        return [smallest]
    elif amount - smallest >= smallest:
        amount -= smallest
        lst.extend([smallest] + [make_change(amount, rest)])
        return lst
    elif amount - smallest < smallest:
        return [make_change(amount, rest)]


def remove_one(coins, coin):
    """Remove one coin from a dictionary of coins. Return a new dictionary,
    leaving the original dictionary coins unchanged.

    >>> coins = {2: 5, 3: 2, 6: 1}
    >>> remove_one(coins, 2) == {2: 4, 3: 2, 6: 1}
    True
    >>> remove_one(coins, 6) == {2: 5, 3: 2}
    True
    >>> coins == {2: 5, 3: 2, 6: 1} # Unchanged
    True
    """
    copy = dict(coins)
    count = copy.pop(coin) - 1
    if count:
        copy[coin] = count
    return copy

2 个答案:

答案 0 :(得分:1)

make_change函数返回列表或None。这意味着两件事:(a)你不需要将它包装在另一个列表中; (b)在将其返回值与其他列表连接之前,您需要检查None。如果我们将这些点应用于您的代码,我们会得到类似的结果:

change = make_change(amount, rest)
if change is None:
    return None
else:
    return [smallest] + change

更大的问题(为什么函数返回None为25)是由你的算法引起的:它是贪婪的(无论如何都消耗最小的硬币),因此如果它沿着不可能的路径前进就会失败。您需要添加一些回溯逻辑。

答案 1 :(得分:0)

最直接的方法是使用递归。假设您必须使用列表[14,7,3,1]中的硬币对100进行更改。 如果最短列表使用14硬币,则其长度将比使用相同硬币改变86的最短路径长一倍。如果它没有使用14硬币,那么它将是使用[7,3,1]中的硬币改变100的最短方式。递归会照顾你的回溯。