遗传算法(GA)求解VRPTW问题(附MATLAB代码)这篇推文中的链接失效,请各位小伙伴点击左下方阅读原文,提取代码( 提取码:2996)


今天小编为大家继续讲解一下遗传算法(GA)求解VRPTW问题(附MATLAB代码)这篇推文中的MATLAB代码,这份代码一共包含35个函数,昨天讲解了前18个函数,今天咱们继续讲解。


19 | Fitness函数计算适应度值

因为目标函数越小越好,而在选择操作时需要将适应度值大的个体选择出来,所以这里我们将适应度函数设为惩罚函数的倒数



%%      @作者:随心390%      @微信公众号:优化算法交流地%%% 适配值函数     %输入:%个体的长度%输出:%个体的适应度值function FitnV=Fitness(len)FitnV=1./len;


20 | Select函数选择操作

选择操作我们就采用轮盘赌选择按照适应度值的大小选择若干个适应度值大的个体进行后续的交叉、变异以及局部搜索操作。



%%      @作者:随心390%      @微信公众号:优化算法交流地%%% 选择操作%输入%Chrom 种群%FitnV 适应度值%GGAP:选择概率%输出%SelCh  被选择的个体function SelCh=Select(Chrom,FitnV,GGAP)NIND=size(Chrom,1);NSel=max(floor(NIND*GGAP+.5),2);ChrIx=Sus(FitnV,NSel);SelCh=Chrom(ChrIx,:);


21 | Sus函数确认被选择个体的索引号



%%      @作者:随心390%      @微信公众号:优化算法交流地%% 输入:%FitnV  个体的适应度值%Nsel   被选择个体的数目% 输出:%NewChrIx  被选择个体的索引号function NewChrIx = Sus(FitnV,Nsel)
% Identify the population size (Nind)   [Nind,ans] = size(FitnV);
% Perform stochastic universal sampling   cumfit = cumsum(FitnV);   trials = cumfit(Nind) / Nsel * (rand + (0:Nsel-1)');   Mf = cumfit(:, ones(1, Nsel));   Mt = trials(:, ones(1, Nind))';   [NewChrIx, ans] = find(Mt < Mf & [ zeros(1, Nsel); Mf(1:Nind-1, :) ] <= Mt);
% Shuffle new population   [ans, shuf] = sort(rand(Nsel, 1));   NewChrIx = NewChrIx(shuf);


22 | Recombin函数交叉操作




















%%      @作者:随心390%      @微信公众号:优化算法交流地%%% 交叉操作% 输入%SelCh  被选择的个体%Pc     交叉概率%输出:% SelCh 交叉后的个体function SelCh=Recombin(SelCh,Pc)NSel=size(SelCh,1);for i=1:2:NSel-mod(NSel,2)    if Pc>=rand %交叉概率Pc        [SelCh(i,:),SelCh(i+1,:)]=OX(SelCh(i,:),SelCh(i+1,:));    endend


23 | OX函数将两个个体进行交叉

交叉操作我们使用OX交叉的方式,小编用实例进行演示。比如说有两个父代个体为
父代1:1 2 3 4 5 6 7 8
父代2:8 7 6 5 4 3 2 1
这时随机选择两个交叉位置a和b,比如说a=3,b=6,那么交叉的片段为:
父代1:1 2 | 3 4 5 6 | 7 8
父代2:8 7 | 6 5 4 3 | 2 1
然后将父代2的交叉片段移动到父代1的前面,将父代1的交叉片段移动到父代2的前面,则这两个父代个体变为:
父代1:6 5 4 3 1 2 3 4 5 6 7 8
父代2:3 4 5 6 8 7 6 5 4 3 2 1
然后从前到后把第2个重复的基因位删除掉,我们先把两个父代个体中重复的基因位标记出来:
父代1:
3 1 2 6 7 8
父代2:
8 7 4 3 2 1
然后把第2个重复的基因位删除,形成两个子代个体。
子代1:6 5 4 3 1 2 7 8
子代2:3 4 5 6 8 7 2 1




%%      @作者:随心390%      @微信公众号:优化算法交流地%%输入:%a和b为两个待交叉的个体%输出:%a和b为交叉后得到的两个个体function [a,b]=OX(a,b)L=length(a);while 1    r1=randsrc(1,1,[1:L]);    r2=randsrc(1,1,[1:L]);    if r1~=r2        s=min([r1,r2]);        e=max([r1,r2]);        a0=[b(s:e),a];        b0=[a(s:e),b];        for i=1:length(a0)            aindex=find(a0==a0(i));            bindex=find(b0==b0(i));            if length(aindex)>1                a0(aindex(2))=[];            end            if length(bindex)>1                b0(bindex(2))=[];            end            if i==length(a)                break            end        end        a=a0;        b=b0;        break    endend


24 | Mutate函数变异操作

变异操作比较简洁,就是先生成两个位置,然后将这两个位置上的基因进行交换。比如说,有如下个体:
父代:1 2 3 4 5 6 7 8
这时随机选择两个变异位置a和b,比如说a=3,b=6,那么交换后的个体为:
子代:1 2 6 4 5 3 7 8



%%      @作者:随心390%      @微信公众号:优化算法交流地%%% 变异操作%输入:%SelCh  被选择的个体%Pm     变异概率%输出:% SelCh 变异后的个体function SelCh=Mutate(SelCh,Pm)[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));    endend


