仿生算法求解TSP最短路径问题(Matlab实现)

说到最短路径的求解,我们想到的往往是Dijkstra算法、Floyd算法、SPFA算法,这些算法都非常的经典,这些算法往往保证了路径最短,但是走过的路径可能构不成一个环,也就是说上述算法在修路,修桥这些方面能够很好地被应用,因为用到的材料会保证最少,但如果你是计划着出去旅游,准备着将N个旅游景点都走完,并保证每个景点只去一次,最后正好还回到你家的话(例如TSP问题),今天我们要学习的这种仿生算法就能很好地解决你的问题。

仿生算法其实是模拟的人类基因的,染色体交叉,变异,它体现了达尔文“物竞天择,适者生存”的思想,通过代数的增加,子代会越来越适应环境,不适应的在迭代的过程中会被逐渐淘汰掉,这样就保证了结果比较优(如果迭代次数设置地大一点,结果基本可以认为最优了)

https://www.bilibili.com/video/av22056690/?redirectFrom=h5

这个链接是B站上的一个教学视频,读者可以打开看看,讲得很详细,这是第四章的内容

关键词:种群大小、父代、子代、收敛、交叉、交叉概率、变异、变异概率、代沟

以下是代码实现(代码一块一块地粘在变异环境中就能运行,你只需要修改参数即可):

主函数:

clear              %运用了仿生学中的生物遗传变异,如果感觉结果不是很优,你可以增大迭代次数,这里设了200次
clc
close all
X = [16.47,96.10
    16.47,94.44
    20.09,92.54
    22.39,93.37
    25.23,97.24
    22.00,96.05
    20.47,97.02
    17.20,96.29
    16.30,97.38
    14.05,98.12
    16.53,97.38
    21.52,95.59
    19.41,97.13
    20.09,92.55];   %各个城市的坐标位置
NIND = 100;         %种群大小
MAXGEN = 200;       %最大迭代次数
Pc = 0.9;           %交叉概率,相当于基因遗传的时候染色体交叉
Pm = 0.05;          %染色体变异
GGAP = 0.9;         %这个是代沟,通过遗传方式得到的子代数为父代数*GGAP
D = Distance(X);    %通过这个函数可以计算i,j两点之间的距离
N = size(D,1);      %计算有多少个坐标点
%%初始化种群
Chrom = InitPop(NIND,N);    %Chrome代表的种群
%%在二维图上画出所有的坐标点
%figure
%plot(X(;,1),X(;,2),'o');
%%画出随机解得路线图
DrawPath(Chrom(1,:),X)
pause(0.0001)
%输出随机解的路线和总距离
disp('初始种群中的一个随机值')
OutputPath(Chrom(1,:));%其中一个个体
Rlength = PathLength(D,Chrom(1,:));
disp(['总距离;',num2str(Rlength)]);
disp('~~~~~~~~~~~~~~~~~~~~~~~~~~~~~')
%优化
gen = 0;
figure;
hold on;
box on;
xlim([0,MAXGEN])
title('优化过程')
xlabel('代数')
ylabel('最优值')
ObjV = PathLength(D,Chrom);     %计算当前路线长度,即上面随机产生的那些个体路径
preObjV = min(ObjV);%找出当前个体重最小的那个
while gen<MAXGEN
    %%计算适应度
    ObjV = PathLength(D,Chrom);     %计算路线长度
    %fprintf('%d   %1.10f\n',gen,min(ObjV))
    line([gen - 1,gen],[preObjV,min(ObjV)]);pause(0.0001);
    preObjV = min(ObjV);
    FitnV = Fitness(ObjV);
    %%选择
    SelCh = Select(Chrom,FitnV,GGAP);
    %%交叉操作
    SelCH = Recombin(SelCh,Pc);
    %%变异
    SelCh = Mutate(SelCh,Pm);
    %%逆转操作
    SelCh = Reverse(SelCh,D);
    %%重插入子代的新种群
    Chrom = Reins(Chrom,SelCh,ObjV);
    %%更新迭代次数
    gen = gen + 1;
end
%%画出最优解的路线图
ObjV = PathLength(D,Chrom);     %计算路线长度
[minObjV,minInd] = min(ObjV);
DrawPath(Chrom(minInd(1),:),X)
%%输出最优解的路线和距离
disp('最优解:')
p = OutputPath(Chrom(minInd(1),:));
disp(['总距离:',num2str(ObjV(minInd(1)))]);
disp('~~~~~~~~~~~~~~~~~~~~~~~~~~~~~')

城市间距离计算函数

function D = Distance(a)
%%计算两两城市之间的距离
%输入 a 各城市的位置坐标
%输出 D 两两城市之间的距离

