目录
(1)背包问题
(2)集合覆盖问题
(3)NP完全问题
(4)小结
本章内容:
- 学习如何处理没有快速算法的问题(NP完全问题)。
- 学习近似算法,使用它们找到NP问题的近似解。
- 学习贪婪策略。
(1)背包问题
假设你是个贪婪的小偷,背着可装35磅重东西的背包,在商场伺机盗窃各种可装入背包的商品。你力图往背包中装入价值最高的商品,你会使用哪种算法呢?你可使用贪婪策略:先盗窃可装入背包的最贵商品,再盗窃还可装入背包的最贵商品。但是此次这种贪婪策略可不好使了,例如,你可盗窃的商品有下面三种。
你的背包可装入35磅的东西,音响再贵,你把它给偷了,但背包没有空间装其他东西了。
你偷到了价值3000美元的东西。且慢!如果不是偷音响,而是投笔记本电脑和吉他,总价值为3500美元!
在这里,贪婪策略显然不能获得最优解,但非常接近。下一章将介绍如何找出最优解。
(2)集合覆盖问题
假设你办了个广播节目,要让全美50个州的听众都收听的到。为此,你需要决定在哪些广播台播出。在每个广播台播出都需要支付费用,因此你力图在尽可能少的广播台播出。现有广播台名单和每个广播台覆盖的区域。
如何找出覆盖全美50个州的最小广播台集合?解法为列出所有可能的集合,在这些集合中选出覆盖全美50个州的最小集合。我们需要计算每个可能的子集需要的时间。这非常耗时。
近似解法:
- 选出这样一个广播台,即它覆盖了最多的未覆盖州。即便这个广播台覆盖了一些已覆盖的州,也没有关系。
- 重复第一步,直到覆盖了所有的州。
这是一种近似算法。判断近似算法优劣的标准如下:
- 速度有多快;
- 得到的近似解与最优解的接近程度。
下面来看看解决这个问题的代码。
# You pass an array in, and it gets converted to a set.
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()while states_needed:best_station = Nonestates_covered = set()#一次for循环找出最佳best_station,知道states_needed为空for station, states in stations.items():covered = states_needed & statesif len(covered) > len(states_covered):best_station = stationstates_covered = coveredstates_needed -= states_coveredfinal_stations.add(best_station)print(final_stations)
(3)NP完全问题
旅行商问题,旅行商需要前往5个不同的城市,他需要找出前往这5个城市的最短路径。为此,必须计算每条可能的路径。
前往5个城市时,可能的路径有120条。这就是NP完全问题,需要计算所有可能的路径。
如何识别NP完全问题:
NP完全问题无处不在!如果能够判断一个问题属于NP完全问题,就不用去寻找完美的解决方案!而是使用近似算法即可。以下条件可帮助判断问题是不是NP完全问题。
- 元素较少时算法的运行速度非常快,但随着元素数量的增加,速度会变得非常慢。
- 涉及“所有组合”的问题通常是NP完全问题。
- 不能将问题分成小问题,必须考虑各种可能的情况。这可能是NP完全问题。
- 如果问题涉及序列(如旅行商问题中的城市序列)且难以解决,它可能就是NP完全问题。
- 如果问题涉及集合(如广播台集合)且难以解决,它可能就是NP完全问题。
- 如果问题可转换为集合覆盖问题或旅行商问题,那它肯定是NP完全问题。
(4)小结
- 贪婪算法寻找局部最优解,企图以这种方式获得全局最优解。
- 对于NP完全问题,还没有找到快速解决的方案。
- 面临NP完全问题时,最佳的做法是使用近似算法。
- 贪婪算法易于实现,运行速度快,是不错的近似算法。