繁体   English   中英

动态编程最佳找零

[英]Dynamic Programming Optimal Coin Change

我一直在审查一些动态编程问题,并且在寻找最小数量的硬币来进行更改方面,我一直费时费力。

假设我们有价值分别为25、10和1的硬币,我们将零钱更改为30。贪婪将返回25和5(1),而最优解将返回3(10)。 这是本书中有关此问题的代码:

def dpMakeChange(coinValueList,change,minCoins):
   for cents in range(change+1):
      coinCount = cents
      for j in [c for c in coinValueList if c <= cents]:
            if minCoins[cents-j] + 1 < coinCount:
               coinCount = minCoins[cents-j]+1
      minCoins[cents] = coinCount
   return minCoins[change]

如果有人可以帮助我解决这段代码(我开始感到困惑的第4行),那将很棒。 谢谢!

在我看来,代码正在解决每个美分值直到目标美分值的问题。 给定目标值v和一组硬币C ,您知道最优硬币选择S的形式必须为union(S', c) ,其中c是来自C某种硬币,而S'v - value(c)的最优解v - value(c) (不好意思)。 因此,该问题具有最佳子结构 动态编程方法是解决所有可能的子问题。 它需要几分cents * size(C)步长,而如果您只是尝试强行使用直接解决方案,则该过程会更快地崩溃。

def dpMakeChange(coinValueList,change,minCoins):
   # Solve the problem for each number of cents less than the target
   for cents in range(change+1):

      # At worst, it takes all pennies, so make that the base solution
      coinCount = cents

      # Try all coin values less than the current number of cents
      for j in [c for c in coinValueList if c <= cents]:

            # See if a solution to current number of cents minus the value
            # of the current coin, with one more coin added is the best 
            # solution so far  
            if minCoins[cents-j] + 1 < coinCount:
               coinCount = minCoins[cents-j]+1

      # Memoize the solution for the current number of cents
      minCoins[cents] = coinCount

   # By the time we're here, we've built the solution to the overall problem, 
   # so return it
   return minCoins[change]

如果您熟悉图论,这是一种思考硬币更改问题的方法,该问题可能会有用。

假设您具有通过以下方式定义的图形:

  • 从0到您感兴趣的价值(例如39美分或其他)之间的每一单位货币(例如几美分)都有一个节点。
  • 任意两个节点之间都有一条弧线,该弧线之间正好是您允许使用的硬币的币值分隔开的(例如,如果您允许使用镍币,则该节点之间的弧度在34美分和29美分之间)。

现在,您可以将硬币兑换问题视为从您的关注价值降至零的最短路径问题,因为硬币数量将与路径中的弧形数量完全相同。

该算法不使用图论术语,但基本上是在做同样的事情:外环遍及所有“分”(或图论框架中的节点),而内环遍及所有“分”。从当前弧到下一个弧的弧(coinValueList中的值)。 总之,他们正在寻找从零到您感兴趣的价值的最短路径。 (将值减小到零,将值减小到零是无关紧要的。不过,传统上我们会向下搜索到零。)

当我意识到可以将许多问题转换为图形问题时,我才真正开始理解动态编程。 (但是请注意,并非所有人都可以。有些是超图,有些甚至还没有。但是这对我很有帮助。)

我认为第四行令人困惑,因为虽然Python可以在列表理解中选择/过滤(transform(x) for x in iterable if condition(x)) ,则(transform(x) for x in iterable if condition(x)) ,但for x in iterable:表达式中的for x in iterable:它的标准不能做到相同。

因此,人们绕过的一种(俗气的imo)方法是将两者焊接在一起。 他们创建了一个列表c for c in coinValueList ,实际上并没有进行任何转换(因此c for c in coinValueListc for c in coinValueList )只是为了在它们上添加if c <= cents子句。 然后将其用作for x in iterable:表达式中for x in iterable:的标准for x in iterable: 我怀疑那是您一些困惑的来源。

编写该行的另一种方法可能是:

...
for eachCoinValue in filter(lambda x: x <= cents, coinValueList):
...

甚至更清楚地讲,使用“意图揭示变量”将是:

...
smallEnoughCoins = filter(lambda each: each <= cents)
for each in smallEnoughCoins:
    ...

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM