MATLAB实现遗传算法优化VRP问题

一、问题描述

车辆路径问题(Vehicle Routing Problem, VRP)是物流配送领域的经典组合优化问题。其基本描述为:一个配送中心(仓库)拥有若干辆容量有限的车辆,需要为多个地理位置分散的客户配送货物,每个客户有特定的需求量。目标是规划出一组总行驶距离最短的车辆配送路线,同时满足以下约束条件:

  • 每条路线从仓库出发,最终返回仓库
  • 每个客户有且仅有一辆车为其服务
  • 每条路线上的客户总需求量不超过车辆的最大载重容量

二、数学模型

2.1 参数定义

符号 含义
V={0,1,2,...,N}V = \{0, 1, 2, ..., N\}V={0,1,2,...,N} 节点集合,0为仓库,1~N为客户
KKK 车辆集合
QQQ 车辆最大载重容量
did_idi 客户 iii 的需求量
cijc_{ij}cij 节点 iiijjj 的距离
xijkx_{ijk}xijk 0-1变量,车辆 kkk 是否从 iii 行驶到 jjj

2.2 目标函数

min⁡∑k∈K∑i∈V∑j∈Vcij⋅xijk\min \sum_{k \in K} \sum_{i \in V} \sum_{j \in V} c_{ij} \cdot x_{ijk}minkKiVjVcijxijk

2.3 约束条件

每个客户被服务一次:
∑k∈K∑j∈Vxijk=1∀i∈V∖{0}\sum_{k \in K} \sum_{j \in V} x_{ijk} = 1 \quad \forall i \in V \setminus \{0\}kKjVxijk=1iV{0}

流量守恒:
∑i∈Vxijk=∑i∈Vxjik∀j∈V,∀k∈K\sum_{i \in V} x_{ijk} = \sum_{i \in V} x_{jik} \quad \forall j \in V, \forall k \in KiVxijk=iVxjikjV,kK

容量约束:
∑i∈V∖{0}di⋅∑j∈Vxijk≤Q∀k∈K\sum_{i \in V \setminus \{0\}} d_i \cdot \sum_{j \in V} x_{ijk} \leq Q \quad \forall k \in KiV{0}dijVxijkQkK

车辆从仓库出发:
∑j∈V∖{0}x0jk=1∀k∈K\sum_{j \in V \setminus \{0\}} x_{0jk} = 1 \quad \forall k \in KjV{0}x0jk=1kK

车辆返回仓库:
∑i∈V∖{0}xi0k=1∀k∈K\sum_{i \in V \setminus \{0\}} x_{i0k} = 1 \quad \forall k \in KiV{0}xi0k=1kK

三、遗传算法设计

3.1 算法流程

  1. 编码:采用客户排列编码,染色体为客户访问顺序的排列
  2. 初始化:随机生成若干个客户排列作为初始种群
  3. 适应度评估:按容量约束分割路线,计算总路径长度
  4. 选择:锦标赛选择
  5. 交叉:顺序交叉(Order Crossover, OX)
  6. 变异:交换变异
  7. 迭代:重复3-6直到满足终止条件

3.2 编码与解码

编码方式: 染色体长度为客户总数,每个基因位表示一个客户的编号,染色体顺序代表客户被服务的先后顺序。

解码方式: 按车辆容量约束,将客户排列依次分配给各车辆,形成多条路线。

3.3 交叉算子

采用顺序交叉(OX)算子,随机选择两个交叉点,保留父代1的中间段,其余位置按父代2的顺序填充。

3.4 变异算子

采用交换变异算子,随机选择两个位置交换其基因值。

四、MATLAB实现

4.1 主程序

%% GA-VRP: 遗传算法求解车辆路径问题
clc; clear; close all;

%% 问题参数
rng(42);
numCustomers = 20;           % 客户数量
vehicleCapacity = 100;       % 车辆容量
popSize = 100;               % 种群大小
maxGen = 200;                % 最大迭代次数
crossoverRate = 0.8;         % 交叉概率
mutationRate = 0.1;          % 变异概率

%% 生成测试数据
depot = [50, 50];                                      % 仓库坐标
customers = 100 * rand(numCustomers, 2);               % 客户坐标
points = [depot; customers];                           % 所有点
demands = randi([1, 15], numCustomers, 1);             % 客户需求

% 距离矩阵
n = numCustomers + 1;
dist = zeros(n, n);
for i = 1:n
    for j = i+1:n
        d = sqrt(sum((points(i,:) - points(j,:)).^2));
        dist(i,j) = d;  dist(j,i) = d;
    end
end

%% 初始化种群
pop = zeros(popSize, numCustomers);
for i = 1:popSize
    pop(i,:) = randperm(numCustomers);
end

%% 主循环
bestFitness = inf;
bestRoute = [];
bestHistory = zeros(1, maxGen);

for gen = 1:maxGen
    % 计算适应度
    fitness = zeros(popSize, 1);
    for i = 1:popSize
        fitness(i) = evaluateVRP(pop(i,:), demands, dist, vehicleCapacity);
    end
    
    % 记录最优
    [minFit, idx] = min(fitness);
    if minFit < bestFitness
        bestFitness = minFit;
        bestRoute = pop(idx,:);
    end
    bestHistory(gen) = bestFitness;
    
    % 锦标赛选择
    newPop = zeros(popSize, numCustomers);
    for i = 1:popSize
        candidates = randi(popSize, 1, 3);
        [~, bestIdx] = min(fitness(candidates));
        newPop(i,:) = pop(candidates(bestIdx),:);
    end
    
    % 顺序交叉
    for i = 1:2:popSize-1
        if rand < crossoverRate
            child1 = crossoverOX(newPop(i,:), newPop(i+1,:));
            child2 = crossoverOX(newPop(i+1,:), newPop(i,:));
            newPop(i,:) = child1;
            newPop(i+1,:) = child2;
        end
    end
    
    % 交换变异
    for i = 1:popSize
        if rand < mutationRate
            idx1 = randi(numCustomers);
            idx2 = randi(numCustomers);
            newPop(i, [idx1, idx2]) = newPop(i, [idx2, idx1]);
        end
    end
    
    pop = newPop;
    if mod(gen, 20) == 0
        fprintf('迭代 %d/%d: 最优路径长度 = %.2f\n', gen, maxGen, bestFitness);
    end
