百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术分析 > 正文

算法(六):图解贪婪算法

liebian365 2025-03-04 13:00 12 浏览 0 评论

算法(六):图解贪婪算法

算法简介

参考
:www.cnblogs.com/steven_oyj/…

贪婪算法(贪心算法)是指在对问题进行求解时,在每一步选择中都采取最好或者最优(即最有利)的选择,从而希望能够导致结果是最好或者最优的算法。

贪婪算法所得到的结果往往不是最优的结果(有时候会是最优解),但是都是相对近似(接近)最优解的结果。

  • 贪婪算法并没有固定的算法解决框架,算法的关键是贪婪策略的选择,根据不同的问题选择不同的策略。
  • 必须注意的是策略的选择必须具备无后效性,即某个状态的选择不会影响到之前的状态,只与当前状态有关,所以对采用的贪婪的策略一定要仔细分析其是否满足无后效性。

比如前边介绍的最短路径问题(广度优先、狄克斯特拉)都属于贪婪算法,只是在其问题策略的选择上,刚好可以得到最优解。

基本思路

其基本的解题思路为:

1.建立数学模型来描述问题

2.把求解的问题分成若干个子问题

3.对每一子问题求解,得到子问题的局部最优解

4.把子问题对应的局部最优解合成原来整个问题的一个近似最优解

案例

这边的案例来自"算法图解"一书

案例一

区间调度问题:

假设有如下课程,希望尽可能多的将课程安排在一间教室里:

课程开始时间结束时间美术9AM10AM英语9:30AM10:30AM数学10AM11AM计算机10:30AM11:30AM音乐11AM12PM

这个问题看似要思考很多,实际上算法很简单:

1.选择结束最早的课,便是要在这教室上课的第一节课 2.接下来,选择第一堂课结束后才开始的课,并且结束最早的课,这将是第二节在教室上的课。

重复这样做就能找出答案,这边的选择策略便是结束最早且和上一节课不冲突的课进行排序,因为每次都选择结束最早的,所以留给后面的时间也就越多,自然就能排下越多的课了。

每一节课的选择都是策略内的局部最优解(留给后面的时间最多),所以最终的结果也是近似最优解(这个案例上就是最优解)。 (该案例的代码实现,就是一个简单的时间遍历比较过程)

案例二

背包问题:有一个背包,容量为35磅 , 现有如下物品

物品重量价格吉他151500音响303000笔记本电脑202000显示器292999笔1200

要求达到的目标为装入的背包的总价值最大,并且重量不超出。

方便计算所以只有3个物品,实际情况可能是成千上万。

同上使用贪婪算法,因为要总价值最大,所以每次每次都装入最贵的,然后在装入下一个最贵的,选择结果如下:

选择: 音响 + 笔,总价值 3000 + 200 = 3200

并不是最优解: 吉他 + 笔记本电脑, 总价值 1500 + 2000 = 3500

当然选择策略有时候并不是很固定,可能是如下:

(1)每次挑选价值最大的,并且最终重量不超出:

选择: 音响 + 笔,总价值 3000 + 200 = 3200

(2)每次挑选重量最大的,并且最终重量不超出(可能如果要求装入最大的重量才会优先考虑):

选择: 音响 + 笔,总价值 3000 + 200 = 3200

(3)每次挑选单位价值最大的(价格/重量),并且最终重量不超出:

选择: 笔+ 显示器,总价值 200 + 2999 = 3199

如上最终的结果并不是最优解,在这个案例中贪婪算法并无法得出最优解,只能得到近似最优解,也算是该算法的局限性之一。该类问题中需要得到最优解的话可以采取动态规划算法(后续更新,也可以关注我的公众号第一时间获取更新信息)。

案例三

集合覆盖问题:

假设存在如下表的需要付费的广播台,以及广播台信号可以覆盖的地区。 如何选择最少的广播台,让所有的地区都可以接收到信号。

广播台覆盖地区K1ID,NV,UTK2WA,ID,MTK3OR,NV,CAK4NV,UTK5CA,AZ......

如何找出覆盖所有地区的广播台的集合呢,听起来容易,实现起来很复杂,使用穷举法实现:

(1) 列出每个可能的广播台的集合,这被称为幂集。假设总的有n个广播台,则广播台的组合总共有2?个

(2) 在这些集合中,选出覆盖全部地区的最小的集合,假设n不在,但是当n非常大的时候,假设每秒可以计算10个子集

广播台数量n子集总数2?需要的时间5323.2秒101024102.4秒32429496729613.6年1001.26*1003o4x1023年

目前并没有算法可以快速计算得到准备的值, 而使用贪婪算法,则可以得到非常接近的解,并且效率高:

选择策略上,因为需要覆盖全部地区的最小集合:

(1) 选出一个广播台,即它覆盖了最多未覆盖的地区即便包含一些已覆盖的地区也没关系 (2) 重复第一步直到覆盖了全部的地区

