1 理论基础
1.1 模拟退火算法基本原理
模拟退火(simulated annealing,SA)算法的思想最早是由Metropolis等提出的。其出发点是基于物理中固体物质的退火过程与一般的组合优化问题之间的相似性。模拟退火法是一种通用的优化算法,其物理退火过程由以下三部分组成:
(1)加温过程。其目的是增强粒子的热运动,使其偏离平衡位置。当温度足够高时,固体将熔为液体,从而消除系统原先存在的非均匀状态。
(2)等温过程。对于与周围环境交换热量而温度不变的封闭系统,系统状态的自发变化总是朝自由能减少的方向进行的,当自由能达到最小时,系统达到平衡状态。
(3)冷却过程。使粒子热运动减弱,系统能量下降,得到晶体结构。
其中,加温过程对应算法的设定初温,等温过程对应算法的Metropolis抽样过程,冷却过程对应控制参数的下降。这里能量的变化就是目标函数,要得到的最优解就是能量最低态。Metropolis准则是SA算法收敛于全局最优解的关键所在,Metropolis准则以一定的概率接受恶化解,这样就使算法跳离局部最优的陷阱。
模拟退火算法为求解传统方法难处理的TSP问题提供了一个有效的途径和通用框架,并逐渐发展成一种迭代自适应启发式概率性搜索算法。模拟退火算法可以用以求解不同的非线性问题,对不可微甚至不连续的函数优化,能以较大概率求得全局优化解,该算法还具有较强的鲁棒性、全局收敛性、隐含并行性及广泛的适应性,并且能处理不同类型的优化设计变量(离散的、连续的和混合型的),不需要任何的辅助信息,对目标函数和约束函数没有任何要求。利用Metropolis算法并适当地控制温度下降过程,在优化问题中具有很强的竞争力,本章研究基于模拟退火算法的TSP算法。
SA算法实现过程如下(以最小化问题为例):
1.2 TSP问题介绍
2 案例背景
2.1 问题描述
本案例以14个城市为例,假定14个城市的位置坐标如表1所列。寻找出一条最短的遍历14个城市的路径。
表1 各个城市的坐标
2.2 解题思路及步骤
1.算法流程
模拟退火算法求解TSP问题流程图如图1所示。
2.模拟退火算法实现
(1)控制参数的设置
需要设置的主要控制参数有降温速率q、初始温度T0、结束温度Tend以及链长L。
(2)初始解
对于n个城市的TSP问题,得到的解就是对1~n的一个排序,其中每个数字为对应城市的编号,如对10个城市的TSP问题{1,2,3,4,5,6,7,8,9,10},则|1|10|2|4|5|6|8|7|9|3就是一个合法的解,采用产生随机排列的方法产生一个初始解S。
(3)解变换生成新解
通过对当前解S1进行变换,产生新的路径数组即新解,这里采用的变换是产生随机数的方法来产生将要交换的两个城市,用二邻域变换法产生新的路径,即新的可行解S2。例如n=10时,产生两个[1,10]范围内的随机整数r1和r2,确定两个位置,将其对换位置,如r1=4,r2=7
(4)Metropolis准则
若路径长度函数为f(S),则当前解的路径为f(S1),新解的路径为f(S2),路径差为df=f(S2)-f(S1),则Metropolis准则为
(5)降 温
利用降温速率q进行降温,即T=qT,若T小于结束温度,则停止迭代输出当前状态,否则继续迭代。
3 MATLAB程序实现
3.1 计算距离矩阵
利用给出的N个城市的坐标,算出N个城市的两两之间的距离,得到距离矩阵(N×N)。计算函数为Distance,得到初始种群。
function D=Distanse(a)
%% 计算两两城市之间的距离
%输入 a 各城市的位置坐标
%输出 D 两两城市之间的距离
row=size(a,1);
D=zeros(row,row);
for i=1:rowfor j=i+1:rowD(i,j)=((a(i,1)-a(j,1))^2+(a(i,2)-a(j,2))^2)^0.5;D(j,i)=D(i,j);end
end
3.2 画路线轨迹图
画出给的路线的轨迹图函数为DrawPath,程序代码如下:
function DrawPath(Chrom,X)
%% 画路径函数
%输入
% Chrom 待画路径
% X 各城市坐标位置
R=[Chrom(1,:) Chrom(1,1)]; %一个随机解(个体)
figure;
hold on
plot(X(:,1),X(:,2),'o','color',[0.5,0.5,0.5])
plot(X(Chrom(1,1),1),X(Chrom(1,1),2),'rv','MarkerSize',20)
for i=1:size(X,1)text(X(i,1)+0.05,X(i,2)+0.05,num2str(i),'color',[1,0,0]);
end
A=X(R,:);
row=size(A,1);
for i=2:row[arrowx,arrowy] = dsxy2figxy(gca,A(i-1:i,1),A(i-1:i,2));%坐标转换annotation('textarrow',arrowx,arrowy,'HeadWidth',8,'color',[0,0,1]);
end
hold off
xlabel('横坐标')
ylabel('纵坐标')
title('轨迹图')
box on
3.3 输出路径函数
将得到的路径输出显示在Command Window中,函数名为OutputPath。
function p=OutputPath(R)
%% 输出路径函数
%输入:R 路径
R=[R,R(1)];
N=length(R);
p=num2str(R(1));
for i=2:Np=[p,'—>',num2str(R(i))];
end
disp(p)
3.4 可行解路线长度函数
计算可行解的路线长度函数为PathLength,程序代码如下:
function len=PathLength(D,Chrom)
%% 计算各个体的路径长度
% 输入:
% D 两两城市之间的距离
% Chrom 个体的轨迹
[row,col]=size(D);
NIND=size(Chrom,1);
len=zeros(NIND,1);
for i=1:NINDp=[Chrom(i,:) Chrom(i,1)];i1=p(1:end-1);i2=p(2:end);len(i,1)=sum(D((i1-1)*col+i2));
end
3.5 生成新解
解变换生成新解函数为NewAnswer,程序代码如下:
function S2=NewAnswer(S1)
%% 输入
% S1:当前解
%% 输出
% S2:新解
N=length(S1);
S2=S1;
a=round(rand(1,2)*(N-1)+1); %产生两个随机位置 用来交换
W=S2(a(1));
S2(a(1))=S2(a(2));
S2(a(2))=W; %得到一个新路线
3.6Metropolis准则函数
Metropolis准则函数为Metropolis,程序代码如下:
function [S,R]=Metropolis(S1,S2,D,T)
%% 输入
% S1: 当前解
% S2: 新解
% D: 距离矩阵(两两城市的之间的距离)
% T: 当前温度
%% 输出
% S: 下一个当前解
% R: 下一个当前解的路线距离
%%
R1=PathLength(D,S1); %计算路线长度
N=length(S1); %得到城市的个数R2=PathLength(D,S2); %计算路线长度
dC=R2-R1; %计算能力之差
if dC<0 %如果能力降低 接受新路线S=S2;R=R2;
elseif exp(-dC/T)>=rand %以exp(-dC/T)概率接受新路线S=S2;R=R2;
else %不接受新路线S=S1;R=R1;
end
3.7 主函数
模拟退火算法参数设置如表1所列。
主函数代码如下:
clc;
clear;
close all;
%%
tic
T0=1000; % 初始温度
Tend=1e-3; % 终止温度
L=500; % 各温度下的迭代次数(链长)
q=0.9; %降温速率%% 加载数据
load CityPosition1;
%%
D=Distanse(X); %计算距离矩阵
N=size(D,1); %城市的个数
%% 初始解
S1=randperm(N); %随机产生一个初始路线%% 画出随机解的路径图
DrawPath(S1,X)
pause(0.0001)
%% 输出随机解的路径和总距离
disp('初始种群中的一个随机值:')
OutputPath(S1);
Rlength=PathLength(D,S1);
disp(['总距离:',num2str(Rlength)]);%% 计算迭代的次数Time
Time=ceil(double(solve(['1000*(0.9)^x=',num2str(Tend)])));
count=0; %迭代计数
Obj=zeros(Time,1); %目标值矩阵初始化
track=zeros(Time,N); %每代的最优路线矩阵初始化
%% 迭代
while T0>Tendcount=count+1; %更新迭代次数temp=zeros(L,N+1);for k=1:L%% 产生新解S2=NewAnswer(S1);%% Metropolis法则判断是否接受新解[S1,R]=Metropolis(S1,S2,D,T0); %Metropolis 抽样算法temp(k,:)=[S1 R]; %记录下一路线的及其路程end%% 记录每次迭代过程的最优路线[d0,index]=min(temp(:,end)); %找出当前温度下最优路线if count==1 || d0<Obj(count-1)Obj(count)=d0; %如果当前温度下最优路程小于上一路程则记录当前路程elseObj(count)=Obj(count-1);%如果当前温度下最优路程大于上一路程则记录上一路程endtrack(count,:)=temp(index,1:end-1); %记录当前温度的最优路线T0=q*T0; %降温fprintf(1,'%d\n',count) %输出当前迭代次数
end
%% 优化过程迭代图
figure
plot(1:count,Obj)
xlabel('迭代次数')
ylabel('距离')
title('优化过程')%% 最优解的路径图
DrawPath(track(end,:),X)%% 输出最优解的路线和总距离
disp('最优解:')
S=track(end,:);
p=OutputPath(S);
disp(['总距离:',num2str(PathLength(D,S))]);
disp('-------------------------------------------------------------')
toc
4 结果分析
优化前的一个随机路线轨迹图如图2所示。
随机路线为6—>3—>2—>11—>14—>7—>4—>8—>13—>12—>10—>5—>9—>1—>6。
总距离为71.4209.
优化后的路线如图3所示。
最优解为:
9—>11—>1—>8—>13—>7—>12—>6—>5—>4—>3—>14—>2—>10—>9
总距离:29.6889
优化迭代过程如图4所示。
由图4可以看出,优化前后路径长度得到很大改进,80代以后路径长度已经保持不变了,可以认为已经是最优 解了。 上面的程序中城市数只有14个,对于更多的城市,坐标随意的城市也是可以计算的,例如N=50,坐标X使用随机数产生:
X=rand(N,2)*10;
这时调整对应的参数,如表2所列。
即可得到如下结果:优化前的轨迹如图5所示,优化后的轨迹如图6所示,优化迭代过程如图7所示。
初始种群中的个随机解:
22—>31—>50—>37—>11—>3—>23—>46—>19—>28—>48—>1—>47—>12—>45—>42—>40—>39—>33—>34—>18—>41—>25—>13—>49—>27—>36—>29—>16—>6—>2—>14—>21—>5—>24—>32—>38—>43—>7—>4—>26—>8—>9—>20—>44—>35—>15—>30—>17—>10—>22
总距离:271.7719
最优解:
33—>45—>13—>50—>20—>26—>25—>15—>2—>17—>18—>8—>41—>3—>6—>34—>38—>47—>14—>42—>44—>7—>36—>22—>39—>4—>37—>46—>1—>5—>30—>43—>27—>31—>32—>21—>9—>11—>29—>23—>40—>49—>28—>19—>48—>12—>35—>16—>24—>10—>33
总距离:71.3071
5 延伸阅读
5.1 模拟退火算法的改进
上述程序是比较经典的模拟退火解决TSP问题的算法,程序可以做进一步的改进,在解变换产生新解的过程中只使用了交换两个城市的方法,可以采用下面的方法进行改进,例如:
(1)使用逆转操作,即选择两个城市后,逆转这两个城市之间的所有城市。
(2)选择3个城市,将两个城市之间的城市插入到第3个城市的后面(这两个城市之间不包括第3个城市),
5.2 算法的局限性
问题规模n比较小时,得到的一般都是最优解,当规模比较大时,一般只能得到的近似解。这时可以通过增大种群大小和增加最大遗传代数使优化值更接近最优解。