贪心算法也称为贪婪算法。
下述找零问题应用的方法就是贪心算法。现在思考,有没有更好的找零办法,使找出的硬币个数更少呢?也就是说,对于找零问题的这个实例,这个解是不是最优的呢?实际上,可以证明,就这些面额的硬币来说,对于所有正整数的找零金额,贪心算法都会输出一个最优解。
贪心算法在解决问题的策略上目光短浅,只根据当前已有的信息就做出认为是最好的选择,而且一旦做出了选择,不管将来有什么结果,这个选择都不会改变。换言之,贪心算法并不是从整体最优考虑,它所做出的选择只是在某种意义上的局部最优。
一、问题描述
来看一个找零钱的例子,在我国广泛使用的硬币的面额是 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;
}
}
运行截图: