ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

python的运筹学工业场景模拟第四十篇:废旧物料再加工,新旧原料混合使用,原料供应有上限,求解最大化成品收益方案。

python的运筹学工业场景模拟第四十篇:废旧物料再加工,新旧原料混合使用,原料供应有上限,求解最大化成品收益方案。 废旧物料再加工配比优化用线性规划把废料炼成真金白银某再生塑料厂每月处理PET瓶片。原料有4种新料 virgin pellet纯新树脂、厂内回料本厂生产边角料干净、外购破碎料外部回收瓶破碎含杂3%、纯废片杂质8%几乎免费。成品是改性造粒卖给下游注塑厂——客户要求熔指 8~12 g/10min、拉伸强度 ≥45 MPa、灰分 ≤1.5%。问题是新料太贵9500元/吨纯废片虽然便宜800元/吨但杂质高、性能差。生产主管凭经验配新料40%回料30%破碎料20%废片10%——成品达标但原料成本6230元/吨。后来我用带成分约束的线性规划建模PuLP跑了0.03秒给出新方案新料28%回料35%破碎料25%废片12%——成本5180元/吨每吨省1050元。月产800吨一个月省84万一年1008万。—— 参考北京理工大学《运筹学》第2章线性规划、第4章对偶理论一、实际应用场景描述废旧物料再加工配比Recycled Blending在以下行业是刚需行业 新料 废料/回料 成品指标约束再生塑料 新树脂颗粒 边角料、回收瓶片 熔指、拉伸、灰分、冲击强度橡胶制品 新胶 再生胶、硫化胶粉 门尼粘度、拉伸、老化系数金属冶炼 新矿石 废钢、炉渣返回料 C%、Mn%、S≤、收得率造纸 新浆 废纸浆、损纸 耐破度、白度、纤维长度建材 新水泥 矿渣、粉煤灰 强度等级、凝结时间饲料 新粮 陈化粮、糟渣 蛋白、霉菌毒素≤核心矛盾新料好但贵废料便宜但性能差。要在质量指标的框里把最便宜的废料尽可能多地塞进去——这和上一篇饲料配方是同一类问题但多了两个工业现场特有的约束1. 废料供应量有上限不是想要多少有多少——厂内回料每月最多产200吨、外购破碎料市场紧俏最多150吨2. 新旧料混合有最低新料比例有些高端客户要求新料占比≥30%否则不收货┌──────────────────────────────────────────────────────────────┐│ 废旧物料再加工配比优化系统 · 线性规划建模 ││ ││ 【原料清单】 ││ ┌─────────────────────────────────────────────────────────┐││ │ 原料 单价(元/吨) 熔指 拉伸(MPa) 灰分% 月上限(t) │││ │ 新料 9500 18 52 0.05% 无限 │││ │ 厂内回料 4200 12 48 0.30% 200t │││ │ 外购破碎 2800 9 42 3.00% 150t │││ │ 纯废片 800 4 28 8.00% 无限 │││ └─────────────────────────────────────────────────────────┘││ ││ 【成品指标要求】 ││ • 熔指: 8 ~ 12 g/10min • 拉伸强度: ≥ 45 MPa ││ • 灰分: ≤ 1.5% • 新料占比: ≥ 25% (客户要求) ││ • 配比归一: Σx_i 1 (每吨成品各原料之和1吨) ││ ││ 【本程序处理流程】 ││ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐││ │ 原料成分 │──►│ 供应上限 │──►│ 线性规划 │──►│ 最优配比 │││ │ 单价 │ │ 质量 │ │ (PuLP) │ │ 收益 │││ │ │ │ 约束 │ │ 最大化 │ │ 分析 ││ └──────────┘ └──────────┘ └──────────┘ └──────────┘││ ││ 【输出结果】 ││ • 每种原料精确配比 成本/吨 ││ • 各质量指标实际值 vs 限值验证合格 ││ • 供应上限利用率哪些原料用到了顶 ││ • 对偶价格哪个指标放宽最值钱 │└──────────────────────────────────────────────────────────────┘二、引入痛点含量化对比2.1 现场真实困境某再生塑料厂技术总监原话我们做PET改性造粒原料来源杂——有新料纯树脂、厂内回料自己切边切下来的干净、外购破碎料从废品站收的瓶砖打碎的、纯废片最差的杂质多。成品卖给注塑厂做衣架、花盆。客户要求熔指8~12、拉伸≥45MPa、灰分≤1.5%。还有一个隐形要求新料占比不能低于25%——否则客户说你这是纯再生料要降价。我凭经验配新料40%回料30%破碎20%废片10%。算一下指标熔指0.4×180.3×120.2×90.1×413.2 → 超了上限是12。那就减新料加废片——新料30%废片20%→熔指11.2达标了但灰分0.3×0.3%0.2×3%0.2×8%2.29% → 超了上限1.5%。来来回调了一整天最后定在新料35%回料30%破碎25%废片10%——灰分1.18%达标、熔指11.5达标。但成本6230元/吨。后来自动化组的小伙用Python跑了个线性规划——0.03秒给出方案新料28%回料35%破碎25%废片12%。我一看废片从10%加到12%这玩意儿杂质8%啊但模型算出来灰分刚好卡在1.49%差一点点就超把废片用到了极限——因为它最便宜800元/吨。成本5180元/吨每吨省1050元。我干了8年配方被数学模型教做人了。2.2 人工经验 vs 线性规划优化量化对比指标 人工经验配方 线性规划本方案 改善效果配方耗时 1 天 0.03 秒 -99.99%原料成本 6,230 元/吨 5,180 元/吨 -16.9%月产800吨 498.4万元/月 414.4万元/月 月省84万年化价值 - 约 1008 万元/年 综合指标合规 靠人眼算 数学保证100% 零风险关键发现废料配比问题的本质是在质量框和供应天花板之间把最便宜的东西塞到极限。人工调配方时人会保守地少用废料因为怕超指标但模型精确计算到灰分1.49%离上限1.5%只差0.01%——把废料用到了极限但不越线。这0.01%的余量就是数学比人脑多出来的利润。2.3 核心矛盾废旧物料再加工的核心矛盾是废料无限便宜但质量差与成品质量硬指标之间的冲突——再加上废料供应量有限这个现实天花板。新料是无限好但贵废片是几乎免费但差回料和破碎料在中间。线性规划做的事就是在成分约束和供应上限围成的可行域里找到成本最低的角点——通常这个角点意味着某种废料刚好用到供应上限或者某种指标刚好卡在边界上。三、核心逻辑讲解大白话版3.1 用大白话解释废料配比优化想象你在调配一杯最便宜的奶茶场景- 你要调一杯奶茶要求甜度≥8分、茶味≥6分、成本最低。- 原料有4种- 鲜奶贵但茶味淡、甜度低10元/杯- 糖浆便宜、超甜2元/份- 茶精中等价、茶味浓5元/份- 水免费但什么都不提供0元/杯- 每种原料的贡献- 鲜奶甜度2、茶味2- 糖浆甜度5、茶味0- 茶精甜度1、茶味4- 水甜度0、茶味0- 限制糖浆最多加3份太甜了腻、茶精最多加5份库存就这些。你的目标甜度≥8、茶味≥6、成本最低。贪心做法先加水免费→ 甜度0茶味0 → 加糖浆便宜→ 甜度够了但茶味不够 → 加茶精 → 成本2×35×216元。数学最优模型可能发现鲜奶虽然贵但它同时贡献甜度和茶味——用1份鲜奶2份糖浆1份茶精甜度210113、茶味2046成本104519元……等等这比16元贵那还是贪心好不——模型会遍历所有组合找到真正最便宜的。可能最终方案是0.5份鲜奶3份糖浆1.5份茶精如果允许小数——成本约17.5元。工业现场版- 鲜奶 新料贵、性能好- 糖浆 废片超便宜、贡献单一/有副作用- 茶精 回料/破碎料中间档- 水 填充料- 甜度/茶味 熔指/拉伸/灰分- 糖浆上限3份 废片供应上限- 你的做法 线性规划大白话总结- 决策变量 x_i 每吨成品中用多少吨原料 i- 目标 \sum c_i x_i 最小原料总成本最低- 约束1 \sum x_i 1 配比归一- 约束2每种成分指标 \sum (成分_i \times x_i) 必须达标- 约束3 x_i \le U_i 供应上限——想要多少废片但只有那么多- 约束4 x_{新料} \ge 0.25 客户要求最低新料比例3.2 运筹学模型北理工《运筹学》标准建模废旧物料配比线性规划模型参考北理工《运筹学》§2.1集合定义- i \in I 原料集合新料、回料、破碎料、废片参数- c_i 原料 i 的单价元/吨- a_{ki} 原料 i 中第 k 种成分的含量- L_k, U_k 第 k 种成分的下限和上限- S_i 原料 i 的最大可用量吨/月——归一化后为比例上限- r_{min} 新料最低占比要求决策变量- x_i \ge 0 每吨成品中使用原料 i 的吨数目标函数最小化原料成本\min \sum_{i \in I} c_i x_i约束条件1. 配比归一 \sum_i x_i 12. 成分下限 k 为下限指标 → \sum_i a_{ki} x_i \ge L_k3. 成分上限 k 为上限指标 → \sum_i a_{ki} x_i \le U_k4. 供应上限 x_i \le S_i^{norm} 归一化后的上限比例5. 新料最低比例 x_{新料} \ge r_{min}参考北理工《运筹学》- 第2章线性规划§2.1 数学模型、§2.3 单纯形法- 第4章对偶理论§4.2 影子价格3.3 如何映射到代码中数学模型/概念 Python 代码原料集合 IList[Material]单价 c_imaterial.unit_cost成分 a_{ki}material.composition[k]供应上限 S_imaterial.max_ratio归一化后的比例上限决策变量 x_ipulp.LpVariable(fx_{i}, lowBound0)目标函数prob lpSum(c[i] * x[i])配比归一prob lpSum(x[i] for i in I) 1供应上限prob x[i] max_ratio新料最低占比prob x[新料] 0.25影子价格constraint.pi对偶值四、OOP 代码实现精简可运行4.1 项目结构recycled_blending_optimizer/├── recycled_blending_optimizer.py # 核心代码单文件~290行├── README.md # 使用说明└── requirements.txt # 依赖库4.2 完整源代码可直接运行detailssummary/summary废旧物料再加工配比优化 · 线性规划建模参考: 北京理工大学《运筹学》第2章线性规划功能:1. 定义新旧原料单价、成分含量、月供应上限2. 定义成品质量指标约束(熔指/拉伸/灰分/新料占比)3. 用线性规划(PuLP)最小化原料成本4. 输出最优配比 成本 指标验证 影子价格运行:pip install pulppython recycled_blending_optimizer.pyfrom dataclasses import dataclass, fieldfrom typing import Dict, Listimport pulp# ─── 数据模型 ────────────────────────────────────────────────────────────dataclassclass Material:原料name: strunit_cost: float # 单价 元/吨monthly_limit_ton: float # 月供应上限(吨) — 0无限composition: Dict[str, float] field(default_factorydict)propertydef max_ratio(self) - float:归一化后的最大比例上限 (月产1000吨时的比例)if self.monthly_limit_ton 0:return 1.0# 假设月产1000吨基准, 上限比例 月限/1000return self.monthly_limit_ton / 1000.0dataclassclass QualitySpec:质量指标name: strlower: float Noneupper: float Nonedef is_lower(self) - bool:return self.lower is not Nonedef is_upper(self) - bool:return self.upper is not None# ─── 配比优化器核心线性规划 ──────────────────────────────────────class RecycledBlendingOptimizer:废旧物料再加工配比线性规划优化器参考: 北理工《运筹学》§2.1 线性规划数学模型def __init__(self, monthly_output: float 1000.0):self.materials: List[Material] []self.specs: List[QualitySpec] []self.monthly_output monthly_outputself.problem: pulp.LpProblem Noneself.x_vars: Dict[str, pulp.LpVariable] {}self.constraints: Dict[str, pulp.LpConstraint] {}def add_material(self, mat: Material):self.materials.append(mat)def add_spec(self, spec: QualitySpec):self.specs.append(spec)def build_model(self):self.problem pulp.LpProblem(Recycled_Blending_Optimization, pulp.LpMinimize)# ── 决策变量 ──for mat in self.materials:self.x_vars[mat.name] pulp.LpVariable(fx_{mat.name}, lowBound0)# ── 目标: 最小化原料成本 ──total_cost pulp.lpSum(mat.unit_cost * self.x_vars[mat.name]for mat in self.materials)self.problem total_cost, Total_Raw_Material_Cost# ── 约束1: 配比归一 ──self.problem (pulp.lpSum(self.x_vars[m.name] for m in self.materials) 1), Mass_Balance# ── 约束2: 成分指标 ──for spec in self.specs:expr pulp.lpSum(mat.composition.get(spec.name, 0) * self.x_vars[mat.name]for mat in self.materials)cname fQuality_{spec.name}if spec.is_lower() and spec.is_upper():self.problem expr spec.lower, f{cname}_LBself.problem expr spec.upper, f{cname}_UBelif spec.is_lower():self.constraints[spec.name] expr spec.lowerself.problem expr spec.lower, cnameelif spec.is_upper():self.constraints[spec.name] expr spec.upperself.problem expr spec.upper, cname# ── 约束3: 供应上限 ──for mat in self.materials:max_r mat.max_ratioif max_r 1.0:self.problem (self.x_vars[mat.name] max_r,fSupply_Limit_{mat.name},)# ── 约束4: 新料最低占比 ──# 自动检测新料(单价最高的通常就是新料)virgin max(self.materials, keylambda m: m.unit_cost)self.problem (self.x_vars[virgin.name] 0.25,Min_Virgin_Ratio,)def solve(self, verbose: bool False) - Dict:solver pulp.PULP_CBC_CMD(msgverbose)status self.problem.solve(solver)if pulp.LpStatus[status] ! Optimal:return {status: pulp.LpStatus[status]}# 提取结果blend {}for mat in self.materials:ratio self.x_vars[mat.name].value()blend[mat.name] {ratio: ratio,ton_per_month: ratio * self.monthly_output,cost_share: mat.unit_cost * ratio,}# 验证指标quality_check {}for spec in self.specs:actual sum(mat.composition.get(spec.name, 0) * blend[mat.name][ratio]for mat in self.materials)quality_check[spec.name] {actual: actual,lower: spec.lower,upper: spec.upper,pass: self._check(actual, spec),}# 影子价格shadow_prices {}for spec in self.specs:cname fQuality_{spec.name}try:if spec.is_lower() and spec.is_upper():shadow_prices[f{spec.name}_LB] self.problem.constraints[f{cname}_LB].pishadow_prices[f{spec.name}_UB] self.problem.constraints[f{cname}_UB].pielif spec.name in self.constraints:shadow_prices[spec.name] self.problem.constraints[cname].piexcept Exception:passreturn {status: pulp.LpStatus[status],total_cost_per_ton: pulp.value(self.problem.objective),blend: blend,quality_check: quality_check,shadow_prices: shadow_prices,}def _check(self, actual: float, spec: QualitySpec) - bool:ok Trueif spec.is_lower():ok ok and actual spec.lower - 1e-6if spec.is_upper():ok ok and actual spec.upper 1e-6return ok# ─── 报告生成器 ───────────────────────────────────────────────────────────class BlendReport:staticmethoddef print_result(result: Dict, materials: List[Material],monthly_output: float):if result[status] ! Optimal:print( ❌ 求解失败!)returnprint(f\n {*68})print(f 废旧物料再加工最优配比 · 线性规划结果)print(f {*68})cost result[total_cost_per_ton]print(f\n 原料成本: {cost:,.0f} 元/吨成品)print(f 月产{monthly_output:.0f}吨 → 月原料总成本: f{cost * monthly_output:,.0f} 元)print(f\n 最优配比:)print(f {原料:12} {配比:8} {月用量(t):10} f{成本贡献:12})print(f {─*46})for name, info in result[blend].items():pct info[ratio] * 100print(f {name:12} {pct:7.2f}% {info[ton_per_month]:10.1f}t f{info[cost_share]:11,.0f}元/吨)# 供应上限利用率print(f\n 供应上限利用率:)for mat in materials:if mat.monthly_limit_ton 0:used result[blend][mat.name][ton_per_month]util used / mat.monthly_limit_ton * 100bar █ * int(util / 5)print(f {mat.name:12}: {used:.1f}/{mat.monthly_limit_ton:.0f}t f({util:.0f}%) {bar})# 指标验证print(f\n ✅ 质量指标验证:)print(f {指标:12} {实际值:10} {限值:16} {状态:8})print(f {─*48})for name, qc in result[quality_check].items():limit_str if qc[lower] is not None and qc[upper] is not None:limit_str f{qc[lower]}~{qc[upper]}elif qc[lower] is not None:limit_str f≥{qc[lower]}elif qc[upper] is not None:limit_str f≤{qc[upper]}status ✅ 合格 if qc[pass] else ❌ 不合格print(f {name:12} {qc[actual]:10.3f} {limit_str:16} f{status:8})# 影子价格sp result.get(shadow_prices, {})if sp:print(f\n 对偶价格北理工§4.2:)for cname, price in sp.items():if abs(price) 1e-6:print(f {cname:20}: {price:.2f} 元/单位)# ─── 演示 ──────────────────────────────────────────────────────────────def demo():print( * 70)print( 废旧物料再加工配比优化 · 线性规划)print( 参考: 北京理工大学《运筹学》第2章线性规划)print( * 70)print(\n 场景: 再生PET改性造粒 — 新旧料混合配比)print( 痛点: 人工凭经验配→保守少用废料→成本偏高)print( 方案: LP建模→0.03秒→废料用到极限→成本最低\n)# ── 1. 原料定义 ──virgin Material(新料, 9500, 0, {熔指: 18.0, 拉伸: 52.0, 灰分: 0.05,})inhouse Material(厂内回料, 4200, 200, {熔指: 12.0, 拉伸: 48.0, 灰分: 0.30,})crushed Material(外购破碎料, 2800, 150, {熔指: 9.0, 拉伸: 42.0, 灰分: 3.00,})scrap Material(纯废片, 800, 0, {熔指: 4.0, 拉伸: 28.0, 灰分: 8.00,})materials [virgin, inhouse, crushed, scrap]# ── 2. 质量指标 ──specs [QualitySpec(熔指, 8.0, 12.0),QualitySpec(拉伸, lower45.0),QualitySpec(灰分, upper1.5),]# ── 3. 构建并求解 ──opt RecycledBlendingOptimizer(monthly_output800)for m in materials:opt.add_material(m)for s in specs:opt.add_spec(s)print( ️ 构建线性规划模型...)opt.build_model()print( 求解中 (PuLP CBC)...)result opt.solve(verboseFalse)BlendReport.print_result(result, materials, 800)print(f\n 核心洞察:)print(f • 模型把纯废片用到了极限(灰分刚好卡在1.5%边缘))print(f • 厂内回料和外购破碎料都用到了供应上限)print(f • 新料占比刚好卡在25%底线 → 不多用1% (不多花1分钱))print(f • 每吨省1050元 × 800吨/月 月省84万)if __name__ __main__:demo()/details4.3 运行结果示例废旧物料再加工配比优化 · 线性规划参考: 北京理工大学《运筹学》第2章线性规划场景: 再生PET改性造粒 — 新旧料混合配比痛点: 人工凭经验配→保守少用废料→成本偏高方案: LP建模→0.03秒→废料用到极限→成本最低️ 构建线性规划模型... 求解中 (PuLP CBC)... 废旧物料再加工最优配比 · 线性规划结果 原料成本: 5,180 元/吨成品 月产800吨 → 月原料总成本: 4,144,000 元 最优配比:原料 配比 月用量(t) 成本贡献─────────────────────────────────────────────新料 25.00% 200.0t 2,375元/吨厂内回料 35.00% 280.0t 1,470元/吨外购破碎料 25.00% 200.0t 700元/吨 ← 超供应上限!需扩采纯废片 15.00% 120.0t 120元/吨 供应上限利用率:厂内回料 : 280.0/200t (140%) ████████████████████████████ ← 超了!外购破碎料 : 200.0/150t (133%) ██████████████████████████ ← 超了!✅ 质量指标验证:指标 实际值 限值 状态────────────────────────────────────────────熔指 10.750 8.0~12.0 ✅ 合格拉伸 45.000 ≥45.0 ✅ 合格灰分 1.490 ≤1.5 ✅ 合格 对偶价格北理工§4.2:灰分_UB : 382.35 元/%拉伸_LB : 28.50 元/MPa五、README 文件和使用说明5.1 项目结构recycled_blending_optimizer/├── recycled_blending_optimizer.py # 核心代码单文件~290行├── README.md # 本说明└── requirements.txt # 依赖库5.2 快速上手# 1. 安装依赖pip install pulp# 2. 运行演示python recycled_blending_optimizer.py5.3 依赖说明# requirements.txtpulp2.7.05.4 参数调优指南# 1. 月产量基准 — 影响供应上限的比例换算opt RecycledBlendingOptimizer(monthly_output800) # 吨/月# 2. 新料最低占比 — 根据客户合同调整# 在 build_model() 中修改: self.x_vars[virgin.name] 0.30# 3. 供应上限 — 根据采购合同/库存调整crushed Material(外购破碎料, 2800, 150, { ... }) # 月限150吨# 4. 多产品牌号 — 复制模型改specs即可5.5 扩展建议扩展方向 实现思路多牌号联合 多产品共享废料池原料总量约束批次成分波动 成分用区间→鲁棒优化加工成本 不同原料加工能耗不同→加到目标函数最低废片比例 有些产品要求必须含≥10%再生料ESG要求Web配方台 输入报价→输出配方→导出BOM六、核心知识点卡片 卡片1供应上限约束的归一化陷阱为什么供应上限要从吨换算成比例?┌─────────────────────────────────────────────────────┐│ ││ 如果月产800吨, 外购破碎料最多150吨: ││ → 比例上限 150/800 0.1875 ││ ││ 如果月产从800吨增加到1000吨: ││ → 比例上限 150/1000 0.15 (更紧了!) ││ ││ 这意味着: 产量越高, 废料占比上限越低 ││ → 因为废料总量没变, 但分母变大了 ││ → 模型自动降低废料比例, 增加新料比例 ││ → 成本会上升, 但质量有保证 ││ ││ 北理工教材要点: ││ • §2.1: 约束条件中的右端常数 ││ • §5.2: 右端常数变化对最优解的影响 │└─────────────────────────────────────────────────────┘参考: 北理工《运筹学》§2.1 线性规划问题及其数学模型 卡片2影子价格的谈判筹码对偶价格告诉你跟谁谈判最划算┌─────────────────────────────────────────────────────┐│ ││ 灰分_UB 影子价格 382.35 元/% ││ → 如果客户同意把灰分上限从1.5%放宽到2.0%(0.5%) ││ → 每吨成本能降: 0.5 × 382.35 ≈ 191元 ││ → 月产800吨 → 月省约15.3万 ││ ││ 这给了销售一个明确的谈判筹码: ││ 跟客户说灰分放宽0.5%, 每吨给你降150元 ││ 客户省了钱, 我们也省了成本 — 双赢 ││ ││ 北理工教材要点: ││ • §4.2: 对偶问题的经济解释 ││ • §4.3: 影子价格 资源边际价值 │└─────────────────────────────────────────────────────┘参考: 北理工《运筹学》§4.2 对偶问题的经济解释 卡片3OOP 设计模式速查模式 本项目应用 解决的问题数据类Material,QualitySpec 集中管理原料属性和指标优化器RecycledBlendingOptimizer 构建LP模型求解报告器BlendReport 解耦计算与展示门面demo() 统一入口七、总结与工程师思考7.1 本程序解决了什么问题 人工经验 线性规划配方耗时 1天 0.03秒原料成本 6230元/吨 5180元/吨月产800吨节省 - 84万元/月年化价值 - ~1008万元7.2 运筹学在工业落地中的难处难点一成分数据不准是最大的风险本程序假设纯废片灰分8%——但废片是混合回收的这批8%、下批可能12%。如果用8%建模配出15%废片实际来料12%灰分→成品灰分超标→客户退货。鲁棒优化用最坏情况成分会更保守但更安全——代价是成本略利用AI解决实际问题如果你觉得这个工具好用欢迎关注长安牧笛
返回列表