Python | 動態(tài)規(guī)劃求解TSP

主要參考:Python求解tsp問題(動態(tài)規(guī)劃,簡單易懂)CSDN博客

解題思路主要有兩部分:

  1. i為當前節(jié)點(城市)舟山,S為還沒有遍歷的節(jié)點(城市集合)攒岛,表示從第i個節(jié)點起,經歷S集合中所有的點鲤脏,到達終點的最短路徑長度。


  2. 回溯找到最優(yōu)的路徑吕朵,需要將S集合一一對應一個數字(類似于編碼猎醇,一般用二進制),然后比如從節(jié)點i等于0開始努溃,未經歷集合為S={1硫嘶,2,3}梧税,而下一步最優(yōu)的節(jié)點 j 等于2沦疾,那么M[i][s]=j,回溯時只用從M[0][S]向后推即可第队。

import numpy as np
import itertools
import random
import matplotlib.pyplot as plt
# 設置中文識別
plt.rcParams['font.sans-serif'] = ['SimHei']

# tsp問題
class Solution:
    def __init__(self,X,start_node):
        self.X = X #距離矩陣
        self.start_node = start_node #開始的節(jié)點
        self.array = [[0]*(2**(len(self.X)-1)) for i in range(len(self.X))] #記錄處于x節(jié)點哮塞,未經歷M個節(jié)點時,矩陣儲存x的下一步是M中哪個節(jié)點
 
    def transfer(self, sets):
        su = 0
        for s in sets:
            su = su + 2**(s-1) # 二進制轉換
        return su
 
    # tsp總接口
    def tsp(self):
        s = self.start_node
        num = len(self.X)
        cities = list(range(num)) #形成節(jié)點的集合
        # past_sets = [s] #已遍歷節(jié)點集合
        cities.pop(cities.index(s)) #構建未經歷節(jié)點的集合
        node = s #初始節(jié)點
        return self.solve(node, cities) #求解函數
 
    def solve(self, node, future_sets):
        # 迭代終止條件斥铺,表示沒有了未遍歷節(jié)點彻桃,直接連接當前節(jié)點和起點即可
        if len(future_sets) == 0:
            return self.X[node][self.start_node]
        d = 99999
        # node如果經過future_sets中節(jié)點,最后回到原點的距離
        distance = []
        # 遍歷未經歷的節(jié)點
        for i in range(len(future_sets)):
            s_i = future_sets[i]
            copy = future_sets[:]
            copy.pop(i) # 刪除第i個節(jié)點晾蜘,認為已經完成對其的訪問
            distance.append(self.X[node][s_i] + self.solve(s_i,copy))
        # 動態(tài)規(guī)劃遞推方程邻眷,利用遞歸
        d = min(distance)
        # node需要連接的下一個節(jié)點
        next_one = future_sets[distance.index(d)]
        # 未遍歷節(jié)點集合
        c = self.transfer(future_sets)
        # 回溯矩陣眠屎,(當前節(jié)點,未遍歷節(jié)點集合)——>下一個節(jié)點
        self.array[node][c] = next_one
        return d

# 計算兩點間的歐式距離
def distance(vector1,vector2):
    d=0;
    for a,b in zip(vector1,vector2):
        d+=(a-b)**2;
    return d**0.5;

首先在(10肆饶,10)的坐標上改衩,隨機生成10個城市:

# 隨機生成10個坐標點
n = 10
random_list = list(itertools.product(range(1, n), range(1, n)))
cities = random.sample(random_list, n)

x = []
y = []
for city in cities:
    x.append(city[0])
    y.append(city[1])

fig = plt.figure()
plt.scatter(x,y,label='城市位置',s=30)
plt.xlabel('x')
plt.ylabel('y')
plt.title('TSP問題 隨機初始城市')

plt.legend()
plt.show()

使用歐氏距離計算兩兩城市之間的距離:

distence_matrix = np.zeros([n,n])
for i in range(0, n):
    for j in range(n):
        distence = distance(cities[i],cities[j])
        distence_matrix[i][j] = distence