这是一种近似算法(approximation algorithm,贪婪算法的一种)。在获取到精确的最优解需要的时间太长时,便可以使用近似算法,判断近似算法的优劣标准如下:

  • 速度有多快
  • 得到的近似解与最优解的接近程度

在本例中贪婪算法是个不错的选择,不仅运行速度快,本例运行时间O(n2),最坏的情况,假设n个广播台,每个广播台就覆盖1个地区,n个地区,总计需要查询n*n=O(n2),实现可查看后面的java代码实现

广播台数量n子集总数2?穷举需要时间贪婪算法5323.2秒2.5秒1032102.4秒10秒323213.6年102.4秒100324x1023年1000秒

此时算法选出的是K1, K2, K3, K5,符合覆盖了全部的地区,可能不是预期中的K2, K3,K4,K5(也许预期中的更便宜,更便于实施等等)

NP完全问题

案例四:

旅行商问题

假设有旅行商需要从下面三个城市的某一个城市出发,如何规划路线获取行程的最短路径。

存在3!(阶乘)=6种可能情况:

A->B->C
A->C->B
B->A->C
B->C->A
C->A->B
C->B->A
复制代码

这边和之前求最短路径的算法(广度搜索、狄克斯特拉、贝尔曼-福特),最大的差别是没有固定源点(起点),,每一个节点都可能是源点,并且需要经过每一个节点,所以若穷举法则不得不找出每一种可能并进行比较。

当城市数量为n,则可能性为n!,假设每秒处理判断一个路线

数量n总数n!穷举需要时间5120120秒103242天

而使用贪婪算法,随机选择从一个城市出发,比如A,每次选择从最近的还没去过的城市出发,则可以得到近似最优解。

第一次比较n-1个城市 第二次比较n-2个城市 ... 第n-1次比较1个城市 第n次不存在需要比较的了个

0+1+2+3+..+(n-1) ≈ O(n2/2)

数量n总数n!穷举需要时间贪婪需要时间5120120秒12.5秒103242天50秒

类似上述集合覆盖问题、旅行商问题,都属于NP完全问题,在数学领域上并没有快速得到最优解的方案,贪婪算法是最适合处理这类问题的了。

如何判断是NP完全问题的:

1.元素较少时,一般运行速度很快,但随着元素数量增多,速度会变得非常慢 2.涉及到需要计算比较"所有的组合"情况的通常是NP完全问题 3.无法分割成小问题,必须考虑各种可能的情况。这可能是NP完全问题 4.如果问题涉及序列(如旅行商问题中的城市序列)且难以解决,它可能就是NP完全问题 5.如果问题涉及集合(如广播台集合)且难以解决,它可能就是NP完全问题 6.如果问题可转换为集合覆盖问题或旅行商问题,那它肯定是NP完全问题

小结

1.贪婪算法可以寻找局部最优解,并尝试与这种方式获得全局最优解

2.得到的可能是近似最优解,但也可能便是最优解(区间调度问题,最短路径问题(广度优先、狄克斯特拉))

3.对于完全NP问题,目前并没有快速得到最优解的解决方案

4.面临NP完全问题,最佳的做法就是使用近似算法

5.贪婪算法(近似算法)在大部分情况下易于实现,并且效率不错

java实现

贪婪算法需要根据具体问题,选择对应的策略来实现,所以这边只取集合覆盖问题做个示例:

/**
 * 贪婪算法 - 集合覆盖问题
 * @author Administrator
 *
 */
public class Greedy {
	
	public static void main(String[] args){
		//初始化广播台信息
		HashMap> broadcasts = new HashMap>();
		broadcasts.put("K1", new HashSet(Arrays.asList(new String[] {"ID","NV","UT"})));
		broadcasts.put("K2", new HashSet(Arrays.asList(new String[] {"WA","ID","MT"})));
		broadcasts.put("K3", new HashSet(Arrays.asList(new String[] {"OR","NV","CA"})));
		broadcasts.put("K4", new HashSet(Arrays.asList(new String[] {"NV","UT"})));
		broadcasts.put("K5", new HashSet(Arrays.asList(new String[] {"CA","AZ"})));
		//需要覆盖的全部地区
		HashSet allAreas = new HashSet(Arrays.asList(new String[] {"ID","NV","UT","WA","MT","OR","CA","AZ"}));
		//所选择的广播台列表
		List selects = new ArrayList();
		
		HashSet tempSet = new HashSet();
		String maxKey = null;
		while(allAreas.size()!=0) {
			maxKey = null;
			for(String key : broadcasts.keySet()) {
				tempSet.clear();
				HashSet areas = broadcasts.get(key);
				tempSet.addAll(areas);
				//求出2个集合的交集,此时tempSet会被赋值为交集的内容,所以使用临时变量
				tempSet.retainAll(allAreas);
				//如果该集合包含的地区数量比原本的集合多
				if (tempSet.size()>0 && (maxKey == null || tempSet.size() > broadcasts.get(maxKey).size())) {
					maxKey = key;
				}
			}
			
			if (maxKey != null) {
				selects.add(maxKey);
				allAreas.removeAll(broadcasts.get(maxKey));
			}
		}
		System.out.print("selects:" + selects);
	}
}
复制代码
执行完main方法打印信息如下:
selects:[K1, K2, K3, K5]
复制代码

