1. 程式人生 > >找零問題(Java實現)——貪心演算法

找零問題(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;
	}
}

執行截圖: