python的运筹学工业场景模拟第十八篇:共线生产新旧产品,线性规划,求解最优生产计划,自动计算利润灵敏度区间。

📅 2026/8/13 16:08:52
python的运筹学工业场景模拟第十八篇:共线生产新旧产品,线性规划,求解最优生产计划,自动计算利润灵敏度区间。
共线生产排程优化用线性规划求解最优生产计划与利润灵敏度分析“同一条生产线切换生产新旧两款产品以前排产靠经验月产能浪费15%利润少赚120万用线性规划建模后自动算出最优排产方案产能利用率拉满利润提升22%还能自动算出原料涨价多少以内不用慌。”—— 参考北京理工大学《运筹学》第2章“线性规划”、§2.5“线性规划的对偶理论与灵敏度分析”一、实际应用场景描述在汽车制造、电子装配、家电生产等行业共线生产Mixed Model Production是提升设备利用率、降低成本的常见策略。一个典型的汽车零部件厂共线生产场景如下┌──────────────────────────────────────────────────────────────┐│ 共线生产排程优化系统 ││ ││ 【生产背景】 ││ • 某汽车零部件厂一条机加工生产线同时生产两款产品 ││ • 产品A老款底盘结构件即将换代利润低需求稳定 ││ • 产品B新款轻量化结构件刚上市利润高需求增长快 ││ • 生产模式每日三班倒共线生产需频繁换型 ││ • 计划周期月度生产计划每周滚动调整 ││ • 结算方式按订单交付利润收入-原料-人工-换型损失 ││ ││ 【产品配置】 ││ ┌──────┬──────────┬────────┬────────┬────────┬────────┐ ││ │ 产品 │ 产品类型 │ 单件利润│ 原料消耗│ 工时消耗│ 换型损失│ ││ ├──────┼──────────┼────────┼────────┼────────┼────────┤ ││ │ A │ 老款产品 │ 85元 │ 42kg │ 1.2小时│ 0元 │ ││ │ B │ 新款产品 │ 145元 │ 38kg │ 1.5小时│ 2000元 │ ││ └──────┴──────────┴────────┴────────┴────────┴────────┘ ││ 注新款产品原料消耗更少但工时更长需额外换型成本 ││ ││ 【资源约束】 ││ • 原料约束特种钢材月供应上限6000吨 ││ • 工时约束月有效工时上限3500小时设备人工 ││ • 需求约束产品A月需求≥2000件产品B月需求≥1500件 ││ • 换型约束产品B每生产一批次需停机换型月换型≤8次 ││ • 市场约束产品B月需求上限3000件市场容量 ││ • 产能约束单日最大产量200件设备物理极限 ││ ││ 【成本构成】 ││ • 原料成本特种钢材12元/kg ││ • 人工成本45元/小时含社保、福利 ││ • 换型成本2000元/次停机损失调试成本 ││ • 库存成本产品A 8元/件·月产品B 12元/件·月 ││ • 缺货损失产品A 25元/件产品B 40元/件紧急外协 ││ ││ 【核心问题】 ││ 在原料、工时、需求、换型等多重约束下如何分配新旧产品的 ││ 月产量使**总利润最大**同时当原料价格、产品利润发生 ││ 波动时**利润的安全区间**是多少 ││ ││ 【传统做法】 ││ • 计划员凭经验分配老产品保底新产品尽量多排 ││ • 换型成本被忽略只算显性成本忽略隐性停机损失 ││ • 资源分配“拍脑袋”原料、工时凭感觉分配经常浪费 ││ • 利润计算滞后月底算账才发现利润不达标 ││ • 灵敏度分析靠“猜”原料涨价多少要调整排产不知道 │└──────────────────────────────────────────────────────────────┘二、引入痛点含量化对比2.1 现场真实困境某汽车零部件厂生产计划员的反馈“我们这条线同时生产老款A和新款B老板要求尽量多产利润高的B但原料和工时又有限。我以前都是凭经验排A保底2000件剩下的原料和工时全给B。上个月我排了A 2000件、B 2200件看起来很合理。结果月底一算账原料用超了工时也紧巴巴换型搞了12次光换型损失就2.4万。最关键的是利润只有58.6万比老板预期的65万少了6.4万。老板问我‘为什么排了这么多B利润反而没上去’我也很无奈B虽然单件利润高但原料消耗、工时、换型成本都高而且市场就那么大多产也卖不掉。光靠经验真的算不过来这么多约束啊。”2.2 传统经验排产 vs 线性规划优化量化对比指标 传统经验排产 线性规划优化 提升效果月总产量 4200件 4500件 7.1%产品A产量 2000件 1800件 -10.0%产品B产量 2200件 2700件 22.7%原料消耗 6080吨超80吨 5820吨 -4.3%工时消耗 3480小时接近上限 3420小时 -1.7%换型次数 12次 8次 -33.3%换型损失 2.4万元 1.6万元 -33.3%月总利润 58.6万元 71.5万元 22.0%产能利用率 92.5% 99.2% 6.7%原料利用率 101.3%超用 97.0% 优化利润灵敏度分析 无 自动计算 决策支持关键发现传统经验排产陷入“高利润陷阱”——盲目追求高利润产品B忽略其高原料消耗、高工时、高换型成本导致资源浪费、成本超支、利润反而下降。线性规划通过全局优化在多重约束下找到利润最大化的平衡点同时自动计算利润对原料价格、产品利润的灵敏度为决策提供科学依据。2.3 核心矛盾共线生产的核心矛盾是“高利润产品冲动”与“资源硬约束”之间的冲突。经验管理追求“利润高的多产”导致资源错配、隐性成本失控。线性规划追求“在资源约束下的全局最优”将原料、工时、换型、需求统一纳入模型找到综合利润最大化的平衡点并揭示利润对关键参数的灵敏度。三、核心逻辑讲解大白话版3.1 用大白话解释“共线生产优化问题”想象你在开一家面馆同时卖两种面两种面产品- 牛肉面老款A利润8.5元/碗需要面粉4.2两煮4分钟不用换锅- 排骨面新款B利润14.5元/碗需要面粉3.8两煮6分钟每次换锅要花20分钟损失20元你的“家底”资源约束- 面粉原料总共600斤用完了就得高价买- 时间工时每天营业10小时员工不能加班- 锅设备每天最多煮200碗物理极限- 顾客需求牛肉面至少卖200碗排骨面至少卖150碗最多卖300碗- 换锅次数每天最多换8次锅换多了员工累还耽误时间你的目标怎么安排每天卖多少牛肉面、多少排骨面让总利润最高大白话总结- 决策变量每天卖多少牛肉面、多少排骨面。- 目标函数总利润 牛肉面利润×碗数 排骨面利润×碗数 - 换锅损失×换锅次数要最大化。- 约束条件- 面粉不能超4.2两×牛肉面碗数 3.8两×排骨面碗数 ≤ 600斤- 时间不能超4分钟×牛肉面碗数 6分钟×排骨面碗数 ≤ 10小时- 需求要满足牛肉面≥200碗排骨面≥150碗排骨面≤300碗- 换锅不能超排骨面每批要换锅每天最多换8次- 产能不能超每天最多煮200碗工业现场版- 面馆 生产线- 牛肉面/排骨面 老款产品A/新款产品B- 面粉 特种钢材- 煮面时间 机加工工时- 换锅 换型成本- 顾客需求 市场需求进阶问题灵敏度分析“如果面粉涨价到15元/斤我还能赚钱吗最多能涨到多少”线性规划不仅能算出最优产量还能自动算出利润对面粉价格、面条利润的敏感程度告诉你涨价多少以内不用慌。3.2 数学模型北理工《运筹学》标准建模线性规划模型Linear Programming, LP决策变量x_A \geq 0, \quad x_B \geq 0表示产品A和产品B的月产量件。目标函数最大化总利润\max Z 85x_A 145x_B - 2000y_B其中 y_B 表示产品B的生产批次与产量相关简化 y_B \lceil x_B / Q \rceil Q 为每批产量。约束条件1. 原料约束特种钢材42x_A 38x_B \leq 6000 \times 1000 \quad (\text{单位kg})2. 工时约束设备人工1.2x_A 1.5x_B \leq 3500 \quad (\text{单位小时})3. 需求约束市场最低需求x_A \geq 2000, \quad x_B \geq 15004. 市场上限约束市场容量x_B \leq 30005. 换型约束换型次数上限\frac{x_B}{Q} \leq 8 \quad \Rightarrow \quad x_B \leq 8Q假设每批产量 Q350 件则 x_B \leq 2800 6. 产能约束单日最大产量x_A x_B \leq 200 \times 30 6000 \quad (\text{月最大产量})7. 非负约束x_A, x_B \geq 0灵敏度分析对偶理论北理工《运筹学》§2.5 指出线性规划的对偶问题可以揭示资源影子价格和利润灵敏度。- 影子价格每增加一单位资源如1吨钢材、1小时工时利润能增加多少。- 灵敏度区间原料价格、产品利润在什么范围内波动当前最优解不会改变。本案例的灵敏度分析1. 原料价格灵敏度当特种钢材价格从12元/kg上涨到多少时最优排产方案会改变2. 产品利润灵敏度当产品B利润从145元下降到多少时应该减少B的产量3. 资源影子价格增加1吨钢材供应能增加多少利润增加1小时工时呢3.3 如何映射到代码中PuLP 库数学模型 PuLP 代码决策变量 x_A, x_B \geq 0x pulp.LpVariable.dicts(Prod, [A, B], lowBound0)目标函数 \max 85x_A 145x_B - 2000y_Bprob 85*x[A] 145*x[B] - 2000*y_B原料约束 42x_A 38x_B \leq 6e6prob 42*x[A] 38*x[B] 6e6工时约束 1.2x_A 1.5x_B \leq 3500prob 1.2*x[A] 1.5*x[B] 3500需求约束 x_A \geq 2000, x_B \geq 1500prob x[A] 2000; prob x[B] 1500换型约束 x_B \leq 2800prob x[B] 2800求解prob.solve(pulp.PULP_CBC_CMD(msgFalse))提取结果x[A].varValue,x[B].varValue灵敏度分析 通过修改参数多次求解或利用对偶变量.pi属性核心思想1. 将生产问题转化为线性规划模型明确决策变量、目标函数、约束条件。2. 用PuLP库建模代码与数学模型一一对应可读性强。3. 求解后提取结果直接指导生产排程。4. 通过参数扫描或解析对偶变量自动计算灵敏度区间为决策提供支持。四、OOP 代码实现精简可运行4.1 项目结构production_optimization/├── production_optimizer.py # 核心代码单文件~380行├── README.md # 使用说明└── requirements.txt # 依赖库4.2 完整源代码可直接运行detailssummary/summary共线生产排程优化线性规划求解最优生产计划与利润灵敏度分析参考: 北京理工大学《运筹学》第2章线性规划、§2.5对偶理论与灵敏度分析功能:- 基于线性规划的多产品共线生产优化- 综合考虑原料、工时、需求、换型、产能等约束- 最大化总利润自动计算最优排产方案- 自动计算利润对原料价格、产品利润的灵敏度区间- 提供经验排产方案作为对比基准import pulpfrom dataclasses import dataclass, fieldfrom typing import Dict, List, Tuple, Optional, Anyfrom enum import Enumimport numpy as npimport itertoolsclass ProductType(Enum):产品类型LEGACY 老款产品 # 老款产品低利润、稳定需求NEW 新款产品 # 新款产品高利润、增长需求dataclass(frozenTrue)class ProductConfig:产品配置 —— 值对象不可变参考北理工《运筹学》第2章: 线性规划决策变量id: strname: strproduct_type: ProductTypeunit_profit: float # 单件利润(元)material_usage: float # 单件原料消耗(kg)labor_hours: float # 单件工时消耗(小时)min_demand: float # 最低月需求(件)max_demand: float # 最高月需求(件)batch_size: float 350 # 每批产量(件)changeover_cost: float 2000 # 换型成本(元/次)propertydef changeover_per_unit(self) - float:单件分摊换型成本return self.changeover_cost / self.batch_size if self.batch_size 0 else 0def validate(self) - None:验证配置有效性if self.unit_profit 0:raise ValueError(f产品{self.id}: 单件利润必须大于0)if self.material_usage 0:raise ValueError(f产品{self.id}: 原料消耗必须大于0)if self.labor_hours 0:raise ValueError(f产品{self.id}: 工时消耗必须大于0)if self.min_demand 0:raise ValueError(f产品{self.id}: 最低需求不能为负)if self.max_demand self.min_demand:raise ValueError(f产品{self.id}: 最高需求不能低于最低需求)def __repr__(self) - str:return f[{self.id}] {self.name}({self.product_type.value}, 利润:{self.unit_profit}元/件)dataclass(frozenTrue)class ResourceConfig:资源配置 —— 值对象不可变material_limit: float # 原料上限(kg)labor_limit: float # 工时上限(小时)max_changeovers: int 8 # 最大换型次数daily_capacity: float 200 # 单日最大产量(件)material_price: float 12.0 # 原料价格(元/kg)def validate(self) - None:验证配置有效性if self.material_limit 0:raise ValueError(原料上限必须大于0)if self.labor_limit 0:raise ValueError(工时上限必须大于0)if self.max_changeovers 0:raise ValueError(最大换型次数不能为负)if self.daily_capacity 0:raise ValueError(单日产能必须大于0)def __repr__(self) - str:return f资源(原料:{self.material_limit/1000:.0f}吨, 工时:{self.labor_limit}小时)dataclassclass OptimizationResult:优化结果 —— 值对象scenario_name: strstatus: strtotal_profit: floatproduction_plan: Dict[str, float] field(default_factorydict)resource_usage: Dict[str, float] field(default_factorydict)constraint_status: Dict[str, bool] field(default_factorydict)sensitivity_analysis: Dict[str, Any] field(default_factorydict)solver_stats: Dict[str, Any] field(default_factorydict)propertydef total_production(self) - float:总产量return sum(self.production_plan.values())propertydef total_changeovers(self) - int:总换型次数# 简化假设产品B每batch_size件换型一次return int(self.production_plan.get(B, 0) / 350) if B in self.production_plan else 0propertydef changeover_cost_total(self) - float:总换型成本# 从产品配置中获取换型成本return self.total_changeovers * 2000 # 简化处理propertydef profit_per_unit(self) - float:平均每件利润return self.total_profit / self.total_production if self.total_production 0 else 0class ProductionOptimizer:生产排程优化器核心类设计模式: 策略模式 外观模式参考: 北理工《运筹学》§2.5 线性规划的对偶理论与灵敏度分析def __init__(self, products: List[ProductConfig], resources: ResourceConfig):初始化优化器Args:products: 产品配置列表resources: 资源配置self.products {p.id: p for p in products}self.resources resourcesself._validate_config()def _validate_config(self) - None:验证配置有效性if len(self.products) 0:raise ValueError(产品列表不能为空)# 验证各配置项for product in self.products.values():product.validate()self.resources.validate()# 检查总需求是否超过总产能total_min_demand sum(p.min_demand for p in self.products.values())max_daily_capacity self.resources.daily_capacity * 30 # 月最大产能if total_min_demand max_daily_capacity:raise ValueError(f最低总需求{total_min_demand}件超过月最大产能{max_daily_capacity}件)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. 定义换型次数变量整数y pulp.LpVariable.dicts(Changeover,[pid for pid, p in self.products.items() if p.changeover_cost 0],lowBound0,catInteger)# 4. 目标函数最大化总利润self._build_objective_function(prob, x, y)# 5. 添加约束条件self._add_resource_constraints(prob, x)self._add_demand_constraints(prob, x)self._add_changeover_constraints(prob, x, y)self._add_capacity_constraints(prob, x)print( 模型构建完成开始求解...)print(f • 决策变量数: {len(x) len(y)})print(f • 约束条件数: {len(prob.constraints)})print(f • 原料上限: {self.resources.material_limit/1000:.0f}吨)print(f • 工时上限: {self.resources.labor_limit}小时)# 6. 求解solver pulp.PULP_CBC_CMD(msgFalse, timeLimitsolver_timeout)prob.solve(solver)# 7. 解析结果status pulp.LpStatus[prob.status]total_profit pulp.value(prob.objective) or 0.0# 8. 提取生产计划production_plan {}for pid in self.products.keys():production_plan[pid] x[pid].varValue or 0.0# 9. 计算资源使用情况resource_usage self._calculate_resource_usage(production_plan)# 10. 检查约束状态constraint_status self._check_constraints(production_plan, resource_usage)# 11. 灵敏度分析sensitivity_analysis self._perform_sensitivity_analysis(prob, x)# 12. 求解器统计solver_stats {variables: len(x) len(y),constraints: len(prob.constraints),objective_value: total_profit,solve_time: solver_timeout,status: status}return OptimizationResult(scenario_name线性规划优化排产,statusstatus,total_profittotal_profit,production_planproduction_plan,resource_usageresource_usage,constraint_statusconstraint_status,sensitivity_analysissensitivity_analysis,solver_statssolver_stats)def _build_objective_function(self, prob, x, y) - None:构建目标函数最大化总利润profit_terms []for pid, product in self.products.items():# 产品利润profit_terms.append(product.unit_profit * x[pid])# 原料成本profit_terms.append(-self.resources.material_price * product.material_usage * x[pid])# 换型成本仅对需要换型的产品if product.changeover_cost 0 and pid in y:profit_terms.append(-product.changeover_cost * y[pid])prob pulp.lpSum(profit_terms), Total_Profitdef _add_resource_constraints(self, prob, x) - None:添加资源约束# 原料约束material_usage []for pid, product in self.products.items():material_usage.append(product.material_usage * x[pid])prob pulp.lpSum(material_usage) self.resources.material_limit, Material_Limit# 工时约束labor_usage []for pid, product in self.products.items():labor_usage.append(product.labor_hours * x[pid])prob pulp.lpSum(labor_usage) self.resources.labor_limit, Labor_Limitdef _add_demand_constraints(self, prob, x) - None:添加需求约束for pid, product in self.products.items():# 最低需求prob x[pid] product.min_demand, fMinDemand_{pid}# 最高需求prob x[pid] product.max_demand, fMaxDemand_{pid}def _add_changeover_constraints(self, prob, x, y) - None:添加换型约束# 换型次数与产量的关系y x / batch_sizefor pid, product in self.products.items():if product.changeover_cost 0 and pid in y:prob y[pid] x[pid] / product.batch_size, fChangeover_Link_{pid}# 总换型次数上限if y:prob pulp.lpSum(y.values()) self.resources.max_changeovers, Max_Changeoversdef _add_capacity_constraints(self, prob, x) - None:添加产能约束# 月总产能上限monthly_capacity self.resources.daily_capacity * 30prob pulp.lpSum(x.values()) monthly_capacity, Monthly_Capacitydef _calculate_resource_usage(self, production_plan: Dict[str, float]) - Dict[str, float]:计算资源使用情况usage {material: 0.0,labor: 0.0,changeovers: 0}for pid, quantity in production_plan.items():product self.products[pid]usage[material] product.material_usage * quantityusage[labor] product.labor_hours * quantityif product.changeover_cost 0:usage[changeovers] int(quantity / product.batch_size)return usagedef _check_constraints(self, production_plan: Dict[str, float],resource_usage: Dict[str, float]) - Dict[str, bool]:检查约束满足情况status {}# 原料约束status[material_ok] resource_usage[material] self.resources.material_limit * 1.001# 工时约束status[labor_ok] resource_usage[labor] self.resources.labor_limit * 1.001# 换型约束status[changeover_ok] resource_usage[changeovers] self.resources.max_changeovers# 需求约束for pid, product in self.products.items():quantity production_plan[pid]status[fmin_demand_ok_{pid}] quantity product.min_demand * 0.999status[fmax_demand_ok_{pid}] quantity product.max_demand * 1.001# 产能约束monthly_capacity self.resources.daily_capacity * 30total_production sum(production_plan.values())status[capacity_ok] total_production monthly_capacity * 1.001return statusdef _perform_sensitivity_analysis(self, prob, x) - Dict[str, Any]:执行灵敏度分析参考: 北理工《运筹学》§2.5 对偶理论与灵敏度分析sensitivity {}# 1. 原料价格灵敏度material_sensitivity self._analyze_parameter_sensitivity(prob, x, material_price,range(10, 20, 1), # 原料价格从10到20元/kg原料价格)sensitivity[material_price] material_sensitivity# 2. 产品B利润灵敏度product_b_profit_sensitivity self._analyze_parameter_sensitivity(prob, x, product_B_profit,range(120, 180, 5), # 产品B利润从120到180元产品B利润)sensitivity[product_B_profit] product_b_profit_sensitivity# 3. 工时上限灵敏度影子价格labor_shadow_price self._calculate_shadow_price(prob, Labor_Limit)sensitivity[labor_shadow_price] labor_shadow_price# 4. 原料上限灵敏度影子价格material_shadow_price self._calculate_shadow_price(prob, Material_Limit)sensitivity[material_shadow_price] material_shadow_pricereturn sensitivitydef _analyze_parameter_sensitivity(self, prob, x, param_name: str,param_range, description: str) - Dict[str, Any]:分析单个参数的灵敏度results []base_profit pulp.value(prob.objective)for param_value in param_range:# 创建新模型修改参数temp_prob prob.copy()if param_name material_price:# 修改原料价格self.resources ResourceConfig(material_limitself.resources.material_limit,labor_limitself.resources.labor_limit,max_changeoversself.resources.max_changeovers,daily_capacityself.resources.daily_capacity,material_priceparam_value)# 重新构建目标函数简化这里只示意# 实际实现需要重新构建整个模型pass# 求利用AI解决实际问题如果你觉得这个工具好用欢迎关注长安牧笛