簡體   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