《算法图解》——第八章 贪婪算法

       第八章    贪婪算法

1  简单的贪婪算法

每步都采取最优的做法,每步都选择局部最优解。


 

 

2  背包问题

有些情况下,完美是优秀的敌人。如果你只需要找到一个大致解决问题的算法,贪婪算法挺不错,因为实现容易,结果与正确结果相当接近。

练习
8.1 你在一家家具公司工作,需要将家具发往全国各地,为此你需要将箱子装上卡车。每个箱子的尺寸各不相同,你需要尽可能利用每辆卡车的空间,为此你将如何选择要装上卡车的箱子呢?请设计一种贪婪算法。使用这种算法能得到最优解吗?

选择可以装入卡车中最大的箱子,不断重复,直到不能再装,这种算法得不到最优解。

8.2 你要去欧洲旅行,总行程为7天。对于每个旅游胜地,你都给它分配一个价值——表示你有多想去那里看看,并估算出需要多长时间。你如何将这次旅行的价值最大化?请设计一种贪婪算法。使用这种算法能得到最优解吗?

不断地挑选可以在剩下的时间内完成的价值最大的活动,知道剩下的时间不能够完成任何活动为止。同样这种算法得不到最优解。


 

 

3  集合覆盖问题

假设你办了个广播节目,要让全美50个州的听众都收听得到。为此,你需要决定在哪些广播台播出。在每个广播台播出都需要支付费用,因此你力图在尽可能少的广播台播出。每个广播台都覆盖特定的区域,不同广播台的覆盖区域可能重叠。

具体方法如下:

①列出每个可能的广播台集合,这被称为幂集(power set)。可能的子集有2**n个。

②在这些集合中,选出覆盖全美50个州的最小集合。

由于可能的子集有2**n个,因此运行时间为O(2**n)。

用贪婪算法可得到非常接近的解:

①选出这样一个广播台,它覆盖了最多的未覆盖的州。即使有重复的州也没有关系

②重复第一步,直到覆盖了所有的州

这是一种近似算法。判断近似算法优劣的标准如下:

①速度有多快

②得到的近似解与最优解的接近程度。在这个例子中贪婪算法的运行时间为O(n**2)


 

 

上述问题代码实现过程(简化问题):

①准备工作,首先,创建一个列表,其中包含要覆盖的州:states_needed = set(["mt", "wa", "or", "id", "nv", "ut","ca", "az"])(使用集合的不重复特点);还需要有可供选择的广播清单,用散列表来表示它:

stations = {}
stations["kone"] = set(["id", "nv", "ut"])
stations["ktwo"] = set(["wa", "id", "mt"])
stations["kthree"] = set(["or", "nv", "ca"])
stations["kfour"] = set(["nv", "ut"])
stations["kfive"] = set(["ca", "az"])

其中,键为电台名字,值为覆盖的州。最后用一个集合来保存最终选择的电台:final_stations = set()

②计算答案

需要从中选择覆盖了最多的未覆盖州的广播台。将整个广播台存储在best_station 中。

states_needed = (["mt", "wa", "or", "id", "nv", "ut","ca", "az"])    #这个代码有问题没解决
stations = {}
stations["kone"] = (["id", "nv", "ut"])
stations["ktwo"] = (["wa", "id", "mt"])
stations["kthree"] = (["or", "nv", "ca"])
stations["kfour"] = (["nv", "ut"])
stations["kfive"] = (["ca", "az"])
final_stations = ()
while states_needed:
best_station = ()
states_covered = ()
for station, states_for_station in stations.items():
covered = states_needed and states_for_station
if len(covered) > len(states_covered):
best_station = station
states_covered = covered
states_needed -= states_covered
final_stations.add(best_station)
print(final_stations)      #这是结果set(['ktwo', 'kthree', 'kone', 'kfive'])

states_covered 是一个集合,包含该广播台覆盖的所有未覆盖的州。 for 循环迭代每个广播台,并确定它是否是最佳的广播台。下面来看看这个 for 循环的循环体。

covered 是一个集合,包含同时出现在 states_needed 和states_for_station 中的州;

贪婪算法和精确算法的运行时间对比:

练习
下面各种算法是否是贪婪算法。
8.3 快速排序。否
8.4 广度优先搜索。是
8.5 狄克斯特拉算法。是


 

 

4  NP完全问题

旅行商问题详解:

2个城市时,2条;3个城市时,6条;4个城市时,24条;同理:N个城市就是N!条,这被称为阶乘函数。

 

如何识别NP完全问题:

①元素较少时算法的运行速度非常快,但随着元素数量的增加,速度会变得非常慢。

②涉及“所有组合”的问题通常是NP完全问题。

③不能将问题分成小问题,必须考虑各种可能的情况。这可能是NP完全问题。

④如果问题涉及序列(如旅行商问题中的城市序列)且难以解决,它可能就是NP完全问题。

⑤如果问题涉及集合(如广播台集合)且难以解决,它可能就是NP完全问题。

⑥如果问题可转换为集合覆盖问题或旅行商问题,那它肯定是NP完全问题。

练习
8.6 有个邮递员负责给20个家庭送信,需要找出经过这20个家庭的最短路径。请问这是一个NP完全问题吗?类似旅行商问题,是一个NP完全问题

8.7 在一堆人中找出最大的朋友圈(即其中任何两个人都相识)是NP完全问题吗?类似集合覆盖问题,同样是一个NP完全问题

8.8 你要制作美国地图,需要用不同的颜色标出相邻的州。为此,你需要确定最少需要使用多少种颜色,才能确保任何两个相邻州的颜色都不同。请问这是NP完全问题吗?也是


 

 

5  小结

贪婪算法寻找局部最优解,企图以这种方式获得全局最优解。

对于NP完全问题,还没有找到快速解决方案。

面临NP完全问题时,最佳的做法是使用近似算法。

贪婪算法易于实现、运行速度快,是不错的近似算法。

 

posted @ 2018-04-23 23:01  方玲是个小可爱  阅读(944)  评论(1编辑  收藏  举报