matlab遗传算法求解(TSP)附matlab代码

问题描述

旅行商问题(traveling saleman problem, TSP)又被称为推销员问题、货郎担问题,该问题是最基本的路线问题。该问题寻求单一旅行者由起点出发,通过所有给定的需求点之后,最后再回到起点的最小路径成本,路径的限制是每个需求点只能拜访一次。最早的旅行商问题的数学模型是由Dantzig(1959)等学者提出的。

基本流程

算法原理不多赘述,主要接实例验证

(1)初始化:设置进化代数计数器t=0,设置最大进化代数T,随机生成M个个体作为初始群体P(0)。 

(2)个体评价:计算群体中各个个体的适应度。 

(3)选择:将选择算子作用于群体。选择的目的是把优化的个体直接遗传到下一代或通过配对交叉产生新的个体再遗传到下一代。

(4)交叉运算:将交叉算子作用于群体。遗传算法中起核心作用的就是交叉算子。

(5)变异:将变异算子作用于群体。即是对群体中的个体串的某些的基因值作变动。群体P(t)经过选择、交叉、变异之后得到下一代群体P(t+1)。 

算法实现

以求遍历个点总距离最小为例。数据输入分为两种,一种是坐标输入(距离矩阵需要根据坐标计算),另一种是直接序号输入(各点之间距离已知,但坐标未知)。输入不同仅影响主函数部分,对变异、交叉等函数并不影响。参考了该文章

主函数(坐标已知距离未知时)

%% Define cities coordination
city20(:,1) = 100 * rand(20,1); 
city20(:,2) = 100 * rand(20,1); 
N=20;                            %城市数
city_coordinate=city20;
s=100;                           %样本数(种群)
c=30;                            %替换个数
pc=0.8;                          %交叉概率crossover
pm=0.1;                          %变异概率mutation
times=3000;                      %最大迭代次数
time=0;                          %实际迭代次数
%%初始化变量,设定总群,总适应度以及最短距离以及最小距离。
pop=zeros(s,N+1);                %初始种群+适应度,之所以加N+1,因为最后还要回到原点。
pop_fit_aver=[];%总适应度
min_dis=[];                      %最短距离
pop_min=[];%最短距离的基因
%初始化,其中s为种群数
for i=1:s   
    pop(i,1:N)=randperm(N);    %%randperm函数产生1-N任意排列的N维无重复数组,代表城市访问顺序。
end
%%求距离矩阵
    city_distance=zeros(N,N); %%通过zeros构造
    for i=1:N
        for j=1:N
            city_distance(i,j)=((city_coordinate(i,1)-city_coordinate(j,1))^2+...
                (city_coordinate(i,2)-city_coordinate(j,2))^2)^0.5;     %%matlab是以矩阵的思维编写的,则每个距离都会存入city_diatance
        end
    end
%%

%适应度函数编写
[individual_fit,sum,min1,min_index]=GroupFit(city_distance,N,pop,s);   
         %%通过城市距离;城市数。pop(城市的随机分布)。s是城市分布的例子个数
sumP=sum;
pop_fit_aver=[pop_fit_aver;sum];        %%构造总群适应度函数
min_dis=[min_dis;min1];                 %%构造最小距离  min_dis=min1
pop(:,N+1)=individual_fit;              %%列矩阵的个体适应度函数
pop_min=[pop_min;pop(min_index,:)];
%% 选择父代
pop=selection(pop,N,s,c);
for k=1:times
    time=time+1;
    E_new_new=[];   %子代
    for j=1:s/2    
        a=rand(1);
        b=rand(1);
    %%  交叉crossover
            if a>pc          
                ;
            else
                crosspoint=rand(1,2);
                crosspoint=floor(crosspoint*N)+1;
                [pop(j,:),pop(j+s/2,:)]=Crossover(pop(j,:),pop(j+s/2,:),crosspoint,N);       
            end
                        
%% 变异mutation
            if b>pm         
                ;
            else
                pop(j,:)=Mutation(pop(j,:),N);
                pop(j+s/2,:)=Mutation(pop(j+s/2,:),N);
            end
            E_new_new=[E_new_new;pop(j,:);pop(j+s/2,:)]; %每次迭代保留优秀种群写E_new_new中
  
    end
    [individual_fit,sum,min1,min_index]=GroupFit(city_distance,N,E_new_new,s); 
                                                  %%适应度向量,适应度函数,最小距离,最小距离下标
                                                  %%参数:城市距离矩阵,城市数,每代最优种群的几何,种群数
    sumS=sum;
    pop_fit_aver=[pop_fit_aver;sum];              %%适应度函数
    min_dis=[min_dis;min1];                       %%每次迭代的最小值的向量集合
    E_new_new(:,N+1)=individual_fit;              %%目标函数(城市之间的距离)
    pop_min=[pop_min;E_new_new(min_index,:)];
    if(abs(sumS-sumP)<0.001)%退出条件             %%迭代的条件
        break;
    end
    pop=selection(E_new_new,N,s,c);               %%选择算子
