TSP(中国旅行商问题)
本次博客是對于博客提出疑問的思考與實踐。
目錄
- 1. 問題描述
- 2. 現有的解決辦法
- 2.1 模擬退火算法
- 2.2 遺傳算法
- 2.2.1 遺傳算法簡介
- 2.2.2 遺傳算法在TSP問題中的使用
四個圖論經典問題
在這里主要討論第四個問題,中國郵政問題
1. 問題描述
給定一些城市與城市之間的距離,求解訪問每座城市一次且能返回出發點的最短路徑。
其圖論描述為:
給定圖G=(V,A)\mathrm{G }= (\mathrm{V},\mathrm{ A})G=(V,A),其中V\mathrm{V}V為頂點集,A\mathrm{A}A為各頂點相互連接的邊集,設d(ij)d(_{ij})d(ij?)是由頂點i和頂點j之間的距離所產生的距離矩陣,要求確定一條hamilton回路,即遍歷所有頂點當且僅當一次的最短距離。
旅行商問題可分為如下兩類:
其中,非對稱旅行商問題較難求解,我們一般討論對稱旅行商問題。
旅行商問題數學模型:
min?ti∈T∑i=1nd(ti,ti+1))\min_{t_i \in \mathrm{T}}{\sum_{i=1}^n}{d(t_{i},t_{i+1}))}ti?∈Tmin?i=1∑n?d(ti?,ti+1?))
T={t1,t2,…,tn}\mathrm{T} = \{t_1, t_2, \dots, t_n\}T={t1?,t2?,…,tn?}代表對于城市V={v1,v2,…,vn}\mathrm{V}=\{v_1, v_2, \dots, v_n\}V={v1?,v2?,…,vn?}的一個訪問順序,其中ti∈V,i∈{1,2,…,n}且tn+1=t1t_i \in \mathrm{V}, i\in\{1, 2, \dots, n\}且t_{n+1} = t_1ti?∈V,i∈{1,2,…,n}且tn+1?=t1?。
該問題是一個典型的優化組合難題,已被證實屬于NP完全問題,即沒有確定的算法能在多項式時間內得到問題的最優解。1
2. 現有的解決辦法
現有數據集
數據集讀取: tsplib95
巡回旅行商問題是典型的NP難題。如果使用暴力搜索求解TSP問題,其時間復雜度為O((n?1)!)O((n-1)!)O((n?1)!),減一是因為起始點確定。基于TSP的問題特性,構造性算法成為最先開發的求解算法,如最近領點,最近合并,最近插入,最遠插入,最近添加,貪婪插入等。但是由于構造性算法優化質量較差,迄今為止已開發了許多性能較好的改進型搜索算法。主要有:
2.1 模擬退火算法
模擬退火算法思想:
模擬退火算法區別于爬山思想(只向比當前高的地方爬,容易陷入局部極小值),就是加入了一個概率方程組指導決策:
Pk(i→j)={1,f(i)≤f(j)exp?(?f(i)?f(j)t),otherwiseP_k{(i\to j)} = \begin{cases} 1,f(i)\leq f(j) \\ \exp{(-\frac{f(i)-f(j)}{t})},\textrm{otherwise} \end{cases} Pk?(i→j)={1,f(i)≤f(j)exp(?tf(i)?f(j)?),otherwise?
其中f(i)f(i)f(i)表示適應度函數,t表示溫度。
概率方程翻譯過來表示,當溫度t較大的時候,接受比當前差的狀態轉移;當溫度較小的時候,只接受比當前好的狀態轉移。
偽代碼:
用python實現代碼:(dantzig42.tsp)
# 模擬退火算法import tsplib95 import numpy as np import random import math import json# 初始溫度值 t_high = 10000temperature = 10000# 溫度下界 t_low = 0.1# 冷卻速度 cool_rate = 0.001# tsplib文件路徑 path = "D:\\dataset\\tsp\\dantzig42.tsp\\dantzig42.tsp"def read_tsp_question(tsp_path):return tsplib95.load(tsp_path)def calculate_seq_distance(city_seq):distance = 0for i in np.arange(0, len(city_seq)-1):distance += problem.get_weight(city_seq[i], city_seq[i+1])# print("city_seq[{}], city_seq[{}] distance = {}".format(i,i+1,problem.get_weight(city_seq[i], city_seq[i+1])))distance += problem.get_weight(city_seq[len(city_seq)-1], city_seq[0])return distancedef get_new_city_seq(city_seq):new_city_seq = city_seq.copy()# 隨機選擇兩個位點, 交換兩個城市的位置first_index = random.randint(0, len(city_seq)-1)second_index = random.randint(0, len(city_seq)-1)while first_index == second_index:second_index = random.randint(0, len(city_seq) - 1)temp = new_city_seq[first_index]new_city_seq[first_index] = new_city_seq[second_index]new_city_seq[second_index] = tempreturn new_city_seqdef initial_sequence(city_num):initial_seq = np.arange(1, city_num + 1)initial_seq[10] = 32initial_seq[31] = 11seq_str = "[1,2,25,4,5,6,7,8,9,10,32,12,13,14,15,21,17,18,19,20,16,22,23,24,3,26,27,28,29,30,31,36,33,34,35,11,37,38,39,40,41,42]"initial_seq = json.loads(seq_str)return initial_seqdef acceptance_probability(next_distance, current_distance, temperature):if next_distance < current_distance:return 2else:return math.exp((current_distance-next_distance)/temperature)if __name__ == "__main__":problem = read_tsp_question(path)# 城市數量city_num = problem.dimension# 循環中最好的城市序列best_city_sequence = Nonebest_distance = -1# 當前的城市序列current_city_seq = Nonecurrent_distance = -1# 下一步的城市序列next_city_seq = Nonewhile temperature >= t_low:if temperature == t_high and current_city_seq is None:current_city_seq = initial_sequence(city_num)best_city_sequence = current_city_seqcurrent_distance = calculate_seq_distance(current_city_seq)best_distance = current_distanceprint("initial_distance = {}".format(current_distance))next_city_seq = get_new_city_seq(current_city_seq)next_distance = calculate_seq_distance(next_city_seq)if acceptance_probability(next_distance, current_distance, temperature) > random.random():current_city_seq = next_city_seqcurrent_distance = next_distanceif current_distance < best_distance:best_city_sequence = current_city_seqbest_distance = current_distance# print("current_distance = {}\n".format(current_distance))temperature *= (1-cool_rate)print("best_city_seq: {}".format(best_city_sequence))print("best_distance: {}".format(best_distance))由于∪i=142{xi∣xi=i}\cup_{i=1}^{42}\{x_i|x_i =i\}∪i=142?{xi?∣xi?=i}就是最優解(dantzig42 : 699),就初始化一個距離為1287的序列當作初始解,體現模擬退火算法的優化效果。
2.2 遺傳算法
2.2.1 遺傳算法簡介
遺傳算法是一種基于自然選擇與基因遺傳學原理的隨機并行搜索算法,是一種尋找全局最優且不需要任何初始化信息的高效優化方法。它將問題的解集看做是一個種群,通過不斷的選擇、交叉、變異等遺傳操作,使解的質量越來越好。該算法具有全局尋優能力,適用性、解決非線性優化問題具有較強的魯棒性。對問題沒有特定限制、計算過程簡單、對搜索空間沒有特殊要求、易于與其他算法結合等特點,,在函數優化、圖像處理、系統辨識、自動控制、經濟預測和工程優化等領域得到了廣泛的應用, 在求解NP完全問題方面是一種較為有效的全局方法。1
簡單遺傳算法求解 TSP 問題的主要計算過程如下:
Step 1: 確定編碼機制, 生成初始種群。解決 TSP問題通常采用城市序號對路徑進行編碼, 按照訪問城市的順序排列組成編碼。
Step 2: 計算種群中每個個體的適應度值.。TSP 求解是要尋找使目標函數最小的個體, 因此選擇適應度函數 fitness(𝑖) = 𝐷/𝑓(𝑅𝑖).。設置常數𝐷, 防止路徑值過大而導致適應度函數倒數接近于 0。 可以看出, 巡游路徑越小, 適應度值越大。
Step 3: 選擇算子。通常采用精英個體保存策略和賭輪選擇算子, 即適應度最高的個體一定被選擇. 計算每個個體在整個種群適應度中的被選擇概率和累計概率分別為pi=fitness(i)∑i=1popsizefitness(i)p_i=\frac{\textrm{fitness}(i)}{\sum_{i=1}^{\textrm{popsize}}\textrm{fitness}(i)}pi?=∑i=1popsize?fitness(i)fitness(i)?, Qi=∑j=1ip(j)Q_i = \sum_{j=1}^ip(j)Qi?=∑j=1i?p(j)。通過隨機數 𝑟 所在的區間范圍選擇遺傳個體。
Step 4: 交叉算子.。由交叉概率 𝑝𝑐 選擇若干父體并進行配對, 按照交叉算法的規則生成新個體, 常用的規范方法有單點交叉、部分映射交叉、循環交叉等。
Step 5: 變異算子。為了保持種群個體的多樣性,防止陷入局部最優, 需要按照某一變異概率 𝑝𝑚 隨機確定變異個體, 并實行相應變異操作, 通常采用逆序變異算子。
Step 6: 迭代終止條件。 若滿足預定的終止條件(達到最大迭代次數), 則停止迭代, 所得的路徑認為是滿意的路徑; 否則, 轉至 Step 2, 計算新一代種群中每個個體的適應度值。
簡單遺傳算法往往存在收斂速度慢、易陷入局部最優和優化精度低等明顯不足。 如何在提高算法收斂速度的同時確保種群多樣性, 使尋優結果接近最優解是遺傳算法不斷改進的目標。
2.2.2 遺傳算法在TSP問題中的使用
上述對于遺傳算法的描述是通用的,只是闡述了遺傳算法的思想與基本的步驟。閱讀了幾篇用遺傳算法求解TSP問題的論文,優化方法包括了上述的六步。比如說,第一步中的初始化種群,就有提出使用貪婪算法生成初始化種群的。
編碼方式:
最常規的編碼方式為二進制編碼,但是不使用于此問題。此問題的一個組合形如(4,2,1,3,4)(4,2,1,3,4)(4,2,1,3,4),每個數字代表一個城市,一個序列代表著游覽城市的序列,是一個回路。如果使用二進制編碼,不僅要考慮數據溢出的問題,而且還要考慮交叉之后的序列存在重復城市編號。
TSP問題中的交叉算子:
路徑表示(path representation)是表示旅程對應的基因編碼的最自然、最簡單的表示方法。例如,旅程 5-1-7-8-9-4-6-2-3 可以直接表示為(5 1 7 8 9 4 6 2 3),基于路徑表示的編碼方法,要求一個個體(即一條旅程)的染色體編碼中不允許有重復的基因碼,也就是說要滿足任一個城市必須而且只能訪問一次的約束。這樣,基本遺傳算法的交叉操作生成的個體一般不能滿足這一約束條件。為此,人們提出了一個稱為重排操作的新操作來處理這類表示問題,它包括三種操作:部分匹配交叉(Partially Matched Crossover,PMX)、郭濤交叉(Guo Tao Crossover,GTX)、循環交叉(Cycle Crossover,CX)。主要是在傳統交叉操作上加入了“不允許有重復基因碼”的限制。
下述是遺傳算法選擇,交叉,變異的常見算法:2
選擇算子:
| 1 | 輪盤賭選擇(回放式隨機采樣) | 選擇誤差較大 | DeJong,Brindle |
| 2 | 無回放式隨機采樣 | 降低選擇誤差,復制數(f/(f+1)),操作不便 | DeJong,Brindle |
| 3 | 確定采樣 | 選擇誤差更小,操作簡易 | Brindle |
| 4 | 柔性分段式選擇 | 有效防止基因缺失但需要選擇有關參數 | Yun |
| 5 | 自適應柔性分段式動態群體采樣 | 群體自適應變化,提高搜索效率 | Yun |
| 6 | 無放回式余數隨機采樣 | 群體自適應變化,提高搜索效率 | DeJong,Brindle |
| 7 | 均勻排序 | 與適應度的大小差異程序正負無關 | Back |
| 8 | 穩態選擇 | 保留父代中的一些高適應度的串 | Syswerda |
| 9 | 隨機比賽 | Brindle | |
| 10 | 選擇評價 | Whitley | |
| 11 | 最優串選擇 | 全局收斂,提高搜索效率 | DeJong.Back |
| 12 | 最優串保留 | 保證全局收斂 | Yun |
| 13 | 適應度線性尺度變換 | 簡單,可消除遺傳早期的超級串現象 | Bagley |
| 14 | 適應度指數尺度變換 | Gillies | |
| 15 | 適應度自適應線性尺度變換 | 符合遺傳機理 | Yun |
交叉算子:
| 1 | 單點交叉 | 基于 GA 的成員 | Holland,DeJong,Goldberg | 符號 |
| 2 | 雙點交叉 | 使用較多 | Cavicchio,Booker | 符號 |
| 3 | 均勻交叉 | 每一位以相同的概率交叉 | Syswerda,whitely,Yun | 符號 |
| 4 | 多點交叉 | 交換點大于 2 | DeJong,Spears | 符號 |
| 5 | 部分匹配(PMX) | Goldberg | 序號 | |
| 6 | 序號交叉(OX) | Davis | 序號 | |
| 7 | 圓交叉(CX) | Smith | 序號 | |
| 8 | 基于位置交叉 | Syswerda | 序號 | |
| 9 | 算術交叉 | Michalewicz | 實數 | |
| 10 | 啟發式交叉 | 應用領域知識 | Grffenstette | 序號 |
變異算子:
| 1 | 常規位突變 | 基本 GA 的成員 | DeJong | 符號 |
| 2 | 有效基因突變 | 避免有效基因缺失 | Yun | 符號 |
| 3 | 自適應有效基因突變 | 最低有效基因個數自適應變換 | Yun | 符號 |
| 4 | 概率自調整突變 | 由兩個串的相似性確定突變概率 | Whitley | 符號 |
| 5 | 均勻突變 | 每一個實數元素以相同的概率在域內變動 | Michalewicz | 實數 |
| 6 | 非均勻突變 | 使整個矢量在解空間輕微變動 | Michalewicz | 實數 |
| 7 | 三次高斯近似突變 | Bosworth,Foo,zeigler | 實數 |
于瑩瑩,陳燕,李桃迎.改進的遺傳算法求解旅行商問題[J].控制與決策,2014,29(08):1483-1488. ?? ??
王銀年. 遺傳算法的研究與應用[D].江南大學,2009. ??
總結
以上是生活随笔為你收集整理的TSP(中国旅行商问题)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 剪辑视频,在视频背景上随机添加图片
- 下一篇: 人生之路1.20代码 第一部分