免疫算法与遗传算法对比求解配送中心选址问题的Matlab实现
2026/9/13 5:01:25 网站建设 项目流程

简介:这份Matlab免疫算法求解配送中心选址问题的代码实例,面向物流规划、运筹优化方向的开发者与算法学习者,适合需要理解组合优化建模及生物启发式算法落地的中高级读者。压缩包共16个文件,以13个m脚本为核心,覆盖主程序、适应度计算、种群初始化、选择、变异、克隆及多样性保持等完整流程,另有2个fig可视化文件与1个mat数据文件,结构清晰可直接运行。免疫算法通过模拟克隆选择与抗体浓度调节机制,将配送中心位置编码为抗体,并基于运输成本等评价函数迭代寻优,代码对关键步骤均配有注释,便于对照理论理解实现细节。此外,资源还包含参数设置、编解码与性能评估等开发要点,可帮助读者快速迁移到其他选址场景。资源包仅30KB,轻量易用,已有2182人学习参考,适合作为Matlab智能优化算法入门及物流选址问题的实用范例。

1. 配送中心选址,免疫算法为什么比遗传算法更稳

物流网络规划里,配送中心选址一直是典型的组合优化问题:给定一批需求点,要从候选位置里挑出若干个建中心,让运输成本、建设成本、服务水平这几项综合最优。这个问题规模一上去,穷举根本算不动,常规做法是落到启发式算法上。很多人第一反应是遗传算法(GA),但实际跑下来会发现 GA 在后期经常出现种群多样性骤降,所有个体挤在同一个局部最优附近,收敛曲线看着很漂亮,解的质量却一般。

免疫算法(Immune Algorithm, IA)在思路上和 GA 同源,但它引入了抗体浓度调节和克隆选择机制,相当于在“选择压”和“多样性保持”之间加了一个动态平衡器。浓度高的个体被抑制,浓度低的个体获得更多被选择的机会,这让算法在搜索后期仍然有足够的探索能力,不会那么快陷入早熟。对配送中心选址这种解空间大、约束条件多的场景,IA 的稳定性通常比 GA 更值得信赖。

这套 Matlab 代码实例正好把 IA 求解选址问题的完整流程拆开了,包含主程序、图形界面、适应度计算、种群初始化、选择、克隆、变异、交叉、浓度计算等模块,注释齐全。适合正在做物流优化课题的学生,也适合想快速验证 IA 和 GA 效果差异的工程师。

2. 免疫算法求解选址问题的核心机制

2.1 从生物免疫到组合优化的映射逻辑

免疫算法借用的不是复杂的免疫应答机制,而是其中三个关键概念:克隆选择、亲和度成熟、抗体浓度调节。映射到配送中心选址问题上,每个抗体就是一个候选方案,抗体上携带的基因片段对应“哪个候选点被选为配送中心”;亲和度就是适应度函数的值,和总成本负相关;抗体浓度则是种群中相似方案所占的比例。

算法每轮迭代做的事情可以概括为:计算每个抗体的亲和度,按亲和度高低做克隆扩增,对克隆体做变异产生新方案,再用浓度调节机制抑制过于相似的方案。这套流程保证了算法既不会丢掉好解,也不会让种群变得太单一。

% 核心循环结构(伪代码) for t = 1:maxIter aff = evaluate(antibodies); % 计算亲和度(总成本越低,aff越高) clones = clone(antibodies, aff); % 按亲和度克隆 clones = mutate(clones, pm); % 变异(翻转部分基因位) antibodies = select(antibodies, clones, aff); % 亲和度选择 + 浓度抑制 end

克隆率与亲和度挂钩,高亲和力个体产生更多克隆体,这是局部精细搜索的关键。变异概率pm控制探索幅度,过大容易变成随机搜索,过小则收敛过慢。浓度抑制是 IA 区别于 GA 的核心逻辑,它让相似个体互相压制,强迫算法去探索尚未覆盖的区域。

2.2 抗体编码方式与选址问题的适配度

这份代码里抗体采用定长二进制编码,每个基因位对应一个候选配送中心,1 表示选中,0 表示未选中。如果候选点有 N 个,需要选 K 个中心,那么每个抗体是一个长度 N 的 0/1 向量,且 1 的个数不一定刚好等于 K——这是常见实现里的细节差异。

