找零問題(Java實現)——貪心演算法
貪心演算法也稱為貪婪演算法。
下述找零問題應用的方法就是貪心演算法。現在思考,有沒有更好的找零辦法,使找出的硬幣個數更少呢?也就是說,對於找零問題的這個例項,這個解是不是最優的呢?實際上,可以證明,就這些面額的硬幣來說,對於所有正整數的找零金額,貪心演算法都會輸出一個最優解。
貪心演算法在解決問題的策略上目光短淺,只根據當前已有的資訊就做出認為是最好的選擇,而且一旦做出了選擇,不管將來有什麼結果,這個選擇都不會改變。換言之,貪心演算法並不是從整體最優考慮,它所做出的選擇只是在某種意義上的區域性最優。
一、問題描述
來看一個找零錢的例子,在我國廣泛使用的硬幣的面額是 1元、5角、1角、5分、2分、1分(當然現在沒有分了)。假如某顧客買完東西,現在要找給他 9分錢,要求找出的硬幣個數最少,那麼如何用這些面額的硬幣找零呢?這裡,採用的方法是:首先選出一個面值不超過 9 分的最大硬幣,即 5 分錢硬幣;從 9 分中減去 5 分,剩下 4 分,再選出一個面值不超過 4 分的最大硬幣,即 2 分硬幣;從 4 分中減去 2 分,剩下 2 分,最後選出一個面值不超過 2 分的最大硬幣,即 2 分硬幣。
用一個公式即可表示為 9 = 5+2+2,總計找出的硬幣個數為 3 。
二、貪心演算法的求解過程
貪心演算法的典型應用是求解最優化問題,演算法往往從一個初始狀態出發,來構造問題的解,以滿足約束方程為條件,運用貪心策略不斷的擴充解集合,直至得到問題的解。
用貪心法求解問題應該考慮以下幾個方面。
(1)候選集合C:為了構造問題的解決方案,有一個候選集合C作為問題的可能解,即問題的最終解均取自於候選集合C。例如,在找零問題中,各種面值的硬幣構成候選集合。
(2)解集合S:初始時為空,隨著貪心選擇的進行,解集合S不斷擴充套件,直到構成一個滿足問題的完整解。例如,在找零問題中,已找出的硬幣構成解集合。
(3)解決函式solution: 檢在解集合S是否構成問題的完整解。例如,在找零問題中,解決函式是已找出的硬幣的總金額應恰好等於應找額。
(4)選擇函式select: 即貪心策略,這是貪心演算法的關鍵,它指出哪個候選物件最有希望構成問題的解,選擇函式通常和目標函式有關。例如,在找零問題中,貪心策略就是在候選集合中選擇面值最大的硬幣。
(5)可行函式feasible:檢查在原解集合中加人一個候選物件後是否可行,即解集合擴充套件後是否滿足約束條件。例如,在找零問題中,可行函式是每步選擇的硬幣 和已找出 的硬幣相加不超過應找額。
三、程式原始碼及執行截圖
package SF;
public class 找零錢問題 {
int i;
static int n = 5;
double m = 0.09;
double m1 = 0.09;
//主函式
public static void main(String[] args) {
double[] V = new double[]{1,0.5,0.1,0.05,0.02,0.01};
int[] X = new int[6];
找零錢問題 p = new 找零錢問題();
X=p.Greedy(V);
int s = X[0]+X[1]+X[2]+X[3]+X[4]+X[5];
System.out.println("得到找零錢問題的最優解為:X = { "+X[0]+" "+X[1]+" "+X[2]+" "+X[3]+" "+X[4]+" "+X[5]+" }共需要最少"+s+"枚硬幣。");
}
//貪心演算法-找零問題
int[] Greedy(double C[]){
int i=0;
double x = 0;
double[] S = new double[n+1];
int[] Z = new int[n+1];
for(i=0;i<=n;i++){
if( solution(S)==0 ){
x = select(C);
if (feasible(S,x)==1)
{
S[i] = x;
Z[this.i]++;
m1 = ((double)((int)(m1*100) - (int)(x*100)))/100;
}
}
}System.out.println("使用硬幣集為:{ "+S[0]+" "+S[1]+" "+S[2]+" }");
return Z;
}
//解決函式 solution
int solution(double X[]){
double s = 0;
for(int i=0;i<=n;i++){
s=((double)(int)(s*100)+(int)(X[i]*100))/100;
}
if(s==m)
return 1;
else
return 0;
}
//選擇函式 select
double select(double X[]){
int i = 0;
double x = 0;
do{X[i]=0;i++;}
while(X[i]>m1||X[i]==0);
this.i = i;
x = X[i];
return x;
}
//可行函式 feasible
double feasible(double X[],double x){
int i;
int j=1;
double y=0;
for(i=0;i<=n;i++){
y=y+X[i];
}
y=y+x;
return j;
}
}
執行截圖: