最小硬币变化:从该算法重建解决方案

时间:2015-04-03 14:15:44

标签: python-2.7 dynamic-programming

我已经为Python中的经典最小硬币更改solution实现了动态编程puzzle,并且对我简短易懂(对我而言)的解决方案非常满意:

def memoize(fcn):
    cache = {}
    def decorated(d, p):
        if p not in cache:
            cache[p] = fcn(d, p)
        return cache[p]
    return decorated

@memoize
def mc(d, p):
    if p in d: return 1
    cands = [n for n in d if n < p]
    if not cands: return 10**20
    return 1 + min ([mc(d, p-d_i) for d_i in cands])

In [101]:
d = [1, 5, 10, 25, 21]
p = 63
mc(d, p)
Out[101]:
3

但是现在我想我会尝试实际退出最佳解决方案中使用的硬币。即对于上述情况,我想输出21 + 21 + 21。

但是,根据我的程序制定,我认为没办法轻易做到这一点。我必须跟踪整个解决方案树,然后根据减去的更改递归移动节点,以便到达该特定节点。

那么,有一种简单的方法来修改我的解决方案吗?感谢

1 个答案:

答案 0 :(得分:2)

试试这个:

def memoize(fcn, cache = {}):
    def decorated(d, p):
        if p not in cache:
            cache[p] = fcn(d, p)
        return cache[p]
    return decorated

@memoize
def mc(d, p):
    if p in d:
        return (1, [p])

    cands = [n for n in d if n < p]

    if not cands:
        return (10**20, [])

    d_i, pair = min(
        [(d_i, mc(d, p-d_i)) for d_i in cands],
        key=lambda e: e[1][0]
    )

    return (1 + pair[0], pair[1] + [d_i])

在你的例子中,它将返回一对(3,[21,21,21])。

这是一个快速的解决方案。如果您需要集成或分发它,我建议您使用OOP样式,如下所示:

class MinimumCoinChangeSolver(object):
    def __init__(self):
        self.coins = None
        self.count = None
        self.__cache = {}


    def solve(self, coins, amount):
        self.reset_cache()
        self.coins = self.__recursive_solve(coins, amount)
        self.count = len(self.coins)


    def reset_cache(self):
        self.__cache = {}


    def __recursive_solve(self, coins, amount):
        # try to find solution in cache
        if amount in self.__cache:
            solution = self.__cache[amount]

        else:
            # if given amount equals to one of teh coins, return list which contains corresponding coin (it equals to amount)
            if amount in coins:
                solution = [amount]

            else:
                # find coins candidates, they are less then given amount
                coins_cands = filter(
                    lambda coin: coin < amount,
                    coins
                )

                # if there is no coins candidate, return no coins (it is an empty list on coins)
                if not coins_cands:
                    solution = []

                else:
                    # try to use every coin among the candidates, and recursively find list of coins in every case
                    solutions = map(
                        lambda coin: self.__recursive_solve(coins, amount - coin),
                        coins_cands
                    )

                    # ignoring empty solutions
                    solutions = filter(
                        lambda sol: bool(sol),
                        solutions
                    )

                    # if there is no non-empty solutons, return empty list
                    if not solutions:
                        solution = []

                    else:
                        # choose the solution which has minimum number of coins
                        min_solution = min(
                            solutions,
                            key=lambda sol: len(sol)
                        )

                        # remember corresponding coin
                        coin = amount - sum(min_solution)

                        # solution
                        solution = min_solution + [coin]

        # save solution in cache
        self.__cache[amount] = solution

        # return
        return solution



if __name__ == "__main__":
    solver = MinimumCoinChangeSolver()
    solver.solve([1, 5, 10, 25, 21], 63)
    print solver.coins
    print solver.count