免疫优化算法在物流配送中心选址中的应用

一、问题建模——带容量的多候选配送中心选址

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 + 可行域投影(相当于受体编辑)
posted @ 2026-06-03 08:25  yu8yu7  阅读(10)  评论(0)    收藏  举报