优化变革制定解决方案

时间:2014-02-26 20:44:49

标签: python algorithm optimization

我在这里编写了一个Python解决方案,它解决了以下问题:如何使用给定面额n的最少数量的硬币进行给定金额d

def min_coin_change(n, d):
    mini = float("inf")
    for den in d:
        diff = n - den
        if diff == 0:
            ans = 1
        elif diff < 0:
            ans = float("inf")
        else:
            ans = 1 + min_coin_change(n - den, d)
        mini = min(mini, ans)
    return mini

虽然我的解决方案有效,但n大于50或d的长度大于5时需要很长时间。如何加速我的代码以便它能够正常工作对于相当大的输入?我错过了一个可以大大加快我的代码速度的技巧吗?

4 个答案:

答案 0 :(得分:2)

请注意,您应该将功能设置为从最大的硬币开始,循环查看最大可能数量的最大硬币,然后使用下一个最大硬币重复。

因此,den应按递减顺序排序

def changecoin(d, denval)
  count = 0
  while d > denval:
    d -= denval
    count += 1
  return count, d

现在使用下一个最小值denval和新d值调用递归,并适当增加计数。

newcount = 0
for denval in den:
    count, d = changecoin(d, denval)
    newcount += count

实际上,查看代码,可以通过消除while来修改changecoin函数,以便可以编写内联代码

count = 0
for denval in den
  count += d//denval
  d = d % denval

这将返回计数以及d中剩余的任何值。如果d小于denval,则计数增量为0,其余d不变。当d变为0时,for循环是不间断的,但是在den中有足够的条目可以省略测试

  if d < 1:
    break

答案 1 :(得分:2)

我们可以假设硬币面额是明智的吗?这个问题的一般情况将允许奇怪的硬币面额如[100, 90, 1]和简单的“贪婪”算法将找不到最佳解决方案(例如,对于180,最佳解决方案是两个90美分,而简单的贪心算法会建议一个100美分和80美分)。

如果我们能够采用合理的货币(就像任何真实货币一样),那么我建议你使用整数除法和模数的组合来找出每个硬币的使用数量。

如果我们不能假设合理的货币,那么这个问题对于动态编程解决方案来说是理想的。在动态编程中,您构建了一个记忆中间结果的表,与简单的递归解决方案相比,这节省了大量时间。

Skiena书中的动态编程有一个很好的解释 The Alogorithm Design Manual

http://www.algorist.com/

以下是我在网上找到的一些链接:

http://www.avatar.se/molbioinfo2001/dynprog/dynamic.html

http://www.codechef.com/wiki/tutorial-dynamic-programming

答案 2 :(得分:1)

这是因为它是递归的

阅读此What is memoization and how can I use it in Python?(以及前2个答案)

“memoization”会记住您已经计算过的内容(例如10美分),因此您不会以指数次数重新计算它们。

您可以按原样复制Mwmoize类, 并且只是“装饰”你的功能,如第二个答案所述


对于懒惰

class Memoize:
    def __init__(self, f):
        self.f = f
        self.memo = {}
    def __call__(self, *args):
        if not args in self.memo:
            self.memo[args] = self.f(*args)
        return self.memo[args]

然后在定义

之前添加装饰器
@Memoize
def min_coin_change(n,d)......

该函数的其余部分是相同的

然后你称之为

min_coin_change(30,(25,10,5,1))

答案 3 :(得分:1)

这是您程序的更快版本。我改变了两件事:

  • 实现细节:所有递归程序都可以重写为等效的迭代程序(在这种情况下,使用forwhile循环)。在大多数语言中,迭代版本更快,因为不需要维护堆栈。

  • 算法:我使用的是贪婪算法,首先从最大值的硬币开始,然后尝试较小的硬币。如其他答案中所指出的,这不能保证是最佳的,但运行速度非常快(返回的解决方案中的硬币数量是线性的)。请查看this page以获得最佳(但速度较慢)的动态编程解决方案。

def min_coin_change2(n, d):
    current_amount = 0
    used_coins = []

    for coin_value in sorted(d, reverse=True):
        while n - current_amount >= coin_value:
            current_amount += coin_value
            used_coins.append(coin_value)

    excuse = '' if current_amount == n else ', but that is not what you asked for'
    return 'I can get to %d using %s%s' % (current_amount, used_coins, excuse)

让我们试一试:

print min_coin_change2(101, [50, 20, 10, 1])
Out[25]: 'I can get to 101 using [50, 50, 1]'

再次,当无法获得所需的金额时

print min_coin_change2(101, [50, 20, 10])
Out[26]: 'I can get to 100 using [50, 50], but that is not what you asked for'

再次,当贪心算法找到次优解时

print min_coin_change2(180, [100, 90, 20])
Out[2]: 'I can get to 180 using [100, 20, 20, 20, 20]'

但最佳解决方案是[90, 90]