9.3 工程应用案例:生产计划优化


9.3 工程应用案例:生产计划优化

用 JuMP 解一个带约束的生产排程问题:三种产品、两类资源、目标利润最大化。工程类问题的精髓在"把业务语言翻译成约束",翻译完求解只是一行 optimize!

业务场景

一个车间生产三种产品,受工时与原料约束:

产品 单件利润 单件工时 单件原料
A 30 2 1
B 50 3 2
C 45 1 4

每周可用工时 260、原料 180,C 因合同至少生产 10 件。求最优周计划。

翻译成 JuMP 模型

] add JuMP HiGHS using JuMP, HiGHS m = Model(HiGHS.Optimizer) @variable(m, a >= 0) @variable(m, b >= 0) @variable(m, c >= 10) # 合同底线 @constraint(m, 工时, 2a + 3b + c <= 260) @constraint(m, 原料, a + 2b + 4c <= 180) @objective(m, Max, 30a + 50b + 45c) optimize!(m) println("A = ", value(a), " B = ", value(b), " C = ", value(c)) println("最大利润 = ", objective_value(m))

约束宏里那个中文名(工时原料)不只是注释——@constraint 会把它注册为约束引用,事后可以查它的对偶价格:

dual(工时) # 工时每多一小时,利润增加多少 —— 瓶颈的货币化 shadow_price(原料)

对偶价格的工程价值

对偶值 解读 决策含义
大的正值 该资源是紧瓶颈 加班/外购原料可能划算
资源有富余 增加供给无收益

对偶价格把"该不该扩产能"从拍脑袋变成算术题——这是优化模型比穷举方案多出来的那层价值。

约束围出的决策空间

约束围出的决策空间

⚠️ 常见坑:变量取值可能是小数(比如 12.7 件产品)。要么接受排产取整的近似,要么换用整数规划(@variable(m, a, Int) 换分支定界求解器),别默默向下取整——那可能违反约束。

💡 关键直觉:建模的时间花在"约束找全"上。漏掉一条现实约束(比如仓储上限),得到的"最优解"在执行时必然翻车。写约束前先跟业务方过一遍清单。

从解到决策:完整的解读过程

求解器的输出是一组数,工程的产出是一个决定。把本例走完整。第一步,看原始解:假设求解器给出 A、B、C 各几十件、利润约三千多的方案。第二步,读紧约束:检查两条约束的剩余量——工时用满而原料有余,则工时是瓶颈;对照对偶价格,工时的对偶值是正数(比如 15),含义是"每周多买 1 小时工时,利润涨约 15 元",若外购工时单价低于 15,扩工时就是划算的投资。第三步,做敏感性实验而不是相信单点解:

# 利润系数 ±10% 批量重解,看最优产量结构的稳健性 using JuMP, HiGHS function resolve(profit_c) m = Model(HiGHS.Optimizer); set_silent(m) @variable(m, a >= 0); @variable(m, b >= 0); @variable(m, c >= 10) @constraint(m, 2a + 3b + c <= 260) @constraint(m, a + 2b + 4c <= 180) @objective(m, Max, 30a + 50b + profit_c * c) optimize!(m) round.(Int, value.([a, b, c])) end [resolve(pc) for pc in (40.5, 45.0, 49.5)] # C 利润 ±10% 的三组方案

解读三组方案:若产量结构基本不动,说明计划对报价波动稳健,可以放心执行;若结构剧烈切换(比如 C 利润稍降就大幅减产 C),说明计划踩在切换点上,签长期合同前要把报价谈稳。第四步才是写结论:最优产量、瓶颈资源、扩产建议、敏感性提示四段式。这个"解—瓶颈—敏感—结论"的链条是运筹类报告的通用骨架,比单纯贴一个最优值有用得多。

常见建模错误

三个真实教训。一是约束写错方向:把"至少生产 10 件"写成 c <= 10,求解器会愉快给出违法合同的最优解,且没有任何报错——求解器只保证"满足你写的",不保证"你想的是";防法是把每条约束用一句话业务语义写在注释里,逐条核对。二是单位不统一:利润按"元"而工时按"分钟",最优解整体失真;建模前先列一张单位表。三是忽略整数性后手工取整:最优解 B = 43.7 件向下取 43 可能没事,但 C = 10.4 取 10 直接违反"至少 10"的合同底线时才暴露;正确做法是取整后重新检查所有约束,或一开始就用整数变量。三类错误的共同解药是"求解后校验":把解代回每条约束,用代码而不是眼睛验证。

扩展:整数规划与多周期排产

两个自然的进阶方向。其一,整数性升级:产量必须整件时把变量声明为整数类型,求解器从连续优化切换到分支定界,求解时间会上升(小规模仍在秒级),换来的是可直接执行的计划;合同"要么不产、要么至少十件"这类半连续约束也只有整数变量能表达。其二,多周期排产:把"每周"扩展为"四周",决策变量变成 4 倍,新增周之间的库存衔接约束(上周剩余 = 本周可用减本周消耗),模型规模线性增长而结构不变——JuMP 宏在循环里批量生成变量与约束,这正是 3.3 代码生成思想在建模里的应用。两个方向做完,这个案例就从教学题长成了车间真的能用的排产工具雏形,剩下的工程化(数据接入、结果回写)交给 7.4 的服务化模式即可。

求解结果的工程化还有两个细节:一是把求解器的状态(Optimal / Infeasible / TimeLimit 等)写进输出,建模者肉眼看到"Optimal"和看到"TimeLimit"的信任度完全不同;二是给目标函数的值留一个独立的校验步骤——把最优解代回目标表达式手算一遍,与求解器报告的 objective value 对比,若不一致,多半是模型里某个系数写错了。这两步加起来不到十行代码,却是防止"求解器报告漂亮、落地全错"的最后防线。

本节要点回顾

  • 三宏建模型@variable 定决策、@constraint 定边界、@objective 定目标;
  • 命名约束可查对偶价格,把瓶颈变成可计算的货币量;
  • 整数性是要显式声明的建模决策;
  • 建模功夫在约束清单的完整性,不在求解调用;
  • "解—瓶颈—敏感—结论"四段式是运筹报告的通用骨架。

作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U