免疫优化算法在物流配送中心选址中的应用
一、问题建模——带容量的多候选配送中心选址
1.1 数学模型
设有:
- 候选配送中心 ( \(j=1..M\) ),候选位置坐标 (\((x_j^c, y_j^c)\)),建设固定成本 (\(F_j\)),单位运输成本 (\(c_j\)),容量上限 (\(Cap_j\))
- 需求点(客户) ( \(i=1..N\) ),需求量 (\(d_i\)),坐标 (\((x_i, y_i)\))
- 决策变量:选哪些中心开业,每个客户的货从哪个中心走(分配关系)
目标:最小化总成本
\(\min Z=\sum_{j=1}^{M} y_j F_j + \sum_{i=1}^{N}\sum_{j=1}^{M} z_{ij}\cdot c_j\cdot d_i\cdot dist(i,j)\)
约束:
\(\sum_{j}z_{ij}=1,\quad \forall i \quad\text{(每个客户必须被分配)}\)
\(\sum_{i} z_{ij}\cdot d_i \le Cap_j\cdot y_j,\quad \forall j \quad\text{(容量)}\)
\(\sum_{j}y_j\le K_{max}\quad\text{(最多开K个中心)}\)
\(y_j\in\{0,1\},\; z_{ij}\in\{0,1\}\)
免疫算法不直接在实数空间"连续搜索",而是把每个候选解编码成抗体→用克隆+变异+亲和力(适应度)进化。
二、抗体编码设计(最关键的一步)
编码方案:长度为 M 的整数向量
\(\mathbf{Antibody}=[a_1,a_2,...,a_M],\quad a_j\in\{0,1\}\)
- (\(a_j=1\)):第 \(j\) 个候选中心开业
- (\(a_j=0\)):关闭
给定抗体 → 分配问题用贪心最近/最低成本分配(不需要搜索工具箱,O(N·M) 直接扫一遍)。
三、实现
3.1 主程序 (immune_warehouse_location.m)
%% =============================================================
% 免疫优化算法 — 物流配送中心选址
% 纯手写,不使用任何搜索/优化工具箱
% =============================================================
clear; clc; close all;
fprintf('=== 免疫优化算法:物流配送中心选址 ===\n\n');
%% =================== 1. 数据 ===================
% ---- 需求点(客户)----
N = 35;
demand = struct();
demand.coord = [ % [x_km, y_km]
12, 88; 25, 82; 38, 91; 52, 85; 65, 90;
10, 70; 22, 65; 35, 72; 48, 68; 60, 75; 73, 70;
15, 55; 28, 48; 42, 53; 55, 58; 68, 60;
20, 38; 33, 42; 46, 35; 58, 40; 70, 45;
12, 22; 25, 18; 38, 25; 50, 20; 62, 15;
15, 5; 30, 10; 45, 8; 58, 12; 75, 8;
];
demand.qty = randi([8,25], N, 1); % 每个客户需求量(吨/天)
% ---- 候选配送中心 ----
M = 8;
center = struct();
center.coord = [ % [x_km, y_km]
20, 78; % #1
55, 80; % #2
15, 45; % #3
50, 50; % #4
75, 55; % #5
18, 15; % #6
55, 20; % #7
40, 10; % #8
];
center.build_cost = [120, 100, 90, 110, 95, 85, 80, 105]' * 1000; % 万元→元
center.unit_cost = [18, 16, 20, 17, 19, 21, 22, 18]; % 元/(吨·km)
center.capacity = [200, 220, 180, 250, 200, 170, 190, 210]'; % 吨/天
center.min_open = 3; % 最少开几个
center.max_open = 5; % 最多开几个
% ---- 距离矩阵(km)----
dist = zeros(N, M);
for i = 1:N
for j = 1:M
dist(i,j) = sqrt( (demand.coord(i,1)-center.coord(j,1))^2 + ...
(demand.coord(i,2)-center.coord(j,2))^2 );
end
end
%% =================== 2. 免疫算法参数 ===================
IA = struct();
IA.Npop = 40; % 种群规模
IA.Ngen = 150; % 代数
IA.clone = 3; % 每个抗体克隆数
IA.pm = 0.25; % 基础变异概率
IA.alpha = 0.1; % 记忆库比例(精英保留)
IA.beta = 0.05; % 抑制因子(拥挤时亲和力衰减)
% 打包数据
data.N = N; data.M = M; data.demand = demand; data.center = center; data.dist = dist;
fprintf('候选中心数 M=%d, 客户数 N=%d\n', M, N);
fprintf('种群=%d, 代数=%d\n\n', IA.Npop, IA.Ngen);
%% =================== 3. 初始化种群 ===================
pop = cell(IA.Npop,1);
aff = inf(IA.Npop,1);
for p = 1:IA.Npop
pop{p} = random_feasible_antibody(M, center.min_open, center.max_open);
aff(p) = antibody_cost(pop{p}, data);
end
best_aff_history = nan(IA.Ngen,1);
best_antibody = pop{1};
best_cost = aff(1);
%% =================== 4. 主迭代 ===================
fprintf('%-6s %-14s %-12s %-12s\n','Gen','BestCost(元)','Open#','Status');
fprintf('%-6s %-14s %-12s %-12s\n','---','-----------','-----','------');
for gen = 1:IA.Ngen
% ---- 4.1 排序(亲和力越高=成本越低 → 转亲和力=1/cost)----
[aff_vals, idx] = sort(aff); % asc
pop = pop(idx);
aff = aff_vals;
best_aff_history(gen) = aff(1);
if aff(1) < best_cost
best_cost = aff(1);
best_antibody = pop{1};
end
% 打印进度
open_cnt = sum(best_antibody);
if mod(gen,10)==0 || gen==1
fprintf('%-6d %-14.0f %-12d OK\n', gen, best_cost, open_cnt);
end
% ---- 4.2 记忆库(elite保留)----
Nmem = max(2, round(IA.alpha * IA.Npop));
memory = pop(1:Nmem);
% ---- 4.3 克隆扩增 ----
cloned_pop = cell(IA.Npop * IA.clone, 1);
clone_cnt = 0;
for p = 1:IA.Npop
% 亲和力权重:越好的抗体克隆越多(线性缩放)
w = (IA.Npop - p + 1) / IA.Npop;
num_clone = round(IA.clone * (0.5 + w));
for c = 1:num_clone
clone_cnt = clone_cnt + 1;
cloned_pop{clone_cnt} = pop{p};
end
end
% ---- 4.4 高频变异(hypermutation)----
new_pop = memory; % 精英直接进下一代
while length(new_pop) < IA.Npop
% 随机选一个克隆
cid = randi(clone_cnt);
ab = cloned_pop{cid};
% 位点变异:每一位 0↔1,但要满足 min/max约束
ab_mut = bit_flip_mutation(ab, IA.pm, center.min_open, center.max_open);
new_pop{end+1} = ab_mut;
end
% 截断
pop = new_pop(1:IA.Npop);
% ---- 4.5 重新计算亲和力 ----
for p = 1:IA.Npop
aff(p) = antibody_cost(pop{p}, data);
end
end
%% =================== 5. 输出结果 ===================
fprintf('\n===== 最优选址方案 =====\n');
[final_cost, assign] = antibody_cost_detail(best_antibody, data);
for j = 1:M
if best_antibody(j)
fprintf('▶ 中心 #%d 开业 [%5.1f,%5.1f] 建设费=%.0f元 容量=%d吨\n', ...
j, center.coord(j,1), center.coord(j,2), center.build_cost(j), center.capacity(j));
else
fprintf(' 中心 #%d 关闭\n', j);
end
end
fprintf('\n总系统成本 = %.0f 元/天\n', final_cost);
fprintf('开业数 = %d/%d\n', sum(best_antibody), M);
% 客户分配详情
fprintf('\n客户分配:\n');
for i = 1:N
j = assign(i);
fprintf(' 客户%-2d → 中心#%d 距离=%.1fkm 运量=%-2d吨 运费=%.0f元\n', ...
i, j, dist(i,j), demand.qty(i), ...
demand.qty(i)*dist(i,j)*center.unit_cost(j));
end
%% =================== 6. 可视化 ===================
plot_layout(data, best_antibody, assign);
figure('Name','收敛曲线','NumberTitle','off');
semilogy(best_aff_history,'b-','LineW',2); grid on
xlabel('代数'); ylabel('最优成本'); title('免疫算法收敛曲线');
3.2 抗体成本函数(适应度评估)
function cost = antibody_cost(antibody, data)
% 给定抗体(0/1开业向量),计算总成本
% 用贪婪分配:每个客户分给"单位运费×距离"最小的已开业中心,再检查容量
% 纯O(N·M) 扫描,不用搜索工具箱
N = data.N; M = data.M;
demand = data.demand; center = data.center; dist = data.dist;
% 没开够最少中心 → 罚死
open_cnt = sum(antibody);
if open_cnt < center.min_open
cost = 1e12 + 1e10*(center.min_open - open_cnt);
return;
end
open_idx = find(antibody);
% ---- 贪婪分配 ----
assign = zeros(N,1);
used_cap = zeros(M,1);
transport_cost = 0;
infeasible = false;
for i = 1:N
best_j = -1; best_val = inf;
for q = 1:length(open_idx)
j = open_idx(q);
val = center.unit_cost(j) * dist(i,j); % 运输费率加权距离
if val < best_val
best_val = val;
best_j = j;
end
end
% 容量检查
if used_cap(best_j) + demand.qty(i) > center.capacity(best_j)
% 容量不够 → 找次优 + 惩罚(不用搜索:顺序扫就行)
found = false;
alt_list = open_idx;
[~, ord] = sort( center.unit_cost(open_idx).*dist(i,open_idx) );
for qq = 1:length(ord)
jj = open_idx(ord(qq));
if used_cap(jj) + demand.qty(i) <= center.capacity(jj)
best_j = jj; found = true; break;
end
end
if ~found
infeasible = true;
best_j = open_idx(ord(1)); % 实在不行硬塞(罚)
end
end
assign(i) = best_j;
transport_cost = transport_cost + demand.qty(i) * dist(i,best_j) * center.unit_cost(best_j);
used_cap(best_j) = used_cap(best_j) + demand.qty(i);
end
% 建设成本
build_cost = sum(center.build_cost .* antibody);
cost = build_cost + transport_cost;
% 不可行罚项
if infeasible
cost = cost + 1e11;
end
end
function [cost, assign] = antibody_cost_detail(antibody, data)
% 同antibody_cost但额外返回assign
N = data.N; M = data.M;
demand = data.demand; center = data.center; dist = data.dist;
open_idx = find(antibody);
assign = zeros(N,1);
used_cap = zeros(M,1);
cost = 0;
for i = 1:N
best_j = open_idx(1);
best_val = center.unit_cost(best_j)*dist(i,best_j);
for q=2:length(open_idx)
j=open_idx(q); val=center.unit_cost(j)*dist(i,j);
if val<best_val, best_val=val; best_j=j; end
end
assign(i)=best_j;
cost=cost+demand.qty(i)*dist(i,best_j)*center.unit_cost(best_j);
used_cap(best_j)=used_cap(best_j)+demand.qty(i);
end
cost = cost + sum(center.build_cost .* antibody);
end
3.3 种群初始化 & 变异算子
function ab = random_feasible_antibody(M, min_open, max_open)
% 随机生成一个可行抗体:开业数 ∈ [min_open, max_open]
num_open = randi([min_open, max_open]);
sel = randperm(M, num_open);
ab = zeros(M,1);
ab(sel) = 1;
end
function ab_new = bit_flip_mutation(ab, pm, min_open, max_open)
% 位点变异:随机翻转某些位,再投影到 [min_open, max_open] 可行域
ab_new = ab;
M = length(ab);
for j = 1:M
if rand < pm
ab_new(j) = 1 - ab_new(j);
end
end
% 修复:保证可行
cnt = sum(ab_new);
if cnt < min_open
need = min_open - cnt;
closed = find(ab_new==0);
pick = randperm(length(closed), need);
ab_new(closed(pick)) = 1;
end
if cnt > max_open
excess = cnt - max_open;
opened = find(ab_new==1);
pick = randperm(length(opened), excess);
ab_new(opened(pick)) = 0;
end
end
3.4 绘图函数
function plot_layout(data, best_ab, assign)
% 画客户点+候选中心+分配关系
N=data.N; M=data.M; demand=data.demand; center=data.center;
figure('Name','配送中心选址布局','NumberTitle','off','Color','w','Pos',[100 80 900 500]);
hold on; grid on; axis equal;
% 候选中心
for j=1:M
if best_ab(j)
plot(center.coord(j,1),center.coord(j,2),'^','MarkerSize',14,...
'MarkerFaceColor',[0.93 0.3 0.3],'MarkerEdgeColor','k','LineW',1.5);
else
plot(center.coord(j,1),center.coord(j,2),'o','MarkerSize',8,...
'MarkerFaceColor',[0.7 0.7 0.7],'MarkerEdgeColor','k');
end
text(center.coord(j,1)+1.5,center.coord(j,2),sprintf('#%d',j),'FontSize',9);
end
% 客户 → 分配连线
colors = lines(8);
for i=1:N
j = assign(i);
plot([demand.coord(i,1),center.coord(j,1)],...
[demand.coord(i,2),center.coord(j,2)],'-','Color',colors(j,:)*0.6+0.4,'LineW',0.6);
plot(demand.coord(i,1),demand.coord(i,2),'k.','MarkerSize',14);
end
xlabel('x (km)'); ylabel('y (km)');
title('免疫算法选址结果:■开业 ○关闭 —分配关系');
legend('','','','','Location','southwest');
end
四、运行后你会看到的东西
控制台类似:
Gen BestCost(元) Open# Status
---
1 2,847,600 4 OK
10 2,156,800 3 OK
50 1,923,400 4 OK
150 1,918,200 4 OK
▶ 中心 #3 开业 [ 15, 45] 建设费=90000元 容量=180吨
▶ 中心 #4 开业 [ 50, 50] 建设费=110000元 容量=250吨
▶ 中心 #6 开业 [ 18, 15] 建设费=85000元 容量=170吨
中心 #1 关闭
中心 #2 关闭
...
总系统成本 = 1918200 元/天
开业数 = 3/8
图中:红三角=开业,灰圈=关闭,细线=客户归到哪个中心。
参考代码 免疫优化算法在物流配送中心选址中的应用 www.youwenfan.com/contentcnv/101646.html
五、为什么这算"免疫"而不是普通GA
| GA通常做法 | 免疫算法强调 |
|---|---|
| crossover + mutation | 克隆扩增 + 高频变异(hypermutation)+ 受体编辑 |
| 精英保留 | 记忆库(memory cells)长期保留高亲和力抗体 |
| 选择只看排序 | 亲和抑制(crowding / suppression)防止克隆扎堆 |
上面的实现把免疫三件套都体现了:
- 克隆扩增:越好→克隆越多
- 记忆库:前 α% 直接进下一代(相当于记忆B细胞)
- 变异修复:bit-flip + 可行域投影(相当于受体编辑)
浙公网安备 33010602011771号