我们在模拟退火(SA)算法求解旅行商(TSP)问题这篇推文讲解了SA求TSP问题的大致思路,今天为各位详细讲解一下这篇推文中的MATLAB代码(PS:我目前使用的是MATLAB R2019b)。公众号后台回复 SATSP 即可提取代码。
01 | SA_TSP主函数
%% @作者:随心390% @微信公众号:优化算法交流地%clc;clear;close all;%% 输入数据x=[38.24,39.57,40.56,36.26,33.48,37.56,38.42,37.52,41.23,41.17,36.08,38.47,38.15,37.51,35.49,39.36,38.09,36.09,40.44,40.33,40.37,37.57];y=[20.42,26.15,25.32,23.12,10.54,12.19,13.11,20.44,9.100,13.05,-5.210,15.13,15.35,15.17,14.32,19.56,24.36,23,13.57,14.15,14.23,22.56];vertexs=[x;y]';n=length(x); %城市数目h=pdist(vertexs);dist=squareform(h); %距离矩阵%% 参数MaxOutIter=300; %外层循环最大迭代次数MaxInIter=15; %里层循环最大迭代次数T0=0.025; %初始温度alpha=0.99; %冷却因子pSwap=0.2; %选择交换结构的概率pReversion=0.5; %选择逆转结构的概率pInsertion=1-pSwap-pReversion; %选择插入结构的概率%% 初始化currRoute=randperm(n); %随机构造初始解currL=RouteLength(currRoute,dist); %初始解总距离bestRoute=currRoute; %初始将初始解赋值给全局最优解bestL=currL; %初始将初始解总距离赋值给全局最优解总距离BestLOutIter=zeros(MaxOutIter,1); %记录外层循环的每一代最优解的总距离T=T0; %温度初始化%% 模拟退火for outIter=1:MaxOutIter for inIter=1:MaxInIter newRoute=Neighbor(currRoute,pSwap,pReversion,pInsertion); %经过邻域结构后产生的新的路线 newL=RouteLength(newRoute,dist); %该路线的总距离 %如果新路线比当前路线更好,则更新当前路线,以及当前路线总距离 if newL<=currL currRoute=newRoute; currL=newL; else %如果新路线不如当前路线好,则采用退火准则,以一定概率接受新路线 delta=(newL-currL)/currL; %计算新路线与当前路线总距离相差的百分比 P=exp(-delta/T); %计算接受新路线的概率 %如果0~1的随机数小于P,则接受新路线,并更新当前路线,以及当前路线总距离 if rand<=P currRoute=newRoute; currL=newL; end end %将当前路线与全局最优路线进行比较,如果当前路线更好,则更新全局最优路线,以及全局最优路线总距离 if currL<=bestL bestRoute=currRoute; bestL=currL; end end %记录外层循环每次迭代的全局最优路线的总距离 BestLOutIter(outIter)=bestL; %显示外层循环每次迭代的信全局最优路线的总距离 disp(['第' num2str(outIter) '次迭代:全局最优路线总距离 = ' num2str(BestLOutIter(outIter))]); %更新当前温度 T=alpha*T; %画出外层循环每次迭代的全局最优路线图 figure(1); PlotRoute(bestRoute,x,y) pause(0.01);end%% 打印外层循环每次迭代的全局最优路线的总距离变化趋势图figure;plot(BestLOutIter,'LineWidth',1);xlabel('迭代次数');ylabel('距离');
02 | RouteLength函数RouteLength函数是求一条路线的总距离。
%% @作者:随心390% @微信公众号:优化算法交流地%%输入route: 路线%输入dist: 距离矩阵%输出L: 该路线总距离function L=RouteLength(route,dist) n=length(route); route=[route route(1)]; L=0; for k=1:n i=route(k); j=route(k+1); L=L+dist(i,j); endend03 | Neighbor函数
Neighbor函数是求出路线1经过邻域结构后的路线2。
%% @作者:随心390% @微信公众号:优化算法交流地%%输入route1:路线1%输入pSwap:选择交换结构的概率%输入pReversion:选择逆转结构的概率%输入pInsertion:选择插入结构的概率%输出route2:经过邻域变换后的路线2function route2=Neighbor(route1,pSwap,pReversion,pInsertion)index=Roulette(pSwap,pReversion,pInsertion);if index==1 %交换结构 route2=Swap(route1);elseif index==2 %逆转结构 route2=Reversion(route1);else %插入结构 route2=Insertion(route1);endend04 | Roulette函数
Roulette函数是轮盘赌选择,输出结果为序号。
%% @作者:随心390% @微信公众号:优化算法交流地%%输入pSwap:选择交换结构的概率%输入pReversion:选择逆转结构的概率%输入pInsertion:选择插入结构的概率%输出index:最终选择哪一个邻域结构,即序号:1 2 3function index=Roulette(pSwap,pReversion,pInsertion)p=[pSwap pReversion pInsertion];r=rand;c=cumsum(p);index=find(r<=c,1,'first');end05 | Swap函数
06 | Reversion函数%
% @作者:随心390
% @微信公众号:优化算法交流地
%
%比如说有6个城市,当前解为123456,我们随机选择两个位置,然后将这两个位置上的元素进行交换。
%比如说,交换2和5两个位置上的元素,则交换后的解为153426。
%输入route1:路线1
%输出route2:经过交换结构变换后的路线2
function route2=Swap(route1)
n=length(route1);
seq=randperm(n);
I=seq(1:2);
i1=I(1);
i2=I(2);
route2=route1;
route2([i1 i2])=route1([i2 i1]);
end
07 | Insertion函数%
% @作者:随心390
% @微信公众号:优化算法交流地
%
%有6个城市,当前解为123456,我们随机选择两个位置,然后将这两个位置之间的元素进行逆序排列。
%比如说,逆转2和5之间的所有元素,则逆转后的解为154326。
%输入route1:路线1
%输出route2:经过逆转结构变换后的路线2
function route2=Reversion(route1)
n=length(route1);
seq=randperm(n);
I=seq(1:2);
i1=min(I);
i2=max(I);
route2=route1;
route2(i1:i2)=route1(i2:-1:i1);
end
08 | PlotRoute函数%
% @作者:随心390
% @微信公众号:优化算法交流地
%
%有6个城市,当前解为123456,我们随机选择两个位置,然后将这第一个位置上的元素插入到第二个元素后面。
%比如说,第一个选择5这个位置,第二个选择2这个位置,则插入后的解为125346。
%输入route1:路线1
%输出route2:经过插入结构变换后的路线2
function route2=Insertion(route1)
n=length(route1);
seq=randperm(n);
I=seq(1:2);
i1=I(1);
i2=I(2);
if i1<i2
route2=route1([1:i1-1 i1+1:i2 i1 i2+1:end]);
else
route2=route1([1:i2 i1 i2+1:i1-1 i1+1:end]);
end
end
%% @作者:随心390% @微信公众号:优化算法交流地%%输入route:路线%输入x,y:x,y坐标function PlotRoute(route,x,y)route=[route route(1)];plot(x(route),y(route),'k-o','MarkerSize',10,'MarkerFaceColor','w','LineWidth',1.5);xlabel('x');ylabel('y');end