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


           第八章    贪婪算法

    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完全问题时,最佳的做法是使用近似算法。

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

  • 相关阅读:
    HTML基础 整理
    今天课堂总结
    课后习题
    JAVA的文件创建
    JAVA_输入输出流 异常处理
    12.23流水账号
    12.22 repeater 删除
    12.22 repeater 修改
    12.22 repeater 添加
    12.22 repeater 主页
  • 原文地址:https://www.cnblogs.com/NEWzyz/p/8922506.html
Copyright © 2020-2023  润新知