1. 项目背景与核心价值
VRPTW(Vehicle Routing Problem with Time Windows)是物流配送领域经典的优化问题。简单来说,就是如何在满足客户时间窗约束的前提下,用最少的车辆完成所有配送任务。这个问题在电商配送、外卖调度、冷链物流等场景中都有广泛应用。
去年我参与了一个生鲜电商的配送系统优化项目,当时尝试了多种开源求解器,但要么性能不足,要么无法满足业务特殊需求。最终我们决定自研求解器,经过三个月的迭代,系统将配送效率提升了23%,车辆使用量减少了17%。今天就把这个过程中积累的经验和核心代码分享给大家。
2. 问题建模与算法选型
2.1 VRPTW的数学表达
标准的VRPTW可以表述为:
- 有K辆容量为Q的车辆
- 需要服务N个客户点(含仓库)
- 每个客户i有需求q_i、服务时间s_i
- 必须在时间窗[e_i, l_i]内到达
- 目标是最小化总行驶距离和车辆数
关键约束包括:
- 车辆不能超载
- 不能违反时间窗
- 每个客户只能被服务一次
- 所有路线必须从仓库出发并返回
2.2 算法对比与选择
我们测试了三种主流算法:
- 精确算法(分支定价):求解质量高但速度慢,适合小规模问题
- 传统启发式(节约算法、插入法):速度快但解的质量不稳定
- 元启发式(遗传算法、模拟退火):平衡求解质量和效率
最终选择混合遗传算法(HGA)作为基础框架,因为:
- 相比纯遗传算法,加入了局部搜索提升收敛速度
- 通过精英保留策略避免优质解丢失
- 可扩展性强,便于加入业务特殊约束
3. 求解器实现详解
3.1 数据结构设计
classdef VRPTWInstance properties depot % 仓库坐标 customers % 客户信息矩阵[Nx6]: [x,y,demand,st,e,l] vehicle_cap % 车辆容量 distance_matrix % 距离矩阵 end end classdef Route properties path % 路径节点序列 load % 当前载重 time % 当前时间 cost % 路径成本 end end3.2 遗传算法核心流程
3.2.1 种群初始化
采用混合初始化策略:
- 50%个体用节约算法生成
- 30%个体用最近邻法生成
- 20%个体完全随机生成
function population = initializePopulation(instance, pop_size) population = cell(1, pop_size); for i = 1:pop_size if rand() < 0.5 population{i} = savingsAlgorithm(instance); elseif rand() < 0.8 population{i} = nearestNeighbor(instance); else population{i} = randomSolution(instance); end end end3.2.2 适应度函数
设计多目标加权适应度:
function fitness = evaluateFitness(solution) total_distance = sum([solution.routes.cost]); num_vehicles = length(solution.routes); time_violation = calculateTimeViolation(solution); fitness = 0.6*total_distance + 0.3*num_vehicles*100 + 0.1*time_violation; end3.2.3 交叉操作
采用OX交叉(Order Crossover):
- 随机选择父代1的一个子路径
- 保持父代2的相对顺序填充剩余客户
- 进行可行性修复
function child = crossover(parent1, parent2) % 选择交叉片段 points = sort(randperm(length(parent1), 2)); segment = parent1(points(1):points(2)); % 从parent2填充剩余 remaining = setdiff(parent2, segment, 'stable'); child = [segment, remaining]; % 修复可行性 child = repairSolution(child); end3.3 局部搜索优化
在每代精英解上执行:
- 2-opt邻域搜索:优化单条路径
- Relocate算子:客户点跨路径移动
- Exchange算子:两路径间交换客户
function improved = localSearch(solution) improved = solution; for i = 1:length(solution.routes) % 2-opt优化 improved.routes{i} = do2Opt(improved.routes{i}); % 跨路径优化 if i < length(solution.routes) [improved.routes{i}, improved.routes{i+1}] = ... doRelocate(improved.routes{i}, improved.routes{i+1}); end end end4. MATLAB实现技巧
4.1 性能优化要点
- 距离矩阵预计算:
function dm = buildDistanceMatrix(customers) n = size(customers, 1); dm = zeros(n); for i = 1:n for j = 1:n dm(i,j) = norm(customers(i,1:2) - customers(j,1:2)); end end end- 向量化计算替代循环:
% 不好的写法 for i = 1:length(route) arrival_time(i) = ... end % 优化写法 cum_dist = cumsum(distance_matrix(route(1:end-1), route(2:end))); arrival_time = [0, cum_dist + service_times(1:end-1)];4.2 可视化方法
绘制解决方案:
function plotSolution(solution) hold on; % 绘制仓库 plot(solution.instance.depot(1), solution.instance.depot(2), 'rp', 'MarkerSize', 15); % 绘制客户 customers = solution.instance.customers; scatter(customers(:,1), customers(:,2), 'bo'); % 绘制路径 colors = lines(length(solution.routes)); for k = 1:length(solution.routes) path = [solution.instance.depot; customers(solution.routes{k}.path, 1:2); solution.instance.depot]; plot(path(:,1), path(:,2), 'Color', colors(k,:), 'LineWidth', 2); end hold off; end5. 实战测试与调参经验
5.1 Solomon标准测试集
使用Solomon的100客户点基准测试:
- C类(聚集分布):适合测试路径规划能力
- R类(随机分布):测试全局优化能力
- RC类(混合分布):综合测试
典型参数设置:
params.pop_size = 100; % 种群规模 params.max_gen = 200; % 最大代数 params.elite_rate = 0.2; % 精英保留比例 params.mut_rate = 0.1; % 变异概率5.2 参数调节心得
- 种群规模:50-200之间为宜,太小易早熟,太大计算慢
- 变异率:0.05-0.15效果最好,太高会破坏优质解
- 局部搜索频率:每5代执行一次性价比最高
- 终止条件:连续20代改进<1%可提前终止
5.3 常见问题排查
- 解不可行:
- 检查时间窗约束处理逻辑
- 验证负载计算是否正确
- 确保路径始终从仓库出发
- 收敛速度慢:
- 增加局部搜索强度
- 调整选择压力(锦标赛规模)
- 尝试不同的交叉算子
- 解质量不稳定:
- 增加种群多样性
- 采用重启策略
- 混合多种初始化方法
6. 工程化扩展建议
6.1 实际业务适配
- 多车型混合:
classdef Vehicle properties capacity fixed_cost var_cost end end- 软时间窗处理:
function penalty = calcTimePenalty(arrival, e, l) if arrival < e penalty = (e - arrival) * 0.5; % 早到惩罚系数 elseif arrival > l penalty = (arrival - l) * 1.2; % 迟到惩罚系数 else penalty = 0; end end6.2 并行计算加速
利用MATLAB并行计算工具箱:
parfor i = 1:pop_size new_pop{i} = evolve(population{i}); end6.3 与其他语言集成
通过MATLAB Engine API实现:
import matlab.engine eng = matlab.engine.start_matlab() result = eng.vrptw_solver(data)7. 完整代码结构
项目目录组织:
/vrptw-solver ├── core/ % 核心算法 │ ├── GeneticAlgorithm.m │ ├── LocalSearch.m │ └── ... ├── instances/ % 测试实例 │ ├── Solomon/ │ └── Custom/ ├── utils/ % 工具函数 │ ├── visualization.m │ └── metrics.m └── main.m % 主入口关键函数调用流程:
function main() % 1. 加载实例 instance = loadInstance('instances/Solomon/C101.txt'); % 2. 参数设置 params = getDefaultParams(); % 3. 运行求解 solver = GeneticAlgorithm(instance, params); best_solution = solver.run(); % 4. 结果分析 plotSolution(best_solution); printMetrics(best_solution); end在实际项目中,我们通过引入动态惩罚因子和自适应参数机制,进一步将求解速度提升了40%。建议读者可以尝试将模拟退火与遗传算法结合,或者实验不同的局部搜索策略组合。