Python数模笔记-模拟退火算法(4)旅行商问题
1、旅行商問題(Travelling salesman problem, TSP)
旅行商問題是經典的組合優化問題,要求找到遍歷所有城市且每個城市只訪問一次的最短旅行路線,即對給定的正權完全圖求其總權重最小的Hamilton回路:設有 n個城市和距離矩陣 D=[dij],其中dij表示城市i到城市j的距離(i,j = 1,2 … n),則問題是要找出遍訪每個城市恰好一次的一條回路并使其路徑長度為最短。旅行商問題屬于NP完全問題,其全局優化解的計算量以問題規模的階乘關系增長。旅行商問題不僅作為一類典型的組合優化問題經常被用作算法研究和比較的案例,許多實際應用問題如路徑規劃、交通物流、網絡管理也可轉化為旅行商問題。
目前,旅行商問題的研究主要集中于探索和發展各種高效近似最優解的優化方法,包括基于問題特征信息(如城市位置、距離、角度等)構造的各種啟發式搜索算法,以及通過模擬或解釋自然規律而發展的模擬退火算法、遺傳算法、蟻群算法、神經網絡算法等智能優化算法或將二者相結合的混合算法。
模擬退火算法不僅可以解決連續函數優化問題,KIRKPATRICK在1983年成功將其應用于求解組合優化問題。模擬退火算法現已成為求解旅行商問題的常用方法,通常采用反序、移位和交換等操作算子產生新解。
歡迎關注 Youcans 原創系列,每周更新數模筆記
Python數模筆記-PuLP庫
Python數模筆記-StatsModels統計回歸
Python數模筆記-Sklearn
Python數模筆記-NetworkX
Python數模筆記-模擬退火算法
2、模擬退火算法求解旅行商問題
模擬退火算法要從當前解的鄰域中產生新的候選解,解的表達形式和鄰域結構對算法收斂非常重要。組合優化問題不同于函數優化,其自變量不是連續變化的,目標函數不僅依賴于自變量的數值,而且與變量的排列次序有關。極端地,旅行商問題的路徑長度僅取決于排列次序,因此常用城市編號的序列來表示解。
新解的產生機制是在當前解序列的基礎上進行變換操作,隨機改變序列中某些點的排列次序,常見的基本變換操作有交換算子(Swap Operator)、反序算子(Inversion Operator)、移位算子(Move Operator)等。交換算子將當前路徑 S_now 中的第 i 個城市 Ci 與第 j 個城市 Cj 的位置交換;反序算子也稱2-opt,將當前路徑 S_now 中從第 i 個城市 Ci 到第 j 個城市 Cj 之間的城市排列順序反向翻轉;移位算子相當于 Or-opt 操作t,將當前路徑 S_now 中的第 i 個城市 Ci 移動到第 j 個城市 Cj 之后的位置。
3、 程序說明
下段給出了模擬退火算法求解旅行商問題的 Python程序。對于程序中的一些細節處理,說明如下:
4、模擬退火算法求解旅行商問題 Python 程序
# 模擬退火算法求解旅行商問題 Python程序 # Program: SimulatedAnnealing_v6.py # Purpose: Simulated annealing algorithm for traveling salesman problem # v1.0: = 關注 Youcans,分享原創系列 https://blog.csdn.net/youcans = # 模擬退火求解旅行商問題(TSP)基本算法 # Copyright 2021 YouCans, XUPT # Crated:2021-05-01# -*- coding: utf-8 -*- import math # 導入模塊 math import random # 導入模塊 random import pandas as pd # 導入模塊 pandas 并簡寫成 pd import numpy as np # 導入模塊 numpy 并簡寫成 np YouCans import matplotlib.pyplot as plt # 導入模塊 matplotlib.pyplot 并簡寫成 pltnp.set_printoptions(precision=4) pd.set_option('display.max_rows', 20) pd.set_option('expand_frame_repr', False) pd.options.display.float_format = '{:,.2f}'.format# 子程序:初始化模擬退火算法的控制參數 def initParameter():# custom function initParameter():# Initial parameter for simulated annealing algorithmtInitial = 100.0 # 設定初始退火溫度(initial temperature)tFinal = 1 # 設定終止退火溫度(stop temperature)nMarkov = 1000 # Markov鏈長度,也即內循環運行次數alfa = 0.98 # 設定降溫參數,T(k)=alfa*T(k-1)return tInitial,tFinal,alfa,nMarkov# 子程序:讀取TSPLib數據 def read_TSPLib(fileName):# custom function read_TSPLib(fileName)# Read datafile *.dat from TSPlib# return coordinates of each city by YouCans, XUPTres = []with open(fileName, 'r') as fid:for item in fid:if len(item.strip())!=0:res.append(item.split())loadData = np.array(res).astype('int') # 數據格式:i Xi Yicoordinates = loadData[:,1::]return coordinates# 子程序:計算各城市間的距離,得到距離矩陣 def getDistMat(nCities, coordinates):# custom function getDistMat(nCities, coordinates):# computer distance between each 2 CitiesdistMat = np.zeros((nCities,nCities)) # 初始化距離矩陣for i in range(nCities):for j in range(i,nCities):# np.linalg.norm 求向量的范數(默認求 二范數),得到 i、j 間的距離distMat[i][j] = distMat[j][i] = round(np.linalg.norm(coordinates[i]-coordinates[j]))return distMat # 城市間距離取整(四舍五入)# 子程序:計算 TSP 路徑長度 def calTourMileage(tourGiven, nCities, distMat):# custom function caltourMileage(nCities, tour, distMat):# to compute mileage of the given tourmileageTour = distMat[tourGiven[nCities-1], tourGiven[0]] # dist((n-1),0)for i in range(nCities-1): # dist(0,1),...dist((n-2)(n-1))mileageTour += distMat[tourGiven[i], tourGiven[i+1]]return round(mileageTour) # 路徑總長度取整(四舍五入)# 子程序:繪制 TSP 路徑圖 def plot_tour(tour, value, coordinates):# custom function plot_tour(tour, nCities, coordinates)num = len(tour)x0, y0 = coordinates[tour[num - 1]]x1, y1 = coordinates[tour[0]]plt.scatter(int(x0), int(y0), s=15, c='r') # 繪制城市坐標點 C(n-1)plt.plot([x1, x0], [y1, y0], c='b') # 繪制旅行路徑 C(n-1)~C(0)for i in range(num - 1):x0, y0 = coordinates[tour[i]]x1, y1 = coordinates[tour[i + 1]]plt.scatter(int(x0), int(y0), s=15, c='r') # 繪制城市坐標點 C(i)plt.plot([x1, x0], [y1, y0], c='b') # 繪制旅行路徑 C(i)~C(i+1)plt.xlabel("Total mileage of the tour:{:.1f}".format(value))plt.title("Optimization tour of TSP{:d}".format(num)) # 設置圖形標題plt.show()# 子程序:交換操作算子 def mutateSwap(tourGiven, nCities):# custom function mutateSwap(nCities, tourNow)# produce a mutation tour with 2-Swap operator# swap the position of two Cities in the given tour# 在 [0,n) 產生 2個不相等的隨機整數 i,ji = np.random.randint(nCities) # 產生第一個 [0,n) 區間內的隨機整數while True:j = np.random.randint(nCities) # 產生一個 [0,n) 區間內的隨機整數if i!=j: break # 保證 i, j 不相等tourSwap = tourGiven.copy() # 將給定路徑復制給新路徑 tourSwaptourSwap[i],tourSwap[j] = tourGiven[j],tourGiven[i] # 交換 城市 i 和 j 的位置————簡潔的實現方法return tourSwapdef main():# 主程序 = 關注 Youcans,分享原創系列 https://blog.csdn.net/youcans =# # 讀取旅行城市位置的坐標coordinates = np.array([[565.0, 575.0], [25.0, 185.0], [345.0, 750.0], [945.0, 685.0], [845.0, 655.0],[880.0, 660.0], [25.0, 230.0], [525.0, 1000.0], [580.0, 1175.0], [650.0, 1130.0],[1605.0, 620.0], [1220.0, 580.0], [1465.0, 200.0], [1530.0, 5.0], [845.0, 680.0],[725.0, 370.0], [145.0, 665.0], [415.0, 635.0], [510.0, 875.0], [560.0, 365.0],[300.0, 465.0], [520.0, 585.0], [480.0, 415.0], [835.0, 625.0], [975.0, 580.0],[1215.0, 245.0], [1320.0, 315.0], [1250.0, 400.0], [660.0, 180.0], [410.0, 250.0],[420.0, 555.0], [575.0, 665.0], [1150.0, 1160.0], [700.0, 580.0], [685.0, 595.0],[685.0, 610.0], [770.0, 610.0], [795.0, 645.0], [720.0, 635.0], [760.0, 650.0],[475.0, 960.0], [95.0, 260.0], [875.0, 920.0], [700.0, 500.0], [555.0, 815.0],[830.0, 485.0], [1170.0, 65.0], [830.0, 610.0], [605.0, 625.0], [595.0, 360.0],[1340.0, 725.0], [1740.0, 245.0]])# fileName = "../data/eil76.dat" # 數據文件的地址和文件名# coordinates = read_TSPLib(fileName) # 調用子程序,讀取城市坐標數據文件# 模擬退火算法參數設置tInitial,tFinal,alfa,nMarkov = initParameter() # 調用子程序,獲得設置參數nCities = coordinates.shape[0] # 根據輸入的城市坐標 獲得城市數量 nCitiesdistMat = getDistMat(nCities, coordinates) # 調用子程序,計算城市間距離矩陣nMarkov = nCities # Markov鏈 的初值設置tNow = tInitial # 初始化 當前溫度(current temperature)# 初始化準備tourNow = np.arange(nCities) # 產生初始路徑,返回一個初值為0、步長為1、長度為n 的排列valueNow = calTourMileage(tourNow,nCities,distMat) # 計算當前路徑的總長度 valueNowtourBest = tourNow.copy() # 初始化最優路徑,復制 tourNowvalueBest = valueNow # 初始化最優路徑的總長度,復制 valueNowrecordBest = [] # 初始化 最優路徑記錄表recordNow = [] # 初始化 最優路徑記錄表# 開始模擬退火優化過程iter = 0 # 外循環迭代次數計數器while tNow >= tFinal: # 外循環,直到當前溫度達到終止溫度時結束# 在當前溫度下,進行充分次數(nMarkov)的狀態轉移以達到熱平衡for k in range(nMarkov): # 內循環,循環次數為Markov鏈長度# 產生新解:tourNew = mutateSwap(tourNow, nCities) # 通過 交換操作 產生新路徑valueNew = calTourMileage(tourNew,nCities,distMat) # 計算當前路徑的總長度deltaE = valueNew - valueNow# 接受判別:按照 Metropolis 準則決定是否接受新解if deltaE < 0: # 更優解:如果新解的目標函數好于當前解,則接受新解accept = Trueif valueNew < valueBest: # 如果新解的目標函數好于最優解,則將新解保存為最優解tourBest[:] = tourNew[:]valueBest = valueNewelse: # 容忍解:如果新解的目標函數比當前解差,則以一定概率接受新解pAccept = math.exp(-deltaE/tNow) # 計算容忍解的狀態遷移概率if pAccept > random.random():accept = Trueelse:accept = False# 保存新解if accept == True: # 如果接受新解,則將新解保存為當前解tourNow[:] = tourNew[:]valueNow = valueNew# 平移當前路徑,以解決變換操作避開 0,(n-1)所帶來的問題,并未實質性改變當前路徑。tourNow = np.roll(tourNow,2) # 循環移位函數,沿指定軸滾動數組元素# 完成當前溫度的搜索,保存數據和輸出recordBest.append(valueBest) # 將本次溫度下的最優路徑長度追加到 最優路徑記錄表recordNow.append(valueNow) # 將當前路徑長度追加到 當前路徑記錄表print('i:{}, t(i):{:.2f}, valueNow:{:.1f}, valueBest:{:.1f}'.format(iter,tNow,valueNow,valueBest))# 緩慢降溫至新的溫度,iter = iter + 1tNow = tNow * alfa # 指數降溫曲線:T(k)=alfa*T(k-1)# 結束模擬退火過程# 圖形化顯示優化結果figure1 = plt.figure() # 創建圖形窗口 1plot_tour(tourBest, valueBest, coordinates)figure2 = plt.figure() # 創建圖形窗口 2plt.title("Optimization result of TSP{:d}".format(nCities)) # 設置圖形標題plt.plot(np.array(recordBest),'b-', label='Best') # 繪制 recordBest曲線plt.plot(np.array(recordNow),'g-', label='Now') # 繪制 recordNow曲線plt.xlabel("iter") # 設置 x軸標注plt.ylabel("mileage of tour") # 設置 y軸標注plt.legend() # 顯示圖例plt.show()print("Tour verification successful!")print("Best tour: \n", tourBest)print("Best value: {:.1f}".format(valueBest))exit()# = 關注 Youcans,分享原創系列 https://blog.csdn.net/youcans = if __name__ == '__main__':main()5、運行結果
程序的運行結果只供參考,顯然這并不是最優結果。
Tour verification successful! Best tour: [18 7 40 2 16 17 31 38 39 36 24 27 26 11 50 3 5 4 23 47 37 14 42 98 32 10 51 13 12 25 46 28 29 1 6 41 20 30 21 0 48 35 34 33 43 45 1549 19 22 44] Best value: 9544.0= 關注 Youcans,分享原創系列 https://blog.csdn.net/youcans =
版權說明:
原創作品
Copyright 2021 YouCans, XUPT
Crated:2021-05-04
關注 Youcans,分享原創系列 https://blog.csdn.net/youcans
Python數模筆記-PuLP庫(1)線性規劃入門
Python數模筆記-PuLP庫(2)線性規劃進階
Python數模筆記-PuLP庫(3)線性規劃實例
Python數模筆記-StatsModels 統計回歸(1)簡介
Python數模筆記-StatsModels 統計回歸(2)線性回歸
Python數模筆記-StatsModels 統計回歸(3)模型數據的準備
Python數模筆記-StatsModels 統計回歸(4)可視化
Python數模筆記-Sklearn (1)介紹
Python數模筆記-Sklearn (2)聚類分析
Python數模筆記-Sklearn (3)主成分分析
Python數模筆記-Sklearn (4)線性回歸
Python數模筆記-Sklearn (5)支持向量機
Python數模筆記-模擬退火算法(1)多變量函數優化
Python數模筆記-模擬退火算法(2)約束條件的處理
Python數模筆記-模擬退火算法(3)整數規劃問題
Python數模筆記-模擬退火算法(4)旅行商問題
總結
以上是生活随笔為你收集整理的Python数模笔记-模拟退火算法(4)旅行商问题的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Python数模笔记-模拟退火算法(3)
- 下一篇: 稀疏自编码器一览表