end
[a,min_index]=min(min_dis);                       %%min函数可以求得 该向量min_dis最小值和最小值所在的index
time1=1:time+1;        %%为什么要加1,因为没有迭代前,原始种群也有一个最小值

time1=1:time+1;        %%为什么要加1,因为没有迭代前,原始种群也有一个最小值
figure%画平均适应度折线图
plot(time1,min_dis,'k.');
grid on;
title('每代最小值散点图');
xlabel('迭代的次数');
ylabel('最短的距离');

%%
最优序列储存在E_new_new(最后一行)中,最小距离储存于min_dis(最后一行)。
%%

主函数(各点之间距离已知,但无坐标)

最优序列储存在E_new_new(最后一行)中,最小距离储存于min_dis(最后一行)。

clear
N=[];                            %城市数
city_distance=zeros(N,N); %%通过zeros构造预分配内存
city_distance=[]; %直接输入各个点之间的距离矩阵(二维矩阵,比如横表头是1-9个点,竖表头也为这个顺序,中间内容为各个点之间的相互距离,该矩阵应有一条对角线均为0,并且数据关于该对角线对称)
s=100;                           %样本数(种群)
c=30;                            %替换个数
pc=0.8;                          %交叉概率crossover
pm=0.1;                          %变异概率mutation
times=3000;                      %最大迭代次数
time=0;                          %实际迭代次数
%%初始化变量,设定总群,总适应度以及最短距离以及最小距离。
pop=zeros(s,N+1);                %初始种群+适应度,之所以加N+1,因为最后还要回到原点。
pop_fit_aver=[];%总适应度
min_dis=[];                      %最短距离
pop_min=[];%最短距离的基因
%初始化,其中s为种群数
for i=1:s   
    pop(i,1:N)=randperm(N);   %%randperm函数产生1-N任意排列的N维无重复数组,代表城市访问顺序
end
 
%适应度函数编写
[individual_fit,sum,min1,min_index]=GroupFit(city_distance,N,pop,s);   
         %%通过城市距离;城市数。pop(城市的随机分布)。s是城市分布的例子个数
sumP=sum;
pop_fit_aver=[pop_fit_aver;sum];        %%构造总群适应度函数
min_dis=[min_dis;min1];                 %%构造最小距离  min_dis=min1
pop(:,N+1)=individual_fit;              %%列矩阵的个体适应度函数
pop_min=[pop_min;pop(min_index,:)];
%% 选择父代
pop=selection(pop,N,s,c);
for k=1:times
    time=time+1;
    E_new_new=[];   %子代
    for j=1:s/2    
        a=rand(1);
        b=rand(1);
    %%  交叉crossover
            if a>pc          
                ;
            else
                crosspoint=rand(1,2);
                crosspoint=floor(crosspoint*N)+1;
                [pop(j,:),pop(j+s/2,:)]=Crossover(pop(j,:),pop(j+s/2,:),crosspoint,N);       
            end
                        
%% 变异mutation
            if b>pm         
                ;
            else
                pop(j,:)=Mutation(pop(j,:),N);
                pop(j+s/2,:)=Mutation(pop(j+s/2,:),N);
            end
            E_new_new=[E_new_new;pop(j,:);pop(j+s/2,:)];           %%每次迭代保留优秀种群写入E_new_new中
  
    end
    [individual_fit,sum,min1,min_index]=GroupFit(city_distance,N,E_new_new,s); 
                                                  %%适应度向量,适应度函数,最小距离,最小距离下标
                                                  %%参数:城市距离矩阵,城市数,每代最优种群的几何,种群数
    sumS=sum;
    pop_fit_aver=[pop_fit_aver;sum];              %%适应度函数
    min_dis=[min_dis;min1];                       %%每次迭代的最小值的向量集合
    E_new_new(:,N+1)=individual_fit;              %%目标函数(城市之间的距离)
    pop_min=[pop_min;E_new_new(min_index,:)];
    if(abs(sumS-sumP)<0.001)%退出条件             %%迭代的条件
        break;
    end
    pop=selection(E_new_new,N,s,c);               %%选择算子
end
[a,min_index]=min(min_dis);                       %%min函数可以求得 该向量min_dis最小值和最小值所在的index
time1=1:time+1;        %%为什么要加1,因为没有迭代前,原始种群也有一个最小值
% 
time1=1:time+1;        %%为什么要加1,因为没有迭代前,原始种群也有一个最小值
figure%画平均适应度折线图
plot(time1,min_dis,'k.');
grid on;
title('每代最小值散点图');
xlabel('迭代的次数');
ylabel('最短的距离');

%%
最优序列储存在E_new_new(最后一行)中,最小距离储存于min_dis(最后一行)。

适应度函数

距离取倒数作为适应度,分母越小(距离越小,求最小距离),适应度越大。

function [individual_fit,num,min_distance,index] = GroupFit(city_distance,N,pop,s)%种群适应度
    individual_distance=zeros(s,1);     %%目的是求得最短路劲的值,给定INDIVIDUAL为sx1的矩阵
    for j=1:s
        sum_distance=0;
        for i=1:N-1
            sum_distance=sum_distance+city_distance(pop(j,i),pop(j,i+1));
        end
        sum_distance=sum_distance+city_distance(pop(j,N),pop(j,1));        %%最后回到起点
        individual_distance(j,1)=sum_distance;                             %%individual_distance是一个列矩阵
    end
    [min_distance,index]=min(individual_distance);    %%找到最短的距离
    individual_fit=1./individual_distance;           %%分子越小,分母越大
    num=0;
    for i=1:s
      num=num+individual_fit(i,1);
    end
end

选择算子

function [pop_ok]=selection(pop,N,s,c)%选择父代
    pop=sortrows(pop,N+1);
    for i=1:c
        pop(i,:)=pop(s+1-i,:);
    end
    randIndex=randperm(size(pop,1));
    pop=pop(randIndex,:);
    pop_ok=pop;
end

交叉算子

function [a,b]=Crossover(pop1,pop2,crosspoint,N)%交叉
    A=pop1;
    if(crosspoint(:,1)<crosspoint(:,2))    %%由于交叉的位置是随机的,有可能是从小到大,也有可能是从大到小
        pop1(crosspoint(:,1):crosspoint(:,2))=pop2(crosspoint(:,1):crosspoint(:,2));
        pop2(crosspoint(:,1):crosspoint(:,2))=A(1,crosspoint(:,1):crosspoint(:,2));
        while 1
            tbl = tabulate(pop1(1:N));   %%Create Frequency table
            if (tbl(:,3)<=(100/N))   %%如果个数超过一个就退出循环
                break;
            end
            [pop1,pop2]=SwapRepeat(tbl,pop1,pop2,crosspoint(:,1),crosspoint(:,2),N);%%执行存在重复的编码问题
        end
    else
        pop1(crosspoint(:,2):crosspoint(:,1))=pop2(crosspoint(:,2):crosspoint(:,1));
        pop2(crosspoint(:,2):crosspoint(:,1))=A(1,crosspoint(:,2):crosspoint(:,1));
        while 1
            tbl = tabulate(pop1(1:N));
            if (tbl(:,3)<=(100/N))
                break;
            end
            [pop1,pop2]=SwapRepeat(tbl,pop1,pop2,crosspoint(:,2),crosspoint(:,1),N);
        end
    end
    a=pop1;b=pop2;
end

去重函数(matlab自带去重函数 unique函数,所以个人认为该部分可以简化,如果懒得改那就直接用

function [a,b]=SwapRepeat(tbl,pop1,pop2,c1,c2,N)%基因去重
    i=100/N;
    for k=1:(c1-1)              %%从左侧开始找起
        if tbl(pop1(k),3)>i     %%数重复出现
            kk=find(pop1(c1:c2)==pop1(k))+c1-1;  %%发现重复数在原本数组中的位置
            kkk=pop1(k);        %%备份自变量在k时的值
            pop1(k)=pop2(kk);   %%pop1和pop2
            pop2(kk)=kkk;       %%pop2
        end
    end
    for k=c2+1:N                %%从右侧开始找起
        if tbl(pop1(k),3)>i
            kk=find(pop1(c1:c2)==pop1(k))+c1-1;
            kkk=pop1(k);
            pop1(k)=pop2(kk);
            pop2(kk)=kkk;
        end
    end
    a=pop1;
    b=pop2;
end

变异算子

function [a]=Mutation(pop0,N)%变异
    crosspoint=rand(1,2);       %%随机产生一个1x2的,范围在0-1内的向量Vector
    crosspoint=floor(crosspoint*N)+1;
    if(crosspoint(:,1)<crosspoint(:,2))
        sub=pop0(crosspoint(:,1):crosspoint(:,2));
        sub=SwapGene(sub,crosspoint(:,1),crosspoint(:,2));
        pop0(crosspoint(:,1):crosspoint(:,2))=sub;
    else
        sub=pop0(crosspoint(:,2):crosspoint(:,1));
        sub=SwapGene(sub,crosspoint(:,2),crosspoint(:,1));
        pop0(crosspoint(:,2):crosspoint(:,1))=sub;
    end
    a=pop0;
end

其中算子 SwapGene.m

function [a]=SwapGene(sub,c1,c2)%交换
    kk=ceil((c2-c1)/2);
    kkk=(c2-c1)+2; %本来数量角度需要加1,但是为了能够表示最后一位数之后的数,就需要加2,反正代码可以跑。
    for k=1:kk
        kkkk=sub(k);
        sub(k)=sub(kkk-k);
        sub(kkk-k)=kkkk;
    end
    a=sub;
end
  • 4
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值