遗传算法解决旅行商(TSP)问题
声明:
1.本文源代码来自书目《智能优化算法及其MATLAB实例(第3版)》,目的在于为MATLAB初学者提供更简明的代码解析,方便读者了解算法及MATLAB编程基本原理。 2.文中代码每一行后都有相应注释,因此本文是一篇适合所有代码水平的学习者阅读的文章。如果觉得有帮助,麻烦点个赞哦!
例2.3 旅行商问题(TSP)。假设有一个旅行商人要拜访去全国31个省会城市,他需要选择所要走的路径,路径的限制是每个城市只能拜访一次,而且最后要回到原来出发的城市。对路径选择的要求是:所选路径为所有路径中最小值。
全国31个省会城市的坐标为[1304 2312;3639 1315;4177 2244;3712 1399;3488 1535;3326 1556;3238 1229;4196 1044;4312 790;4386 570;3007 1970;2562 1756;
2788 1491;2381 1676;1332 695;3715 1678;3918 2179;4061 2370;3780 2212;3676 2578;4029 2838;4263 2931;3429 1908;3507 2376;3394 2643;3439 3201;2935 3240;3140 3550;2545 2357;2778 2826;2370 2975]。
用MATLAB和遗传算法实现如下:
%%%%%%%%%遗传算法解决TSP%%%%%%%%%%%
clear all; %清除所有变量
close all; %清图
clc; %清屏
C=[1304 2312;3639 1315;4177 2244;3712 1399;3488 1535;3326 1556;...
3238 1229;4196 1044;4312 790;4386 570;3007 1970;2562 1756;...
2788 1491;2381 1676;1332 695;3715 1678;3918 2179;4061 2370;...
3780 2212;3676 2578;4029 2838;4263 2931;3429 1908;3507 2376;...
3394 2643;3439 3201;2935 3240;3140 3550;2545 2357;2778 2826;...
2370 2975]; %31个省会城市坐标
N=size(C,1); %定义TSP规模,即省会城市数目,size(C,1)表示返回C第一个维度的长度
D=zeros(N); %定义任意两个城市距离间隔矩阵并初始化
%%%%%%%%%求任意两个城市距离间隔矩阵%%%%%%
for i=1:N
for j=1:N
D(i,j)=((C(i,1)-C(j,1))^2+(C(i,2)-C(j,2))^2)^0.5; %套用平面坐标距离公式
end
end
NP=400; %种群规模
G=2000; %最大遗传代数
f=zeros(NP,N); %定义用于储存种群的矩阵
F=[]; %定义中间矩阵变量
for i=1:NP
f(i,:)=randperm(N);%随机生成初始种群,randperm返回不重复的从1到N整数序列
end
R=f(1,:); %定义存储最优方案染色体的数组并初始化
len=zeros(NP,1);%定义该NP行一列的数组存储每个方案的路径长度
fitness=zeros(NP,1);%定义NP行一列数组存储归一化后的适应度值(即路径长度)
gen=0; %当前遗传代数
%%%%%%%%%%%%遗传算法循环%%%%%%%%%%%%%%%
while gen<G
%%%%%%%%%%计算路径长度%%%%%%%%%%%%%
for i=1:NP
len(i,1)=D(f(i,N),f(i,1)); %初始化长度为第1个到第N个城市直线距离
for j=1:(N-1)
len(i,1)=len(i,1)+D(f(i,j),f(i,j+1));%累加1至N距离到总长度中
end
end
maxlen=max(len);%最长路径
minlen=min(len);%最短路径
%%%%%%%%%更新最短路径%%%%%%%%%%%%%%
rr=find(len==minlen);%保存所有路径长度为minlen的方案并返回索引值
R=f(rr(1,1),:); %更新具体的最优方案
%%%%%%%%计算归一化适应度值%%%%%%%%%
for i=1:length(len) %对种群中每个方案染色体
fitness(i,1)=(1-((len(i,1)-minlen)/(maxlen-minlen+0.001)));%归一化适应度值
end
%%%%%%%%选择操作%%%%%%%%%%%%%%%%%%
nn=0;
for i=1:NP %对每个方案染色体
if fitness(i,1)>=rand %若归一化适应度值比产生的随机数大
nn=nn+1; %则该方案被选择
F(nn,:)=f(i,:); %存放被选择的所有方案
end
end
[aa,bb]=size(F); %aa,bb分别表示F的第一维和第二维长度
while aa<NP %若最后被选择的方案染色体数小于原种群数
%设置这个循环来使得被选择的父代与适量交叉的子代相加达到原种群规模
%%%%%%%%交叉操作(没有交叉概率Pc,但是同样进行随机操作)%%%%%%%%
nnper=randperm(nn);%产生1至aa的随机不重复数序列
A=F(nnper(1),:); %定义待交叉个体A并初始化
B=F(nnper(2),:); %定义待交叉个体B并初始化
W=ceil(N/10); %定义每次发生交叉的基因个数为总基因数/10向上取整
p=unidrnd(N-W+1); %随机选择交叉范围,从p到p+W
for i=1:W %确保交叉之后每个方案中不会重复经过同一个城市的方法
x=find(A==B(p+i-1)); %返回A中等于B待交叉位置对应的城市坐标的索引
y=find(B==A(p+i-1)); %返回B中等于A待交叉位置对应的城市坐标的索引
temp=A(p+i-1);
A(p+i-1)=B(p+i-1);
B(p+i-1)=temp; %上三步交换了待交叉位置的基因
temp=A(x);
A(x)=B(y);
B(y)=temp; %这三步交换了A、B中曾提及的重复基因,保证了子代每个方案染色体上无重复基因
end
%%%%%%%%%%变异操作%%%%%%%%%%%%%%
p1=floor(1+N*rand()); %产生向下取整方式的随机整数p1
p2=floor(1+N*rand()); %产生向下取整方式的随机整数p2
while p1==p2 %防止两个随机产生的整数变异位相同
p1=floor(1+N*rand());
p2=floor(1+N*rand());
end
tmp=A(p1);
A(p1)=A(p2);
A(p2)=tmp; %交换两个位置基因完成了子代A的变异
tmp=B(p1);
B(p1)=B(p2);
B(p2)=tmp; %交换两个位置基因完成了子代B的变异
F=[F;A;B]; %合并被选择出的nn个(第一次循环时为nn)父代和2个子代
[aa,bb]=size(F);
end
if aa>NP
F=F(1:NP,:); %如果最后产生子代种群规模超出了NP(可能会超过1个),则截取前NP个
end
f=F; %更新下一代种群
f(1,:)=R; %保留本代最优个体到下一代
clear F; %清除中间矩阵变量F
gen=gen+1;
Rlength(gen)=minlen; %记录适应度进化过程
end
figure
plot(Rlength) %画出适应度进化曲线
xlabel('迭代次数')
ylabel('目标函数值')
title('适应度进化曲线')
figure %画出最优方案对应的路线图
for i=1:N-1
plot([C(R(i),1),C(R(i+1),1)],[C(R(i),2),C(R(i+1),2)],'bo-');%线段连接相邻两点,实线,蓝色,标记o
hold on;
end
plot([C(R(N),1),C(R(1),1)],[C(R(N),2),C(R(1),2)],'bo-');%连接第一个和第N个,使图形闭合
title(['优化最短距离:',num2str(minlen)]);
最终结果如下: