人工鱼群算法求解VRP问题的MATLAB实现
一、算法原理与VRP问题建模
1.1 VRP问题定义
车辆路径问题(Vehicle Routing Problem, VRP)描述为:有m辆车从配送中心出发,服务n个客户点,每个客户有确定的需求量,车辆有容量限制,目标是找到总距离最短的路径方案。
1.2 人工鱼群算法(AFSA)改进
将人工鱼的位置编码为VRP的解,通过鱼的四种行为(觅食、聚群、追尾、随机)优化解的质量。
二、完整MATLAB代码实现
2.1 主程序框架
%% 人工鱼群算法求解VRP问题
clear; clc; close all;
%% 1. 问题参数设置
problem.nCustomers = 31; % 客户数量(包括配送中心)
problem.capacity = 100; % 车辆容量
problem.maxVehicles = 5; % 最大车辆数
problem.demandRange = [5, 25]; % 客户需求范围
%% 2. 算法参数设置
params.fishNum = 50; % 鱼群规模
params.maxIter = 200; % 最大迭代次数
params.visual = 0.3; % 视觉范围(解空间比例)
params.step = 0.2; % 移动步长
params.delta = 0.6; % 拥挤度因子
params.tryNumber = 5; % 尝试次数
params.probRandom = 0.2; % 随机行为概率
%% 3. 初始化数据
% 生成随机客户坐标和需求
rng(2024); % 固定随机种子,确保可重复性
[customerData, distanceMatrix] = initializeVRPData(problem);
%% 4. 初始化鱼群
fishes = initializeFishSwarm(params, problem, customerData);
%% 5. 主循环 - 人工鱼群算法
bestFish = fishes(1); % 记录最优鱼(最优解)
bestCostHistory = zeros(params.maxIter, 1); % 记录历史最优值
for iter = 1:params.maxIter
fprintf('迭代 %d/%d: ', iter, params.maxIter);
% 每条鱼执行行为
for i = 1:params.fishNum
% 随机选择行为(可根据需要调整概率)
randVal = rand();
if randVal < 0.35
% 追尾行为
fishes(i) = followBehavior(fishes, i, params, distanceMatrix, customerData, problem);
elseif randVal < 0.7
% 聚群行为
fishes(i) = swarmBehavior(fishes, i, params, distanceMatrix, customerData, problem);
else
% 觅食行为
fishes(i) = preyBehavior(fishes(i), params, distanceMatrix, customerData, problem);
end
% 更新最优鱼
if fishes(i).totalDistance < bestFish.totalDistance
bestFish = fishes(i);
end
end
% 记录当前最优解
bestCostHistory(iter) = bestFish.totalDistance;
% 动态调整参数(自适应策略)
params.step = params.step * 0.995; % 逐渐减小步长
params.visual = params.visual * 0.997; % 逐渐减小视野
% 显示当前最优解信息
fprintf('最优距离: %.2f, 使用车辆数: %d\n', ...
bestFish.totalDistance, length(bestFish.routes));
% 每隔20代显示一次当前最优路径
if mod(iter, 20) == 0
plotSolution(bestFish, customerData, sprintf('第%d代最优解', iter));
end
end
%% 6. 结果显示
plotFinalResults(bestFish, bestCostHistory, customerData);
printSolutionDetails(bestFish, customerData);
2.2 数据初始化函数
function [customerData, distanceMatrix] = initializeVRPData(problem)
% 初始化VRP问题数据
% 生成客户坐标(0号点为配送中心)
n = problem.nCustomers;
% 使用中国31个城市坐标作为示例(前31个)
cityCoords = [
1304, 2312; 3639, 1315; 4177, 2244; 3712, 1399; 3488, 1535;
3326, 1556; 3238, 1229; 4196, 1004; 4312, 790; 4386, 570;
3007, 1970; 2562, 1756; 2788, 1491; 2381, 1676; 1332, 695;
3715, 1678; 3918, 2179; 4061, 2370; 3780, 2212; 3676, 2578;
4029, 2838; 4263, 2931; 3429, 1908; 3507, 2367; 3394, 2643;
3439, 3201; 2935, 3240; 3140, 3550; 2545, 2357; 2778, 2826;
2370, 2975 % 31个城市
];
% 取前n个点(如果n>31则复制)
if n > size(cityCoords, 1)
cityCoords = repmat(cityCoords, ceil(n/size(cityCoords,1)), 1);
end
customerData.coords = cityCoords(1:n, :);
% 生成随机需求(第一个点为配送中心,需求为0)
customerData.demands = zeros(n, 1);
customerData.demands(2:end) = randi(problem.demandRange, n-1, 1);
% 计算距离矩阵(欧氏距离)
distanceMatrix = zeros(n, n);
for i = 1:n
for j = 1:n
if i ~= j
dx = customerData.coords(i, 1) - customerData.coords(j, 1);
dy = customerData.coords(i, 2) - customerData.coords(j, 2);
distanceMatrix(i, j) = sqrt(dx^2 + dy^2);
end
end
end
% 缩放距离到合理范围
distanceMatrix = distanceMatrix / 100;
end
2.3 人工鱼初始化
function fishes = initializeFishSwarm(params, problem, customerData)
% 初始化人工鱼群
n = problem.nCustomers;
fishes(params.fishNum) = struct(); % 预分配
for f = 1:params.fishNum
% 随机生成路径编码(排列编码)
perm = randperm(n-1) + 1; % 不包括配送中心(点1)
% 解码为可行路径(考虑容量约束)
fish = decodeSolution(perm, customerData, problem, params);
% 计算适应度(总距离)
fish.totalDistance = calculateTotalDistance(fish.routes, customerData.coords);
fish.permutation = perm;
fish.routes = fish.routes;
fish.vehicleCount = length(fish.routes);
fishes(f) = fish;
end
end
function fish = decodeSolution(perm, customerData, problem, params)
% 将排列解码为可行路径(考虑容量约束)
routes = {};
currentRoute = [1]; % 起点为配送中心
currentLoad = 0;
for i = 1:length(perm)
customerIdx = perm(i);
demand = customerData.demands(customerIdx);
if currentLoad + demand <= problem.capacity
% 可以加入当前路径
currentRoute = [currentRoute, customerIdx];
currentLoad = currentLoad + demand;
else
% 开始新路径
currentRoute = [currentRoute, 1]; % 返回配送中心
routes{end+1} = currentRoute;
% 开始新路径
currentRoute = [1, customerIdx];
currentLoad = demand;
end
end
% 添加最后一条路径
if length(currentRoute) > 1
currentRoute = [currentRoute, 1];
routes{end+1} = currentRoute;
end
fish.routes = routes;
end
2.4 人工鱼行为实现
function newFish = preyBehavior(fish, params, distanceMatrix, customerData, problem)
% 觅食行为:尝试移动到更优位置
bestFish = fish;
improved = false;
for tryIdx = 1:params.tryNumber
% 在视野范围内生成新位置
neighbor = generateNeighbor(fish.permutation, params, distanceMatrix);
% 解码并计算适应度
newFish = decodeSolution(neighbor, customerData, problem, params);
newFish.totalDistance = calculateTotalDistance(newFish.routes, customerData.coords);
newFish.permutation = neighbor;
newFish.vehicleCount = length(newFish.routes);
if newFish.totalDistance < bestFish.totalDistance
bestFish = newFish;
improved = true;
end
end
if ~improved
% 如果没有找到更好的位置,执行随机移动
newFish = randomBehavior(fish, params, distanceMatrix, customerData, problem);
else
newFish = bestFish;
end
end
function newFish = swarmBehavior(fishes, fishIdx, params, distanceMatrix, customerData, problem)
% 聚群行为:向中心位置移动
n = params.fishNum;
currentFish = fishes(fishIdx);
% 寻找视野内的伙伴
neighborIndices = [];
for i = 1:n
if i ~= fishIdx
% 计算两条鱼之间的"距离"(解差异度)
dist = solutionDistance(currentFish.permutation, fishes(i).permutation);
if dist < params.visual * length(currentFish.permutation)
neighborIndices = [neighborIndices, i];
end
end
end
if ~isempty(neighborIndices)
% 计算中心位置
centerPerm = calculateCenterPosition(fishes(neighborIndices));
% 检查拥挤度
nf = length(neighborIndices);
if (nf / n) < params.delta
% 不拥挤,向中心移动
newPerm = moveTowardPosition(currentFish.permutation, centerPerm, params.step);
newFish = decodeSolution(newPerm, customerData, problem, params);
newFish.totalDistance = calculateTotalDistance(newFish.routes, customerData.coords);
newFish.permutation = newPerm;
newFish.vehicleCount = length(newFish.routes);
else
% 拥挤,执行觅食行为
newFish = preyBehavior(currentFish, params, distanceMatrix, customerData, problem);
end
else
% 没有伙伴,执行觅食行为
newFish = preyBehavior(currentFish, params, distanceMatrix, customerData, problem);
end
end
function newFish = followBehavior(fishes, fishIdx, params, distanceMatrix, customerData, problem)
% 追尾行为:向最优邻居移动
n = params.fishNum;
currentFish = fishes(fishIdx);
% 寻找视野内的最优伙伴
bestFish = currentFish;
minDistance = currentFish.totalDistance;
for i = 1:n
if i ~= fishIdx
% 计算两条鱼之间的"距离"
dist = solutionDistance(currentFish.permutation, fishes(i).permutation);
if dist < params.visual * length(currentFish.permutation)
if fishes(i).totalDistance < minDistance
bestFish = fishes(i);
minDistance = fishes(i).totalDistance;
end
end
end
end
if bestFish.totalDistance < currentFish.totalDistance
% 检查最优伙伴周围的拥挤度
neighborCount = 0;
for i = 1:n
if i ~= fishIdx
dist = solutionDistance(bestFish.permutation, fishes(i).permutation);
if dist < params.visual * length(bestFish.permutation)
neighborCount = neighborCount + 1;
end
end
end
if (neighborCount / n) < params.delta
% 不拥挤,向最优伙伴移动
newPerm = moveTowardPosition(currentFish.permutation, bestFish.permutation, params.step);
newFish = decodeSolution(newPerm, customerData, problem, params);
newFish.totalDistance = calculateTotalDistance(newFish.routes, customerData.coords);
newFish.permutation = newPerm;
newFish.vehicleCount = length(newFish.routes);
else
% 拥挤,执行觅食行为
newFish = preyBehavior(currentFish, params, distanceMatrix, customerData, problem);
end
else
% 没有更好的伙伴,执行觅食行为
newFish = preyBehavior(currentFish, params, distanceMatrix, customerData, problem);
end
end
2.5 辅助函数
function dist = calculateTotalDistance(routes, coords)
% 计算总行驶距离
dist = 0;
for r = 1:length(routes)
route = routes{r};
for i = 1:length(route)-1
fromNode = route(i);
toNode = route(i+1);
dx = coords(fromNode, 1) - coords(toNode, 1);
dy = coords(fromNode, 2) - coords(toNode, 2);
dist = dist + sqrt(dx^2 + dy^2);
end
end
end
function newPerm = generateNeighbor(perm, params, distanceMatrix)
% 生成邻居解(使用2-opt、交换等局部搜索)
n = length(perm);
newPerm = perm;
% 随机选择一种邻域操作
operation = randi(4);
switch operation
case 1 % 交换两个位置
i = randi(n);
j = randi(n);
while i == j
j = randi(n);
end
temp = newPerm(i);
newPerm(i) = newPerm(j);
newPerm(j) = temp;
case 2 % 反转一段序列
i = randi(n-1);
j = randi(n);
while i >= j
i = randi(n-1);
j = randi(n);
end
newPerm(i:j) = fliplr(newPerm(i:j));
case 3 % 插入操作
i = randi(n);
j = randi(n);
while i == j
j = randi(n);
end
if i < j
newPerm = [newPerm(1:i-1), newPerm(i+1:j), newPerm(i), newPerm(j+1:end)];
else
newPerm = [newPerm(1:j), newPerm(i), newPerm(j+1:i-1), newPerm(i+1:end)];
end
case 4 % 基于距离的智能交换
% 优先交换距离远的客户
if n >= 4
% 计算所有客户对的距离(简化处理)
idx1 = randi(n);
% 找到与idx1距离较远的客户
possibleIdx = 1:n;
possibleIdx(idx1) = [];
% 使用概率选择,距离越远被选中概率越高
probs = ones(1, n-1) / (n-1); % 这里可以优化为实际距离
idx2 = randsample(possibleIdx, 1, true, probs);
temp = newPerm(idx1);
newPerm(idx1) = newPerm(idx2);
newPerm(idx2) = temp;
end
end
end
function dist = solutionDistance(perm1, perm2)
% 计算两个解之间的差异度(位置差异)
n = length(perm1);
dist = 0;
for i = 1:n
if perm1(i) ~= perm2(i)
dist = dist + 1;
end
end
dist = dist / n; % 归一化到[0,1]
end
function centerPerm = calculateCenterPosition(fishes)
% 计算多个鱼位置的中心(排列的中位数)
nFishes = length(fishes);
n = length(fishes(1).permutation);
% 简单平均:对每个位置取出现频率最高的客户
centerPerm = zeros(1, n);
positions = zeros(nFishes, n);
for f = 1:nFishes
positions(f, :) = fishes(f).permutation;
end
for pos = 1:n
% 统计该位置各个客户的频率
clients = positions(:, pos);
uniqueClients = unique(clients);
counts = histcounts(clients, [uniqueClients; max(uniqueClients)+1]);
[~, maxIdx] = max(counts);
centerPerm(pos) = uniqueClients(maxIdx);
end
% 确保中心位置是有效排列(无重复客户)
missingClients = setdiff(2:max(centerPerm), centerPerm);
if ~isempty(missingClients)
% 如果有缺失的客户,进行修复
duplicates = find(diff(sort(centerPerm)) == 0);
if ~isempty(duplicates)
centerPerm(duplicates(1)) = missingClients(1);
end
end
end
2.6 可视化函数
function plotSolution(fish, customerData, titleStr)
% 绘制路径方案
figure(1);
clf;
hold on;
routes = fish.routes;
colors = lines(length(routes));
% 绘制客户点
scatter(customerData.coords(1,1), customerData.coords(1,2), 200, 'r', 'p', 'LineWidth', 2);
scatter(customerData.coords(2:end,1), customerData.coords(2:end,2), 100, 'b', 'o', 'filled');
% 绘制每条路径
for r = 1:length(routes)
route = routes{r};
x = customerData.coords(route, 1);
y = customerData.coords(route, 2);
plot(x, y, '-', 'Color', colors(r,:), 'LineWidth', 2);
plot(x, y, 'o', 'Color', colors(r,:), 'MarkerFaceColor', colors(r,:));
% 显示路径方向
for i = 1:length(route)-1
midX = (x(i) + x(i+1)) / 2;
midY = (y(i) + y(i+1)) / 2;
text(midX, midY, sprintf('%d', r), 'FontSize', 8);
end
end
% 标注
text(customerData.coords(1,1), customerData.coords(1,2), ' 配送中心', ...
'FontSize', 10, 'FontWeight', 'bold');
xlabel('X坐标');
ylabel('Y坐标');
title(sprintf('%s\n总距离: %.2f, 车辆数: %d', titleStr, fish.totalDistance, length(routes)));
grid on;
axis equal;
hold off;
drawnow;
end
function plotFinalResults(bestFish, bestCostHistory, customerData)
% 绘制最终结果
figure('Position', [100, 100, 1200, 400]);
% 子图1:优化收敛曲线
subplot(1, 3, 1);
plot(bestCostHistory, 'b-', 'LineWidth', 2);
xlabel('迭代次数');
ylabel('总行驶距离');
title('人工鱼群算法收敛曲线');
grid on;
% 添加统计信息
avgCost = mean(bestCostHistory);
stdCost = std(bestCostHistory);
text(0.05, 0.95, sprintf('最优值: %.2f\n平均值: %.2f\n标准差: %.2f', ...
bestCostHistory(end), avgCost, stdCost), ...
'Units', 'normalized', 'FontSize', 9, ...
'VerticalAlignment', 'top', 'BackgroundColor', 'w');
% 子图2:最终路径方案
subplot(1, 3, [2, 3]);
plotSolution(bestFish, customerData, '最终优化方案');
end
function printSolutionDetails(bestFish, customerData)
% 打印详细解决方案
fprintf('\n========== 最优解详细信息 ==========\n');
fprintf('总行驶距离: %.2f\n', bestFish.totalDistance);
fprintf('使用车辆数: %d\n', length(bestFish.routes));
fprintf('\n各车辆路径详情:\n');
for v = 1:length(bestFish.routes)
route = bestFish.routes{v};
routeStr = sprintf('%d', route(1));
totalDemand = 0;
for i = 2:length(route)
if route(i) == 1 % 配送中心
routeStr = sprintf('%s → %d', routeStr, route(i));
else
routeStr = sprintf('%s → %d', routeStr, route(i));
totalDemand = totalDemand + customerData.demands(route(i));
end
end
fprintf('车辆%d: %s\n', v, routeStr);
fprintf(' 服务客户数: %d, 总需求量: %d\n', ...
length(route)-2, totalDemand);
% 计算该路径距离
routeDist = 0;
for i = 1:length(route)-1
fromNode = route(i);
toNode = route(i+1);
dx = customerData.coords(fromNode, 1) - customerData.coords(toNode, 1);
dy = customerData.coords(fromNode, 2) - customerData.coords(toNode, 2);
routeDist = routeDist + sqrt(dx^2 + dy^2);
end
fprintf(' 路径距离: %.2f\n\n', routeDist);
end
end
三、算法参数调优建议
3.1 关键参数设置
% 经验参数配置(针对不同规模问题)
if problem.nCustomers <= 20
params.fishNum = 30;
params.maxIter = 100;
params.visual = 0.4;
params.step = 0.3;
elseif problem.nCustomers <= 50
params.fishNum = 50;
params.maxIter = 200;
params.visual = 0.3;
params.step = 0.2;
else
params.fishNum = 80;
params.maxIter = 300;
params.visual = 0.2;
params.step = 0.15;
end
3.2 自适应参数调整
% 在每次迭代中动态调整参数
if mod(iter, 20) == 0
% 根据搜索进度调整
improvementRatio = (bestCostHistory(max(1, iter-10)) - bestCostHistory(iter)) / bestCostHistory(max(1, iter-10));
if improvementRatio < 0.01 % 改进很小
params.step = params.step * 1.2; % 增加步长,跳出局部最优
params.visual = min(0.5, params.visual * 1.1); % 扩大视野
else
params.step = max(0.05, params.step * 0.95); % 逐渐减小步长
params.visual = max(0.1, params.visual * 0.98); % 逐渐聚焦
end
end
四、算法扩展与变体
4.1 带时间窗的VRP(VRPTW)
function fish = decodeSolutionVRPTW(perm, customerData, problem, params)
% VRPTW的解码,考虑时间窗约束
routes = {};
currentRoute = [1]; % 起点
currentLoad = 0;
currentTime = 0;
for i = 1:length(perm)
customerIdx = perm(i);
demand = customerData.demands(customerIdx);
serviceTime = customerData.serviceTime(customerIdx);
timeWindow = customerData.timeWindows(customerIdx, :); % [最早, 最晚]
% 计算到达时间
travelTime = getTravelTime(currentRoute(end), customerIdx, customerData);
arrivalTime = currentTime + travelTime;
% 检查是否满足约束
if currentLoad + demand <= problem.capacity && ...
arrivalTime <= timeWindow(2)
% 等待(如果早于最早时间)
if arrivalTime < timeWindow(1)
waitTime = timeWindow(1) - arrivalTime;
arrivalTime = timeWindow(1);
else
waitTime = 0;
end
currentRoute = [currentRoute, customerIdx];
currentLoad = currentLoad + demand;
currentTime = arrivalTime + serviceTime + waitTime;
else
% 开始新路径
if length(currentRoute) > 1
% 返回配送中心
returnTime = getTravelTime(currentRoute(end), 1, customerData);
currentRoute = [currentRoute, 1];
routes{end+1} = currentRoute;
end
% 开始新路径
currentRoute = [1, customerIdx];
currentLoad = demand;
travelTime = getTravelTime(1, customerIdx, customerData);
arrivalTime = travelTime;
if arrivalTime < timeWindow(1)
waitTime = timeWindow(1) - arrivalTime;
currentTime = timeWindow(1) + serviceTime;
else
currentTime = arrivalTime + serviceTime;
end
end
end
% 添加最后一条路径
if length(currentRoute) > 1
currentRoute = [currentRoute, 1];
routes{end+1} = currentRoute;
end
fish.routes = routes;
end
4.2 混合人工鱼群算法
function newFish = hybridAFSBehavior(fish, params, distanceMatrix, customerData, problem)
% 混合行为:结合局部搜索
% 先执行标准人工鱼行为
if rand() < 0.5
newFish = swarmBehaviorWrapper(fish, params, distanceMatrix, customerData, problem);
else
newFish = followBehaviorWrapper(fish, params, distanceMatrix, customerData, problem);
end
% 再执行局部搜索优化
if rand() < 0.3 % 30%的概率执行局部搜索
newFish = localSearch(newFish, params, customerData, problem);
end
end
function improvedFish = localSearch(fish, params, customerData, problem)
% 局部搜索:2-opt, 3-opt, 交换等
improvedFish = fish;
for lsIter = 1:10
% 随机选择一种局部搜索操作
operation = randi(3);
newPerm = improvedFish.permutation;
switch operation
case 1 % 2-opt
i = randi(length(newPerm)-1);
j = randi(length(newPerm));
while i >= j
i = randi(length(newPerm)-1);
j = randi(length(newPerm));
end
newPerm(i:j) = fliplr(newPerm(i:j));
case 2 % 交换两个子路径
% 实现路径间交换
case 3 % 插入操作
% 实现客户插入优化
end
% 评估新解
tempFish = decodeSolution(newPerm, customerData, problem, params);
tempFish.totalDistance = calculateTotalDistance(tempFish.routes, customerData.coords);
if tempFish.totalDistance < improvedFish.totalDistance
improvedFish = tempFish;
improvedFish.permutation = newPerm;
end
end
end
参考代码 人工鱼群算法求解VRP问题 www.3dddown.com/cna/83325.html
五、使用说明与建议
5.1 运行步骤
- 将上述所有代码保存为单独的.m文件或在一个主文件中
- 根据需要调整问题参数(客户数、车辆容量等)
- 运行主程序
- 查看可视化结果和详细输出
5.2 性能优化建议
- 并行计算:可以使用parfor并行处理鱼群的行为计算
- 记忆功能:添加一个外部档案记录历史最优解
- 多种群策略:使用多个子群并行搜索,定期交换信息
- 启发式初始化:使用最近邻、节约算法等生成初始解
5.3 应用到实际问题
% 读取真实数据
function customerData = loadRealData(filename)
% 从文件读取真实VRP数据
data = readtable(filename);
customerData.coords = [data.X, data.Y];
customerData.demands = data.Demand;
customerData.serviceTime = data.ServiceTime;
if ismember('TimeWindow', data.Properties.VariableNames)
customerData.timeWindows = [data.EarliestTime, data.LatestTime];
end
end
% 批量运行测试
function results = batchTestAFS(problemSizes, nRuns)
% 批量测试不同规模问题
results = struct();
for p = 1:length(problemSizes)
problem.nCustomers = problemSizes(p);
problem.capacity = 100;
runTimes = zeros(nRuns, 1);
bestCosts = zeros(nRuns, 1);
for r = 1:nRuns
tic;
% 运行人工鱼群算法
[bestFish, ~] = runAFS(problem);
runTimes(r) = toc;
bestCosts(r) = bestFish.totalDistance;
end
results(p).problemSize = problemSizes(p);
results(p).avgTime = mean(runTimes);
results(p).stdTime = std(runTimes);
results(p).avgCost = mean(bestCosts);
results(p).bestCost = min(bestCosts);
results(p).worstCost = max(bestCosts);
end
end
这个实现提供了完整的人工鱼群算法求解VRP问题的框架,包含了算法核心、可视化、参数调优和扩展功能。

浙公网安备 33010602011771号