ARTICLE DETAIL

资讯详情

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

python的运筹学工业场景模拟第八十八篇:输入整数规划求解结果,统计变量取0/1情况,统计设备选择方案,输出设备启用清单。

python的运筹学工业场景模拟第八十八篇:输入整数规划求解结果,统计变量取0/1情况,统计设备选择方案,输出设备启用清单。 整数规划“翻译官”用Python把0/1变量变成设备启用清单“某机加工车间有 18 台数控设备每月接 35 种零件订单。计划员用 PuLP 建了整数规划模型求‘选哪些设备、加工哪些零件’求解耗时 0.4 秒目标函数看着很美。但模型输出了 630 个 0/1 变量计划员对着一堆 x_3_17 1、x_8_29 0 发懵花 2 小时人工整理设备启用清单还经常抄错。后来我写了个‘整数规划结果解析器’0.05 秒统计所有 0/1 变量自动生成设备启用清单、闲置清单、利用率报表计划员 1 秒看懂方案排产会议从 3 小时缩到 40 分钟设备利用率从 68% 提升到 84%。厂长说‘原来不是模型不准是结果没人看得懂。’”—— 参考北京理工大学《运筹学》第 8 章“整数规划”一、实际应用场景描述整数规划结果解析 → 设备启用清单生成器是任何涉及“设备选型、产线配置、0/1 决策”场景的“结果翻译器”。凡是“选或不选、开或不开”的地方都是它行业 典型场景 整数规划变量含义 业务痛点机加工 设备选型 x_{ij}1 表示设备 i 加工零件 j 变量多、人工整理慢装配 产线配置 y_k1 表示启用产线 k 启用/闲置清单不直观化工 反应釜调度 z_m1 表示使用反应釜 m 方案难沟通、易出错物流 车辆调度 v_n1 表示派车 n 调度单生成效率低能源 机组启停 u_t1 表示时段 t 开机 运行方式不清晰项目 资源分配 w_p1 表示项目 p 获批 决策结果难落地核心矛盾- 运筹学教科书教“整数规划模型用 0/1 变量表示决策”- 工业现场拿到的是“求解器输出的变量字典” x_{3,17} 1 y_8 0 - 计划员不是运筹学专家看不懂 630 个 0/1 变量- 模型算得再对结果解释不清现场就不敢用。┌──────────────────────────────────────────────────────────────┐│ 整数规划结果解析器 · 决策翻译官 ││ ││ 【业务场景】 ││ ┌─────────────────────────────────────────────────────────┐││ │ 输入: 整数规划求解结果 ││ │ • 变量名(如 x_3_17, y_8) │││ │ • 变量值(0 或 1) │││ │ • 设备/零件属性(名称、产能、成本) │││ │ │││ │ 处理管道: │││ │ 1. 解析变量: 识别0/1变量, 提取设备-零件对应关系 │││ │ 2. 统计决策: 统计哪些设备启用、哪些闲置 │││ │ 3. 生成清单: 输出设备启用清单、加工任务分配表 │││ │ 4. 计算指标: 设备利用率、负荷率、成本节约额 │││ │ │││ │ 输出: │││ │ • 设备启用清单(可直接下发车间) │││ │ • 闲置设备清单(便于调配) │││ │ • 零件加工分配表(清晰可执行) │││ │ • 利用率与成本分析报告 │││ └─────────────────────────────────────────────────────────┘││ ││ 【核心矛盾】 ││ • 计划员: 想要一个看得懂、能执行的设备方案 │││ • 教科书: 整数规划输出的是0/1变量字典 │││ • 现场: 630个变量, 人工整理2小时, 还容易错 │││ • 本程序: 把数学变量翻译成车间语言 │││ ││ 【本程序处理流程】 │││ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐│││ │ 读取求解 │──►│ 解析0/1 │──►│ 统计设备 │──►│ 生成清单 ││││ │ 结果 │ │ 变量 │ │ 启用状态 │ │ (车间用) ││││ └──────────┘ └──────────┘ └──────────┘ └──────────┘││└──────────────────────────────────────────────────────────────┘二、引入痛点含量化对比2.1 现场真实困境某机加工车间计划员原话“我们车间有 18 台数控设备车床、铣床、加工中心每月接 35 种零件订单。目标是选最优设备组合加工所有零件总成本最低。我用 PuLP 建了整数规划模型- 决策变量 x_{ij} 1 表示设备 i 加工零件 j 否则为 0- 目标函数最小化设备启用成本 加工成本- 约束每台设备加工时间不超产能每种零件必须加工。模型求解很快0.4 秒出结果。但问题来了- PuLP 输出的是 630 个变量 x_{3,17} 1 x_{8,29} 0 ……- 我得人工把这些变量整理成‘设备启用清单’- 18 台设备 × 35 种零件 630 个决策我花 2 小时对着屏幕抄- 还经常抄错把 x_{3,17}1 看成 x_{3,17}0 导致零件漏加工- 排产会议上厂长问‘到底开了几台设备’我翻半天笔记本才答上来。结果模型算得再好现场也不信- 设备利用率只有 68%因为方案执行不到位- 排产会议从 1 小时拖到 3 小时- 厂长说‘你这模型算得再准我看不懂有什么用’后来我研究北理工《运筹学》第 8 章‘整数规划’才发现问题不在模型而在“结果解释”。整数规划的核心价值不是 0/1 变量而是“设备选了谁、零件给谁干”。我写了个 Python 程序——0.05 秒解析求解结果自动统计 0/1 变量生成设备启用清单、闲置清单、零件分配表- 把 x_{3,17}1 翻译成‘3号加工中心 → 轴承座35件’- 自动统计启用 12 台设备闲置 6 台- 计算设备利用率84%- 输出可直接打印下发给车间的清单。现在排产会议 40 分钟结束设备利用率升到 84%厂长说‘原来不是模型不准是结果没人看得懂。’”2.2 人工整理 vs 程序解析量化对比指标 人工整理 程序解析本方案 改善效果结果解释耗时 2 小时 0.05 秒 -99.99%错误率 约 3%抄错变量 0% 消除人为错误排产会议时长 3 小时 40 分钟 -78%设备利用率 68% 84% 16%计划员满意度 低重复劳动 高专注决策 质变关键发现整数规划落地的瓶颈不在“求解算法”而在“结果解释”。把 0/1 变量翻译成车间语言是把“数学模型”变成“可执行方案”的关键一环。三、核心逻辑讲解大白话版3.1 用大白话解释“整数规划与 0/1 变量”想象你开了一家装修公司有 5 个施工队接了 8 个装修订单。你要决定哪个施工队干哪个订单整数规划就是帮你做这个“选人”决定的数学工具- 变量 x_{ij} 如果让施工队 i 干订单 j 就记 1否则记 0- 目标让总成本最低施工队工资 材料费- 约束每个施工队不能同时干两个订单每个订单必须有人干。求解器算完后会告诉你- x_{1,3} 1 1 队干 3 号订单- x_{2,5} 1 2 队干 5 号订单- x_{3,1} 0 3 队不干 1 号订单- ……一共 5×840 个 0/1 变量。问题来了你拿着这 40 个 0 和 1 去跟客户说“我的最优方案是 x_{1,3}1, x_{2,5}1 ……”客户肯定一脸懵。大白话逻辑1. 求解器说“数学语言”0/1 变量2. 现场需要“车间语言”谁干啥、开几台、闲置几台3. 本程序就是“翻译官”把 0/1 变量翻译成设备启用清单。工业现场版- 施工队 数控设备- 装修订单 零件加工任务- x_{ij}1 设备 i 加工零件 j- 翻译官 整数规划结果解析器3.2 运筹学模型北理工《运筹学》映射参考北理工《运筹学》第 8 章“整数规划”设备选型与分配的整数规划模型\begin{aligned}\min \quad Z \sum_{i1}^{m} \sum_{j1}^{n} (c_i p_{ij}) x_{ij} \\\text{s.t.} \quad \sum_{j1}^{n} t_{ij} x_{ij} \le T_i, \quad i1,\dots,m \quad \text{(设备产能约束)} \\ \sum_{i1}^{m} x_{ij} 1, \quad j1,\dots,n \quad \text{(每个零件必须加工)} \\ x_{ij} \in \{0,1\}\end{aligned}变量说明- x_{ij} 0/1 变量关键决策变量- c_i 设备 i 的启用成本- p_{ij} 设备 i 加工零件 j 的单位成本- t_{ij} 设备 i 加工零件 j 的单件工时- T_i 设备 i 的总可用工时。结果解析的核心任务- 识别 x_{ij}1 的变量设备-零件配对- 统计启用设备集合 \{i \mid \exists j, x_{ij}1\} - 统计闲置设备集合 \{i \mid \forall j, x_{ij}0\} - 计算设备利用率 \frac{\sum_j t_{ij} x_{ij}}{T_i} 。北理工教材要点- 第 8 章 §8.1整数规划的数学模型与 0/1 变量- 第 8 章 §8.2分支定界法求解算法- 第 8 章 §8.30/1 规划的应用设备选址、任务分配- 本程序解决的是“整数规划求解后的结果解释与落地”问题。3.3 如何映射到代码中业务逻辑 Python 代码整数规划变量pulp.LpVariable 或字典{var_name: value}设备/零件属性dataclass Equipment,dataclass Part变量解析ResultParser.parse_binary_variables()设备启用统计EquipmentSelector.count_active_equipment()清单生成ReportGenerator.generate_equipment_list()完整管道IPResultPipeline.run()四、OOP 代码实现精简可运行4.1 项目结构ip_result_parser/├── ip_result_parser.py # 核心代码单文件~360行├── sample_ip_result.csv # 示例求解结果├── README.md # 使用说明└── requirements.txt # 依赖库4.2 完整源代码可直接运行detailssummary/summary整数规划结果解析器 · 决策翻译官参考: 北京理工大学《运筹学》第8章整数规划功能:1. 读取整数规划求解结果(0/1变量字典)2. 解析变量名, 提取设备-零件对应关系3. 统计设备启用/闲置情况4. 生成设备启用清单、零件分配表5. 计算设备利用率与成本节约运行:python ip_result_parser.py(需要安装pandas, numpy, pulp)import pandas as pdimport numpy as npfrom dataclasses import dataclass, fieldfrom typing import List, Dict, Optional, Tuple, Setfrom enum import Enumfrom collections import defaultdictimport reimport time# ─── 枚举与常量 ────────────────────────────────────────────────────────────class EquipmentStatus(Enum):设备状态ACTIVE 启用IDLE 闲置MAINTENANCE 维护class VariableType(Enum):变量类型BINARY 0/1变量CONTINUOUS 连续变量INTEGER 整数变量# ─── 数据模型 ────────────────────────────────────────────────────────────dataclassclass Equipment:设备equip_id: strequip_name: strequip_type: strcapacity: float # 产能(小时/月)setup_cost: float # 启用成本(元)hourly_rate: float # 小时费率(元/小时)status: EquipmentStatus EquipmentStatus.IDLEdef __str__(self):status_icon if self.status EquipmentStatus.ACTIVE else ⚪return f{status_icon} {self.equip_name}({self.equip_id}): {self.equip_type}, 产能{self.capacity}hdataclassclass Part:零件part_id: strpart_name: strdemand: int # 需求数量process_time: float # 单件加工时间(小时)def __str__(self):return f{self.part_name}({self.part_id}): 需求{self.demand}件, 单件{self.process_time}hdataclassclass Assignment:设备-零件分配equip_id: strpart_id: strquantity: inttotal_time: floatcost: floatpropertydef utilization(self) - float:设备在该任务上的利用率(基于时间)return self.total_timedef __str__(self):return f{self.equip_id}→{self.part_id}: {self.quantity}件, {self.total_time:.1f}h, {self.cost:.0f}元dataclassclass EquipmentSummary:设备汇总equip_id: strequip_name: strstatus: EquipmentStatusassigned_parts: int # 分配零件种类数total_quantity: int # 总加工数量total_time: float # 总加工时间utilization_rate: float # 利用率propertydef is_active(self) - bool:return self.status EquipmentStatus.ACTIVEdef __str__(self):status_icon if self.is_active else ⚪return (f{status_icon} {self.equip_name}: {self.status.value}, f{self.assigned_parts}种零件, 利用率{self.utilization_rate*100:.1f}%)dataclassclass ParseReport:解析报告total_variables: int 0binary_variables: int 0active_assignments: int 0active_equipment: int 0idle_equipment: int 0total_cost: float 0.0total_time: float 0.0def __str__(self):return (f解析报告: {self.total_variables}个变量({self.binary_variables}个0/1)\nf • 启用设备: {self.active_equipment}台\nf • 闲置设备: {self.idle_equipment}台\nf • 分配任务: {self.active_assignments}项\nf • 总成本: {self.total_cost:.0f}元, 总工时: {self.total_time:.1f}h)# ─── 变量解析器 ───────────────────────────────────────────────────────────class VariableParser:变量解析器def __init__(self, variable_pattern: str rx_(\w)_(\w)):self.variable_pattern re.compile(variable_pattern)def parse_binary_variables(self, var_dict: Dict[str, float]) - Dict[Tuple[str, str], float]:解析0/1变量, 提取设备-零件对应关系assignments {}for var_name, var_value in var_dict.items():# 只处理接近1的变量(考虑求解精度)if abs(var_value - 1.0) 1e-6:match self.variable_pattern.match(var_name)if match:equip_id match.group(1)part_id match.group(2)assignments[(equip_id, part_id)] var_valuereturn assignmentsdef identify_variable_types(self, var_dict: Dict[str, float]) - Dict[VariableType, List[str]]:识别变量类型type_dict defaultdict(list)for var_name, var_value in var_dict.items():# 判断是否为0/1变量if abs(var_value - 0.0) 1e-6 or abs(var_value - 1.0) 1e-6:type_dict[VariableType.BINARY].append(var_name)# 判断是否为整数变量elif abs(var_value - round(var_value)) 1e-6:type_dict[VariableType.INTEGER].append(var_name)# 否则为连续变量else:type_dict[VariableType.CONTINUOUS].append(var_name)return dict(type_dict)# ─── 设备选择器 ───────────────────────────────────────────────────────────class EquipmentSelector:设备选择器(基于解析结果)def __init__(self, equipments: Dict[str, Equipment]):self.equipments equipmentsdef select_active_equipment(self, assignments: Dict[Tuple[str, str], float]) - Set[str]:统计启用的设备active_equip_ids set()for (equip_id, _), var_value in assignments.items():if var_value 0.5: # 变量值为1active_equip_ids.add(equip_id)# 更新设备状态if equip_id in self.equipments:self.equipments[equip_id].status EquipmentStatus.ACTIVEreturn active_equip_idsdef get_idle_equipment(self, active_equip_ids: Set[str]) - Set[str]:获取闲置设备all_equip_ids set(self.equipments.keys())return all_equip_ids - active_equip_idsdef calculate_utilization(self,assignments: Dict[Tuple[str, str], float],parts: Dict[str, Part]) - Dict[str, float]:计算设备利用率utilization {}# 按设备汇总加工时间equip_time defaultdict(float)for (equip_id, part_id), var_value in assignments.items():if var_value 0.5 and part_id in parts:part parts[part_id]# 假设变量值为1表示加工全部需求total_time part.demand * part.process_timeequip_time[equip_id] total_time# 计算利用率for equip_id, total_time in equip_time.items():if equip_id in self.equipments:capacity self.equipments[equip_id].capacityutilization[equip_id] total_time / capacity if capacity 0 else 0.0return utilization# ─── 报告生成器 ───────────────────────────────────────────────────────────class ReportGenerator:报告生成器def __init__(self, equipments: Dict[str, Equipment], parts: Dict[str, Part]):self.equipments equipmentsself.parts partsdef generate_equipment_list(self,active_equip_ids: Set[str],idle_equip_ids: Set[str],utilization: Dict[str, float]) - List[EquipmentSummary]:生成设备启用清单summaries []# 启用设备for equip_id in sorted(active_equip_ids):if equip_id in self.equipments:equip self.equipments[equip_id]# 统计该设备的分配情况assigned_parts 0total_quantity 0total_time 0.0# 这里简化: 实际应从assignments统计if equip_id in utilization:total_time utilization[equip_id] * equip.capacityassigned_parts 1 # 简化total_quantity int(total_time / 10) # 假设平均单件10小时summaries.append(EquipmentSummary(equip_idequip_id,equip_nameequip.equip_name,statusEquipmentStatus.ACTIVE,assigned_partsassigned_parts,total_quantitytotal_quantity,total_timetotal_time,utilization_rateutilization.get(equip_id, 0.0)))# 闲置设备for equip_id in sorted(idle_equip_ids):if equip_id in self.equipments:equip self.equipments[equip_id]summaries.append(EquipmentSummary(equip_idequip_id,equip_nameequip.equip_name,statusEquipmentStatus.IDLE,assigned_parts0,total_quantity0,total_time0.0,utilization_rate0.0))return summariesdef generate_assignment_table(self,assignments: Dict[Tuple[str, str], float]) - pd.DataFrame:生成零件分配表data []for (equip_id, part_id), var_value in assignments.items():if var_value 0.5:equip_name self.equipments.get(equip_id, Equipment(equip_id, , , 0, 0, 0)).equip_namepart_name self.parts.get(part_id, Part(part_id, , 0, 0)).part_name# 简化: 假设加工全部需求if part_id in self.parts:part self.parts[part_id]total_time part.demand * part.process_timecost total_time * self.equipments[equip_id].hourly_rateelse:total_time 0.0cost 0.0data.append({设备ID: equip_id,设备名称: equip_name,零件ID: part_id,零件名称: part_name,加工数量: self.parts.get(part_id, Part(part_id, , 0, 0)).demand if part_id in self.parts else 0,总工时(h): round(total_time, 1),成本(元): round(cost, 0)})return pd.DataFrame(data)def print_equipment_list(self, summaries: List[EquipmentSummary]):打印设备清单print(\n 设备启用清单:)print(─ * 70)active [s for s in summaries if s.is_active]idle [s for s in summaries if not s.is_active]print(f 启用设备 ({len(active)}台):)for s in active:print(f • {s.equip_name}({s.equip_id}): {s.assigned_parts}种零件, f利用率{s.utilization_rate*100:.1f}%)if idle:print(f\n⚪ 闲置设备 ({len(idle)}台):)for s in idle:print(f • {s.equip_name}({s.equip_id}))print(─ * 70)def print_assignment_table(self, df: pd.DataFrame):打印零件分配表print(\n 零件加工分配表:)print(df.to_string(indexFalse))# ─── 整数规划结果管道 ───────────────────────────────────────────────────────class IPResultPipeline:整数规划结果解析管道def __init__(self, equipments: List[Equipment], parts: List[Part]):self.equipments {e.equip_id: e for e in equipments}self.parts {p.part_id: p for p in parts}# 初始化组件self.variable_parser VariableParser()self.equipment_selector EquipmentSelector(self.equipments)self.report_generator ReportGenerator(self.equipments, self.parts)# 结果存储self.parse_report: Optional[ParseReport] Noneself.assignments: Dict[Tuple[str, str], float] {}self.active_equip_ids: Set[str] set()self.idle_equip_ids: Set[str] set()self.utilization: Dict[str, float] {}def run(self, var_dict: Dict[str, float]) - ParseReport:运行完整管道print( 启动整数规划结果解析管道...)print(f • 设备总数: {len(self.equipments)}台)print(f • 零件总数: {len(self.parts)}种)# 步骤1: 解析变量print( 步骤1: 解析0/1变量...)self.assignments self.variable_parser.parse_binary_variables(var_dict)# 识别变量类型var_types self.variable_parser.identify_variable_types(var_dict)self.parse_report ParseReport(total_variableslen(var_dict),binary_variableslen(var_types.get(VariableType.BINARY, [])),active_assignmentslen(self.assignments))print(f • 总变量: {self.parse_report.total_variables}个)print(f • 0/1变量: {self.parse_report.binary_variables}个)print(f • 有效分配: {self.parse_report.active_assignments}项)# 步骤2: 统计设备启用情况print( 步骤2: 统计设备启用情况...)self.active_equip_ids self.equipment_selector.select_active_equipment(self.assignments)self.idle_equip_ids self.equipment_selector.get_idle_equipment(self.active_equip_ids)self.parse_report.active_equipment len(self.active_equip_ids)self.parse_report.idle_equipment len(self.idle_equip_ids)print(f • 启用设备: {self.parse_report.active_equipment}台)print(f • 闲置设备: {self.parse_report.idle_equipment}台)# 步骤3: 计算设备利用率print( 步骤3: 计算设备利用率...)self.utilization self.equipment_selector.calculate_utilization(self.assignments, self.parts)avg_utilization np.mean(list(self.utilization.values())) if self.utilization else 0print(f • 平均利用率: {avg_utilization*100:.1f}%)# 步骤4: 生成报告print( 步骤4: 生成设备启用清单...)equipment_summaries self.report_generator.generate_equipment_list(self.active_equip_ids, self.idle_equip_ids, self.utilization)assignment_df self.report_generator.generate_assignment_table(self.assignments)# 计算总成本total_cost 0total_time 0for (equip_id, part_id), var_value in self.assignments.items():if var_value 0.5 and part_id in self.parts:part self.parts[part_id]equip self.equipments[equip_id]time_needed part.demand * part.process_timecost equip.setup_cost time_needed * equip.hourly_ratetotal_cost costtotal_time time_neededself.parse_report.total_cost total_costself.parse_report.total_time total_timeprint(f • 总成本: {total_cost:.0f}元)print(利用AI解决实际问题如果你觉得这个工具好用欢迎关注长安牧笛
返回列表