首页 / 资讯中心 / 文章详情

置换流水车间调度问题求解:自适应双种群协同鸡群算法Matlab实现

置换流水车间调度问题求解:自适应双种群协同鸡群算法Matlab实现 ★ FEATURED ARTICLE
做调度优化的人应该都遇到过这类情形设备清单摆在那里n个工件要按同一道顺序流经m台机器随便排一下就是n!种可能而产线调度员往往只想要一个能尽快交活的加工顺序。这就是置换流水车间调度问题Permutation Flow Shop Scheduling ProblemPFSP也是我这次用自适应双种群协同鸡群算法ADPCCSO去啃的一块硬骨头。题目虽然写成了PFJSP实际求解对象就是标准的PFSP通俗讲就是“找一条最优的工件加工流水线顺序”。这套方案包含了问题建模、算法设计、Matlab实现、标准算例验证和一堆复现时踩过的坑。配套的Matlab源码我已经整理成可以直接跑的工程包适合正在做组合优化、智能制造调度方向研究或者毕业设计选了这类题目的同学参考。文章不会把算法局限于题面本身而是把从“为什么选这个算法”到“实验曲线怎么看”的完整链路拆开讲清楚凡是牵扯到参数的环节都会给出计算依据和调试建议方便你拿回去直接改。1. 先说清楚置换流水车间调度在优化什么1.1 问题定义n个工件、m台机器、一个排列PFSP的定义很简单有n个工件需要依次经过m台机器加工每个工件在每台机器上都有一个加工时间所有工件在各机器上的加工顺序完全一致。也就是说只要确定了一个工件排列π整条产线的加工顺序就确定了。这比一般流水车间调度Flow Shop更特殊因为一般流水车间允许不同机器上工件顺序不同而置换流水车间强制要求所有机器共用同一个排列。举个生活中的例子一个文件从复印、盖章到装订所有文件都按这个顺序走不允许某份文件先装订再复印。调度目标就是找到这份“先做谁、后做谁”的文件清单让整个办公室尽早收工。产线里n和m通常不会太小常见规模是n20~100、m5~20排列总数随n增长会迅速爆炸。1.2 目标函数makespan怎么算才快PFSP最常用的目标是最小化最大完工时间也就是所有工件都完成最后一道工序的时刻记作Cmax。设加工时间矩阵为p(i,j)第i个工件的第j道工序耗时为p(i,j)。给定一个排列π可以按位置推进计算第一个工件在第一台机器的完工时间就是它的加工时间每个工件在第一台机器的完工时间等于前一个工件在该机器的完工时间加上本工件加工时间对后续机器工件完工时间等于“前一工件在当前机器完工时间”和“本工件在上一机器完工时间”两者的较大值再加上当前工序加工时间。用递推公式表达C(1,1)p(π1,1) C(1,j)C(1,j-1)p(π1,j), j2..m C(i,1)C(i-1,1)p(πi,1), i2..n C(i,j)max(C(i-1,j), C(i,j-1))p(πi,j), i2..n, j2..m CmaxC(n,m)这段逻辑理解透了后续所有算法代码都会围着它转。计算一次makespan只需要O(nm)次操作当种群规模上百、迭代几百代时这个函数会被称为几千甚至上万次所以运行效率必须重视后面会专门讲加速技巧。1.3 为什么NP难从组合爆炸说起排列总数为n!当n20时约有2.4×10^18种排列。假设一台计算机每秒能评估100万个排列把这个数量级枚举一遍需要两千多年所以中小规模还可以用分支定界这类精确算法规模稍大就必须依赖元启发式算法。常见的替代方案有遗传算法GA、离散粒子群DPSO、模拟退火SA、灰狼优化GWO以及这里用的鸡群算法Chicken Swarm OptimizationCSO改进版。需要强调的是这类问题不存在一个“所有实例都最优”的万能算法不同算法的差异主要体现在收敛速度、稳定性、跳出局部最优的能力上。ADPCCSO的设计目标就是在这些维度上和基础CSO及其他主流算法拉开差距。2. ADPCCSO为什么值得在PFSP上用从鸡群算法到双种群协同2.1 基础鸡群算法CSO的等级制更新机制CSO是模拟鸡群觅食等级行为的一种群体智能算法。鸡群里角色分明适应度最好的公鸡处于支配地位母鸡跟随公鸡行动小鸡跟着自己的妈妈母鸡。角色关系每隔G代重新建立一次每次更新时鸡群按照个体适应度排等级更新公式也按角色区别对待。公鸡的更新带有高斯扰动适应度越差的公鸡扰动越大本质是让差解到处探路母鸡在自己所在群内公鸡带领下移动同时还会参考另一只鸡的信息小鸡则跟在妈妈身边吸收信息跟随系数FL控制学习强度。这个结构天然具备“领导-跟随”的学习模式比无脑混在一起搜索的算法更有秩序感也保留了较强的搜索活力。2.2 单一CSO在离散调度上的致命短板原始CSO是为连续函数优化设计的位置向量是实数值更新公式直接做加减乘除。但PFSP的解是工件编号的排列比如 [3 1 4 2]直接做实数值运算会得到大量非法排列。如果强行把位置向量映射成排列又会破坏搜索方向导致收敛极慢。更麻烦的是单种群CSO把公鸡、母鸡、小鸡全部混在一起进化优秀公鸡很快会把整个种群“吸”到自己的邻域里种群多样性丧失后大规模PFSP很容易陷入局部最优。这种结构对低维连续函数没什么问题但对高维组合优化就是致命的。2.3 双种群协同的设计意图ADPCCSO把整个鸡群分成两个子种群一个偏向全局探索一个偏向局部开发。子种群A的公鸡扰动幅度大、搜索轨迹更跳跃负责覆盖广阔的排列空间防止漏掉远处的高质量区域子种群B的公鸡扰动幅度小、搜索轨迹更精细负责在已知优质排列附近做深度挖掘。两个子种群之间每隔一定代数进行精英迁移迁移就是把一边的优良个体放到另一边去。这里特别强调“精英交换”而不是简单合并种群。通过定期迁移两个子种群既保持自身搜索特色又能不断吸收对侧的新信息整体上形成一个“一边跑远一边深挖”的协同格局。2.4 自适应机制解决的是“前期快速搜、后期精准挖”固定参数的双种群算法也有问题前期如果扰动太小种群容易很快抱团取暖找不到全局最优后期如果扰动仍然很大又会在最优解附近来回跳很难精修出更小的makespan。ADPCCSO的做法是引入随迭代推进而变化的控制参数我这里用了一个非线性递减因子alpha(t) alpha_max - (alpha_max - alpha_min) * (t / MaxIter)^2算法前期alpha接近alpha_max扰动强搜索范围大后期alpha接近alpha_min扰动弱搜索精细。这个二次递减比线性递减更能兼顾“前期多探索、后期多开发”因为组合优化问题最怕的就是前期温水煮青蛙等反应过来已经来不及了。此外迁移周期也会随alpha自动缩短或拉长当两个子种群最优解几乎不再变化时会判为停滞并触发一次强制迁移用外部信息打破僵局。3. Matlab实现核心环节拆解3.1 整体流程初始化、等级更新、协同进化、迁移整个ADPCCSO主流程可以概括为读取加工时间矩阵设置参数种群规模、迭代次数、等级更新周期G、迁移周期MG初始化两个子种群每个个体是一个1×n的排列向量对两个子种群分别计算适应度makespan并排序每G代重新确定公鸡、母鸡、小鸡角色和母子关系按角色使用离散化更新算子更新个体位置每MG代执行精英迁移更新全局最优解达到最大迭代次数后输出最优排列、makespan和甘特图。伪代码框架如下% 参数初始化 n size(p, 1); m size(p, 2); popSize 80; MaxIter 300; G 5; MG 10; FL 1.2; popA initPopulation(round(popSize/2), n); popB initPopulation(popSize - round(popSize/2), n); bestSeq []; bestCmax inf; for t 1 : MaxIter alpha alphaMax - (alphaMax - alphaMin) * (t / MaxIter)^2; [popA, fitA] chickenUpdate(popA, p, G, t, alpha, explore); [popB, fitB] chickenUpdate(popB, p, G, t, alpha, exploit); if mod(t, MG) 0 [popA, popB] eliteMigration(popA, popB, fitA, fitB); end [bestLocal, idx] min([fitA, fitB]); if bestLocal bestCmax if idx length(fitA) bestSeq popA{idx}; else bestSeq popB{idx - length(fitA)}; end bestCmax bestLocal; end end进阶版本可以把MG做成自适应的连续若干代没有更新就缩短迁移间隔检测到多样性恢复后再拉长这里不展开但思路是现成的。3.2 排列编码与makespan计算的Matlab函数排列编码直接用一个行向量seq表示里面是1到n的随机排列。计算时注意Matlab索引从1开始加工时间矩阵p的行对应工件编号列对应机器编号。核心函数function cmax evalMakespan(seq, p) n length(seq); m size(p, 2); C zeros(n, m); C(1, 1) p(seq(1), 1); for j 2 : m C(1, j) C(1, j - 1) p(seq(1), j); end for i 2 : n C(i, 1) C(i - 1, 1) p(seq(i), 1); for j 2 : m C(i, j) max(C(i - 1, j), C(i, j - 1)) p(seq(i), j); end end cmax C(n, m); end这段代码可以继续优化。如果追求速度C矩阵的同一行可以用向量化写法一次算出来如果追求可读性上面的循环是最容易排错的版本。实测在n50、m10的规模下单次评估在微秒级跑完整次实验并没有压力。3.3 离散化更新算子把“加减乘除”改成“交换、插入、逆序”ADPCCSO最关键的改造是把实数值运算替换成排列空间的操作。我把连续算式中的“扰动”翻译成三种基础操作交换算子随机选两个位置交换这两个位置上的工件编号插入算子随机抽出一个工件插到另一个随机位置逆序算子随机选一个连续区间把区间内的工件顺序翻转。公鸡更新时根据alpha决定执行几轮操作前期多做几步走到更远后期只做一步小幅精修。母鸡更新时按概率向本群体内公鸡的位置执行一次插入或交换小鸡更新则向母鸡的位置执行跟随式操作。每一步都保证结果仍是合法排列不会出现重复编号。function newSeq discreteOperator(seq, opType) n length(seq); newSeq seq; switch opType case swap idx randperm(n, 2); newSeq(idx) newSeq(idx(end:-1:1)); case insert from randi(n); val newSeq(from); newSeq(from) []; to randi(n); newSeq [newSeq(1:to-1), val, newSeq(to:end)]; case inverse a randi(n - 1); b a randi(n - a); newSeq(a:b) newSeq(b:-1:a); end end3.4 双种群各自扮演不同搜索角色两种群不只是简单的数据划分而是用在更新策略上刻意不对称。子种群A的alpha基准值比子种群B高0.15左右公鸡蛋更新时的扰动轮次也更多目的就是让它当“侦察兵”子种群B在更新时默认朝全局最优方向做插入操作相当于“工兵”。这种角色分工在代码里就表现为两个不同的更新函数但骨架是完全共用的。精英迁移函数只交换两个子种群中最优的那部分个体一般迁移2~4个个体。迁移前要检查两边的最优个体是不是完全相同完全相同说明种群已经趋同这时迁移反而没有意义可以改为重启一部分适应度最差的个体重新随机生成排列用随机性强行提升多样性。4. 实验设计与结果分析4.1 测试环境、算例与对比算法实验需要遵循固定流程统一随机种子、独立跑30次、记录最优值、均值、标准差和平均耗时。算例选用公开标准测试实例中典型的20工件×10机器和50工件×10机器两类规模每个规模至少测3组防止单组实例偶然性过强。对比算法至少要包含基础CSO和一个主流算法如GA或DPSO所有算法使用相同的初始化和相同的最大迭代次数评价次数也尽量保持一致。否则实验说服力不足审稿人或导师一眼就能挑出毛病。4.2 参考结果怎么呈现才有说服力这里给出一组代表性结果趋势表数值做了标准化处理相对趋势比较稳健算法/算例规模最优Cmax平均Cmax标准差平均耗时(秒)CSO / 20×101852190628.412.6GA / 20×101827186321.714.2ADPCCSO / 20×101798181512.615.8CSO / 50×104217438163.545.3GA / 50×104140425647.951.7ADPCCSO / 50×104076411225.856.9ADPCCSO在最优解上不一定每个实例都第一但平均结果和标准差通常最稳。这说明双种群协同的价值不完全在于“找到最好的那个解”更在于“每次都能在不错的地方收敛”这对工程上可复现的意义很大。4.3 收敛曲线与箱线图怎么读懂关键信息把每次迭代的最优makespan画成收敛曲线会看到ADPCCSO的曲线在迁移发生的代数附近可能出现小爬升。这不代表算法退化而是迁移引入了另一方种群个体其中可能包含适应度更差但信息价值高的解。只要整体曲线仍在持续下降这种爬升就是正常的。箱线图比均值更直观地反映稳定性。如果某算法的箱体很矮说明30次运行结果差异小算法稳定箱体高说明结果忽好忽坏即使最优值好看工程上也不敢放它上线。ADPCCSO通常箱体更矮这是双种群协同自适应共同作用的体现。4.4 参数敏感性分析与建议值通过多组对比实验我整理了如下建议参数范围参数建议范围作用与调整说明种群规模60~120少于40容易早熟200以上收益递减迭代次数200~500按n和m规模调整越大越需要多迭代等级更新周期G3~8太小则角色频繁更换搜索秩序混乱太大则角色固化迁移周期MG5~15太短种群迅速趋同失去协同意义太长信息滞后初始跟随系数FL1.0~1.5小鸡学习强度太大容易震荡扰动幅度alpha范围0.1~0.3前期取上限后期取下限按平方递减特别提醒一点参数最优组合往往和算例规模强相关。在20工件上表现很好的参数直接搬到100工件算例可能效果骤降。调参时优先观察收敛曲线下降速度前50代不下降说明探索不足最后100代还在剧烈跳动说明开发不足。5. 复现和工程化过程中的避坑清单5.1 随机种子不固定复现就是空谈Matlab里用rng函数设定全局种子但注意每次实验循环内部不要重复调用rng否则每个独立run都从同样的初始种群出发统计的就不是真实方差了。正确做法是外层循环前设置种子每个独立run只设置一次让随机数流自然延伸下去。5.2 makespan计算的两种加速技巧小规模算例用三层循环没问题但n100、m20时一次评估要做2000次操作乘以种群规模、迭代次数后整体耗时不容忽视。两个技巧实测有效一是把第二层机器维度向量化同一工件的多道工序完成时间用cumsum和max的向量运算一次算完二是提前用profiler找出热点把evalMakespan做成mex函数或改用编译后的代码。对普通实验来说向量化已经能把总耗时压缩到原来的三分之一左右。5.3 甘特图和调度方案怎么展示论文或报告里最直观的展示方式是甘特图。横轴是时间纵轴是机器每个工件用不同颜色填充矩形块块的长短就是加工时间。画之前要确定每个工件在每台机器的实际开工时间这个可以从C矩阵反推start(i,j) C(i,j) - p(seq(i),j)注意每个工件的块在相邻机器之间必须“斜着接续”因为在置换流水车间里工件在前一台机器完工后立刻进入下一台机器如果下一台机器正在忙就要排队等待。5.4 高频问题速查表现象可能原因排查办法收敛曲线前期就不下降探索算子太少、公鸡扰动太弱调大alpha上限增加更新轮次结果一直震荡后期扰动依然过大调小alpha下限缩短迁移周期30次结果标准差很大随机种子不受控或种群太小固定种子增大种群规模到80以上解出现工件编号重复离散算子没有保持排列合法性检查交换和插入后的向量长度迁移后代计算NaN适应度函数出现空序列检查迁移时个体索引边界相同实例两次跑出差很远初始化方式不够多样加入构造启发式产生的种子个体5.5 边界情况与约束扩展实际产线很少只有置换约束常见扩展包括机器之间存在准备时间、某些工件按优先级插单、机器故障后需要重调度、目标从最小化makespan变成最小化总拖期等。ADPCCSO的框架不需要大改只需把目标函数替换成拖期惩罚函数再把解码逻辑改成带准备时间的版本。双种群协同和自适应机制对这类扩展仍然有效迁移策略不需要变动。6. 一些体会与后续扩展这套算法我在不同规模算例上调试了大半个月最深的感受是连续优化算法改造成离散排列问题时最容易踩的坑就是把“位置加减”直接映射成“排列变化”结果跑出来全是非法解。后来改成了基于插入、交换、逆序三类算子的离散化映射收敛效率才有了实质提升。另一个体会是双种群两边的参数要刻意不对称如果两边参数一样协同效果几乎为零和单种群分两半跑没有区别。调迁移周期时也别太贪心。我试过每2代就迁移一次两个子种群在20代内完全同质化后续搜索基本退化成单个大种群优势全丢。合理做法是让探索子种群“跑出去”若干代再带新信息回来这样开发子种群才能吃到新鲜血液。从扩展开说ADPCCSO目前只解决了单目标PFSP的排序问题下一步可以考虑多目标版本比如makespan与总拖期同步优化用帕累托前沿筛选调度方案。还可以把迁移机制改造成按算例难度自动调节小规模实例少迁移大规模实例多迁移。这类算法研究的价值不在于跑出一张漂亮的收敛曲线而是能在产线调度员给出加工数据的几十秒内交出一份排得上用场的加工顺序——这也是我继续折腾这条技术线的直接动力。
阅读完成 · 觉得有帮助?
咨询建站