旅行商问题简介
给定一系列城市和每对城市之间的距离,求解访问每一座城市一次并回到起始城市的最短回路。它是组合优化中的一个NP难问题,在运筹学和理论计算机科学中非常重要。
遗传算法
遗传算法已经在之前的博客详细介绍过:遗传算法 因此这边不再多加赘述
由于上次解决的是使用遗传算法解决函数优化问题,交叉操作只要交换0和1就行,而在这次问题中,随机选择两个个体,再随机选择一段基因进行交换,以完成交叉操作,很容易在城市序列中包含重复的城市。
比如一下两个个体之间的交叉
A: 9 1 2 3 5 4 7 6 8 10
B: 10 5 6 4 3 8 7 2 1 9
得到
A: 9 1 2 4 3 8 7 6 8 10
B: 10 5 6 3 5 4 7 2 1 9
可见,两者交换基因段为3 5 4 7和4 3 8 7,保持此段不变,对于A,第一个冲突基因为8,将交换段外冲突基因替换为B中相应位置的基因即5,多次执行直到没有冲突得到基因:
A:9 1 2 4 3 8 7 6 5 10
B:10 8 6 3 5 4 7 2 1 9
而变异操作则是将同一城市序列的两个城市进行交换
代码展示
变异函数 Mutation.m
function a=Mutation(A)
index1=0;index2=0;
nnper=randperm(size(A,2));
index1=nnper(1);
index2=nnper(2);
%fprintf('index1=%d ',index1);
%fprintf('index2=%d ',index2);
temp=0;
temp=A(index1);
A(index1)=A(index2);
A(index2)=temp;
a=A;
end
对调函数 exchange.m
function [x,y]=exchange(x,y)
temp=x;
x=y;
y=temp;
end
染色体的路程代价函数 mylength.m
function len=myLength(D,p)%p是一个排列
[N,NN]=size(D);
len=D(p(1,N),p(1,1));
for i=1:(N-1)
len=len+D(p(1,i),p(1,i+1));
end
end
连点画图函数 plot_route.m
function plot_route(a,R)
scatter(a(:,1),a(:,2),'rx');
hold on;
plot([a(R(1),1),a(R(length(R)),1)],[a(R(1),2),a(R(length(R)),2)]);
hold on;
for i=2:length(R)
x0=a(R(i-1),1);
y0=a(R(i-1),2);
x1=a(R(i),1);
y1=a(R(i),2);
xx=[x0,x1];
yy=[y0,y1];
plot(xx,yy);
hold on;
end
end
适应度函数fit.m
每次迭代都要计算每个染色体在本种群内部的优先级别,类似归一化参数。
function fitness=fit(len,m,maxlen,minlen)
fitness=len;
for i=1:length(len)
fitness(i,1)=(1-(len(i,1)-minlen)/(maxlen-minlen+0.0001)).^m;
end
交叉操作函数 cross.m
function [A,B]=cross(A,B)
L=length(A);
if L<10
W=L;
elseif ((L/10)-floor(L/10))>=rand&&L>10
W=ceil(L/10)+8;
else
W=floor(L/10)+8;
end
%%W为需要交叉的位数
p=unidrnd(L-W+1);%随机产生一个交叉位置
%fprintf('p=%d ',p);%交叉位置
for i=1:W
x=find(A==B(1,p+i-1));
y=find(B==A(1,p+i-1));
[A(1,p+i-1),B(1,p+i-1)]=exchange(A(1,p+i-1),B(1,p+i-1));
[A(1,x),B(1,y)]=exchange(A(1,x),B(1,y));
end
end
main.m
clear;
clc;
%%%%%%%%%%%%%%%输入参数%%%%%%%%
N=25; %%城市的个数
M=100; %%种群的个数
ITER=2000; %%迭代次数
%C_old=C;
m=2; %%适应值归一化淘汰加速指数
Pc=0.8; %%交叉概率
Pmutation=0.05; %%变异概率
%%生成城市的坐标
pos=randn(N,2);
%%生成城市之间距离矩阵
D=zeros(N,N);
for i=1:N
for j=i+1:N
dis=(pos(i,1)-pos(j,1)).^2+(pos(i,2)-pos(j,2)).^2;
D(i,j)=dis^(0.5);
D(j,i)=D(i,j);
end
end
%%生成初始群体
popm=zeros(M,N);
for i=1:M
popm(i,:)=randperm(N);%随机排列,比如[2 4 5 6 1 3]
end
%%随机选择一个种群
R=popm(1,:);
figure(1);
scatter(pos(:,1),pos(:,2),'rx');%画出所有城市坐标
axis([-3 3 -3 3]);
figure(2);
plot_route(pos,R); %%画出初始种群对应各城市之间的连线
axis([-3 3 -3 3]);
%%初始化种群及其适应函数
fitness=zeros(M,1);
len=zeros(M,1);
for i=1:M%计算每个染色体对应的总长度
len(i,1)=myLength(D,popm(i,:));
end
maxlen=max(len);%最大回路
minlen=min(len);%最小回路
fitness=fit(len,m,maxlen,minlen);
rr=find(len==minlen);%找到最小值的下标,赋值为rr
R=popm(rr(1,1),:);%提取该染色体,赋值为R
for i=1:N
fprintf('%d ',R(i));%把R顺序打印出来
end
fprintf('\n');
fitness=fitness/sum(fitness);
distance_min=zeros(ITER+1,1); %%各次迭代的最小的种群的路径总长
nn=M;
iter=0;
while iter<=ITER
fprintf('迭代第%d次\n',iter);
%%选择操作
p=fitness./sum(fitness);
q=cumsum(p);%累加
for i=1:(M-1)
len_1(i,1)=myLength(D,popm(i,:));
r=rand;
tmp=find(r<=q);
popm_sel(i,:)=popm(tmp(1),:);
end
[fmax,indmax]=max(fitness);%求当代最佳个体
popm_sel(M,:)=popm(indmax,:);
%%交叉操作
nnper=randperm(M);
% A=popm_sel(nnper(1),:);
% B=popm_sel(nnper(2),:);
%%
for i=1:M*Pc*0.5
A=popm_sel(nnper(i),:);
B=popm_sel(nnper(i+1),:);
[A,B]=cross(A,B);
% popm_sel(nnper(1),:)=A;
% popm_sel(nnper(2),:)=B;
popm_sel(nnper(i),:)=A;
popm_sel(nnper(i+1),:)=B;
end
%%变异操作
for i=1:M
pick=rand;
while pick==0
pick=rand;
end
if pick<=Pmutation
popm_sel(i,:)=Mutation(popm_sel(i,:));
end
end
%%求适应度函数
NN=size(popm_sel,1);
len=zeros(NN,1);
for i=1:NN
len(i,1)=myLength(D,popm_sel(i,:));
end
maxlen=max(len);
minlen=min(len);
distance_min(iter+1,1)=minlen;
fitness=fit(len,m,maxlen,minlen);
rr=find(len==minlen);
fprintf('minlen=%d\n',minlen);
R=popm_sel(rr(1,1),:);
for i=1:N
fprintf('%d ',R(i));
end
fprintf('\n');
popm=[];
popm=popm_sel;
iter=iter+1;
%pause(1);
end
%end of while
figure(3)
plot_route(pos,R);
axis([-3 3 -3 3]);
figure(4)
plot(distance_min);
测试数据
N=25; %%城市的个数
M=100; %%种群的个数
ITER=2000; %%迭代次数
m=2; %%适应值归一化淘汰加速指数
Pc=0.8; %%交叉概率
Pmutation=0.05; %%变异概率
%%生成城市的坐标
pos=[
0.537667139546100 1.03469300991786
1.83388501459509 0.726885133383238
-2.25884686100365 -0.303440924786016
0.862173320368121 0.293871467096658
0.318765239858981 -0.787282803758638
-1.30768829630527 0.888395631757642
-0.433592022305684 -1.14707010696915
0.342624466538650 -1.06887045816803
3.57839693972576 -0.809498694424876
2.76943702988488 -2.94428416199490
-1.34988694015652 1.43838029281510
3.03492346633185 0.325190539456198
0.725404224946106 -0.754928319169703
-0.0630548731896562 1.37029854009523
0.714742903826096 -1.71151641885370
-0.204966058299775 -0.102242446085491
-0.124144348216312 -0.241447041607358
1.48969760778547 0.319206739165502
1.40903448980048 0.312858596637428
1.41719241342961 -0.864879917324457
0.671497133608081 -0.0300512961962686
-1.20748692268504 -0.164879019209038
0.717238651328839 0.627707287528727
1.63023528916473 1.09326566903948
0.488893770311789 1.10927329761440 ]
;
运行结果
左上图为城市的坐标,一共有25个城市
右上图为初始种群的路线
左下图为最终路线
右下图为迭代次数
可以看出迭代到370次时,已找到最短路径,最短路径的值为26.93760
最短路径序列为
14 11 6 16 23 12 9 10 17 22 3 7 15 20 13 8 5 21 4 19 18 2 24 1 25
不同参数下的运行结果
1.交叉概率为改变,变异概率为0.05,种群个数为100
交叉概率 | 迭代次数 | 最短距离 |
---|---|---|
0.2 | 1968 | 24.75382 |
0.4 | 482 | 24.14478 |
0.6 | 821 | 23.58496 |
0.8 | 304 | 26.08873 |
1 | 1682 | 23.04163 |
在其他参数不变的情况下,随着交叉概率的增加,迭代次数以先减后增再减再增的曲线变化(去掉特殊情况),这说明交叉概率有明显的优势区间,本次实验大概在[0.4,0.8]间取得最优效果。
在交叉概率为0.2时,到达1968次时,曲线发生了变化,后来又变化为之前一样的情况,特殊情况特殊记录
2.交叉概率为0.8,变异概率为改变,种群个数为100
变异概率 | 迭代次数 | 最短距离 |
---|---|---|
0.01 | 1375 | 25.02534 |
0.03 | 321 | 24.80349 |
0.05 | 501 | 23.28053 |
0.07 | 307 | 22.35342 |
0.09 | 1361 | 23.14003 |
%在其他参数不变的情况下,随着变异概率的增加,迭代次数以一种w曲线的变化,在[0.03,0.05]及[0.05,0.07]的迭代次数最少,效果最好。
3.交叉概率为0.8,变异概率为0.05,种群个数改变
种群个数 | 迭代次数 | 最短距离 |
---|---|---|
100 | 1061 | 24.18065 |
200 | 251 | 24.40206 |
300 | 127 | 21.56703 |
400 | 398 | 24.40347 |
500 | 208 | 23.05599 |
明显可以看出,其他参数不变的前提下,随着种群数量的增多,迭代次数先增加后减少。到达[200,300]的时候,效果最好。
实验结论
本次实验与之前的蚁群算法做对比,明显运行速度快于蚁群算法。遗传算法具有良好的全局搜索能力,可以快速地将解空间中的全体解搜索出,而不会陷入局部最优解的快速下降陷阱;并且利用它的内在并行性,可以方便地进行分布式计算,加快求解速度。但是遗传算法的局部搜索能力较差,导致单纯的遗传算法比较费时,在进化后期搜索效率较低。