引用链接
CVRP问题-31个客户点的车辆容量和行驶距离限制问题的GA解决方案
不同点与相同点
不同点:①上文链接中使用了Python进行编程计算,本文使用MATLAB编程;②上文使用了所谓的全局最优染色体、自身最优染色体及染色体本身的一定选择概率作为染色体的父代,本文使用了普遍的精英选择操作产生父代。
相同点:产生染色体编码时,先随机选择一个客户,再根据距离选择下一个距离最近的客户加入路径。
代码
说明:下面代码也可以解决7个和12个客户点的物流配送问题(在下一边文章中),但是下一篇文章中的代码无法解决本文31个客户点的配送问题;区别在于:本文使用了基于距离依次进行客户点的选择。
①主函数main
% 问题:找到精确解的概率较低(但增加迭代次数后,得到次优解的概率较大)
%编码:自然数编码,先随机选择一个客户点,再根据距离依次选择下一个客户点,直到所有客户都遍历到
%解码:根据客户需求量和车辆行驶距离进行分配车辆
%选择:锦标赛和轮盘赌%%轮盘赌的效果完全没有锦标赛的效果好
%交叉:顺序交叉
%变异:两个随机位置互换
tic;
clear;
clc;
close all;
%% 用importdata读取文件并计算各个点之间的距离
CVRP=importdata('c101.txt');
[row,~] = size(CVRP);
dist = zeros(row,row);
for i = 1:row-1
for j = i+1:row
dist(i,j) = sqrt(sum((CVRP(i,2:3)-CVRP(j,2:3)).^2));
end
end
for i = 1:row
dist(i,i) = inf;
end
dist = dist + dist';
row = row - 1;%客户点的数量
%% 参数设置
[N,D,volume,Dist,C0,C1,Iter,Pc,Pm] = constant(row);
GA = zeros(N,D+1);%GA数组位置申请
Path = inf(N,2*D+2);%定义路径的数组,前2*D存放具体路径,2*D+1、2*D+2分别为车辆书及路径长度
%% 初始化
[GA,Path] = initialization(CVRP,dist,N,D,volume,Dist,C0,C1,GA,Path);
%% 迭代
[GA,Path,Gbest] = iteration(N,D,volume,Dist,C0,C1,Iter,Pc,Pm,CVRP,dist,GA,Path);
%% 命令行显示
[value,row] = min(Path(:,2*D+2));
[~,col] = find(Path(row,1:2*D)==1);
[~,k] = size(col);
for i = 1:k-1
disp(['车辆',num2str(i),':',num2str(Path(row,col(i):col(i+1)))]);
end
disp(['最短总路程:',num2str(Path(row,2*D+2))]); %输入多个参数进行显示,除了用num2str显示数值,还需要将所有输入加上方括号!!
plot(Gbest);
draw(CVRP);
hold on;
grid on ;
for i = 1:k-1
c = Path(row,col(i):col(i+1));
plot(CVRP(c,2),CVRP(c,3));
end
toc;
②参数函数constant
function [N,D,volume,Dist,C0,C1,Iter,Pc,Pm] = constant(row)
N = 100;
D = row;
%车辆的容量
volume = 120;
Dist = 250; %车辆最远行驶距离
C0 = 30; %车辆启动成本
C1 = 1; %车辆的行驶成本
Iter = 2000; %迭代数
Pc = 0.8;%交叉算子
Pm = 0.1;%变异算子
③初始化函数initialization
function [GA,Path] = initialization(CVRP,dist,N,D,volume,Dist,C0,C1,GA,Path)
for i = 1:N
dist_matrix = dist;
dist_matrix(:,1) = inf;
k = 1;%路径中节点的个数
GA(i,k) = 1;
while GA(i,k)==1
GA(i,k) = randperm(D+1,1);%随机选择一个起始客户点
end
dist_matrix(:,GA(i,k)) = inf;
while k<=30
k = k + 1;
[~,col] = min(dist_matrix(GA(i,k-1),:));
GA(i,k) = col;
dist_matrix(:,GA(i,k)) = inf;
end
Path(i,:) = pathdecode(CVRP,dist,D,volume,Dist,GA(i,1:D));
GA(i,D+1) = fitness(Path(i,:),D,dist,C0,C1);%计算距离
Path(i,2*D+2) = GA(i,D+1);
end
④编码函数pathdecode
function path = pathdecode(CVRP,dist,D,volume,Dist,x)
%解码
path = inf(1,2*D+2);%存放解码后的路径
k = 1;%路径中已有的节点个数
path(k) = 1;%第一个节点是配送中心
heavy = 0;%初始的车辆载重为0
numcar = 0;%车辆数初始为0
d = 0;%初始车辆的行驶距离
for j = 1:D
k = k + 1;%已有的节点个数加1
if (heavy+CVRP(x(j),4)<volume)&&(d + dist(path(k-1),x(j)) + dist(x(j),1)<Dist)
path(k) = x(j);
heavy = heavy + CVRP(x(j),4);%累计车辆的载重
d = d + dist(path(k-1),x(j));%累计车辆的行驶距离
else
heavy = CVRP(x(j),4);%重置车辆的载货量
path(k) = 1;%添加一个配送中心节点序号
k = k + 1;
path(k) = x(j);%下一个加入路径的节点
d = dist(path(k-1),path(k));%重置车辆的行驶距离
numcar = numcar + 1;%车辆数加1
end
end
k = k + 1;
numcar = numcar + 1;
path(k) = 1;%车辆回到配送中心
path(2*D+1) = numcar;%记录车辆数
⑤适应度函数fitness
function y = fitness(path,D,dist,C0,C1)
y = 0;%计算路径长度
pathlong = path(2*D+1)+1+D;
for i = 1:pathlong-1
y = y + dist(path(i),path(i+1));
%位置 1 处的索引无效。数组索引必须为正整数或逻辑值。原因是函数调用的参数输入问题或者确实是本行有问题!!!!
end
y = C1*y + C0*path(2*D+1);
⑥迭代函数iteration
function [GA,Path,Gbest] = iteration(N,D,volume,Dist,C0,C1,Iter,Pc,Pm,CVRP,dist,GA,Path)
Gbest = zeros(1,Iter);%存放每次迭代的最佳距离值
for iter = 1:Iter
Parent1 = select(N,D,GA);
Parent2 = select(N,D,GA);%分别通过锦标赛选择两个父代群
GA = crossover(N,D,Pc,GA,Parent1,Parent2);%交叉操作
GA = mutation(N,D,Pm,GA);%变异操作
for i = 1:N
Path(i,:) = pathdecode(CVRP,dist,D,volume,Dist,GA(i,1:D));
GA(i,D+1) = fitness(Path(i,:),D,dist,C0,C1);
Path(i,2*D+2) = GA(i,D+1);
end
Gbest(1,iter) = min(GA(:,D+1));
end
⑦选择函数select
function x = select(N,D,GA)
%锦标赛
x = GA;
for i = 1:N
candi = randperm(N,5);%在所有染色体中随机选取5个个体
[fit,row] = min(GA(candi,D+1));%找路径最短的个体
if fit<x(i,D+1)
x(i,:) = GA(candi(row),:);%如果5个中最小的染色体产生的距离小于原数组中的染色体距离,则存放在父代的染色体群体中
end
end
⑧交叉函数crossover
function x = crossover(N,D,Pc,GA,Parent1,Parent2)
%顺序交叉
x = GA;
for i = 1:N
parent1 = Parent1(i,:);
parent2 = Parent2(i,:);%分别选择两个父代群中的第i个染色体进行交叉
if rand>Pc
x(i,:) = parent1;
if parent1(D+1)>parent2(D+1)
x(i,:) = parent2;%当随机数大于交叉概率,选择两个父代中距离最短的那个继承下来;
end
else%当随机数小于交叉概率时,进行交叉操作
point = randperm(D,2);
point1 = min(point);
point2 = max(point);%指定大小交叉点
while max(point)-min(point)>29||max(point)==min(point)
%交叉点之间的距离≥5则交叉不会产生变化
point = randperm(D,2);%随机选择1-D范围内的两个不同的整数
point1 = min(point);
point2 = max(point);%指定大小交叉点
end
x(i,point1:point2) = parent1(point1:point2);%子代交叉点之间是父代1交叉点之间的序号
parent2_1 = [parent2(point2+1:D) parent2(1:point2)];%父代2从交叉点2后一位开始循环一轮到该交叉点
for cross = point1:point2
parent2_1(parent2_1==x(i,cross)) = [];%将parent2_1中与子代中重复的序号去除
end
if point1 == 1
x(i,point2+1:D) = parent2_1;%如果交叉点1是第二位,那直接赋值
elseif point2 ==D
x(i,1:point1-1) = parent2_1;%如果交叉点2是最后一位,也可以直接赋值
else
x(i,1:point1-1) = parent2_1(1:point1-1);%交叉点前的一段序号赋值
x(i,point2+1:D) = parent2_1(point1:end);%交叉点后的一段序号赋值
end
end
end
⑨变异函数mutation
function x = mutation(N,D,Pm,GA)
%变异
x = GA;
for i = 1:N
if rand <= Pm%随机数小于变异概率则进行变异操作
point1 = 0;
point2 = 0;
while point1 == point2%避免两个变异点相同
point = randi([1,D],1,2);%产生2-D之间的两个不同的随机整数
point1 = min(point);
point2 = max(point);%两个变异点根据大小赋值
end
temp = x(i,point1);
x(i,point1) = x(i,point2);
x(i,point2) = temp;%两个变异点互换位置
end
end
⑩绘图函数draw
function draw(CVRP)
figure(5);
% axis([0 110 0 110]);
title('配送路线');
hold on ;
grid on ;
plot(CVRP(:,2),CVRP(:,3),'r.','linewidth',10);%不用循环,
end
结果
说明:除了用算法求解函数的最值问题,其他tsp,cvrp问题可能并不是每次都得到最优解。