row = size(a,1);
D = zeros(row,row);
for i = 1:row
    for j = i+1:row
        D(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

初始化种群

function Chrom = InitPop(NIND,N)%初始化种群,
%输入:
%NIND:种群大小
%N:个体染色体长度(城市个数)
%输出:
%初始种群
Chrom = zeros(NIND,N);  %用于存储种群
for i = 1:NIND
    Chrom(i,:) = randperm(N);%随机生成初始种群,randperm函数的用法是返回一行1~N的整数,这N个数是不同的
end

画出游历路径

function DrawPath(Chrom,X)
%%画路线图函数
%输入:
%Chrom  待画路线
%X      个城市的坐标位置

R =  [Chrom(1,:) Chrom(1,1)]; %一个随机解(个体),一共有14个城市,但是这里R有15个值,因为后面又补了一个Chrom(1,1),“是为了让路径最后再回到起点”,这是R初始值[6,3,11,7,14,8,5,1,2,4,13,9,10,12,6],可以发现前14个值因为randperm函数都是不一样的
figure;
hold on
plot(X(:,1),X(:,2),'o','color',[0.5,0.5,0.5])%X(:,1),X(:,2)分别代表的X轴坐标和Y轴坐标,[0.5,0.5,0.5]表示对应的坐标用圆圈表示
%plot(X(:,1),X(:,2),'o','color',[1,1,1])%X(:,1),X(:,2)分别代表的X轴坐标和Y轴坐标,
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                                 %感觉这个for循环没什么用,我去掉了,对结果也没什么影响,无非即使第一张图上标了个1,可能是我太菜了吧
A = X(R,:);                         %A是将之前的坐标顺序用R打乱后重新存入A中
row = size(A,1);                    %row为坐标数+1
for i = 2:row
    [arrowx,arrowy] = dsxy2figxy(gca,A(i-1:i,1),A(i-1:i,2));    %dsxy2figxy坐标转换函数,记录两个点
    annotation('textarrow',arrowx,arrowy,'HeadWidth',8,'color',[0,0,1]);%将这两个点连接起来
end
hold off
xlabel('横坐标')
ylabel('纵坐标')
title('轨迹图')
box on

在命令行窗口输出路径

function p=OutputPath(R)
%%输出路线函数
%输入   R   路线

R = [R,R(1)];
N = length(R);
p = num2str(R(1));
for i = 2:N
    p = [p,'->',num2str(R(i))];
end
disp(p)

计算种群中每个个体(每个个体代表一种路径)路径总长度

function len = PathLength(D,Chrom)
%%计算所有个体的路线长度
%输入
%D  两两城市之间的距离
%Chrom  个体的轨迹

[row,col] = size(D);
NIND = size(Chrom,1);
len = zeros(NIND,1);
for i = 1:NIND
    p = [Chrom(i,:) Chrom(i,1)];
    i1 = p(1:end-1);
    i2 = p(2:end);
    len(i,1) = sum(D((i1-1)*col+i2));
end

选择较优个体

function SelCh = Select(Chrom,FitnV,GGAP)%%选择操作
%输入:
%Chrom 种群
%FitnV 适应度值
%GGAP 选择概率
%输出:
%SelCh 被选择的个体
NIND = size(Chrom,1);
NSel = max(floor(NIND * GGAP+.5),2);
ChrIx = Sus(FitnV,NSel);
SelCh = Chrom(ChrIx,:);

个体染色体交叉

function SelCh = Recombin(SelCh,Pc)
%交叉操作
%输入:
%SelCh 被选择的个体
%Pc    交叉概率
%输出:
%SelCh 交叉后的个体

NSel = size(SelCh,1);
for i = 1:2:NSel - mod(NSel,2)
    if Pc>=rand %交叉概率PC
        [SelCh(i,:),SelCh(i+1,:)] = intercross(SelCh(i,:),SelCh(i+1,:));
    end
end

变异操作

function SelCh = Mutate(SelCh,Pm)
%变异操作
%输入:
%SelCh  被选择的个体
%Pm  变异概率
%输出:
%SelCh  变异后的个体

[NSel,L] = size(SelCh);
for i = 1:NSel
    if Pm >= rand
        R = randperm(L);
        SelCh(i,R(1:2)) = SelCh(i,R(2:-1:1));
    end
end

逆转操作

function SelCh = Reverse(SelCh,D)
%%进化逆转函数
%输入:
%SelCh  被选择的个体
%D  各城市的距离矩阵
%输出:
%SelCh  进化逆转后的个体

[row,col] = size(SelCh);
ObjV = PathLength(D,SelCh);
SelCh1 = SelCh;
for i = 1:row
    r1 = randsrc(1,1,[1:col]);
    r2 = randsrc(1,1,[1:col]);
    mininverse = min([r1 r2]);
    maxinverse = max([r1 r2]);
    SelCh1(i,mininverse:maxinverse) = SelCh1(i,maxinverse:-1:mininverse);
end
ObjV1 = PathLength(D,SelCh1);%计算路线长度
index = ObjV1<ObjV;
SelCh(index,:)=SelCh1(index,:);

重插操作(在父代中选择当前最优的几个个体插入子代中,选几个要看你代沟设的多少)

function Chrom = Reins(Chrom,SelCh,ObjV)
%%重插入子代的种群
%输入:
%Chrom      父代的种群
%SelCh      子代的种群
%ObjV       父代适应度
%输出:
%Chrom      组合父代与子代后得到的新种群
NIND = size(Chrom,1);
NSel = size(SelCh,1);
[TobjV,index] = sort(ObjV);
Chrom =  [Chrom(index(1:NIND-NSel),:);SelCh];

猜你喜欢

转载自blog.csdn.net/sand8o8time/article/details/81409386
今日推荐