使用動態(tài)規(guī)劃求解TSP問題:

S = Solution(distence_matrix,0)
print("最短距離:" + str(S.tsp()))
# 開始回溯
M = S.array
lists = list(range(len(S.X)))
start = S.start_node
city_order = []
while len(lists) > 0:
    lists.pop(lists.index(start))
    m = S.transfer(lists)
    next_node = S.array[start][m]
    print(start,"--->" ,next_node)
    city_order.append(cities[start])
    start = next_node

得到最終的訪問路線:

x1 = []
y1 = []
for city in city_order:
    x1.append(city[0])
    y1.append(city[1])

x2 = []
y2 = []
x2.append(city_order[-1][0])
x2.append(city_order[0][0])
y2.append(city_order[-1][1])
y2.append(city_order[0][1])

plt.plot(x1,y1,label='路線',linewidth=2,marker='o',markersize=8)
plt.plot(x2,y2,label='路線',linewidth=2,color='r',marker='o',markersize=8)
plt.xlabel('x') 
plt.ylabel('y') 
plt.title('TSP問題 路線圖') 
plt.legend() 
plt.show()
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市驯镊,隨后出現(xiàn)的幾起案子葫督,更是在濱河造成了極大的恐慌,老刑警劉巖板惑,帶你破解...
    沈念sama閱讀 219,490評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件橄镜,死亡現(xiàn)場離奇詭異,居然都是意外死亡冯乘,警方通過查閱死者的電腦和手機洽胶,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,581評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來裆馒,“玉大人姊氓,你說我怎么就攤上這事∨绾茫” “怎么了翔横?”我有些...
    開封第一講書人閱讀 165,830評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長梗搅。 經常有香客問我禾唁,道長,這世上最難降的妖魔是什么些膨? 我笑而不...
    開封第一講書人閱讀 58,957評論 1 295
  • 正文 為了忘掉前任蟀俊,我火速辦了婚禮,結果婚禮上订雾,老公的妹妹穿的比我還像新娘。我一直安慰自己矛洞,他們只是感情好洼哎,可當我...
    茶點故事閱讀 67,974評論 6 393
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著沼本,像睡著了一般噩峦。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上抽兆,一...
    開封第一講書人閱讀 51,754評論 1 307
  • 那天识补,我揣著相機與錄音,去河邊找鬼辫红。 笑死凭涂,一個胖子當著我的面吹牛祝辣,可吹牛的內容都是我干的。 我是一名探鬼主播切油,決...
    沈念sama閱讀 40,464評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼蝙斜,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了澎胡?” 一聲冷哼從身側響起孕荠,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎攻谁,沒想到半個月后稚伍,有當地人在樹林里發(fā)現(xiàn)了一具尸體,經...
    沈念sama閱讀 45,847評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡戚宦,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,995評論 3 338
  • 正文 我和宋清朗相戀三年槐瑞,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片阁苞。...
    茶點故事閱讀 40,137評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡困檩,死狀恐怖,靈堂內的尸體忽然破棺而出那槽,到底是詐尸還是另有隱情悼沿,我是刑警寧澤,帶...
    沈念sama閱讀 35,819評論 5 346
  • 正文 年R本政府宣布骚灸,位于F島的核電站糟趾,受9級特大地震影響,放射性物質發(fā)生泄漏甚牲。R本人自食惡果不足惜义郑,卻給世界環(huán)境...
    茶點故事閱讀 41,482評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望丈钙。 院中可真熱鬧非驮,春花似錦、人聲如沸雏赦。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,023評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽星岗。三九已至填大,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間俏橘,已是汗流浹背允华。 一陣腳步聲響...
    開封第一講書人閱讀 33,149評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人靴寂。 一個月前我還...
    沈念sama閱讀 48,409評論 3 373
  • 正文 我出身青樓磷蜀,卻偏偏與公主長得像,于是被迫代替她去往敵國和親榨汤。 傳聞我的和親對象是個殘疾皇子蠕搜,可洞房花燭夜當晚...
    茶點故事閱讀 45,086評論 2 355