旅行商问题(TravelingSalesmanProblem,TSP)是一个经典的组合优化问题。经典的TSP可以描述为:一个商品推销员要去若干个城市推销商品,该推销员从一个城市出发,需要经过所有城市后,回到出发地。应如何选择行进路线,以使总的行程最短。
一、问题提出
二、代码预备
1.plot([a,b],[c,d])
plot([1,2],[5,10],'-o')
画出一条线段,x范围是[1, 2] ,y范围是[5,10]。这个命令用来实现坐标之间的连接
2.randperm(n)
randperm(5)
生成由1-5组成的一个随机序列(类似于洗牌的操作)
ans= 3 5 1 2 4
三、代码实现
1.输入城市坐标数据
下方是只有是个数据的简单情况:第一行是所有数据的横坐标,第二行则是纵坐标。
经过转置之后,就变成了横轴是城市编号,纵轴的第一列是城市横坐标,第二列则是纵坐标。
这样就可以用n来提取行数,得到城市的数目
coord =[0.6683 0.6195 0.4 0.2439 0.1707 0.2293 0.5171 0.8732 0.6878 0.8488 ;
0.2536 0.2634 0.4439 0.1463 0.2293 0.761 0.9414 0.6536 0.5219 0.3609]' ;
n = size(coord,1); % 城市的数目
2.画出城市的分布散点图
利用循环和num2str命令给城市标号
figure(1) % 新建一个编号为1的图形窗口
plot(coord(:,1),coord(:,2),'o'); % 画出城市的分布散点图
for i = 1:n
text(coord(i,1)+0.01,coord(i,2)+0.01,num2str(i)) % 在图上标上城市的编号(加上0.01表示把文字的标记往右上方偏移一点)
end
hold on % 等一下要接着在这个图形上画图的
3.得到城市距离矩阵
这个方法非常巧妙,如果遍历计算得到距离矩阵,是需要把每个城市之间的距离算两遍的,而经过下面的循环则避免了算两遍导致计算速度下降的情况。
首先行i从2开始,列j则从1到i。这样则避免了重复计算,最后算出来的是一个下三角矩阵。由于对角线上的元素均为0,通过转置则得到了一个上三角矩阵,将其相加就得到了完整的距离矩阵
d = zeros(n); % 初始化两个城市的距离矩阵全为0
for i = 2:n
for j = 1:i
coord_i = coord(i,:); x_i = coord_i(1); y_i = coord_i(2); % 城市i的横坐标为x_i,纵坐标为y_i
coord_j = coord(j,:); x_j = coord_j(1); y_j = coord_j(2); % 城市j的横坐标为x_j,纵坐标为y_j
d(i,j) = sqrt((x_i-x_j)^2 + (y_i-y_j)^2); % 计算城市i和j的距离
end
end
d = d+d';
4.变量初始化
min_result = +inf; % 假设最短的距离为min_result,初始化为无穷大,后面只要找到比它小的就对其更新
min_path = [1:n]; % 初始化最短的路径就是1-2-3-...-n
N = 10000000; % 蒙特卡罗模拟的次数
5.进入循环,开始模拟
代码思想是蒙特卡洛模拟的常用思想,生成一个随机序列后,提取序列的索引进行数值相加得到总距离,最后进入判断,更新最优方案
for k = 1:N % 开始循环
result = 0; % 初始化走过的路程为0
path = randperm(n); % 生成一个1-n的随机打乱的序列
for i = 1:n-1
result = d(path(i),path(i+1)) + result; % 按照这个序列不断的更新走过的路程这个值
end
result = d(path(1),path(n)) + result; % 别忘了加上从最后一个城市返回到最开始那个城市的距离
if result < min_result % 判断这次模拟走过的距离是否小于最短的距离,如果小于就更新最短距离和最短的路径
min_path = path;
min_result = result
end
end
6.得到结果
此处就用到了最初预备知识中,绘制坐标的命令
min_path
min_path = [min_path,min_path(1)]; % 在最短路径的最后面加上一个元素,即第一个点(我们要生成一个封闭的图形)
n = n+1; % 城市的个数加一个(紧随着上一步)
for i = 1:n-1
j = i+1;
coord_i = coord(min_path(i),:); x_i = coord_i(1); y_i = coord_i(2);
coord_j = coord(min_path(j),:); x_j = coord_j(1); y_j = coord_j(2);
plot([x_i,x_j],[y_i,y_j],'-') % 每两个点就作出一条线段,直到所有的城市都走完
pause(0.5) % 暂停0.5s再画下一条线段
hold on
end
五、模型优化
蒙特卡罗模拟是通过生成随机数来代替遍历计算的方法,但如果数量级过大,蒙特卡洛模拟也难以找到较好的解。本实验的城市数目只有10个,但其实换成30个,还用此方法就很难实现了。因为可能情况太多,即使通过模拟也难以找到最优的情况。
当遇到数量级过多的情况,需要启发式算法来解决。当然,另一种思路,也可以通过聚类分析,把距离相近的城市聚类为一类,然后再进行模拟,这种方法不如启发式算法