小白的算法初识课堂(part8)--贪婪算法
學習筆記
學習書目:《算法圖解》- Aditya Bhargava
文章目錄
- 教室調度問題
- 集合覆蓋問題
- 近似算法
- 代碼實現
- NP完全問題
教室調度問題
假如我是一個學校的校長,我們學校有如下課程表:
| 多元統計分析 | 9:00AM | 10:00AM |
| 時間序列分析 | 9:30AM | 10:30AM |
| 計量經濟學 | 10:00AM | 11:00AM |
| 數據挖掘 | 10:30AM | 11:30AM |
| 統計實驗 | 11:00AM | 12:00PM |
我設法讓這些課程都在安排在教室A001上,但是因為有些課上課時間有沖突,所以我們只能盡可多的安排課程在這間教室上。
我們的具體做法如下:
(1) 選出結束最早的課,它就是要在這間教室上的第一堂課。
(2) 接下來,必須選擇第一堂課結束后才開始的課。同樣,你選擇結束最早的課,這將是要在這間教室上的第二堂課。
重復的步驟就能找到正確答案!
由此,在教室A001會上下面3堂課:
| 多元統計分析 | 9:00AM | 10:00AM |
| 計量經濟學 | 10:00AM | 11:00AM |
| 統計實驗 | 11:00AM | 12:00PM |
小伙伴們可能覺得這個算法比較簡單,太顯而易見了!但這正是貪婪算法的優點—簡單易行!貪婪算法很簡單:每步都采取最優的做法。在這個示例中,我們每次都選擇結束最早的課。用專業術語說,就是每步都選擇局部最優解,最終得到的就是全局最優解。
顯然,貪婪算法并非在任何情況下都行之有效,但它易于實現。
集合覆蓋問題
假設我要在Z國辦一個廣播節目,我想讓Z國的12個城市都聽到我的節目。為此,我需要決定在哪些廣播臺播出,在每個廣播臺播出都需要支付一定的費用(假設每個電臺費用一樣),因此,我力圖在盡可能少的廣播臺播出,但一定要覆蓋Z國12個市。
| Tim | A、B、C、G |
| Black | B、E、G、J、L |
| Ada | D、F、A、K |
| Huang | F、H、I、J |
| Bai | C、H |
| Ket | D、E、F、G、K |
| Yellow | L、I、A |
每個廣播臺都覆蓋特定的區域,不同的廣播臺覆蓋的區域可能重疊。
如何找出覆蓋Z國12個市的最小廣播電臺集合呢?
具體方法如下:
(1) 列出每個可能的廣播臺集合,這被稱為冪集(power set),可能的子集有2n2^n2n個,其中n為電臺個數。
(2) 在這些集合中,選出覆蓋Z國12個市的最小集合。
問題是計算每個可能的廣播臺子集需要很長時間。由于可能的子集有2n2^n2n個,因此運行時間為O(2n2^n2n)。如果廣播臺不多,只有5~10個,這是可行的。但如果廣播臺很多,結果將如何呢?隨著廣播臺的增多,需要的時間將激增。假設我們每秒可以計算10個子集,則所需時間如下:
| 5 | 3.2秒 |
| 10 | 102.4秒 |
| 32 | 13.6年 |
| 100 | 4?10214*10^{21}4?1021年 |
現在,我們沒有任何算法可以足夠快地解決這個問題,那該咋整呢?
近似算法
在有些情況下,完美是優秀的敵人。有時候,你只需找到一個能夠大致解決問題的算法,此時貪婪算法正好可派上用場,因為它們實現起來很容易,得到的結果又與正確結果相當接近。
使用下面的貪婪算法可得到非常接近的解:
(1) 選出這樣一個廣播臺,即它覆蓋了最多的未覆蓋城市。即便這個廣播臺覆蓋了一些已覆蓋的城市,也沒有關系。
(2) 重復第一步,直到覆蓋了所有的城市。
這是一種近似算法(approximation algorithm)。在獲得精確解需要的時間太長時,可使用近似算法。判斷近似算法優劣的標準如下:速度有多快;得到的近似解與最優解的接近程度。
在這個例子中,貪婪算法的運行時間為O(n2)O(n^2)O(n2),其中n為電臺個數。
代碼實現
首先,我們先創建一個集合,里面包含了要覆蓋的12個城市:
city_needed = set(['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L'])我們再用散列表表示廣播電臺的清單:
stations = {} stations = {} stations['Tim'] = set(['A', 'B', 'C', 'G']) stations['Black'] = set(['B', 'E', 'G', 'J', 'L']) stations['Ada'] = set(['D', 'F', 'A', 'K']) stations['Huang'] = set(['F', 'H', 'I', 'J']) stations['Bai'] = set(['C', 'H']) stations['Ket'] = set(['D', 'E', 'F', 'G', 'K']) stations['Yellow'] = set(['L', 'I', 'A'])設置一個集合來存儲最終選擇的廣播電臺:
final_stations = set()python代碼:
while city_needed:best_station = Nonecity_covered = set()for station, city_for_station in stations.items():covered = city_needed & city_for_stationprint(covered)if len(covered) > len(city_covered):best_station = stationcity_covered = coveredfinal_stations.add(best_station)print(best_station)city_needed -= city_coveredprint(final_stations)控制臺輸出:
{'Tim', 'Ada', 'Black', 'Huang'}NP完全問題
集合問題屬于NP完全問題,在NP完全問題中,我們需要計算所有的解,并從中選出最小/最短的那個。NP完全問題的簡單定義是,以難解著稱的問題,如旅行商問題和集合覆蓋問題。
NP完全問題無處不在,如果能夠判斷出要解決的問題屬于NP完全問題就好了,這樣就不用去尋找完美的解決方案,而是使用近似算法即可。但要判斷問題是不是NP完全問題很難,易于解決的問題和NP完全問題的差別通常很小。
就比如我們前幾個Blog討論的最短路徑問題,要求我們找出從A出發到B的最短路徑。但是如果題目要求我們找出經由指定幾個點的最短路徑,那這個問題就變成了旅行商問題,也就是NP完全問題。簡而言之,沒辦法判斷問題是不是NP完全問題,但是還是可以從一些蛛絲馬跡中判斷的:
- 元素較少時算法的運行速度非常快,但隨著元素數量的增加,速度會變得非常慢。
- 涉及“所有組合”的問題通常是NP完全問題。
- 不能將問題分成小問題,必須考慮各種可能的情況。
- 如果問題涉及序列(如旅行商問題中的城市序列)且難以解決,它可能就是NP完全問題。
- 如果問題涉及集合(如廣播臺集合)且難以解決,它可能就是NP完全問題。
- 如果問題可轉換為集合覆蓋問題或旅行商問題,那它肯定是NP完全問題。
總結
以上是生活随笔為你收集整理的小白的算法初识课堂(part8)--贪婪算法的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 伐木累暗示速激12
- 下一篇: 小白的算法初识课堂(part9)--SH