蒙特卡洛模拟(6)————旅行商问题

旅行商问题(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个,还用此方法就很难实现了。因为可能情况太多,即使通过模拟也难以找到最优的情况。
当遇到数量级过多的情况,需要启发式算法来解决。当然,另一种思路,也可以通过聚类分析,把距离相近的城市聚类为一类,然后再进行模拟,这种方法不如启发式算法

posted @ 2024-08-07 18:52  卢宇博  阅读(49)  评论(0编辑  收藏  举报