算法设计方法之 贪婪算法

贪婪算法是一种非常直观的求解方法,虽然未必能产生最优解,但是能够近似最优解,用贪婪算法可以求解货箱装载问题,背包问题,拓扑排序问题,最短路径问题等

最优化问题:

明确: 

1. 约束条件

2. 可行解

3. 目标函数

4. 最优解

举个例子:

一个口渴的人想要解渴,他可以得到n种不同的饮料,但是他对每种饮料的满意度值不同,但是单一的一种饮料不足以满足他需要的量,他需要饮用不同种类的饮料。假设他对第i种饮料的满意度值是,他要应用的量是,每种饮料的总量是,总的需求量是t,则问题的模型可以转化为:

通过寻找一组解

使得p达到最大值:

且要满足:

这两个条件。

类似的问题还有货箱装载问题,最小成本通信网络问题,也即最小成本生成树。

贪婪算法思想:

在贪婪算法中,需要逐步构造一个最优解,每一步都在一定的标准下做出最优决策,且在以后的步骤中不能修改,做出决策所依据的标准称为贪婪准则。

如:找零钱问题

1. 机器调度问题:

假设有n个任务,机器个数不限,每个人物的开始时间a,完成时间b,时间段[a,b]为任务的处理时段。

例如:两个任务[1,4],[2,4]是有重叠的,[1,4][4,7]是没有重叠的,一个可行的分配方案是指没有把重叠的任务分配个给同一个机器。最优方案是指栈用的机器数量最少。

假设有7个任务,如果每台机器分配一个,这虽然是一个可行方案,但是明显不是一个最优方案。

贪婪法获得最优方案:每一步分配一个任务,且按照任务开始时间的非递减顺序进行。一台机器如果有至少一个任务,则成为旧机器,没有任务的机器称为新机器。采用的贪婪准则是:根据任务开始的时间,优先将任务分配给可用的旧机器。如果没有,再分配给新机器。

2. 最短路径问题:

如图所示的网络,寻找一条从1到5的最短路径。

箱子排序问题:

采用贪婪算法解决箱子装载问题:

在重量固定的情况下,装载最多的箱子:

箱子重量为:【100, 200, 50, 90, 150, 50, 20, 80】

实现代码:

#include <iostream>

using namespace std;

struct box    // 箱子结构体,有箱子的id和重量 
{	
	int idd;
	int weight;
	
};


void containerLoading(box* p, int capacity, int box_num, int* x)
{
	for(int i=1; i<=box_num-1; i++)    //先对box进行排序 
	{
		int min_weight = p[i].weight;
		int min_index = i;
		for(int j=i+1; j<=box_num; j++)
		{
			if(p[j].weight<min_weight)
			{
				min_weight = p[j].weight;
				min_index = j;
			}
		}
		// 交换p[i]和tmp_box 
		box tmp_box = p[i];
		p[i] = p[min_index];
		p[min_index] = tmp_box;
	}
	
	cout << "排序后的箱子:" << endl;
	cout << "Id" << "  " << "Weight" << endl; 
	for(int i=1; i<=box_num; i++)
	{
		x[i] = 0;
		cout << p[i].idd << "  " << p[i].weight << endl;
	}
	
	for(int i=1; i<box_num && p[i].weight<capacity; i++)
	{
		x[p[i].idd] = 1;   // 第i个被装载的箱子
		capacity -= p[i].weight;    // 剩余容量 
	}
	
	cout << "结果:" << endl;
	for(int i=1; i<=box_num; i++)
	{
		cout << x[i] << " ";
	}
	cout << endl;
}


int main(int argc, char *argv[])
{
	int box_number = 8;
	int number_weight[] = {100, 200, 50, 90, 150, 50, 20, 80}; 
	int xx[box_number+1];
	int c = 400;
	box* boxp = new box[box_number+1];
	for(int i=1; i<=box_number; i++)
	{
		boxp[i].idd = i;
		boxp[i].weight = number_weight[i-1];
	}   // 初始化箱子 
	
	containerLoading(boxp, c, box_number, xx);
	return 0;
}

运行结果:

0/1背包问题:

n个物品和一个容量为c的背包,从n个物品中选取装包的物品,第i个物品重量为w_i, 价值为p_i,在满足约束条件的情况下,使得价值最高:

所以问题描述是:

约束条件是:

这个问题也是类似于上面货物装载的问题:

对于0/1背包问题,可能的贪婪准则:

1. 价值贪婪准则: 每次存剩余的物品中选择加价值最高的

2. 重量贪婪准则:每次从剩余的物品中选择重量最轻的

3. 价值密度贪婪准则:每次选择 价值/重量 最大的物品

但是,正对不同的问题,以上三种情况得到的不一定是最优解:

贪婪启发式方法:

上述问题是一个NP-复杂问题,在上面的贪婪准则中,价值密度虽然不一定得到最优解,但也是一种不错的启发式算法。

对贪婪启发式方法的改进:

首先将最多k件物品放入背包,如果k件物品的重量大于c,则放弃这k件物品,否则,根据背包剩余的容量,将剩余的物品按照价值密度递减的顺序放入。这种方法称为k阶优化。

 

posted @ 2019-04-09 20:43  Alpha205  阅读(265)  评论(0编辑  收藏  举报