end

fprintf('\n===== 最终结果 =====\n');
fprintf('最优总路径长度: %.2f\n', bestFitness);

4.2 适应度评估函数

function [totalDist] = evaluateVRP(route, demands, dist, capacity)
    % 评估染色体适应度(按容量约束分割路线)
    
    [~, segmentsDist] = decodeVRP(route, demands, dist, capacity);
    totalDist = sum(segmentsDist);
end

4.3 解码函数

function [routeDistList, routeDists] = decodeVRP(route, demands, dist, capacity)
    % 按容量约束将染色体解码为车辆路线
    
    segments = {};
    distList = [];
    currentRoute = [];
    currentLoad = 0;
    
    for i = 1:length(route)
        customerIdx = route(i);
        demand = demands(customerIdx);
        
        if currentLoad + demand <= capacity
            currentRoute = [currentRoute, customerIdx];
            currentLoad = currentLoad + demand;
        else
            % 保存当前路线
            d = calcRouteDist(currentRoute, dist);
            segments{end+1} = currentRoute;
            distList = [distList, d];
            % 开始新路线
            currentRoute = [customerIdx];
            currentLoad = demand;
        end
    end
    
    % 最后一条路线
    if ~isempty(currentRoute)
        d = calcRouteDist(currentRoute, dist);
        segments{end+1} = currentRoute;
        distList = [distList, d];
    end
    
    routeDistList = segments;
    routeDists = distList;
end

function d = calcRouteDist(route, dist)
    % 计算一条路线的总距离
    if isempty(route)
        d = 0; return;
    end
    d = dist(1, route(1)+1);  % 仓库到第一个客户
    for j = 1:length(route)-1
        d = d + dist(route(j)+1, route(j+1)+1);
    end
    d = d + dist(route(end)+1, 1);  % 最后一个客户回仓库
end

4.4 顺序交叉函数

function child = crossoverOX(parent1, parent2)
    % 顺序交叉(Order Crossover)
    
    n = length(parent1);
    cut1 = randi(n-1);
    cut2 = randi([cut1+1, n]);
    
    child = zeros(1, n);
    child(cut1:cut2) = parent1(cut1:cut2);
    rest = setdiff(parent2, parent1(cut1:cut2), 'stable');
    pos = 1;
    for j = 1:n
        if j < cut1 || j > cut2
            child(j) = rest(pos);
            pos = pos + 1;
        end
    end
end

五、运行结果

5.1 控制台输出

迭代 20/200: 最优路径长度 = 528.92
迭代 40/200: 最优路径长度 = 449.07
迭代 60/200: 最优路径长度 = 435.45
迭代 80/200: 最优路径长度 = 435.45
...
迭代 200/200: 最优路径长度 = 435.45

===== 最终结果 =====
最优总路径长度: 435.45
使用的车辆数: 2
  车辆1: 路线 [0 -> 19 -> 6 -> 16 -> 14 -> 15 -> 11 -> 5 -> 7 -> 17 -> 20 -> 1 -> 9 -> 0] 长度=253.38
  车辆2: 路线 [0 -> 4 -> 3 -> 18 -> 10 -> 13 -> 2 -> 12 -> 8 -> 0] 长度=182.07

5.2 迭代收敛曲线

在这里插入图片描述

图1:GA-VRP迭代收敛曲线

5.3 最优路径图

在这里插入图片描述

图2:GA-VRP最优路径示意图(20客户示例,2辆车)

六、结果分析

从运行结果可以看出:

  1. 收敛性:算法在前40代快速收敛,从528.92下降至449.07,之后逐步收敛至435.45
  2. 车辆使用:20个客户共使用2辆车,总需求154,两辆车分别承担需求74和80
  3. 路径效率:两辆车行驶距离分别为253.38和182.07,里程平衡性较好

七、参数设置建议

参数 建议范围 说明
种群大小 50~200 太小易早熟,太大计算慢
最大迭代次数 200~500 根据问题规模调整
交叉概率 0.6~0.9 控制搜索与开发的平衡
变异概率 0.05~0.2 维持种群多样性
锦标赛规模 2~5 控制选择压力

八、算法特点与改进方向

优点

  • 全局搜索能力强:能有效避免陷入局部最优
  • 并行性:种群搜索天然适合并行化
  • 适应性强:可处理各种约束条件

缺点

  • 局部搜索能力弱:需要结合局部搜索算子
  • 参数敏感:性能依赖参数调优
  • 编码复杂性:约束条件增加了编码难度

改进方向

  1. 混合遗传算法:结合2-opt局部搜索
  2. 自适应参数:动态调整交叉和变异概率
  3. 精英保留策略:保留历代最优解

九、总结

本文详细介绍了使用遗传算法求解车辆路径问题(VRP)的MATLAB实现。通过合理的编码设计、选择、交叉和变异算子,算法能够高效地找到高质量的配送路线方案。该算法可广泛应用于物流配送、快递调度、校车路线等实际问题。


参考资料

  • Dantzig G, Ramser J. The Truck Dispatching Problem. Management Science, 1959.
  • 《MATLAB智能算法30个案例分析》

本文完整代码可直接运行,如有问题欢迎交流!

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