算法设计方法之 贪婪算法
贪婪算法是一种非常直观的求解方法,虽然未必能产生最优解,但是能够近似最优解,用贪婪算法可以求解货箱装载问题,背包问题,拓扑排序问题,最短路径问题等
最优化问题:
明确:
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阶优化。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)