贪心算法(又称贪婪算法)是指,在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,他所做出的仅是在某种意义上的局部最优解。贪心算法不是对所有问题都能得到整体最优解,但对范围相当广泛的许多问题他能产生整体最优解或者是整体最优解的近似解。
一.贪心算法
1.贪心算法概念
贪婪算法(Greedy algorithm)是一种对某些求最优解问题的更简单、更迅速的设计技术。用贪婪法设计算法的特点是一步一步地进行,常以当前情况为基础根据某个优化测度作最优选择,而不考虑各种可能的整体情况,它省去了为找最优解要穷尽所有可能而必须耗费的大量时间,它采用自顶向下,以迭代的方法做出相继的贪心选择,每做一次贪心选择就将所求问题简化为一个规模更小的子问题,通过每一步贪心选择,可得到问题的一个最优解,虽然每一步上都要保证能获得局部最优解,但由此产生的全局解有时不一定是最优的,所以贪婪法不要回溯。
贪婪算法是一种改进了的分级处理方法。其核心是根据题意选取一种量度标准。然后将这多个输入排成这种量度标准所要求的顺序,按这种顺序一次输入一个量。如果这个输入和当前已构成在这种量度意义下的部分最佳解加在一起不能产生一个可行解,则不把此输入加到这部分解中。这种能够得到某种量度意义下最优解的分级处理方法称为贪婪算法。
2.贪心算法求解思路
基本思路:①建立数学模型来描述问题。②把求解的问题分成若干个子问题。③对每一子问题求解,得到子问题的局部最优解。④把子问题的解局部最优解合成原来解问题的一个解。
实现该算法的过程:从问题的某一初始解出发;while 能朝给定总目标前进一步 do;求出可行解的一个解元素;由所有解元素组合成问题的一个可行解。
3.利用贪婪策略,需要解决的两个问题
确定问题是否能用贪心策略求解;一般来说,适用于贪心策略求解的问题具有以下特点:贪心选择性质和最优子结构性质。
① 贪心选择性质:可通过局部的贪心选择来达到问题的全局最优解。运用贪心策略解题,一般来说需要一步步的进行多次的贪心选择。在经过一次贪心选择之后,原问题将变成一个相似的,但规模更小的问题,而后的每一步都是当前看似最佳的选择,且每一个选择都仅做一次。
② 最优子结构性质:原问题的最优解包含子问题的最优解,即问题具有最优子结构的性质。在背包问题中,第一次选择单位质量最大的货物,它是第一个子问题的最优解,第二次选择剩下的货物中单位重量价值最大的货物,同样是第二个子问题的最优解,依次类推。
二.背包问题
背包问题(Knapsack problem)是一种组合优化的NP完全问题。问题可以描述为:给定一组物品,每种物品都有自己的重量和价格,在限定的总重量内,我们如何选择,才能使得物品的总价格最高。问题的名称来源于如何选择最合适的物品放置于给定背包中。相似问题经常出现在商业、组合数学,计算复杂性理论、密码学和应用数学等领域中。也可以将背包问题描述为决定性问题,即在总重量不超过W的前提下,总价值是否能达到V?它是在1978年由Merkel和Hellman提出的。
题目:有N件物品和一个容量为V的背包。第i件物品的重量是w[i],价值是v[i]。求解将哪些物品装入背包可使这些物品的重量总和不超过背包容量,且价值总和最大。
三.贪心算法求解背包问题
1 import java.util.Arrays;
2
3 /**
4 * 贪心算法--->背包问题
5 * 假设有一个背包最大可以装 150kg 物品
6 * 某物品 重量 价值(元) 性价比(单位重量的价格)
7 * a 35kg 10 10/35
8 * b 30kg 40 40/30
9 * c 60kg 30 30/60
10 * d 50kg 50 50/50
11 * e 40kg 35 35/40
12 * f 10kg 40 40/10
13 * g 25kg 30 30/25
14 *
15 * 求:背包可以装入的最大价值?
16 * @author Administrator
17 *
18 */
19 public class Knapsack {
20 /**
21 * 背包最大容量
22 */
23 private static int KNAPSACK_MAX_CAPACITY=150;
24 /**
25 * 保存各个物品的重量
26 */
27 private static int [] goods_weights=new int [] {35,30,60,50,40,10,25};
28 /**
29 * 保存各个物品的价值
30 */
31 private static int [] goods_values=new int[] {10,40,30,50,35,40,30};
32
33 /**
34 * 贪婪算法实现背包问题求解
35 * @param capacity 背包容量
36 * @param weights 各个物品的重量
37 * @param values 各个物品的价值
38 */
39 private void knapsackGreedy(int capacity,int weights[],int values[]) {
40 int n=weights.length; //物品的数量
41
42 Double[] r=new Double[n]; //保存性价比的数组
43 int [] index=new int[n]; //保存按性价比排序的物品的下标
44 //计算得到各个物品的性价比
45 for (int i = 0; i < n; i++) {
46 r[i]=(double)values[i]/weights[i];
47 index[i]=i; //初始化各个物品的默认性价比排序
48 }
49
50 //对各个物品的性价比进行排序
51 for(int i=0;i<r.length-1;i++) {
52 for(int j=i+1;j<r.length;j++) {
53 if(r[i]<r[j]) {
54 double temp=r[i];
55 r[i]=r[j];
56 r[j]=temp;
57
58 //将排序后性价比的下标更新为性价比排序后的位置
59 int x=index[i];
60 index[i]=index[j];
61 index[j]=x;
62 }
63 }
64 }
65
66 //将排序好的重量和价值分别保存到数组
67 int [] w1=new int[n];
68 int [] v1=new int[n];
69 for(int i=0;i<n;i++) {
70 w1[i]=weights[index[i]];
71 v1[i]=values[index[i]];
72 }
73
74 //将物品装入背包
75 //记录哪些物品已经被装入背包 0 没有装入背包 1 代表已经装入背包
76 int [] x=new int[n];
77 int maxValue=0;
78 for(int i=0;i<n;i++) {
79 if(w1[i]<=capacity) {
80 //还可以装的下
81 x[i]=1; //表示将该物品装入背包
82 System.out.println("物品:"+w1[i]+" 被放进了");
83 maxValue+=v1[i];
84 capacity-=w1[i];
85 }
86 }
87 System.out.println("总共放下的物品的数量为:"+Arrays.toString(x));
88 System.out.println("最大价值为:"+maxValue);
89 }
90
91
92 public static void main(String[] args) {
93 Knapsack k=new Knapsack();
94 k.knapsackGreedy(KNAPSACK_MAX_CAPACITY, goods_weights,
95 goods_values);
96 }
97 }
四.测试结果
物品:10 被放进了
物品:30 被放进了
物品:25 被放进了
物品:50 被放进了
物品:35 被放进了
总共放下的物品的数量为:[1, 1, 1, 1, 0, 0, 1]
最大价值为:170