参考文献:K码农
-http://kmanong.top/kmn/qxw/form/home?top_cate=28

相关推荐

C语言自学课程大纲(c语言入门自学资料)

一、自学C语言,很多人不知道应该如何学习,从哪儿学习,学习又分为几个阶段,总是学着学着就很迷茫???分享C语言的学习路线图,跟着路线图学吧,天天看。...

「linux」定时器方案:红黑树、最小堆和时间轮的原理

一、网络事件和时间事件对于服务端来说,驱动服务端逻辑的事件主要有两个,一个是网络事件,另一个是时间事件;...

程序员怎么会不知道 C10K 问题呢?

昨天的文章中提到了C10K问题,结果好些程序员跑过来问,啥是C10K,我写了这么多年程序,我怎么不知道呢?我说,那你听说过前腿儿猪肉吗?今天简单说说C10K的问题。关于这个问题,Ruby...

朝荐开源 - glib(朝廷百科)

glib是一套通用的实用程序库,它为C语言提供了许多有用的数据结构、工具函数和抽象层,旨在简化C语言的跨平台开发,并提高代码的可重用性和效率。glib是GTK+和GNOME桌面环...

libevent总结(事件处理框架)(libevent libev)

libevent的事件处理框架是一个反应堆模型,而反应堆模型的核心就是io复用,拿epoll来说反应堆模型有两个核心数据结构,一个是epoll维护的内核事件表,一个是保存激活事件的事件队列当然,值得注...

日荐开源 - LibEvent(aldente官网网址)

libevent...

快递单号一键查询,高效追踪包裹物流,省时省力!

在繁忙的现代生活中,快递已成为我们日常生活中不可或缺的一部分。然而,面对众多的快递单号,如何快速、准确地查询包裹的物流信息成为了一个难题。现在,我们为您带来了一款快递单号一键查询工具,让您的物流追踪变...

导入不同快递公司下的单号批量查快递动态,一键解决物流查询难题

看着满屏快递单号陷入沉思?同事小王已经用《快递批量查询高手》一键导入多家快递,批量查询快递信息并统计了…而你还在中通、圆通、申通官网来回切换到鼠标冒烟?是时候亮出这个让快递公司接口“集体颤抖”的...

一键解锁快递查询高效能:批量查询快递,智能排序延误单号

当你的客服团队还在用5个浏览器轮番刷新物流页面时,隔壁仓库的王叔已经用快递批量查询高手把多个个滞留件变成会说话的预警红点!这篇教程将揭秘物流圈的「神器」,让「未更新快递」自动排队到你面前认罪。1.在软...

一站式快递单号查询平台,修改单号刷新快递信息的快递查询教程

一站式快递单号查询平台,支持导入单号查询时修改快递单号,高效刷新快递信息的快递查询教程随着电子商务的繁荣发展,快递业务量不断增长,无论是电商卖家还是普通消费者,对快递信息的查询和管理需求都日益增强。为...

高效快递单号查询,批量查询快递信息,多种查看方式满足你的需求

最近有很多朋友在问,如何查快递,怎么根据条件查看单号呢?不知道如何操作的宝贝们,下面请随小编一起来试试,希望能给大家带来帮助。需要哪些工具?安装一个快递批量查询高手快递单号若干怎么快速查询?步骤1:运...

物流查询达人必备!一键批量查询快递单号,根据发出时间筛选单号

嘿,各位快递查询达人们,是不是经常为海量的快递单号查询而头疼不已?想要一款能够在线批量查询快递动态,还能根据发出物流时间一键筛选所需快递单号信息的神器吗?来来来,让我给你们揭秘一款快递批量查询高手软件...

快递查询神器,多单号导入,筛选保存一键完成

当面对如山的快递单号,你是否曾感到手足无措?每一个单号都需要你逐一输入、查询,再逐个根据时间差进行筛选,这样的工作无疑是对耐心与精力的双重考验。但别担心,今天,我们将为你揭示一款物流行业的秘密武器——...

快递单号查询神器:一键复制粘贴,轻松批量追踪同公司快递

嘿,小伙伴们!还在为手动输入快递单号查询物流信息而烦恼吗?是不是觉得每次都要一个个输入单号,既费时又费力?别急,今天我要给大家介绍一款神奇的软件——快递批量查询高手!这款软件就像你的私人快递助手一样,...

快递单号查询入口自动批量查询快递动态并根据派件员字段排序单号

想象一下,面对堆积如山的快递单号,你不再需要一个个手动输入查询,而是轻轻一点,就能瞬间掌握所有快递的物流动态,甚至还能根据派件员智能排序,让管理变得井井有条。这不再是遥不可及的梦想,快递批量查询高手软...

取消回复欢迎 发表评论: