【Matlab】 遗传算法求解TSP问题
前言
个人实验的一次记录,如有不当欢迎批评指正
TSP(traveling salesman problem,旅行商问题)是典型的NP完全问题,即其最坏情况下的时间复杂度随着问题规模的增大按指数方式增长,到目前为止还未找到一个多项式时间的有效算法。在现实生活中,TSP问题广泛应用于货物零件加工顺序、汽配件喷涂顺序、仓储系统拣货路径规划、光伏板清洁等领域。
自20世纪80年代以来,基于启发式规则的智能优化算法兴起并被快速应用于求解TSP问题,如遗传算法、模拟退火算法、蜂群算法、蚁群算法、萤火虫算法、粒子群算法等,它们普遍具有快速搜索和求解能力,对规模更大的TSP问题也有明显效果。其中,以达尔文进化论、孟德尔遗传变异理论、模式理论为基础的遗传算法逐渐受到重视,它是一种自适应全局优化的概率搜索算法,具有较强的鲁棒性、并行性,容易与其他算法结合等优点,但也存在交叉算子不易操作、容易过早收敛而陷入局部最优、收敛速度慢等缺点
一、问题描述
TSP问题可描述为:已知
m
m
m个城市相互之间的距离,某一旅行商从某个城市出发访问每个城市有且仅有一次,最后回到出发城市,如何安排才使其所走路线距离最短。也就是寻找一条最短的遍历
n
n
n个城市的路径,或者说搜索自然子集
X
=
{
1
,
2
,
⋯
,
n
}
(
X
的元素表示对
n
个城市的编号
)
的一个排列
X=\left\{ 1,2,\cdots ,n \right\} \left( X\text{的元素表示对}n\text{个城市的编号} \right) \text{的一个排列}
X={1,2,⋯,n}(X的元素表示对n个城市的编号)的一个排列
π
(
X
)
=
{
V
1
,
V
2
,
⋯
,
V
n
}
使得
\pi \left( X \right) =\left\{ V_1,V_2,\cdots ,V_{\mathrm{n}} \right\} \text{使得}
π(X)={V1,V2,⋯,Vn}使得
T
d
=
∑
i
=
1
n
+
1
d
(
V
i
,
V
i
+
1
)
+
d
(
V
n
,
V
1
)
T_{\mathrm{d}}=\sum_{i=1}^{n+1}{d\left( V_{\mathrm{i}},V_{\mathrm{i}+1} \right) +d\left( V_{\mathrm{n}},V_1 \right)}
Td=∑i=1n+1d(Vi,Vi+1)+d(Vn,V1)取得最小值,其中
d
(
V
i
,
V
i
+
1
)
d\left( V_{\mathrm{i}},V_{\mathrm{i}+1} \right)
d(Vi,Vi+1)表示城市
V
i
V_{\mathrm{i}}
Vi到城市
V
i
+
1
V_{\mathrm{i+1}}
Vi+1的距离。
二、实验设计
1.问题案例
本案例以14个城市为例,假定14个城市的位置坐标如表1所列。寻找出一条最短的遍历14个城市的路径。具体如下:
编号 | X坐标 | Y坐标 | 编号 | X坐标 | Y坐标 |
---|---|---|---|---|---|
1 | 16.47 | 96.10 | 8 | 17.20 | 96.29 |
2 | 16.47 | 94.44 | 9 | 16.30 | 97.38 |
3 | 20.09 | 92.54 | 10 | 14.05 | 98.12 |
4 | 22.39 | 93.37 | 11 | 16.53 | 97.38 |
5 | 25.23 | 97.24 | 12 | 21.52 | 95.59 |
6 | 22.00 | 96.05 | 13 | 19.41 | 97.13 |
7 | 20.47 | 97.02 | 14 | 20.09 | 94.55 |
2.读入数据
代码如下(示例):
function [popinit,distmat,Villes] = file_init(nbrpop)
%读取包含城市坐标的文件
[FileName,~] = uigetfile('*.txt','选择文本文件');
fid = fopen(FileName,'r');
Villes = dlmread(FileName,'%d%10.4f%10.4f'); %将文件中的值存储在Cities数组中
[nbrville,~] = size(Villes);
fclose(fid);
distmat = zeros(nbrville,nbrville); %距离矩阵
popinit = zeros(nbrpop,nbrville); %初始种群矩阵
%创建初始种群(随机)
for i=1:nbrpop
popinit(i,:) = randperm(nbrville);
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% 欧式距离 %
%填充距离矩阵
for i=1:nbrville
for j=1:nbrville
distmat(i,j) = sqrt( (Villes(i,2)-Villes(j,2))^2 + (Villes(i,3)-Villes(j,3))^2 );
end
end
end
将表格内数据保存为文本格式 中间以空格形式隔开,即可通过该函数读入相关信息。
3.适应度计算
% vectfit:向量包含每次行程的总距离(成本)
% best:向量包含迭代中得到的最佳路径当前的
% minVal:本次行程对应的最佳值(最佳)
function [vectfit,best,minVal,indxBest]=calcFitness (nbrpop ,nbrville, distmat ,popinit)
vectfit = zeros(1,nbrpop);
%计算的适应度向量
for i=1:nbrpop
tmp = popinit(i,:);
for j=1:nbrville
if j==length(tmp)
vectfit(i) = vectfit(i) + distmat(tmp(j),tmp(1));
break;
end
vectfit(i) = vectfit(i) + distmat(tmp(j),tmp(j+1));
end
end
[minVal,indxBest]=min(vectfit) ; %min() 给了我们两个值:最小值和这个值的索引
best=popinit(indxBest , : ) ;
end
我们通过欧氏距离矩阵直接获取各个点之间的距离关系,通过距离换算出其适应度,用于后续的计算。
4. 选择子代
% matelec:包含选择进行繁殖的个体的矩阵
function matselec = rouletteSelect(vectfit,popinit)
Ps = 0.5;
[nbrpop,nbrville] = size(popinit);
nbrpopS = floor(nbrpop*Ps); % 我们选择 Ps*100% 的个人
matselec = zeros(nbrpopS,nbrville);
cumProb = zeros(1,length(vectfit)); % 包含累积适应度的向量
s = 0;
% 累积概率计算
for j=1:length(vectfit)
cumProb(j) = s + vectfit(j);
s = cumProb(j);
end
% 填充选择矩阵进行育种
for i=1:nbrpopS
r = (max(cumProb)-min(cumProb))*rand() + min(cumProb); % min(cumProb) <= r <= max(cumProb)
for j=1:length(cumProb)
if r <= cumProb(j)
matselec(i,:) = popinit(j,:);
break;
end
end
end
end
采取精英策略选择相关的适应度较高的个体用于后续的迭代,通过这种方法,不断的迭代直到满足迭代次数或者达到相关精度。
5. 结果输出
对应轨迹图
其迭代过程如上
实验源码点击此处
总结
对于实际问题,我们需要采取不同的策略与手段,根据实际问题的需求,设计合理的求解方法,不断地深入的分析问题的本质,才有助于我们解决这些问题。