貪心算法:集合覆蓋問題

貪心算法


貪心算法是一種解決問題的思路:每一步選擇局部最優解,最終也許不會得到最優結果,但是也會接近最優結果。
貪心算法具有以下特點:

  1. 每一步選擇局部最優解;
  2. 並非在任何情況下行之有效;
  3. 貪心算法簡單易實現;

教室調度問題


假設有如下課程表,你希望將盡可能多的課程安排在某間教室上:

課程 開始時間 休息時間
美術 9 AM 10 AM
英語 9:30 AM 10:30 AM
數學 10 AM 11 AM
計算機 10:30 AM 11:30 AM
音樂 11 AM 12 PM

在這裏插入圖片描述

你希望在這間教室上儘可能多的課。如何選出儘可能多且時間不衝突的課程呢?貪心算法的做法是:每一步選擇價值回報最大的操作。具體做法如下:

  • 選出結束最早的課,它就是要在這間教室上的第一堂課。
  • 接下來,必須選擇第一堂課結束後纔開始的課。同樣,你選擇結束最早的課,這將是要在這間教室上的第二堂課。
    重複這樣做就能找出答案!最終選擇的結果如下:

在這裏插入圖片描述


揹包問題


假設你是個貪婪的小偷,揹着可裝35磅(10.451磅≈0.45千克)重東西的揹包,在商場伺機盜竊各種可裝入揹包的商品。
貪心算法的策略是:

  1. 盜竊可裝入揹包的最貴商品;
  2. 再盜竊還可裝入揹包的最貴商品,以此類推;

以這種方式可能並不能得到最優解。有時候,你只需找到一個能夠大致解決問題的算法,此時貪婪算法正好可派上用場,因爲它們實現起來很容易,得到的結果又與正確結果相當接近。記住,對有些問題,可能並不存在完美的解,例如 NP 難問題。


集合覆蓋問題


集合覆蓋問題: 選擇最少的集合,覆蓋全部的元素。
假設你辦了個廣播節目,要讓全美 5050 個州的聽衆都收聽得到。爲此,你需要決定在哪些廣播臺播出。在每個廣播臺播出都需要支付費用,因此你力圖在儘可能少的廣播臺播出。現有廣播臺名單如下。

廣播臺 覆蓋的州
(1) KONE ID,NV,UT
(2) KTWO WA,ID,MT
(3) KTHREE OR,NV,CA
(4) KFOUR NV,UT
(5) KFIVE CA,ZA

如何找出覆蓋全美 5050 個州的最小廣播臺集合呢?
最容易考慮到的可能是暴力求解法:列出每一種可能的廣播集合(可能的子集有 2n2^n個);在這些集合中選出能覆蓋全美 5050 個州的最小集合數。假設每秒可篩選出 1010 個子集,其花費的時間如下:

在這裏插入圖片描述

沒有任何算法可以足夠快地解決這個問題!怎麼辦呢?使用貪婪算法可以得到非常接近的解。

  1. 選出這樣一個廣播臺,即它覆蓋了最多的未被選擇的州。即便這個廣播臺覆蓋了一些已經選擇的州,也沒有關係;
  2. 重複第一步,直到覆蓋了所有的州。

貪婪算法是不錯的選擇,它們不僅簡單,而且通常運行速度很快。在這個例子中,貪婪算法的運行時間爲 O(n2)O(n^2),其中 nn 爲廣播臺數量。

解決上述問題的代碼如下:

  • 第一步:準備工作:構建數據結構;
    • 用集合 states_needed 存儲所有的州;
    • 用字典表示電視臺 stations 的覆蓋面;
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 中;
    • 重複第一步,直到覆蓋了所有的州。
while states_needed:
    best_station = None # 最佳電視臺:覆蓋了最多未覆蓋的州
    states_covered = set() # 最佳電視臺與未覆蓋州集合的交集
    for station, states in stations.items():
        covered = states_needed & states # 當前廣播臺與未覆蓋州集合的交集
        if len(covered) > len(states_covered):
            states_covered = covered
            best_station = station
    
    states_needed -= states_covered # 更新未覆蓋州的集合
    final_stations.add(best_station) # 添加貪心算法選擇的電臺

最終打印的結果可能如下:

print(final_stations)
# set(['ktwo', 'kthree', 'kone', 'kfive'])

暴力求解算法貪心算法 求解時間對比:

在這裏插入圖片描述


NP完全問題


什麼樣的問題稱爲 NP 完全問題呢? 就像集合覆蓋問題一樣,你需要計算所有的解,並從中選擇優的解。NP 完全問題以計算量大無法求得最優解而著稱,貪心策略是解決 NP 完全問題一個很重要且有效的方法。它也許不能得到最優解,但是可以得到最接近最優解的結果。


總結


  1. 貪婪算法尋找局部最優解,企圖以這種方式獲得全局最優解。
  2. 對於NP完全問題,還沒有找到快速解決方案。
  3. 面臨NP完全問題時,最佳的做法是使用近似算法。
  4. 貪婪算法易於實現、運行速度快,是不錯的近似算法。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章