25 | LocalSearch函数局部搜索

这里的局部搜索我们使用了大规模邻域搜索算法(LNS)通俗讲解这篇推文中的破坏修复的思想。简单来说我们使用破坏算子从当前解中移除若干个顾客,然后再使用修复算子将被移除的顾客重新插回到破坏的解中。



%%      @作者:随心390%      @微信公众号:优化算法交流地%%% 局部搜索函数%输入:SelCh               被选择的个体%输入:cusnum              顾客数目%输入:cap                 最大载重量%输入:demands             需求量%输入:a                   顾客时间窗开始时间[a[i],b[i]]%输入:b                   顾客时间窗结束时间[a[i],b[i]]%输入:L                   配送中心时间窗结束时间%输入:s                   客户点的服务时间%输入:dist                距离矩阵,满足三角关系,暂用距离表示花费c[i][j]=dist[i][j]%输出:SelCh               进化逆转后的个体function SelCh=LocalSearch(SelCh,cusnum,cap,demands,a,b,L,s,dist,alpha,belta)D=15;                                                       %Remove过程中的随机元素toRemove=15;                                                %将要移出顾客的数量[row,N]=size(SelCh);for i=1:row    [VC,NV,TD,violate_num,violate_cus]=decode(SelCh(i,:),cusnum,cap,demands,a,b,L,s,dist);    CF=costFuction(VC,a,b,s,L,dist,demands,cap,alpha,belta);    %Remove    [removed,rfvc] = Remove(cusnum,toRemove,D,dist,VC);    %Re-inserting    [ReIfvc,RTD] = Re_inserting(removed,rfvc,L,a,b,s,dist,demands,cap);    %计算惩罚函数    RCF=costFuction(ReIfvc,a,b,s,L,dist,demands,cap,alpha,belta);    if RCF<CF        chrom=change(ReIfvc,N,cusnum);        if length(chrom)~=N            record=ReIfvc;            break        end        SelCh(i,:)=chrom;    endend


26 | Remove函数先从原有顾客集合中随机选出一个顾客,然后根据相关性再依次移出需要数量的顾客


用符号表示当前解,表示将要被移走的客户,表示被移走的客户组成的集合,表示移走的客户数目,表示从中移走个客户后剩余的部分解。则Remove过程如下:首先,从中随机移走一个客户到中,作为集合的第一个元素。剩下的个元素按照如下方法来选定:每次从集合中随机选一个客户,将中剩余的客户按照与的相关性由小到大的顺序排列。从中选出与的相关性最大的客户,从中移走,并把它加入到中去。重复该过程次,直到剩下的个元素都选好。相关性的定义为:

其中表示将标准化后的值,在[0,1]之间表示之间的欧式距离表示是否在同一条路径上,即是否由同一辆车服务。如何在同一条路径上时为0,否则为1。也就是说地理位置相距很近的两个客户的相关性比相距很远的要大在同一条路径上的两个客户的相关性比不在同一条路径上的要大实际情况中,不存在完美的相关性函数,如果过度依赖相关性函数选择被移出的客户可能会出现“鼠目寸光”的情况。为避免这种情况的出现,Shaw在算法中加入了随机元素,即,它的结果是相关性大小序列中的一个客户,即假设
然后将中剩余的客户按照与相关性由大到小顺序排列,得到排序后的序列为lst,因此上述过程的结果为lst[index]可以看出当D为1时,被移出的客户是完全随机选择的;当D等于正无穷时,即接近选择相关性最大的客户。也就是说D的值越大,越有利于相关性大的客户。
Remove过程的伪代码如下所示:



%%      @作者:随心390%      @微信公众号:优化算法交流地%%% Remove操作,先从原有顾客集合中随机选出一个顾客,然后根据相关性再依次移出需要数量的顾客%输入cusnum               顾客数量%输入toRemove             将要移出顾客的数量%输入D                    随机元素%输入dist                 距离矩阵%final_vehicles_customer  每辆车所经过的顾客%removed                  被移出的顾客集合%rfvc                     移出removed中的顾客后的final_vehicles_customerfunction [removed,rfvc] = Remove(cusnum,toRemove,D,dist,final_vehicles_customer)
%% Removeinplan=1:cusnum;            %所有顾客的集合visit=ceil(rand*cusnum);    %随机从所有顾客中随机选出一个顾客inplan(inplan==visit)=[];   %将被移出的顾客从原有顾客集合中移出removed=[visit];            %被移出的顾客集合while length(removed)<toRemove    nr=length(removed);             %当前被移出的顾客数量    vr=ceil(rand*nr);               %从被移出的顾客集合中随机选择一个顾客    nip=length(inplan);             %原来顾客集合中顾客的数量    R=zeros(1,nip);                 %存储removed(vr)与inplan中每个元素的相关性的数组    for i=1:nip        R(i)=Relatedness( removed(vr),inplan(i),dist,final_vehicles_customer);   %计算removed(vr)与inplan中每个元素的相关性    end    [SRV,SRI]=sort(R,'descend');            lst=inplan(SRI);                %将inplan中的数组按removed(vr)与其的相关性从高到低排序    vc=lst(ceil(rand^D*nip));       %从lst数组中选择一个客户    removed=[removed vc];           %向被移出的顾客集合中添加被移出的顾客    inplan(inplan==vc)=[];          %将被移出的顾客从原有顾客集合中移出endrfvc=final_vehicles_customer;               %移出removed中的顾客后的final_vehicles_customernre=length(removed);                        %最终被移出顾客的总数量NV=size(final_vehicles_customer,1);         %所用车辆数for i=1:NV    route=final_vehicles_customer{i};    for j=1:nre        findri=find(route==removed(j),1,'first');        if ~isempty(findri)            route(route==removed(j))=[];        end    end    rfvc{i}=route;end
[ rfvc,~ ] = deal_vehicles_customer( rfvc );
end


27 | Relatedness函数求顾客i与顾客j之间的相关性



%%      @作者:随心390%      @微信公众号:优化算法交流地%%% 求顾客i与顾客j之间的相关性%输入i,j    顾客%输入dist   距离矩阵%输入vehicles_customer        每辆车所经过的顾客,用于判断i和j是否在一条路径上%如果在一条路径上为0,不在一条路径上为1%输出Rij      顾客i和顾客j的相关性function Rij=Relatedness( i,j,dist,vehicles_customer )n=size(dist,1)-1;           %顾客数量,-1是因为减去配送中心NV=size(vehicles_customer,1);       %配送车辆数%计算cij'd=dist(i+1,j+1);[md,mindex]=max((dist(i+1,2:end)));c=d/md;%判断i和j是否在一条路径上V=1;                %设初始顾客i与顾客j不在同一条路径上for k=1:NV    route=vehicles_customer{k};         %该条路径上经过的顾客    findi=find(route==i,1,'first');     %判断该条路径上是否经过顾客i    findj=find(route==j,1,'first');     %判断该条路径上是否经过顾客j    %如果findi和findj同时非空,则证明该条路径上同时经过顾客i和顾客j,则V=0    if ~isempty(findi)&&~isempty(findj)        V=0;    endend%计算顾客i与顾客j的相关性Rij=1/(c+V);
end