遗传算法在TSP(旅行商问题)中的应用

一、旅行商问题简介

假设有一个旅行商人要拜访n个城市,他必须选择所要走的路径,路径的限制是每个城市只能拜访一次,而且最后要回到原来出发的城市。路径的选择目标是要求得的路径路程为所有路径之中的最小值。

这里的路径最小为目标常常由其他的等价的条件转换而来比如耗能最小,时间最短等。

别名:旅行推销员问题

  • 图论中的一个等价形式是:给定一个加权完全图(顶点表示城市,边表示道路,权重就会是道路的成本或距离), 求一权值最小的哈密尔顿回路。
二、旅行商问题的遗传算法求解
  1. 遗传编码

    • 假设有27个城市将城市编号 λ 1 , λ 2 , . . . , λ 27 \lambda_1,\lambda_2,...,\lambda_{27} λ1,λ2,...,λ27
    • 通过将城市按照一定的序列排序,比如 λ 1 , λ 3 , λ 27 , λ 11 , . . . , λ 6 , λ 1 \lambda_1,\lambda_3,\lambda_{27},\lambda_{11},...,\lambda_{6},\lambda_1 λ1,λ3,λ27,λ11,...,λ6,λ1表示遗传算法中的一个个体的基因型,而这里的个体则指一个决策方案。
    • 这种排序可以通过一定的随机数函数实现,比如 m a t l a b matlab matlab提供的 r a n d p e r m ( ) randperm() randperm()函数,随机生成一个全排列
  2. 遗传操作

    (1)选择,交叉,变异算子的说明

    • 选择算子

      • 作用:选择满足一定的适应函数函数的标准的个体加入交配池中,这些个体的选择反应了生物世界中的适者生存的原则。

      • 精英策略:

  • 遗传算法(Genetic Algorithm)中的基因,并不一定真实地反映了待求解问题的本质,因此各个基因之间未必就相互独立,如果只是简单地进行杂交,很可能把较好的组合给破坏了,这样就没有达到累积较好基因的目的,反而把原本很好的基因给破坏了。精英保留策略可以避免最优个体不会因为杂交操作而被破坏。

  • 设到第t代时,群体中 a ( t ) a_{(t)} a(t)为最优个体。又设 A ( t + 1 ) A_{(t+1)} A(t+1)为新一代群体,若A(t+1)中不存在比a(t)优的个体 ,则把a(t)加入到A(t+1)中作为A(t+1)的第n+1个个体,这里n为群体的大小。
    为了保持群体的规模不变,如果精英个体被加入到新一代群体中,则可以将新一代群体中适应度值最小的个体淘汰掉。

    • 可根据个人的需要编写选择算子

    • 交叉算子

      • 作用:通过一定的随机操作,比如两个个体之间的基因的片段的交换,或者某一个个体的基因片段之间的交换……从而产生下一代的个体。

      • 这里提供三种交叉算子的代码:

        (尊重原创:

        []: https://blog.csdn.net/Metropolis_cn/article/details/77967659

        )

      function [ race_new ] = ga_cross( race,P_Cross )
      k=race(1,1);
      [m,n]=size(race);
      race=race(1:m,2:n-1);
      [m,n]=size(race);
      race_new=race;
      for i=1:m-1
          flag=rand;
          if(flag<=P_Cross)
              list=randperm(n-1);
              Cross_Node=list(1);
              child=zeros(1,n);
              parent_1=race_new(i,:);
              parent_2=race_new(i+1,:);
              child(Cross_Node)=parent_2(Cross_Node);
              child(Cross_Node+1)=parent_2(Cross_Node+1);
              index_1=find(parent_1 == child(Cross_Node));
              index_2=find(parent_1 == child(Cross_Node+1));
              if(index_1>index_2)
                  parent_1(index_1)=[];
                  parent_1(index_2)=[];
              else
                  parent_1(index_2)=[];
                  parent_1(index_1)=[];
              end
              for j=1:n-2
                  if(j<Cross_Node)
                      child(j)=parent_1(j);
                  end
                  if(j>=Cross_Node)
                      child(j+2)=parent_1(j);
                  end
              end
              race_new(i,:)=child;
          end
      end
      race_new=[k*ones(m,1),race_new,k*ones(m,1)];
      end
    
    function [ race_new ] = ga_exchange( race,P_Cross )
    %ga_exchange实现随机取某一基因点,交换该点和紧跟后面一点的基因型
    k=race(1,1);
    [m,n]=size(race);
    race=race(1:m,2:n-1);
    [m,n]=size(race);
    race_new=race;
    for i=1:m
        flag=rand;
        if(flag<P_Cross)
          list=randperm(n-1);
          temp=race_new(i,list(1));
          race_new(i,list(1))=race_new(i,list(1)+1);
          race_new(i,list(1)+1)=temp;
        end
    end
    race_new=[k*ones(m,1),race_new,k*ones(m,1)];
    end
    
    function [ race_new ] = ga_invert( race,P_Cross )
    %倒序
    %随机取两点,逆转两点之间的基因排列
    k=race(1,1);
    [m,n]=size(race);
    race=race(1:m,2:n-1);
    [m,n]=size(race);
    race_new=race;
    for i=1:m
        flag=rand;
        if(flag<P_Cross)
            temp=race_new(i,:);
            list=randperm(n);
            if(list(1)>list(2))
                start=list(2);
                finish=list(1);
            else
                start=list(1);
                finish=list(2);
            end
            temp_1=temp(1:1:start-1);
            temp_2=temp(finish:-1:start);
            temp_3=temp(finish+1:end);
            temp=[temp_1,temp_2,temp_3];
            race_new(i,:)=temp;
        end
    end
    race_new=[k*ones(m,1),race_new,k*ones(m,1)];
    end
    
  • 交叉的方式也是多种多样的,可以自己根据自己的编码或者实际问题的需要进行编写,交叉算子反应了算法对全局最优解的搜索。

    • 变异算子

      • 作用:将某个个体的基因型中的个别基因随机变化成其他的基因,产生新的个体,即新的决策方案。

      • 以下提供一种变异算子

      function [ race_new ] = ga_mutation( race,P_Mutation )
      %变异
      %随机取两点,交换两点基因型
      k=race(1,1);
      [m,n]=size(race);
      race=race(1:m,2:n-1);
      [m,n]=size(race);
      race_new=race;
      for i=1:m
          flag=rand;
          if(flag<=P_Mutation)
              list=randperm(n);
              temp=race_new(i,list(1));
              race_new(i,list(1))=race_new(i,list(2));
              race_new(i,list(2))=temp;
          end
      end
      race_new=[k*ones(m,1),race_new,k*ones(m,1)];
      end
    
  1. 遗传解码*

    如果前面编码的时候不是简单的将城市用1,2,3……编号的,而是将这些编号通过一定的函数映射到(0,1)上的话,那么这一步则需要解码,即将得到的排列翻译成决策变量。

