欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

4.2

程序员文章站 2022-05-29 14:46:02
...

算法图解笔记——Chapter 8 greedy Algorithm
Author: Seven Zou
Email: aaa@qq.com
Language: Python2.7


8 贪婪算法

本章学习贪婪算法,这与自身的研究方向有些交叉,恰好利用这次学习来仔细研究一下算法。在优化问题中,背包问题、调度问题、集合覆盖问题等都属于经典问题。


8.1 教师调度问题

假设有如下课程表,希望将尽可能多的课程安排在某间教室上。这里就要同时考虑尽可能多且时间不冲突的课程。
4.2
针对算法逻辑如下:

  • 1.选出结束最早的课,它就是要在这间教室上的第一堂课;
  • 2.必须选择第一堂课结束后才开始的课。同样,选择结束最早的课,这将是要在这间教室上的第二堂课;
  • 3.重复以上操作。

这就是贪婪算法的逻辑,每步都采取最优的做法。在优化中,也称为*每步都选择局部最优解,最终推广到全局最优解


8.2 背包问题

假设你是个小偷,背着可装35磅物品的背包。目的是往背包中装入价值最高的物品。针对此问题逻辑可有:

  • 窃取可装入背包的最贵商品;
  • 再窃取还可装入背包的最贵商品。
  • 重复操作。

但还需考虑重量问题。所以此时便不能获得最优解,而是近似最优解。


8.3 集合覆盖问题

假设有广播节目,要让全美50个州的听众都收听得到。需要决定在哪些广播台播出但同时广播台都需收取费用。
4.2
每个广播台都覆盖特定的区域,不同的广播台的覆盖区域可能重叠。
4.2
那么问题就转换成:如何找出覆盖全美50个州的最小广播台集合。那么算法的逻辑可以为:

  • 列出每个可能的广播台集合,这称为幂集(power set)。可能的子集有2n2^n
  • 在这些集合中,选出覆盖全美50个州的最小集合。

那么问题出现了,这个执行逻辑下的运行时间是O(2n)O(2^n)。如果广播台不多,可以执行,但是一旦很多,那么需要的时间就会急剧增加。


近似算法

针对上述情况,使用贪婪算法可以得到非常接近的解。

  • 选出这样的一个广播台,即它覆盖了最多的未覆盖州。即便这个广播台覆盖了一些已覆盖的州,也没有关系。
  • 重复第一步,直到覆盖了所有的州。

这是一种近似算法(approximation algorithm)。判断算法的标准:1.执行速度;2.得到的近似解与最优解的接近程度。下面给出code。

# 准备工作
# 首先,创建一个列表,其中包含要覆盖的州
states_needed = set(["mt", "wa", "or", "id", "nv", "ut""ca", "az"])	# 传入一个数组,转换为集合
# 使用集合来表示要覆盖的州,集合类似于列表,集合不能包含重复的元素,则假设有
arr = [1, 2, 2, 3, 3, 3]
set(arr)	# 转换为集合

# 创建可供选择的广播台清单
stations = {}
station["kone"] = set(["id", "nv", "ut"])
station["ktwo"] = set(["wa", "id", "mt"])
station["kthree"] = set(["or", "nv", "ca"])
station["kfour"] = set(["nv", "ut"])
station["kfive"] = set(["ca", "az"])
final_stations = set()		# 创建一个集合存储最终选择的广播台
# 计算答案
# 正解可能有多个,需要遍历所有的广播台并从中选择覆盖了最多的未覆盖的广播台。
while states_needed:	
	best_station = None			# 将这个覆盖了最多的未覆盖的广播台存储在best_station 中
	states_covered = set()		# 集合:包含该广播台覆盖的所有未覆盖的州
	for station, states_for_station in station.items():		# 循环每个广播台,并确定它是否是最佳的广播台
		covered = states_needed & states_for_station	# 计算并集:两个集合合并
		if len(covered) > len(states_covered):		# 计算交集
			best_station = station
			states_covered = covered
	states_needed -= states_covered			# 更新states_needed 由于该广播台覆盖了一些州,因此不用再覆盖这些州
	final_stations.add(best_station)		# 将best_station添加入最终的广播台列表中

print final_stations
Output: set(['ktwo', 'kthree', 'kone', 'kfive'])

8.4 NP完全问题

解决集合覆盖问题,就必须计算每个可能的集合。那这于TSP问题又很相似。针对TSP问题,涉及到n个城市,就会有n!n!条路线。(第3章),这称为阶乘函数(factorial function)。对于TSP问题和集合覆盖问题共同之处在于:你需要计算所有的解,并从中选出最小/最短的那个。这两个问题都属于NP完全问题。
NP完全问题的简单定义为,以难解著称的问题,如TSP问题和集合覆盖问题。很多非常聪明的人都认为,根本不可能编写出可快速解决这些问题的算法。针对这类问题我们只能去求解它的近似解。
对于NP完全问题如何识别:
-元素较少时算法的运行速度非常快,但随着元素数量的增加,速度会变得非常慢;
-不能将问题分成小问题,必须考虑各种可能的可能。这可能是NP完全问题。
-如果问题涉及序列(如TSP问题中的城市序列)且难以解决,这可能是NP完全问题。
-如果问题设计集合(如广播台集合)且难以解决,这可能是NP完全问题。
-如果问题可转换为集合覆盖问题或TSP问题,那肯定是NP完全问题。


贪婪算法寻找局部最优,最终以此方式来获得全局最优;
对于NP完全问题,还没有找出快速解决方案;
针对NP完全问题,最佳的做发是使用近似算法;
贪婪算法易于实现、运行速度快。


Reference

[美]Aditya Bhargava/袁国忠, 算法图解, 北京:人民邮电出版社, 2017.3.


附个人Github地址: https://github.com/shiqi0404/Algorithm_Diagram,其中包括笔记、Code还有书本pdf版。

相关标签: 算法图解