1、概念
貪婪算法: 每步都採取最優的做法。
優點: 簡單易行。
特點: 得到的結果是最優解或者與最優解相當接近。所以也不是任何情況下行之有效的。可以看成是一個近似算法。
2、教室課表調度問題
課程表如下,如何選出儘可能多且時間不衝突的課程呢?
英語 | 8 :00 AM | 9:00 AM |
---|---|---|
思修 | 8:30 AM | 9:30 AM |
C語言 | 9:00 AM | 10:00 AM |
計算機概論 | 9:40 AM | 10:40 AM |
Python | 10:20 AM | 11:20 AM |
貪婪算法做法具體如下:
- 選出最早結束的課,就是要在這間教室上的第一堂課。
- 接下來,必須選擇第一堂課後纔開始的課。同樣,選擇最早結束的課,這將是要在這間教室上的第二堂課。以此類推。
最終貪婪算法求出這間教室課表安排順序爲: 英語、C語言、Python。
3、揹包問題
舉例揹包問題主要討論貪婪算法並非是在任何情況下行之有效的。
假設你是一個小偷,你揹着可裝35磅重東西的揹包,在商場伺機盜竊各種可裝入揹包的商品。商場的商品重量和價格如下:
商品名 | 價格 | 重量 |
---|---|---|
音響 | 6000美元 | 30磅 |
筆記本電腦 | 4000美元 | 20磅 |
吉他 | 3000美元 | 15磅 |
採用貪婪策略,具體做法如下:
- 盜竊可裝入揹包的最貴商品。
- 再盜竊還可裝入揹包的最貴商品,以此類推。
採用貪婪策略求出只偷到了6000美元的東西,但是如果不偷音響,而是偷筆記本和吉他,那將偷到7000美元的東西。
啓示:
在某些情況下,完美是優秀的敵人。有時候,你只需要找到一個能夠大致解決問題的算法,此時貪婪算法正好可派上用場,因爲他們實現起來很容易,得到的結果又與正確結果相當接近。
4、集合覆蓋問題
假設你辦了一個廣播節目,要讓全國的聽衆都能收聽得到,爲此你需要決定在哪些廣播臺播出,在每個廣播臺都需要支付費用,因此你力圖在儘可能少的廣播臺播出,然而每個廣播臺都覆蓋特定的區域,不同廣播臺的覆蓋區域可能重疊。如何找出覆蓋全國34個省級區域的最小廣播臺集合呢?看起來很簡單,但實現起來非常難。具體方法如下:
- 列出每個可能的廣播臺集合,這個被稱爲冪集,可能的子集爲 個。
- 在這些集合中,選出覆蓋全中國34個省級區域的最小集合。
此時問題來了,這時算法的運行時間爲 ,如果廣播臺很多,沒有任何算法能足夠快地解決這個問題。怎麼辦呢?
貪婪算法可以化解危機,使用貪婪算法可以得到非常接近的解。
使用貪婪算法步驟如下:
- 選出這樣一個廣播臺,即它覆蓋了最多未覆蓋州。即便這個廣播臺覆蓋了一些已覆蓋的省級區域,也沒有關係。
- 重複第一步,直到覆蓋了所有的省級區域。
這個算法的運行時間爲 ,其中n爲廣播臺數量。
代碼(Python3):
-
出於簡化考慮,假設省級區域只有江西省、廣東省、湖北省、福建省、廣西省、雲南省、四川省、山東省。可以創建一個列表,其中包含要覆蓋的省級區域。
states_needed = set(['江西省','廣東省','湖北省','福建省','廣西省','雲南省','四川省','山東省']) #傳入一個列表,將其轉換爲集合
-
廣播臺清單,使用散列表來表示。
stations = {} stations['頻道1'] = set(['江西省','廣東省','福建省']) stations['頻道2'] = set(['四川省','山東省','廣西省']) stations['頻道3'] = set(['山東省','湖北省','福建省']) stations['頻道4'] = set(['雲南省','湖北省','江西省'])
-
最後使用一個集合來存儲最終選擇的廣播臺。
final_stations = set()
-
開始計算,遍歷所有的廣播臺,從中選擇覆蓋了最多的未覆蓋的廣播臺。將這個廣播臺存儲在best_station中。把該廣播臺覆蓋的所有未覆蓋的省級區域放入states_covered集合。for循環每個廣播臺,並確定它是否是最佳的廣播臺。for循環結束後將best_station添加到最終的廣播臺列表中。
best_station = None states_covered = set() for station,states_for_station in stations.items(): covered = states_needed & states_for_station #取交集 if len(covered) > len(states_covered): best_station = station states_covered = covered final_station.add(best_station)
-
由於覆蓋了一些省級區域,因此不需要覆蓋這些省級區域,還需更新states_needed。
states_needed -= states_covered
-
不斷循環,直到states_needed 爲空,這個循環完整代碼:
while states_needed: best_station = None states_covered = set() for station,states_for_station in stations.items(): covered = states_needed & states_for_station #取交集 if len(covered) > len(states_covered): best_station = station states_covered = covered states_needed -= states_covered final_stations.add(best_station)
-
最後打印final_stations,輸出:
5、NP完全問題
NP完全問題: 多項式複雜程度的非確定性問題,簡單的理解就是 “難解”,需要計算所有的解,並從中選出最小/最短的那個。所以對於NP完全問題,一般採用近似求解,採用算法如:貪婪算法。
判斷是否爲NP完全問題:
- 元素較少時算法運行的速度非常快,但隨着元素數量的增加,速度會變得非常慢,這時可能是NP完全問題。
- 涉及”所有組合“的問題通常是NP問題。
- 不能將問題分成小問題,必須考慮各種可能的情況。這個可能是NP完全問題。
- 如果問題涉及序列(旅行商問題中的城市序列)且難以解決,它可能就是NP完全問題。
- 如果問題涉及集合(如廣播集合)且難以解決,它可能就是NP完全問題。
- 如果問題可轉換未集合覆蓋或旅行商問題,那它肯定是NP完全問題。
6、總結
- 貪婪算法尋找局部最優解,企圖以這種方式獲取全局最優解。
- 對於NP完全問題,還沒有找到快速解決的方案。
- 面臨NP完全問題,最佳的做法是使用近似算法。
- 貪婪算法易於實現、運行速度快,是不錯的近似算法。