一,問題介紹
最近一直在看貪心算法的正確性證明(如何證明貪心算法獲得的解一定是最優解),感覺“剪枝”技巧用得比較多。再看了下《算法導論》中貪心算法一章里面的一個練習---找換硬幣問題。這個問題對於某些 面值的硬幣 是有最優解的,故記錄下其中的一些證明思路。
考慮用最少的硬幣數 來找 n 分錢的問題,假設每個硬幣的值都是整數。
如果可換的硬幣的單位是 c 的冪,也就是 c0,c1,... ,ck ,其中整數 c>1,k>=1
證明貪心算法總可以產生一個最優解。
二,找換硬幣的貪心策略
這里的貪心策略很容易想到:總是優先選擇 大面值的硬幣 去找。比如,現有 1分、5分、25分的硬幣可用來找錢,現在我們需要找 n=32分 的零錢,如何找?
優先選擇大面值的嘛,那就是先選 25分;選完之后,還要找32-25=7,那就再選5分的,最終再先2個1分的。即可。
這里:32=25+5+1+1,一共用了4枚硬幣。那么,問題來了!!!還有沒有其他策略 只需要 3 枚硬幣?或者更少的硬幣?
這就需要證明貪心策略 有沒有 最優解了?對於這個問題而言,如果用來找換的硬幣的 面值 滿足某種性質,該貪心策略是有最優解的。
這里說的某種性質就是:可換的硬幣的單位(或者稱 面值)是 c 的冪,也就是 c0,c1,... ,ck ,其中整數 c>1,k>=1
下面給出正確性證明。
這里需要強調一點: 硬幣面值滿足上面的性質時貪心算法一定能產生最優解,但是不滿足時,也有可能有最優解。比如,面值為1,5,10,20,50,100時,貪心找零也一定有最優解。
三,找換硬幣的貪心算法的正確性證明
在進行證明之前,先提一個性質:對於最優解而言,如果使用了面值為 ci 的硬幣去找零,那么 ci 最多只能使用 c-1 個。
因為我們的目標是使用 最少數目的硬幣,對於最優解而言,如果使用了面值為 ci 的硬幣去找零,那么 ci 最多只能使用 c-1 個。
why? 假設使用了 c 個(大於c個也一樣)面值為 ci 的硬幣去找零,那我為什么不用一個 面值為 ci+1 的硬幣去找呢?
這樣,我就可以用更少的硬幣數啊(c>1)。所以說,在最優解里面,如果使用了面值為 ci 的硬幣去找零,那么 面值為ci 的硬幣 最多只能使用 c-1 個。
OK,下面再次用到剪枝的思想來證明貪心選擇的正確性了。
總體思路是:先考察一個最優解,然后證明可對該解進行修改,使其采用貪心選擇,這個選擇將原問題變成一個相似的、但是“更小”的問題。(這里說的“更小”是一種抽象,並不是具體意義上的更小)
k 是可找換的硬幣的種類數,n 是需要找換的價值,a(i)表示使用多少個 面值為 ci 的硬幣。假設硬幣已經按面值從小到大排序。
對於貪心選擇而言,我們一定會選擇面值為 c^j 的硬幣,因為:我們的貪心策略就是總是優先選擇 面值最大的硬幣。
我們的目標是證明,對於所有的非貪心選擇而言,它們都不可能產生最優解。
對於非貪心選擇,是不會選擇 面值為 c^j (or higher)的硬幣的。非貪心選擇使用的硬幣如下:
其中,a(i)表示使用了 a(i)個 面值為 ci 的硬幣,總數之和是n。別忘了,n 就是需要找零的數目
而對於貪心選擇而言,如果面值為 c^j 的硬幣可用(當 n>=c^j 時),我們是優先使用 面值為 c^j 的硬幣的。
因此有:
這說明,在面值為 c^j 的硬幣 可用的情況下,換句話說:在 c^j <=n 的情況下,非貪心選擇沒有使用 c^j 嘛。(選擇了的話,那就是貪心選擇了嘛...~)
這里就會有矛盾了,根據前面提到的性質:有, a(i) <= c-1
從而,證明了貪心選擇的正確性。
再來理解下它:總體思路是:先考察一個最優解,然后證明可對該解進行修改,使其采用貪心選擇,這個選擇將原問題變成一個相似的、但是“更小”的問題。(這里說的“更小”是一種抽象,並不是具體意義上的更小)
這里的最優解是:非貪心選擇下的某個最優解,然后剪枝:將非貪心選擇下的某個元素去掉,然后添入貪心選擇的那個元素。其實就是下面的這個公式。
從而得到了一個更優的問題。
四,參考資料