貪心演算法講解(集合覆蓋問題,旅行商問題求解)
教室排程問題
假設有如下課程表,你希望將盡可能多的課程安排在某間教室上。
你沒法讓這些課都在這間教室上,因為有些課的上課時間有衝突。
你希望在這間教室上儘可能多的課。如何選出儘可能多且時間不衝突的課程呢?
這個問題好像很難,不是嗎?實際上,演算法可能簡單得讓你大吃-一驚。具體做法如下。
- 選出結束最早的課,它就是要在這間教室上的第一-堂課。
- 接下來,必須選擇第一-堂 課結束後才開始的課。同樣,你選擇結束最早的課,這將是要在這間教室上的第二堂課。
- 重複這樣做就能找出答案!下面來試- -試。 美術課的結束時間最早,為10:00 a.m,因此它就是第一堂課。接下來的課必須在10:00 a.m.後開始,且結束得最早。英語課不行,因為它的時間與美術課衝突,但數學課滿足條件。最後,計算機課與數學課的時間是衝突的,但音樂課可以。
很多人都跟我說,這個演算法太容易、太顯而易見,肯定不對。但這正是貪婪演算法的優點一簡單易行! 貪婪演算法很簡單:每步都採取最優的做法。在這個示例中,你每次都選擇結束最早的課。用專業術語說,就是你每步都選擇區域性最優解,最終得到的就是;全域性最優解。信不信由你,對於這個排程問題,上述簡單演算法找到的就是最優解!
貪心演算法可能找到的不是最優解,但接近最優解啦。
集合覆蓋問題
假設你辦了個廣播節目,要讓全美50個州的聽眾都收聽得到。為此,你需要決定在哪些廣播臺播出。在每個廣播臺播出都需要支付費用,因此你力圖在儘可能少的廣播臺播出。現有廣播臺名單如下。每個廣播臺都覆蓋特定的區域,不同廣播臺的覆蓋區域可能重疊。
我們用貪心演算法求解
貪婪演算法可化解危機!使用下面的貪婪演算法可得到非常接近的解。
- 選出這樣-一個廣 播臺,即它覆蓋了最多的未覆蓋州。即便這個廣播臺覆蓋了一些已覆蓋的州,也沒有關係。
- 重複第-步,直到覆蓋了所有的州。
程式碼
出於簡化考慮,這裡假設要覆蓋的州沒有那麼多,廣播臺也沒有那麼多。
全部程式碼
語言:python
#建立一個列表,其中包含要覆蓋的州
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'])
#最後,需要使用一個集合來儲存最終選擇的廣播臺。
finnal_stations=set()
#計算答案
while len(states_needed)>0:#直到所有州都覆蓋完,剩餘列表的州為空
best_stations = None
state_covered = set() # states_ covered是-一個集合, 包含該廣播臺覆蓋的所有未覆蓋的州。
for station, states in stations.items():#for迴圈迭代每個廣播臺,並確定它是否是最佳的廣播臺。
covered=states_needed & states #剩餘列表中的州 and 電視臺覆蓋的州 (取交集)
if len(covered)>len(state_covered): #取交集最大的電視臺
best_stations=station
state_covered=covered
states_needed -=state_covered#剩餘列表裡州
finnal_stations.add(best_stations)
print(finnal_stations)
print('結束')
旅行商問題
旅行商問題(TravelingSalesmanProblem,TSP)一個商品推銷員要去若干個城市推銷商品,該推銷員從一個城市出發,需要遍歷所有城市一次且只能一次,回到出發地。應如何選擇行進路線,以使總的行程最短。目前解決TSP問題的方法有許多種,比如:貪心演算法、動態規劃演算法、分支限界法;也有智慧演算法。本文先介紹貪心演算法:
資料 如下圖,第一列城市名。第二列座標x,第三列座標y
語言:python
貪心演算法思路:隨便選擇出發城市,然後每次選擇要去的下一個城市時,都選擇還沒去的最近的城市。
第一步:讀取資料
import pandas as pd
import numpy as np
import math
import time
dataframe = pd.read_csv("TSP.csv", sep=",", header=None)
v = dataframe.iloc[:, 1:3]#去除第一列12345678910,只保留x,y
print(v)
第二步:計算城市之間的距離
train_v= np.array(v)
train_d=train_v
#初始化距離 為10*10的全0矩陣
dist = np.zeros((train_v.shape[0],train_d.shape[0]))
#print(dist.shape)#(10,10)
#計算距離矩陣
for i in range(train_v.shape[0]):
for j in range(train_d.shape[0]):
dist[i,j] = math.sqrt(np.sum((train_v[i,:]-train_d[j,:])**2))
print(dist)
第三步:計算距離和路徑
"""
s:已經遍歷過的城市
dist:城市間距離矩陣
sumpath:目前的最小路徑總長度
Dtemp:當前最小距離
flag:訪問標記
"""
i=1
n=train_v.shape[0]#城市個數
j=0
sumpath=0#目前的最小路徑總長度
s=[]#已經遍歷過的城市
s.append(0)#從城市0開始
start = time.clock()
while True:
k=1#從1開始,因為人在城市0,所以我們設定先從城市1開始選擇
Detemp=float('inf')#當前最小距離
while True:
flag=0#訪問標記,否0
if k in s:#是否訪問,如果訪問過,flag設為1
flag = 1
if (flag==0) and (dist[k][s[i-1]] < Detemp):#如果未訪問過,並且距離小於最小距離
j = k;
Detemp=dist[k][s[i - 1]];#當前兩做城市相鄰距離
k+=1#遍歷下一城市
if k>=n:
break;
s.append(j)
i+=1;
sumpath+=Detemp
if i>=n:
break;
sumpath+=dist[0][j]#加上dist[0][j] 表示最後又回到起點
end = time.clock()
print("結果:")
print(sumpath)
for m in range(n):
print("%s-> "%(s[m]),end='')
print()
print("程式的執行時間是:%s"%(end-start))
該段程式碼借鑑 他人
程式碼解析:數字k表示當前我們選擇前往下一個城市時,我們需要計算所有未訪問過的城市和當前城市距離。
數字i 用於控制訪問過的城市,我們需要到達每一個城市。
程式碼中有兩個while
裡面那個while表示選擇下一城市時,需要遍歷所有未訪問過的城市,然後選擇距離當前城市最近的城市,賦值給j
外面while,表示我們的每一步,我們需要去每個城市。
作者:電氣-餘登武