(2)遗传算法的一般步骤

在这里插入图片描述

(4)遗传算法求解TSP的完整代码

%% 使用改进型的交叉原则,即遵循适应度互相匹配的原则进行父代的交配:
%%%距离小的染色体与距离小的染色体交配,距离大的染色体与距离大的染色体交配

%% 初始数据的导入,距离矩阵的生成
tic                         %计时开始
clc,clear
load locationMap.mat;       %加载位置信息
sj=locationMap*pi/180;      %单位化为弧度
temp=sj(1,:);               %储存数据中心的位置
sj=[sj;temp];               %完整的31*2的位置矩阵,第1行和第31行为数据中心
d=zeros(31);                %距离矩阵d的初始值设置为0
for i=1:30
  for j=i+1:31
    d(i,j)=6370*acos(cos(sj(i,1)-sj(j,1))*cos(sj(i,2))*cos(sj(j,2))+sin(sj(i,2))*sin(sj(j,2)));
  end
end
d=d+d';                     %生成完整的邻接矩阵

%% 遗传参数的设置
w=50;                       %w为种群的个数
g=150;                      %g为进化的代数
rand('state',sum(clock));   %初始化随机数发生器

%% 通过改良圈算法选取初始种群
for k=1:w  
    c=randperm(29);             %产生1,...,29的一个全排列  
    c1=[1,c+1,31];              %生成初始解
    for t=1:31                  %该层循环是修改圈 
        flag=0;                 %修改圈退出标志
    for m=1:29
      for n=m+2:30
          %满足改良圈的条件,进行改良圈
          if d(c1(m),c1(n))+d(c1(m+1),c1(n+1))<d(c1(m),c1(m+1))+d(c1(n),c1(n+1))
           c1(m+1:n)=c1(n:-1:m+1);  
           flag=1;              %修改圈
        end
      end
    end
   if flag==0
      J(k,c1)=1:31; 
      break %记录下较好的解并退出当前层循环
   end
   end
end
J(:,1)=0; J=J/102; %把整数序列转换成[0,1]区间上的实数,即转换成染色体编码

%% 遗传操作
for k=1:g  
    A=J;                                    %交配产生子代B的初始染色体
    % 模拟杂交,即交配池中的操作
    for i=1:2:w
        ch1(1)=rand;                        
        % 此层循环完成混沌序列迭代生成操作
        for j=2:50
            ch1(j)=4*ch1(j-1)*(1-ch1(j-1)); 
        end
        % 随机数乘以29加上2即为交叉地址
        ch1=2+floor(29*ch1);               %产生交叉操作的地址
        temp=A(i,ch1);                      
        A(i,ch1)=A(i+1,ch1);                %交叉操作
        A(i+1,ch1)=temp;
    end
    
    %模拟变异操作,即实现变异算子
    by=[];                                  %为了防止下面产生空地址,这里先初始化
    while ~length(by)
        by=find(rand(1,29)<0.1);                 %产生变异操作的地址
    end
    num1=length(by); 
    B=J(by,:); %产生变异操作的初始染色体
    ch2=rand;  %产生混沌序列的初始值
    for t=2:2*num1 
           ch2(t)=4*ch2(t-1)*(1-ch2(t-1)); %产生混沌序列
    end
    for j=1:num1
       bw=sort(2+floor(29*rand(1,2)));  %产生变异操作的2个地址
       B(j,bw)=ch2([j,j+1]); %bw处的两个基因发生了变异
    end
       G=[J;A;B]; %父代和子代种群合在一起
       [SG,ind1]=sort(G,2); %把染色体翻译成1,...,32的序列ind1
       num2=size(G,1); long=zeros(1,num2); %路径长度的初始值
       for j=1:num2
           for i=1:30
               long(j)=long(j)+d(ind1(j,i),ind1(j,i+1)); %计算每条路径长度
           end
       end
         [slong,ind2]=sort(long); %对路径长度按照从小到大排序
         J=G(ind2(1:w),:); %精选前w个较短的路径对应的染色体
end
path=ind1(ind2(1),:), flong=slong(1)  %解的路径及路径长度
toc  %计时结束

%% 画图
xx=sj(path,1);yy=sj(path,2);
plot(xx,yy,'-o') %画出路径
grid on
for i = 1:30
    text(sj(i,1),sj(i,2),['   ' num2str(i)]);
end
text(sj(1,1),sj(1,2),'       数据中心(起点/终点)');
  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Blanche117

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值