旅行商问题(TSP)作为世界上著名的NP难题之一,仍然吸引着大批学者的研究。解决该问题的算法也种类繁多,一些启发式、半启发式算法在该问题上广为应用,包括像遗传算法、模拟退火、蚁群算法、粒子群优化算法等解法也颇为常见。
一、旅行商问题的数学模型
旅行商问题 (简称TSP)是运筹学中一个著名的问题, 其一般提法为:
有一个旅行商从城市 1 出发, 需要到 城市2、3、...、n去推销货物, 最后返回城市 1 , 若任意两个城市间的距离已知, 则该旅行商应如何选 择其最佳行走路线?
TSP在图论意义下又常常被称为最小Hamilton圈问题, Euler等人最早研究了该问题的雏形, 后来由英国的 Hamilton爵士作为一个悬赏问题而提出。TSP有着明显的实际意义, 如邮局里负责到各信箱开箱取信的邮递员, 以及去各分局送邮件的汽车等, 都会遇到类似的问题。有趣的是, 还有一些问题表面上看似乎与TSP无关, 而实质上却可以归结为TSP来求解。
若各顶点间的距离为 \(d_{ij}\) 已知。设
\[x_{ij}= \begin{cases}1, & \text { 若 }(i, j) \text { 在回路路径上} \\ 0, & \text { 其他 }\end{cases} \]则经典的TSP可写为如下的 数学规划模型:
\[\begin{aligned} & \min Z=\sum_{i=1}^n \sum_{j=1}^n d_{i j} x_{i j} \\ & \text { s.t }\left\{\begin{array}{lr} \sum_{j=1}^n x_{i j}=1, & i \in V \\ \sum_{i=1}^n x_{i j}=1, & j \in V \\ \sum_{j=s} \sum_{j \in s} x_{i j} \leq|s|-1, & \forall S \subset V, 2 \leq|s| \leq n-1 \\ x_{i j} \in\{0,1\} & \end{array}\right. \end{aligned} \]模型中,\(|V|\) 为集合中所含图的顶点数。前两个意味着对每个点而言,仅有一条边进和一条边出;第三个约束则保证了没有任何子回路解的产生。当 \(d_{ij}=d_{ji}(i,j \in V)\) 时,问题被称为对称型 TSP, 否则称为非对称型 TSP。若对所有 \(1 \leq i,j,k \leq n\), 有不等式 \(d_{i j}+d_{j k} \geq d_{i k}\) 成 立, 则问题被称为是满足三角形不等式的,简称为 \(\Delta\) TSP。
二、TSP问题的R求解
R语言中有专门处理TSP问题的包TSP,可直接调用其核心函数solve_TSP( )进行求解。其使用格式如下:
sovle_TSP(x,method,control)
#x为处理TSP问题的数据格式,method为所采用的求解方法,这里内置了十多种启发式、半启发式算法可供选择。
假设现要求一个从北京出发游遍全国34个省级行政中心的最少路线规划问题,其经纬度见下表,我们用R中的TSP包对该问题求解。
area | 沈阳 | 长春 | 哈尔滨 | 北京 | 天津 | 呼和浩特 | 银川 | 太原 | 石家庄 | 济南 | 郑州 | 西安 |
---|---|---|---|---|---|---|---|---|---|---|---|---|
longitude | 123.429092 | 125.324501 | 126.642464 | 116.405289 | 117.190186 | 111.75199 | 106.23248 | 112.549248 | 114.502464 | 117.000923 | 113.665413 | 108.948021 |
latitude | 41.796768 | 43.886841 | 45.756966 | 39.904987 | 39.125595 | 40.84149 | 38.48644 | 37.857014 | 38.045475 | 36.675808 | 34.757977 | 34.263161 |
area | 武汉 | 南京 | 合肥 | 上海 | 长沙 | 南昌 | 杭州 | 福州 | 广州 | 台北 | 海口 | 南宁 |
longitude | 114.298569 | 118.76741 | 117.283043 | 121.472641 | 112.982277 | 115.892151 | 120.15358 | 119.306236 | 113.28064 | 121.520076 | 110.19989 | 108.320007 |
latitude | 30.584354 | 32.041546 | 31.861191 | 31.231707 | 28.19409 | 28.676493 | 30.287458 | 26.075302 | 23.125177 | 25.030724 | 20.04422 | 22.82402 |
area | 重庆 | 昆明 | 贵阳 | 成都 | 兰州 | 西宁 | 拉萨 | 乌鲁木齐 | 香港 | 澳门 | ||
longitude | 106.504959 | 102.71225 | 106.713478 | 104.065735 | 103.83417 | 101.77782 | 91.1145 | 87.61688 | 114.16546 | 113.54913 | ||
latitude | 29.533155 | 25.040609 | 26.578342 | 30.659462 | 36.06138 | 36.61729 | 29.64415 | 43.82663 | 22.27534 | 22.19875 |
#加载所需要的包
library(TSP)
library(maps)
library(maptools)
library(openxlsx)
library(mapdata)
#读取34个省级中心的经纬度数据
pr<-read.xlsx("city.xlsx")
#计算两点之间的球面距离
f_dis<-function(x,y){
r=6371
x=x*pi/180;y=y*pi/180
a=c(cos(x[2])*cos(x[1]),cos(x[2])*sin(x[1]),sin(x[2]))
b=c(cos(y[2])*cos(y[1]),cos(y[2])*sin(y[1]),sin(y[2]))
cosg=sum(a*b)/sqrt(sum(a^2)*sum(b^2))
dis=r*acos(cosg)
}
k<-cbind(pr$longitude,pr$latitude)
#计算34个城市的两两距离
dis_mat<-matrix(NA,34,34)
for (i in 1:34){
for(j in 1:34){
dis_mat[i,j]=f_dis(k[i,],k[j,])
}
}
colnames(dis_mat)<-rownames(dis_mat)<-pr[,1]
#TSP问题求解
tsp<-TSP(dis_mat)
tour<-solve_TSP(tsp,method="2-opt")
#数字路线
path<-as.integer(tour)
#总长度
tour_length(tsp,tour)
#在地图上标识
map("china", col = "red4", ylim = c(18, 54), panel.first = grid())
map.axes()
title('中国城市')
attach(tsp_map)
points(c(longitude,longitude[1]),c(latitude,latitude[1]),
col=2,pch=20,type="o")
pointLabel(longitude,latitude,area,
col="blue",cex=0.74)
detach(tsp_map)
在多次运行上述代码后,我们可以从众多解中选取一个最优解为 16796.48千米,最佳路线为( "北京" "哈尔滨" "长春" "沈阳" "天津" "济南" "郑州" "西安" "太原" "石家庄" "呼和浩特" "银川" "兰州" "西宁" "乌鲁木齐" "拉萨" "成都" "重庆" "贵阳" "昆明" "南宁" "海口" "澳门" "香港" "广州" "长沙" "南昌" "武汉" "合肥" "南京" "上海" "杭州" "福州" "台北" )。利用maps和maptool包可将路线展现在地图上: