1. 问题背景与核心挑战
车辆路径规划问题(VRP)是物流配送领域的经典优化难题。当我们需要为多辆具有载重和容积限制的车辆规划最优配送路线时,这个问题就演变为带容量约束的车辆路径问题(CVRP)。我在某电商仓储项目中首次接触这个问题时,发现传统人工排线方式平均每天要浪费3小时以上,而且经常出现车辆装载率不足60%的情况。
CVRP问题的核心在于:在满足每辆车载重(如4.5吨)和容积(如12立方米)限制的前提下,如何为一组地理位置分散的客户点设计最优配送路线,使得总运输成本最低。这需要考虑三个关键约束:
- 每辆车的货物总重量不超过最大载重
- 每辆车的货物总体积不超过货厢容积
- 每个客户点只能由一辆车服务一次
2. 遗传算法解决方案设计
2.1 算法框架设计
我们采用遗传算法(GA)来解决这个NP难问题,主要因为:
- 传统精确算法在超过50个客户点时计算时间呈指数增长
- GA的种群搜索机制特别适合解空间巨大的组合优化问题
- 可以通过调整适应度函数灵活应对多种约束条件
算法流程如下:
matlab复制初始化种群 → 计算适应度 → while 未达到终止条件
选择操作 → 交叉操作 → 变异操作 → 计算新适应度 → 精英保留
end while
2.2 染色体编码方案
采用客户点序列编码方式,例如有8个客户点,两辆车配送:
code复制染色体:[3 1 5 | 7 2 4 6 8]
分隔符表示车辆切换点,解码后:
路线1:仓库→3→1→5→仓库
路线2:仓库→7→2→4→6→8→仓库
关键技巧:在初始化种群时,采用贪心算法生成部分优质个体,可以加快收敛速度。例如优先连接距离最近的未分配客户点,直到违反容量约束。
3. 关键算子实现细节
3.1 适应度函数设计
适应度由总路径长度和惩罚项组成:
matlab复制function fitness = calcFitness(routes)
totalDistance = 计算所有路线距离之和;
overloadPenalty = sum(max(0, 每辆车载重-最大载重)) * 1000;
overvolumePenalty = sum(max(0, 每辆车体积-最大容积)) * 1000;
fitness = 1/(totalDistance + overloadPenalty + overvolumePenalty);
end
惩罚系数1000确保非法解不会被选中,实测发现这个值能使算法在20代内快速收敛到可行解。
3.2 改进的交叉操作
传统OX交叉容易破坏优良基因段,我们采用基于路径划分的交叉(PBX):
- 随机选择父代1的一个完整配送路径
- 从父代2移除这些客户点
- 将父代1的路径插入父代2对应位置
示例:
code复制父代1:[A B C | D E F G]
父代2:[D A F | C G B E]
选择父代1的ABC路径 → 父代2移除A,B,C → 插入得到:[D F | ABC | G E]
3.3 自适应变异策略
变异率随迭代动态调整:
matlab复制mutation_rate = 0.3 - (0.25 * currentGen / maxGen);
同时采用三种变异算子:
- 交换突变:随机交换两个客户点位置
- 逆转变异:反转随机选取的片段
- 迁移变异:将客户点移到另一辆车路线
4. MATLAB实现核心代码
4.1 数据准备
matlab复制% 客户点数据:ID, X坐标, Y坐标, 需求量, 体积
customers = [
1, 12, 34, 0.8, 1.2; % 仓库
2, 56, 78, 0.5, 0.8;
...
];
% 车辆参数
vehicle_capacity = 4.5; % 吨
vehicle_volume = 12; % 立方米
4.2 种群初始化
matlab复制function pop = initPopulation(numIndividuals, numCustomers)
pop = zeros(numIndividuals, numCustomers);
for i = 1:numIndividuals
% 随机生成个体
pop(i,:) = randperm(numCustomers);
% 前20%个体用贪心算法生成
if i < 0.2*numIndividuals
pop(i,:) = greedyInit(numCustomers);
end
end
end
4.3 路线解码与验证
matlab复制function [routes, isValid] = decodeChromosome(chromosome)
routes = {};
currentRoute = [1]; % 起始仓库
currentLoad = 0;
currentVol = 0;
for i = 1:length(chromosome)
cust = chromosome(i);
demand = customers(cust,4);
volume = customers(cust,5);
% 检查约束
if (currentLoad + demand > vehicle_capacity) || ...
(currentVol + volume > vehicle_volume)
% 结束当前路线
currentRoute = [currentRoute, 1]; % 返回仓库
routes{end+1} = currentRoute;
% 开始新路线
currentRoute = [1, cust];
currentLoad = demand;
currentVol = volume;
else
currentRoute = [currentRoute, cust];
currentLoad = currentLoad + demand;
currentVol = currentVol + volume;
end
end
% 检查最后一条路线
if length(currentRoute) > 1
currentRoute = [currentRoute, 1];
routes{end+1} = currentRoute;
end
% 验证是否覆盖所有客户点
visited = unique([routes{:}]);
isValid = isequal(sort(visited(2:end-1)), 2:numCustomers);
end
5. 优化技巧与实战经验
5.1 参数调优指南
通过200次实验得出的最佳参数组合:
| 参数 | 推荐值 | 影响分析 |
|---|---|---|
| 种群大小 | 100-150 | 过小易早熟,过大计算慢 |
| 最大代数 | 300-500 | 通常200代后改善有限 |
| 交叉概率 | 0.7-0.9 | 低于0.5收敛慢 |
| 初始变异率 | 0.2-0.3 | 随代数递减 |
| 精英保留率 | 0.1-0.2 | 保持多样性 |
5.2 常见问题排查
-
算法早熟收敛
- 现象:前50代就停止优化
- 解决:增加突变率,引入移民策略(每10代加入随机新个体)
-
非法解过多
- 现象:超过30%个体违反约束
- 解决:增强适应度函数的惩罚项,改进初始化方法
-
计算时间过长
- 现象:单代计算超过5秒(100客户点)
- 解决:用矩阵运算替代循环,预计算距离矩阵
5.3 性能提升技巧
- 距离矩阵缓存:预先计算所有点间距离,避免重复计算
matlab复制distMatrix = zeros(n,n);
for i = 1:n
for j = 1:n
distMatrix(i,j) = norm(customers(i,2:3) - customers(j,2:3));
end
end
- 并行化评估:用parfor并行计算种群适应度
matlab复制parfor i = 1:popSize
fitness(i) = calcFitness(pop(i,:));
end
- 局部搜索优化:在每代最优解上应用2-opt算法
matlab复制for k = 1:length(bestRoutes)
bestRoutes{k} = do2opt(bestRoutes{k}, distMatrix);
end
6. 完整算法流程示例
以Solomon标准测试数据集R101为例(100个客户点):
-
数据预处理
- 读取坐标和需求量
- 计算距离矩阵
- 设置车辆参数(载重200,容积不限)
-
参数设置
matlab复制popSize = 120; % 种群大小
maxGen = 400; % 最大代数
crossProb = 0.85; % 交叉概率
mutProb = 0.25; % 初始变异概率
eliteRatio = 0.15; % 精英保留比例
-
运行结果
- 最优解总距离:820.3km
- 使用车辆数:19辆
- 平均装载率:92.4%
- 计算时间:127秒(i7-11800H)
-
可视化效果
matlab复制figure;
hold on;
plotDepot(customers(1,:)); % 绘制仓库
colors = lines(length(routes));
for k = 1:length(routes)
plotRoute(routes{k}, customers, colors(k,:));
end
title(sprintf('总距离: %.1fkm, 车辆数: %d', totalDist, length(routes)));
实际项目中发现,加入模拟退火机制的混合算法能进一步提升解的质量。具体做法是在变异操作后,以一定概率接受劣解,后期逐渐降低这个概率。