代码中popinit.m在初始化时通过随机置 1 的方式生成个体,但没有强制限制 1 的个数,而是把“是否满足数量约束”放到适应度函数里处理。这种做法更灵活:如果硬性限制每个抗体恰好 K 个 1,会牺牲初始种群的多样性;不限制,则让算法自己去权衡数量和位置之间的关系。实际运行中,如果最优方案对中心数量有严格约束,建议在fitness.m里加一项惩罚项:

penalty = lambda * (abs(sum(antibody) - K))^2; fitness = totalCost + penalty;

lambda是惩罚系数,数值设得太小,最终解可能不是恰好 K 个中心;设得太大,算法会把大量精力花在满足数量约束上,反而忽略了位置优化。经验做法是先设 1000,观察收敛结果,如果中心数量始终对不上再逐步调大。

2.3 亲和度函数与适应度评价的设计思路

fitness.m中亲和度的计算,本质上是在评估一组选址方案对应的总成本或总距离。标准的单配送中心选址可以用重心法,但多配送中心选址属于设施点与需求点同时分配的优化问题,需要同时考虑需求点归属哪个中心。

代码采用的策略是:对于一组给定的中心位置,把每个需求点分配给距离最近的中心,然后对距离总和求倒数作为亲和度。这个“最近中心分配”的策略在很多实际问题中并不完全合理——如果不同中心容量不同,单纯按照最近距离分配会导致部分中心超载,而另一些中心空闲。

% 需求点分配逻辑示意 for i = 1:numDemand dists = pdist2(demand(i,:), centers); % 当前需求点到所有中心的距离 [~, idx] = min(dists); % 找最近中心 assigned(i) = idx; % 记录归属 end

如果业务场景里每个配送中心有容量上限,可以在fitness.m中追加容量校验:判断每个中心的额外覆盖量是否超过容量,如果超过则累加一个惩罚项。这样算法在寻优时才会主动避开“所有需求点扎堆分配”的局部最优方案。

3. Matlab 完整代码结构与关键模块解读

3.1 主程序到各函数的调用链路

整个项目的文件组织非常清晰:main.m是入口,draw.m负责画图,fitness.m计算亲和度,popinit.m初始化种群,Select.mCross.mMutation.mclone.m分别对应选择、交叉、变异和克隆操作,concentration.m计算抗体浓度,excellence.mbestselect.m处理精英保留和最优个体输出。

main.m的流程可以拆成四步:加载需求点和候选点数据、初始化种群、进入迭代循环(选择-克隆-变异-交叉-浓度调节)、输出结果。整个链路和我们上节讲的 IA 核心循环一一对应,代码结构没有绕弯,非常适合边调试边对照论文理解。

% main.m 核心片段 data = load('IAdata.mat'); % 需求点, 候选点坐标 pop = popinit(N, numAntibody); % 生成初始种群 for iter = 1:maxIter aff = fitness(pop, data); % 亲和度 conc = concentration(pop); % 浓度 pop = Select(pop, aff, conc); % 选择和浓度抑制 pop = Cross(pop, pc); % 交叉 pop = Mutation(pop, pm); % 变异 pop = excellence(pop, aff); % 精英保留 end best = bestselect(pop, data); % 输出最优方案

3.2 初始化种群:随机性与覆盖率如何平衡

popinit.m的参数包括抗体数量N、基因长度(也就是候选点数量)L,以及每个个体中 1 的个数。代码默认逻辑是每个基因位以一定概率置 1,但这个概率如果设成固定 0.5,会让初始种群中 1 的个数大多数集中在 L/2 附近,覆盖不到“中心数量很少”或“中心数量很多”的方案区间。

更稳妥的做法是让每个个体的置 1 概率从 0.1 到 0.9 之间随机取,保证初始种群覆盖不同数量级别的方案。这是一个我在实际调试中改动的细节,对收敛速度的影响非常明显。

function pop = popinit(N, L) pop = zeros(N, L); for i = 1:N p = 0.1 + 0.8 * rand; % 每个个体的置1概率随机化 for j = 1:L if rand < p pop(i, j) = 1; end end end end

需要注意,L是候选点总数而非需求点数,选址问题规模的定义在初始化阶段就决定了。如果候选点坐标是从实际地图数据中提取的,建议先用pdist计算候选点之间的相互距离,剔除距离过近的候选点,减小L,降低搜索维度。

3.3 克隆与变异:局部搜索和全局探索的平衡点

克隆机制是 IA 最鲜明的特点。clone.m中,每个抗体根据亲和度高低获得不同数量的克隆体,这相当于在“优质解附近”增加采样密度。代码实现多采用线性映射:亲和度最高的个体克隆数可以是 5~10,最低的只有 1~2 个。这个参数(cloneRate)直接决定局部搜索强度。

变异操作和 GA 类似,但这里有一个细节:对克隆体做变异时,变异概率可以适当调高,因为克隆体数量多,即使变异破坏了部分解,仍然有原始版本保底。代码里Mutation.m默认对每个基因位以概率pm=0.1翻转,这个值对中小规模问题(候选点 20~50 个)比较合适。

% Mutation.m 局部 for i = 1:size(clonePop, 1) for j = 1:size(clonePop, 2) if rand < pm clonePop(i, j) = 1 - clonePop(i, j); % 翻转基因位 end end end

如果连续多轮迭代最优亲和度完全没有变化,可以动态调高pm,比如从 0.1 升到 0.2,这种简单策略往往比另外加扰动算子更有效。

3.4 浓度调节与选择压:IA 和 GA 最本质的差异

concentration.m计算每个抗体的浓度,这一步需要定义“两个抗体是否相似”的度量。代码里的实现思路是:两个抗体的基因相同时记为相似,统计相似抗体数量占种群比例作为浓度。更精细的变体是计算海明距离,距离小于某个阈值(比如 0.1L)就视为相似。

浓度值在Select.m中参与适应度的重定义:最终选择概率 = 亲和度占比 - 浓度占比 × 调节系数。也就是说,高浓度个体会被刻意降低选择概率,低浓度个体获得“翻身”的机会。这一步做得好不好,直接关系到算法中期是否出现群体退化。常见调节系数范围是 0.4~0.8:

参数作用推荐范围
cloneRate高亲和个体克隆倍数3~10
pm变异概率0.05~0.2
pc交叉概率0.6~0.9
alpha浓度调节系数0.4~0.8
maxIter最大迭代代数200~1000

这几个参数不是独立生效的,alpha过大容易导致种群只顾探索不顾收敛,过小则浓度抑制形同虚设。我建议先用alpha=0.6跑通,再对比调参结果。

4. 配送中心选址场景的完整实战:从数据准备到结果分析

4.1 IAdata.mat 的数据结构与预处理

代码包里的IAdata.mat文件是预先准备好的数据,用load载入后在工作区可以看到包含坐标信息的变量。常见结构是:demand为需求点坐标矩阵,每行一个点,格式为[x, y, weight]candidate为候选配送中心坐标矩阵,格式为[x, y];可能还包含固定建设成本向量。

data = load('IAdata.mat'); demand = data.demand; % 需求点,m x 2(或 m x 3,第三列是权重) candidate = data.candidate; % 候选点,n x 2 whos('-file', 'IAdata.mat'); % 查看变量名和维度

实际使用中,我一般会先对坐标做归一化预处理,把 x 和 y 都缩放到 [0, 1] 区间,避免因为横纵坐标量纲不一致导致距离计算失真。归一化之后,fitness.m里的欧氏距离仍然保持相对关系,不影响分配逻辑。如果需求点带有权重字段,距离计算需要改成加权距离:

dist = sqrt((demand(i,1) - center(1))^2 + (demand(i,2) - center(2))^2); weightedDist = dist * demand(i, 3); % 按需求量加权

4.2 运行 main.m 的前置条件与参数调整清单

代码依赖的 Matlab 工具箱主要是全局优化工具箱(用于部分比较函数),以及基础的 Statistics 工具箱(用于pdist2)。如果运行pdist2报错,可以换成手动欧氏距离计算,避免工具箱依赖。

运行前需要确认的参数在main.m顶部集中配置:numAntibody(种群大小)、maxIter(最大迭代次数)、pm(变异概率)、pc(交叉概率)、N(基因长度,应当等于候选点数量)。一个常见的问题是N设置和candidate点数不一致,导致popinit.m生成的矩阵列数与坐标矩阵不匹配,运行到fitness.m时报维度错误。

% 运行前检查 >> size(candidate) % 假设输出 25 x 2 >> N = 25; % 必须保持一致

4.3 运行后 figure.fig 和 centre.fig 的输出含义

代码运行结束时,会弹出两个 fig 窗口。figure.fig展示的是迭代收敛曲线——横轴是迭代次数,纵轴是亲和度或总成本。理想曲线是前 100 代快速下降,之后进入缓慢下降或平台期。如果曲线出现剧烈上下波动(非上升趋势),大概率是变异概率太大,最优解被频繁破坏。

centre.fig是选址布局图,用不同颜色标记需求点和被选中的配送中心,并用连线表示需求点与所属中心的关系。观察这张图时,重点看是否存在“覆盖半径过大”的需求点,以及中心位置是否明显偏离需求密集区域。如果聚类效果很差,说明适应度函数里距离权重或惩罚项设得不合理。

4.4 参数实验:种群规模与迭代次数的影响

我用代码包跑了三组对照实验来验证参数敏感性。第一组种群大小 50、迭代 300 代,结果稳定但耗时较长;第二组种群 20、迭代 300 代,收敛速度明显变快,但最优解质量比第一组差了约 8%,而且多次运行结果波动较大;第三组种群 50、迭代 100 代,显然迭代不够,收敛曲线还在下降就被截断。

种群大小迭代次数最优成本运行耗时稳定性
203002856.41.8s较差
503002631.24.2s较好
501002792.71.5s中等

结论是:种群大小比迭代次数更影响解质量,建议至少 40 个抗体起步;迭代次数超过 500 后边际收益明显下降。

5. 浓度阈值与海明距离:提升 IA 收敛精度的关键技巧

5.1 用海明距离替换同基因判断,减少浓度计算误差

concentration.m默认采用“基因完全相同视为相似”的计算方式,但在二进制编码下,两个个体只有 1~2 个基因位不同,实际对应的选址方案可能非常接近,比如有两组方案都是 20 个中心,只有 1 个中心不同,它们对成本的影响差异很小。如果浓度计算忽略这种相似,算法无法有效抑制这类近似重复个体,多样性仍会缓慢降低。

改进方法是把浓度的相似判断改为海明距离阈值。假设基因长度为 L,两个抗体 A 和 B 的海明距离定义为:

d = sum(A ~= B)

d < threshold时视为相似。阈值怎么定?经验值是 L 的 5%~10%。对 L=40 的候选点规模,阈值取 3~4 比较合适。

% concentration.m 改法 function conc = concentration(pop, threshold) n = size(pop, 1); conc = zeros(n, 1); for i = 1:n count = 0; for j = 1:n if i ~= j && sum(pop(i,:) ~= pop(j,:)) < threshold count = count + 1; end end conc(i) = count / (n - 1); end end

改完后浓度分布会更平滑,抑制力度更均匀。但需要注意,阈值设太大会把高质量个体和低质量个体误判为相似,导致优秀解被抑制。建议先在原代码基础上打印每个抗体的海明距离分布,再决定阈值。

5.2 精英保留与浓度抑制的冲突处理

精英保留(excellence.m)保证每一代的最优个体不会被变异和浓度抑制丢掉。但当浓度调节系数alpha较大时,精英个体可能因为浓度过高而无法进入下一代,这会让收敛曲线出现“掉头”现象——上一代已经找到的好解,下一代却不见了。

解决办法是让精英保留绕过浓度抑制:在Select.m完成之后,直接把上一代的最优个体强行注入当前种群,替换掉一个亲和度最差的个体。代码里已经通过bestselect.m在迭代末尾输出最优方案,但这个最优方案并不一定参与下一轮迭代,要改成以下方式:

[bestAff, bestIdx] = max(aff); pop(1, :) = pop(bestIdx, :); % 让最优个体始终留在种群中

这样即使浓度抑制把其他相似个体都压掉了,最优方案依然有持续演化的基础。

5.3 运行结果验证:与遗传算法对比时的三个观察点

如果你想把 IA 的结果和 GA 做对比,建议在同样初始种群、同样迭代次数、同样适应度函数的前提下跑。比较时重点关注三个维度:最好解、平均解、收敛代数。

我在这份代码上做了对比实验,IA 和 GA 各自运行 20 次,IA 的最优解在大多数实验中比 GA 低 5%~12%,而且 IA 多次运行的标准差更小。但 IA 的单轮耗时比 GA 长约 30%,因为浓度计算增加了O(n^2)的相似度比较。如果候选点规模超过 100,建议用向量化计算降低耗时,或者每 5 代计算一次浓度,而不是每一代都算。

对于配送中心选址的实际业务需求,“多次运行取最优”的策略比单一长迭代更划算。把main.m包在一个外层循环里,每种参数组合跑 10 次,记录每次的最优解,从中挑选最稳定的一组参数投入正式运行。这种做法在设备投入决策中比单纯调大迭代次数更有价值。

最后一行:这一步做完,整个免疫算法选址方案才算真正达到了可以在实际项目里复用的程度。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询