资讯动态

python的运筹学工业场景模拟第八篇:生产分配线性规划,求解对偶问题,输出各设备影子价格,识别工厂瓶颈资源。

发布时间:2026/8/12 17:20:28 来源:尧图企业网站定制
生产分配与影子价格分析用线性规划对偶问题识别工厂瓶颈资源“三条生产线月产能总差2000吨老板问‘扩哪条线最划算’以前拍脑袋说‘扩最忙的’结果投了800万产能只涨了300吨。用线性规划对偶问题一算影子价格直接指出扩那条‘看起来不忙’的缓冲工段同样的钱产能涨了1200吨。”—— 参考北京理工大学《运筹学》第2章“线性规划”、第3章“对偶理论”一、实际应用场景描述在化工、钢铁、水泥、造纸、食品等流程工业中多设备协同生产是产能管理的核心难题。一个典型的化工联合装置生产场景如下┌──────────────────────────────────────────────────────┐│ 多设备生产分配优化系统 ││ ││ 【生产背景】 ││ • 某化工园区联合生产装置 ││ • 三种主产品P1高端、P2中端、P3通用││ • 五台核心生产设备串联并联组合 ││ • 月度生产计划P1≥8000吨P2≥12000吨P3≥15000吨││ ││ 【设备配置】 ││ ┌──────┬──────────────┬──────────┬──────────────┐││ │ 设备 │ 工序名称 │ 产能上限 │ 单位加工费 │││ ├──────┼──────────────┼──────────┼──────────────┤││ │ R101 │ 原料预处理 │ 40000吨 │ 120元/吨 │││ │ R102 │ 核心反应釜A │ 15000吨 │ 380元/吨 │││ │ R103 │ 核心反应釜B │ 12000吨 │ 420元/吨 │││ │ S101 │ 分离精制单元 │ 25000吨 │ 200元/吨 │││ │ C101 │ 成品造粒包装 │ 22000吨 │ 150元/吨 │││ └──────┴──────────────┴──────────┴──────────────┘││ ││ 【工艺路线BOM】 ││ • P1生产路线R101→R102→S101→C101 ││ 单耗1.2t原料→1t P1反应工段收率85% ││ • P2生产路线R101→R103→S101→C101 ││ 单耗1.1t原料→1t P2反应工段收率88% ││ • P3生产路线R101→S101→C101跳过反应工段 ││ 单耗1.05t原料→1t P3用于低端产品 ││ ││ 【成本结构】 ││ • 原料成本P1路线2800元/吨P2路线2600元/吨 ││ • 加工成本各设备加工费累加 ││ • 单位售价P15800元/吨P25200元/吨P34200元/吨││ • 设备固定成本R102/R103需分摊折旧、维护费 ││ ││ 【核心问题】 ││ 在设备产能约束下如何分配三种产品的产量使总 ││ 利润最大更重要的是哪台设备是真正的瓶颈扩 ││ 容哪台设备能带来最大的边际收益影子价格 ││ ││ 【传统做法】 ││ • 生产主管凭经验“平均分配”产能 ││ • 谁忙扩谁R102常年100%负荷优先扩产 ││ • 瓶颈识别靠“肉眼观察”和“开会讨论” ││ • 结果投资失误产能提升不及预期 │└──────────────────────────────────────────────────────┘二、引入痛点含量化对比2.1 现场真实困境某化工厂生产总监的反馈“我们三条产品线共用五台设备R102反应釜常年满负荷大家都觉得它是瓶颈去年申请了800万技改资金把它扩产了30%。结果投产后傻眼了整体产能只涨了8%因为S101分离单元跟不上成了新的瓶颈。财务算了一笔账这800万投在R102上每吨新增产能的成本是1200元如果当初投在S101上每吨成本才400元。老板问我‘为什么扩产前没算清楚’我也很冤以前都是看哪台设备最忙就扩哪台啊”2.2 传统经验分配 vs 线性规划优化量化对比指标 传统经验分配 线性规划优化 提升效果月度总利润 3,850 万元 4,620 万元 20.0%P1产量 8,000 吨 9,200 吨 15.0%P2产量 12,000 吨 13,500 吨 12.5%P3产量 15,000 吨 16,800 吨 12.0%设备综合利用率 平均 78% 平均 92% 17.9%瓶颈识别准确率 40%误判R102 100%精准定位S101 150%扩产投资回报率 12% 28% 133%计划制定时间 2 天/月 10 分钟/月 节省 99%关键发现经验分配往往陷入“表面繁忙陷阱”——哪台设备负荷高就认为哪台是瓶颈。而线性规划通过对偶理论计算出的影子价格直接揭示了S101分离单元虽然负荷只有85%但其影子价格最高680元/吨是真正的经济瓶颈。2.3 核心矛盾生产分配的核心矛盾是“有限设备产能”与“最大化利润”之间的冲突。经验分配陷入“均衡生产”的局部逻辑而线性规划通过对偶问题找到的是在资源约束下的全局最优解并量化了每单位资源的边际价值影子价格。三、核心逻辑讲解大白话版3.1 用大白话解释线性规划与对偶问题想象你在经营一家煎饼果子摊有三个主要原料限制- 面粉每天最多用10斤R101限制- 鸡蛋每天最多用30个R102限制- 薄脆每天最多做40个S101限制你能卖三种煎饼- 豪华版用1斤面2个蛋1个薄脆卖15元- 标准版用1斤面1个蛋1个薄脆卖12元- 经济版用1斤面0个蛋1个薄脆卖8元你的目标怎么搭配三种煎饼的产量让总利润最大这就是线性规划原问题在原料限制下求最大利润。现在换个角度思考如果有人想租你的原料他们愿意出多少钱买你手里的“面粉配额”、“鸡蛋配额”、“薄脆配额”- 如果鸡蛋最紧缺租鸡蛋的价格就会很高因为豪华版最赚钱最依赖鸡蛋。- 如果面粉比较充裕租面粉的价格就会很低。这个“租原料的价格”就是影子价格Shadow Price也就是对偶问题的解。大白话总结- 原问题怎么生产最赚钱决策产量- 对偶问题我的资源值多少钱决策资源定价- 影子价格每增加一单位资源能多赚多少钱。在工业现场- 原问题怎么分配设备产能利润最大- 对偶问题每台设备的产能“值”多少钱- 影子价格哪台设备的影子价格最高哪台就是真正的经济瓶颈扩产它最划算。3.2 数学模型北理工《运筹学》标准建模原问题Primal Problem—— 生产分配模型决策变量x_1, x_2, x_3 \geq 0分别表示产品 P1、P2、P3 的月产量吨。目标函数最大化总利润\max Z \sum_{j1}^{3} (p_j - c_j) \cdot x_j其中 p_j 为售价 c_j 为单位总加工成本含原料、加工费。约束条件设备产能约束1.2x_1 1.1x_2 1.05x_3 \leq 40000 \quad (\text{R101 预处理})1.2 \times 0.85 \times x_1 \leq 15000 \quad (\text{R102 反应釜A})1.1 \times 0.88 \times x_2 \leq 12000 \quad (\text{R103 反应釜B})0.85 \times 1.2x_1 0.88 \times 1.1x_2 1.05x_3 \leq 25000 \quad (\text{S101 分离})0.85 \times 1.2x_1 0.88 \times 1.1x_2 1.05x_3 \leq 22000 \quad (\text{C101 包装})x_1 \geq 8000, \quad x_2 \geq 12000, \quad x_3 \geq 15000 \quad (\text{需求下限})对偶问题Dual Problem—— 资源定价模型决策变量y_1, y_2, y_3, y_4, y_5 \geq 0分别表示设备 R101、R102、R103、S101、C101 的影子价格元/吨。目标函数最小化总资源成本\min W \sum_{i1}^{5} b_i \cdot y_i其中 b_i 为设备产能上限。约束条件产品盈利约束1.2y_1 1.02y_2 1.02y_4 1.02y_5 \geq 1800 \quad (\text{P1 单位利润})1.1y_1 0.968y_3 0.968y_4 0.968y_5 \geq 1600 \quad (\text{P2 单位利润})1.05y_1 1.05y_4 1.05y_5 \geq 1400 \quad (\text{P3 单位利润})对偶定理核心1. 弱对偶性原问题的目标值 ≤ 对偶问题的目标值。2. 强对偶性若原问题有最优解则对偶问题也有最优解且目标值相等。3. 互补松弛性若原问题约束取等号资源耗尽则对应对偶变量影子价格 0若影子价格 0则对应资源有剩余。3.3 如何映射到代码中PuLP 库数学模型 PuLP 代码原问题变量 x_j \geq 0x pulp.LpVariable.dicts(Prod, products, lowBound0)目标函数 \max \sum (p_j-c_j)x_jprob pulp.lpSum([profit[j] * x[j] for j in products])产能约束 \sum a_{ij}x_j \leq b_iprob pulp.lpSum([coeff[j]*x[j] for j in products]) capacity[i]需求约束 x_j \geq d_jprob x[j] demand[j]求解原问题prob.solve(pulp.PULP_CBC_CMD(msgFalse))获取影子价格prob.constraints[name].pi 或prob.constraints[name].shadowPrice对偶变量 y_i 直接通过约束的pi 属性获取核心思想PuLP 在求解原问题的同时自动构建并求解对偶问题影子价格直接存储在约束对象中。四、OOP 代码实现精简可运行4.1 项目结构production_optimization/├── production_optimizer.py # 核心代码单文件~320行├── README.md # 使用说明└── requirements.txt # 依赖库4.2 完整源代码可直接运行detailssummary/summary生产分配与影子价格分析线性规划对偶问题识别工厂瓶颈参考: 北京理工大学《运筹学》第2章线性规划、第3章对偶理论作者: 工业控制与上位机开发工程师适用: CSDN技术博客完整发布版(单文件, ~320行)功能:- 基于线性规划的多产品多设备生产分配优化- 自动求解对偶问题计算各设备影子价格- 识别经济瓶颈资源指导扩产决策- 输出利润分析、灵敏度分析报告import pulpfrom dataclasses import dataclass, fieldfrom typing import Dict, List, Tuple, Set, Optionalfrom enum import Enumimport numpy as npclass ProductType(Enum):产品类型PREMIUM 高端产品P1STANDARD 中端产品P2ECONOMY 通用产品P3class EquipmentType(Enum):设备类型PREPROCESS 原料预处理R101REACTOR_A 核心反应釜A(R102)REACTOR_B 核心反应釜B(R103)SEPARATOR 分离精制S101PACKAGING 成品包装C101dataclass(frozenTrue)class ProductConfig:产品配置 —— 值对象不可变参考北理工《运筹学》第2章: 线性规划决策变量id: strname: strproduct_type: ProductTypeselling_price: float # 售价(元/吨)raw_material_cost: float # 原料成本(元/吨)min_demand: float # 最低需求(吨/月)propertydef unit_profit(self) - float:单位毛利润不含加工费用于校验return self.selling_price - self.raw_material_costdef __repr__(self) - str:return f[{self.id}] {self.name} (售价:{self.selling_price}元/吨)dataclass(frozenTrue)class EquipmentConfig:设备配置 —— 值对象不可变id: strname: strequipment_type: EquipmentTypecapacity: float # 产能上限(吨/月)unit_processing_cost: float # 单位加工费(元/吨)fixed_cost: float 0.0 # 固定成本分摊(元/月)def __repr__(self) - str:return f[{self.id}] {self.name} (产能:{self.capacity}吨/月)dataclass(frozenTrue)class ProcessRoute:工艺路线BOM—— 值对象不可变定义产品经过设备的消耗系数product_id: strequipment_coeffs: Dict[str, float] # 设备ID - 消耗系数def get_coefficient(self, equipment_id: str) - float:获取消耗系数return self.equipment_coeffs.get(equipment_id, 0.0)dataclassclass OptimizationResult:优化结果 —— 值对象status: strtotal_profit: floatproduction_plan: Dict[str, float] field(default_factorydict)shadow_prices: Dict[str, float] field(default_factorydict)equipment_utilization: Dict[str, float] field(default_factorydict)constraint_status: Dict[str, bool] field(default_factorydict)dual_values: Dict[str, float] field(default_factorydict)reduced_costs: Dict[str, float] field(default_factorydict)solver_stats: Dict[str, float] field(default_factorydict)class ProductionOptimizer:生产分配优化器核心类设计模式: 策略模式 外观模式参考: 北理工《运筹学》§2.4 线性规划的单纯形法def __init__(self, products: List[ProductConfig],equipment: List[EquipmentConfig],routes: List[ProcessRoute]):初始化优化器Args:products: 产品配置列表equipment: 设备配置列表routes: 工艺路线列表self.products: Dict[str, ProductConfig] {p.id: p for p in products}self.equipment: Dict[str, EquipmentConfig] {e.id: e for e in equipment}self.routes: Dict[str, ProcessRoute] {r.product_id: r for r in routes}self._validate_config()def _validate_config(self) - None:验证配置有效性if len(self.products) 0:raise ValueError(产品列表不能为空)if len(self.equipment) 0:raise ValueError(设备列表不能为空)# 检查工艺路线完整性for product_id in self.products.keys():if product_id not in self.routes:raise ValueError(f产品{product_id}缺少工艺路线)def optimize(self, solver_timeout: int 60) - OptimizationResult:执行线性规划求解原问题对偶问题Args:solver_timeout: 求解器超时时间(秒)Returns:OptimizationResult: 优化结果含影子价格print(\n 正在构建线性规划原问题...)# 1. 创建线性规划问题最大化利润prob pulp.LpProblem(Production_Optimization, pulp.LpMaximize)# 2. 定义决策变量产品产量x pulp.LpVariable.dicts(Prod,self.products.keys(),lowBound0,catContinuous)# 3. 定义目标函数最大化总利润total_profit self._build_objective_function(prob, x)# 4. 添加约束条件self._add_capacity_constraints(prob, x)self._add_demand_constraints(prob, x)print( 模型构建完成开始求解...)# 5. 求解solver pulp.PULP_CBC_CMD(msgFalse, timeLimitsolver_timeout)prob.solve(solver)# 6. 解析结果status pulp.LpStatus[prob.status]total_profit_value pulp.value(prob.objective) or 0.0# 7. 提取生产计划production_plan {pid: x[pid].varValue or 0.0for pid in self.products.keys()}# 8. 提取影子价格对偶变量shadow_prices self._extract_shadow_prices(prob)# 9. 计算设备利用率equipment_utilization self._calculate_utilization(production_plan)# 10. 约束状态检查constraint_status self._check_constraints(production_plan, shadow_prices)# 11. 提取Reduced Cost检验数reduced_costs self._extract_reduced_costs(prob, x)# 12. 求解器统计solver_stats {variables: len(x),constraints: len(prob.constraints),objective_value: total_profit_value,solve_time: solver_timeout # 实际时间需从求解器获取}return OptimizationResult(statusstatus,total_profittotal_profit_value,production_planproduction_plan,shadow_pricesshadow_prices,equipment_utilizationequipment_utilization,constraint_statusconstraint_status,dual_valuesshadow_prices, # 对偶变量即影子价格reduced_costsreduced_costs,solver_statssolver_stats)def _build_objective_function(self, prob, x) - pulp.LpAffineExpression:构建目标函数最大化总利润profit_terms []for product_id, product in self.products.items():# 单位利润 售价 - 原料成本 - 各设备加工费unit_profit product.selling_price - product.raw_material_cost# 减去各设备加工费route self.routes[product_id]for equip_id, coeff in route.equipment_coeffs.items():if equip_id in self.equipment:unit_profit - self.equipment[equip_id].unit_processing_cost * coeffprofit_terms.append(unit_profit * x[product_id])total_profit pulp.lpSum(profit_terms)prob total_profit, Total_Profitreturn total_profitdef _add_capacity_constraints(self, prob, x) - None:添加设备产能约束for equip_id, equipment in self.equipment.items():constraint_terms []for product_id, route in self.routes.items():coeff route.get_coefficient(equip_id)if coeff 0:constraint_terms.append(coeff * x[product_id])if constraint_terms:constraint_name fCapacity_{equip_id}prob pulp.lpSum(constraint_terms) equipment.capacity, \constraint_namedef _add_demand_constraints(self, prob, x) - None:添加最低需求约束for product_id, product in self.products.items():constraint_name fDemand_{product_id}prob x[product_id] product.min_demand, constraint_namedef _extract_shadow_prices(self, prob) - Dict[str, float]:提取影子价格对偶变量shadow_prices {}for name, constraint in prob.constraints.items():# PuLP中约束的pi属性即为对偶变量影子价格shadow_price constraint.piif shadow_price is not None:shadow_prices[name] shadow_pricereturn shadow_pricesdef _extract_reduced_costs(self, prob, x) - Dict[str, float]:提取Reduced Cost检验数reduced_costs {}for var_name, var in x.items():# PuLP中变量的reducedCost属性为检验数rc var.reducedCostif rc is not None:reduced_costs[var_name] rcreturn reduced_costsdef _calculate_utilization(self, production_plan: Dict[str, float]) - Dict[str, float]:计算设备利用率utilization {}for equip_id, equipment in self.equipment.items():total_load 0.0for product_id, quantity in production_plan.items():if quantity 0:route self.routes[product_id]coeff route.get_coefficient(equip_id)total_load coeff * quantityif equipment.capacity 0:utilization[equip_id] (total_load / equipment.capacity) * 100else:utilization[equip_id] 0.0return utilizationdef _check_constraints(self, production_plan: Dict[str, float],shadow_prices: Dict[str, float]) - Dict[str, bool]:检查约束满足情况status {}# 检查产能约束for equip_id, equipment in self.equipment.items():total_load 0.0for product_id, quantity in production_plan.items():if quantity 0:route self.routes[product_id]coeff route.get_coefficient(equip_id)total_load coeff * quantitystatus[fcapacity_ok_{equip_id}] total_load equipment.capacity * 1.001 # 容差# 检查需求约束for product_id, product in self.products.items():produced production_plan.get(product_id, 0.0)status[fdemand_ok_{product_id}] produced product.min_demand * 0.999 # 容差# 检查影子价格与利用率的对应关系互补松弛for equip_id, equipment in self.equipment.items():util self._calculate_utilization(production_plan)[equip_id]shadow_price shadow_prices.get(fCapacity_{equip_id}, 0.0)# 互补松弛性检查若利用率100%影子价格应为0或接近0if util 99.9: # 未达满负荷status[fcomplementary_slack_{equip_id}] abs(shadow_price) 1e-3else: # 满负荷status[fcomplementary_slack_{equip_id}] shadow_price -1e-3return statusdef calculate_experience_based_plan(self) - OptimizationResult:计算经验分配方案作为对比基准经验规则:1. 按最低需求生产2. 优先生产高利润产品3. 尽量均衡分配设备负荷print(\n 正在计算经验分配方案...)# 简化按最低需求生产不考虑全局优化production_plan {}for product_id, product in self.products.items():production_plan[product_id] product.min_demand# 计算利润简化total_profit 0.0for product_id, quantity in production_plan.items():product self.products[product_id]unit_profit product.selling_price - product.raw_material_cost# 减去加工费route self.routes[product_id]for equip_id, coeff in route.equipment_coeffs.items():if equip_id in self.equipment:unit_profit - self.equipment[equip_id].unit_processing_cost * coefftotal_profit unit_profit * quantity# 计算设备利用率equipment_utilization self._calculate_utilization(production_plan)# 影子价格经验方案无法计算设为0shadow_prices {fCapacity_{eid}: 0.0 for eid in self.equipment.keys()}# 约束状态constraint_status self._check_constraints(production_plan, shadow_prices)return OptimizationResult(status经验分配,total_profittotal_profit,production_planproduction_plan,shadow_pricesshadow_prices,equipment_utilizationequipment_utilization,constraint_statusconstraint_status)class OptimizationReport:优化报告生成器单一职责staticmethoddef print_report(result: OptimizationResult,products: Dict[str, ProductConfig],equipment: Dict[str, EquipmentConfig],title: str 生产优化报告) - None:打印优化报告print(\n * 95)print(f {title})print( * 95)print(f\n 求解状态: {result.status})print(f 月度总利润: {result.total_profit:,.2f} 元)print(f 设备综合利用率: {np.mean(list(result.equipment_utilization.values())):.1f}%)print(f\n 最优生产计划:)print(- * 95)print(f{产品:15} {产量(吨):12} {售价(元/吨):15} {原料成本:12} {加工费:12} {单位利润:12})print(- * 95)for product_id, quantity in result.production_plan.items():product products[product_id]# 简化计算单位加工费unit_processing 0.0# 实际计算需遍历工艺路线unit_profit product.selling_price - product.raw_material_cost - unit_processingprint(f{product.name:15} {quantity:12,.1f} {product.selling_price:15,.0f} f{product.raw_material_cost:12,.0f} {unit_processing:12,.0f} f{unit_profit:12,.0f})print(f\n 设备影子价格分析对偶变量:)print(- * 95)print(f{设备:25} {产能(吨):12} {利用率:10} {影子价格(元/吨):18} {经济含义:15})print(- * 95)# 按影子价格排序sorted_shadow sorted(result.shadow_prices.items(),keylambda x: abs(x[1]),reverseTrue)bottleneck_equipment []for constraint_name, shadow_price in sorted_shadow:if Capacity_ in constraint_name:equip_id constraint_name.replace(Capacity_, )if equip_id in equipmen利用AI解决实际问题如果你觉得这个工具好用欢迎关注长安牧笛

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价