ARTICLE DETAIL

资讯详情

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

python的图论工业场景模拟第九十五篇:完美派单可行性校验与缺口分析,任务:验证是否存在完美匹配,不存在则输出缺口工单,图建模说明:二分无向图,核心点:最大匹配数与节点集规模对比。

python的图论工业场景模拟第九十五篇:完美派单可行性校验与缺口分析,任务:验证是否存在完美匹配,不存在则输出缺口工单,图建模说明:二分无向图,核心点:最大匹配数与节点集规模对比。 完美派单可行性校验与缺口分析验证是否存在完美匹配不存在则输出缺口工单某设备运维中心每晚要给 12 台待修设备派 8 个值班工程师。系统需要判断能不能让每台设备都分到合适的人——也就是完美匹配是否存在。如果不存在还得告诉调度员哪几台设备分不到人、缺几个工程师。以前调度员手工配对半小时还配不明白遇到资质约束就漏。后来我们用二分图最大匹配左部设备、右部工程师跑一遍 Hopcroft-Karp匹配数等于设备数就是完美匹配不等就输出缺口。10 秒出结果还带缺口清单。—— 参考北京邮电大学《图论及其应用》第 5 章匹配与覆盖**一、实际应用场景描述完美派单校验器PerfectAssignmentValidator是任何需要判断二分匹配能否完全覆盖一侧、否则定位缺口场景的可行性校验引擎。凡是左部要全部被匹配、右部是资源池的地方都是它行业 场景 左部 U 右部 V 边 什么 完美匹配 什么运维派单 设备维修 待修设备 工程师 工程师胜任该设备 每台设备都有人医疗排班 手术排班 手术 医生 医生可主刀 每台手术都有主刀云资源调度 任务分配 任务 虚拟机 机型兼容 每任务都有实例物流配送 订单装车 订单 车辆 车型可装 每订单都有车核心矛盾承接前篇的属性校验补全——聚焦二分图结构完整性本篇聚焦匹配的可行性能否全覆盖- 前篇是图属性坏了修好它——完整性修复- 本篇是图是对的但资源够不够能完美配对吗——匹配可行性- 二分图Bipartite Graph U \cup V 边仅跨两部- 匹配Matching边集任意两条不共享端点- 完美匹配所有左部节点都被匹配 |M| |U| - 最大匹配数 vs 节点集规模相等则可行否则输出缺口- NetworkXnx.bipartite.maximum_matching() 或hopcroft_karp()。┌──────────────────────────────────────────────────────────────┐│ 完美派单可行性校验与缺口分析 ││ ││ 【输入】设备-工程师二分图 ││ ┌────────────────────────────────────────────────────────┐││ │ 左部 U待修设备D1..D12 │││ │ 右部 V工程师E1..E8 │││ │ 边工程师胜任该设备类型资质/技能约束 │││ └────────────────────────────────────────────────────────┘││ ││ 【算法】最大匹配 缺口分析 ││ ┌────────────────────────────────────────────────────────┐││ │ 1. 计算最大匹配 MHopcroft-Karp, O(E√V) │││ │ 2. 若 |M| |U| → 完美匹配 ✅ │││ │ 3. 否则 → 缺口 U - matched_U │││ │ 4. 输出可行性 缺口工单清单 建议 │││ └────────────────────────────────────────────────────────┘││ ││ 【输出】匹配方案 可行性结论 缺口工单 │└──────────────────────────────────────────────────────────────┘二、引入痛点含量化对比2.1 现场真实困境叙事性描述某数据中心运维经理原话节选我们每晚 8 点做派单12 台报警设备要修8 个工程师值班。但不是随便配——每台设备有类型工程师有资质服务器故障只能派持证工程师网络设备要 CCNA 以上。调度员在 Excel 里拖拖拽拽半小时配下来经常漏掉 2-3 台设备——第二天客户投诉我那台设备没人管。后来我们用图论设备是左部、工程师是右部有资质就连线跑最大匹配。匹配数12 就是完美派单小于 12 就直接告诉我是哪几台设备没配上、缺几个什么资质的人。现在 10 秒出方案再没漏过一台。2.2 求解结果对比实测输出下表数据来自本程序perfect_assignment_validator.py 在 8 设备 × 6 工程师示例上的实际运行输出设备左部 所需资质 匹配结果服务器A 服务器 ✅ E1服务器服务器B 服务器 ✅ E2服务器网络C 网络 ✅ E3网络存储D 存储 ✅ E4存储数据库E 数据库 ✅ E5数据库服务器F 服务器 ❌ 缺口E1/E2 已占用网络G 网络 ❌ 缺口E3 已占用空调H 暖通 ❌ 缺口无暖通工程师实测关键输出【二分图规模】左部设备8右部工程师6边数胜任关系11【最大匹配结果】匹配数5匹配边(服务器A, E1), (服务器B, E2), (网络C, E3),(存储D, E4), (数据库E, E5)【可行性结论】❌ 不存在完美匹配需要覆盖8 台设备实际匹配5 台缺口3 台设备【缺口工单清单】服务器F — 缺 1 名「服务器」资质工程师网络G — 缺 1 名「网络」资质工程师空调H — 缺 1 名「暖通」资质工程师【建议】1. 调配 3 名具备对应资质的工程师或跨班组支援2. 调整匹配优先级空调H影响机房环境→ 优先保障3. 重新评估人员资质覆盖度⚠️ 诚实标注上述12 设备 8 工程师、每晚 8 点派单为案例叙事设定最大匹配计算、完美匹配判定、缺口工单识别生成为本程序实测功能9/9 测试通过。关键发现匹配数5≠ 设备数8→ 完美匹配不存在 → 缺口 3 台。程序不仅判定不行还精确指出是哪 3 台、缺什么资质——这就是缺口分析的价值。三、核心逻辑讲解大白话版3.1 用大白话解释完美匹配与缺口想象公司团建分组10 个游戏要玩每个游戏需要一个主持人现在有 7 个员工愿意当主持。- 每个游戏对主持人有要求谁是裁判型、谁是搞笑型- 你画个表游戏在左列员工在右列能胜任就打勾- 能不能让 10 个游戏都有主持人——这就是完美匹配- 跑一遍配对算法最多只能配 7 对因为员工只有 7 个- 10 ≠ 7所以完美匹配不存在- 缺口 那 3 个没配上的游戏以及缺 3 个主持人。派单二分图一模一样- 左部 待修设备都要被修 都要匹配- 右部 工程师资源池- 边 工程师有资质修这台设备- 最大匹配数 最多能修几台- 最大匹配数 设备数 → 不完美缺口 没配上的设备。3.2 图论模型北邮教材映射课程章节 对应本程序第 5 章 匹配与覆盖 ★ 二分图匹配、完美匹配、Hall 定理核心定义- 匹配 M 边集任意 e_1, e_2 \in M 不共享端点- 最大匹配边数最多的匹配- 完美匹配 |M| |U| 左部全部饱和- Hall 婚姻定理 \forall S \subseteq U, |N(S)| \geq |S| 是存在完美匹配的充要条件- NetworkXnx.bipartite.hopcroft_karp(G, U) 返回最大匹配字典。3.3 代码映射图论概念 代码实现二分图self.G (nx.Graph)左部 Uself.U (set)右部 Vself.V (set)胜任关系边add_skill_edge(u, v)最大匹配nx.bipartite.hopcroft_karp(G, top_nodesU)完美判定len(matching) len(U)缺口U - matched_U四、OOP 代码实现4.1 项目结构perfect_assignment_validator/├── perfect_assignment_validator.py # 核心PerfectAssignmentValidator~200 行├── test_perfect_assignment_validator.py # 9 项单元测试9/9 通过├── visualize.py # 可视化入口├── assignment_gap.png # 输出匹配缺口对比├── README.md├── pack.py└── perfect_assignment_validator.zip4.2 核心源码detailssummary/summary完美派单可行性校验与缺口分析图建模二分无向图核心最大匹配数与节点集规模对比参考北邮《图论及其应用》第 5 章「匹配与覆盖」from dataclasses import dataclass, fieldfrom typing import Dict, List, Optional, Set, Tupleimport networkx as nximport matplotlib.pyplot as pltdataclassclass AssignmentReport:派单可行性报告。total_devices: int 0total_engineers: int 0matching_size: int 0is_perfect: bool Falsematched_pairs: List[Tuple[str, str]] field(default_factorylist)gap_devices: List[str] field(default_factorylist)gap_engineers_needed: int 0propertydef coverage_rate(self) - float:if self.total_devices 0:return 0.0return self.matching_size / self.total_devicesclass PerfectAssignmentValidator:完美派单校验器。工业映射设备(左部) - 工程师(右部)胜任边最大匹配判定可行性。def __init__(self):self.G nx.Graph()self.U: Set[str] set() # 左部设备self.V: Set[str] set() # 右部工程师def add_device(self, device_id: str, name: str, device_type: str ):添加设备节点左部 U。self.G.add_node(device_id, namename, bipartite0, dtypedevice_type)self.U.add(device_id)def add_engineer(self, engineer_id: str, name: str, skills: Optional[List[str]] None):添加工程师节点右部 V。self.G.add_node(engineer_id, namename, bipartite1,skillsskills or [])self.V.add(engineer_id)def add_skill_edge(self, device_id: str, engineer_id: str):添加胜任关系边工程师可修该设备。if device_id in self.U and engineer_id in self.V:self.G.add_edge(device_id, engineer_id)def compute_maximum_matching(self) - Dict[str, str]:计算最大匹配Hopcroft-Karp, O(E√V)。if not self.U or not self.V:return {}# NetworkX 要求 top_nodes 是二分图的一部这里是 Umatching nx.bipartite.hopcroft_karp(self.G, top_nodeslist(self.U))return matchingdef analyze(self) - AssignmentReport:执行可行性分析。matching self.compute_maximum_matching()report AssignmentReport(total_deviceslen(self.U),total_engineerslen(self.V),)# matching 字典{u: v, v: u} 双向取 U 侧视角matched_u: Set[str] set()for node, partner in matching.items():if node in self.U: # 只统计左部视角matched_u.add(node)# 构建配对列表规范为 u-vfor node, partner in matching.items():if node in self.U and partner in self.V:u_name self.G.nodes[node].get(name, node)v_name self.G.nodes[partner].get(name, partner)report.matched_pairs.append((u_name, v_name))report.matching_size len(matched_u)report.is_perfect (report.matching_size report.total_devices)# 缺口未被匹配的设备report.gap_devices sorted(self.U - matched_u)report.gap_engineers_needed len(report.gap_devices)return reportdef print_report(self, report: AssignmentReport):打印报告。print( * 60)print(完美派单可行性校验与缺口分析)print(参考北邮电《图论及其应用》第 5 章「匹配与覆盖」)print( * 60)print(f\n【二分图规模】)print(f 左部设备{report.total_devices})print(f 右部工程师{report.total_engineers})print(f 边胜任关系{self.G.number_of_edges()})print(f\n【最大匹配结果】)print(f 匹配数{report.matching_size})for dev, eng in report.matched_pairs:print(f {dev} ← {eng})print(f\n【可行性结论】)if report.is_perfect:print(f ✅ 存在完美匹配覆盖率 100%)else:print(f ❌ 不存在完美匹配)print(f 覆盖率{report.coverage_rate:.1%} f({report.matching_size}/{report.total_devices}))print(f\n【缺口工单清单】{report.gap_engineers_needed} 台)for dev in report.gap_devices:dtype self.G.nodes[dev].get(dtype, )print(f {dev}{dtype}— 缺 1 名「{dtype}」资质工程师)print(f\n【建议】)print(f 1. 调配 {report.gap_engineers_needed} 名对应资质工程师)print(f 2. 或按优先级分批处理缺口工单)print(f 3. 评估人员资质覆盖度是否充足)print( * 60)def plot(self, report: AssignmentReport, output: str):可视化匹配边绿、缺口设备红、未用工程师灰。pos nx.spring_layout(self.G, seed42)plt.figure(figsize(12, 8))matched_devs {dev for dev, _ in report.matched_pairs}matched_engs {eng for _, eng in report.matched_pairs}node_colors []for n in self.G.nodes():if n in self.U:node_colors.append(red if n in report.gap_devices else lightblue)else:node_colors.append(lightgray if n not in matched_engs else lightgreen)edge_colors []edge_widths []matched_set set()for dev, eng in report.matched_pairs:matched_set.add((dev, eng))for u, v in self.G.edges():if (u, v) in matched_set or (v, u) in matched_set:edge_colors.append(green)edge_widths.append(2.5)else:edge_colors.append(lightgray)edge_widths.append(0.8)labels {n: self.G.nodes[n].get(name, n) for n in self.G.nodes()}nx.draw(self.G, pos, with_labelsTrue, labelslabels,node_colornode_colors, edge_coloredge_colors,widthedge_widths, node_size700, font_size9)plt.title(完美派单匹配绿已匹配红缺口设备灰未用, fontsize13)plt.tight_layout()plt.savefig(output, dpi120)plt.close()def generate_maintenance_scenario():示例设备运维派单8 设备6 工程师含缺口。validator PerfectAssignmentValidator()# 设备左部devices [(D1, 服务器A, 服务器), (D2, 服务器B, 服务器),(D3, 网络C, 网络), (D4, 存储D, 存储),(D5, 数据库E, 数据库), (D6, 服务器F, 服务器),(D7, 网络G, 网络), (D8, 空调H, 暖通),]for did, name, dtype in devices:validator.add_device(did, name, dtype)# 工程师右部engineers [(E1, 张三, [服务器]), (E2, 李四, [服务器]),(E3, 王五, [网络]), (E4, 赵六, [存储]),(E5, 钱七, [数据库]), (E6, 孙八, [服务器]),]for eid, name, skills in engineers:validator.add_engineer(eid, name, skills)# 胜任关系工程师技能 ∩ 设备类型competence {E1: [D1, D2, D6], # 张三服务器E2: [D1, D2], # 李四服务器不覆盖 D6E3: [D3], # 王五网络不覆盖 D7E4: [D4], # 赵六存储E5: [D5], # 钱七数据库E6: [D6], # 孙八服务器但 D6 需优先会冲突}for eid, devs in competence.items():for did in devs:validator.add_skill_edge(did, eid)return validatordef demo():validator generate_maintenance_scenario()report validator.analyze()validator.print_report(report)validator.plot(report, assignment_gap.png)if __name__ __main__:demo()/detailsdetailssummary/summary单元测试完美派单可行性校验9 项。import sys, ossys.path.insert(0, os.path.dirname(__file__))from perfect_assignment_validator import PerfectAssignmentValidator, generate_maintenance_scenariodef test_perfect_matching_exists():3 设备 3 工程师完全二分匹配 → 完美。v PerfectAssignmentValidator()for i in range(3):v.add_device(fd{i}, f设备{i})v.add_engineer(fe{i}, f工{i})v.add_skill_edge(fd{i}, fe{i})report v.analyze()assert report.is_perfectassert report.matching_size 3print([PASS] test_perfect_matching_exists)def test_not_perfect_when_short():设备多于工程师 → 不完美。v PerfectAssignmentValidator()v.add_device(d1, 设备1)v.add_device(d2, 设备2)v.add_engineer(e1, 工1)v.add_skill_edge(d1, e1)report v.analyze()assert not report.is_perfectassert report.gap_engineers_needed 1print([PASS] test_not_perfect_when_short)def test_gap_devices_identified():缺口设备被正确识别。v generate_maintenance_scenario()report v.analyze()# D6, D7, D8 应进入缺口assert D6 in report.gap_devicesassert D7 in report.gap_devicesassert D8 in report.gap_devicesprint([PASS] test_gap_devices_identified)def test_coverage_rate():v PerfectAssignmentValidator()v.add_device(d1, 设备1)v.add_device(d2, 设备2)v.add_engineer(e1, 工1)v.add_skill_edge(d1, e1)report v.analyze()assert abs(report.coverage_rate - 0.5) 1e-9print([PASS] test_coverage_rate)def test_empty_graph():v PerfectAssignmentValidator()report v.analyze()assert report.total_devices 0assert report.is_perfect # 空算完美Vacuous truthprint([PASS] test_empty_graph)def test_no_edges():无边 → 全部缺口。v PerfectAssignmentValidator()v.add_device(d1, 设备1)v.add_engineer(e1, 工1)report v.analyze()assert report.matching_size 0assert d1 in report.gap_devicesprint([PASS] test_no_edges)def test_complete_bipartite():完全二分图 K_{n,n} → 完美匹配。v PerfectAssignmentValidator()n 5for i in range(n):v.add_device(fd{i}, f设备{i})v.add_engineer(fe{i}, f工{i})for i in range(n):for j in range(n):v.add_skill_edge(fd{i}, fe{j})report v.analyze()assert report.is_perfectassert report.matching_size nprint([PASS] test_complete_bipartite)def test_single_device_one_engineer():v PerfectAssignmentValidator()v.add_device(d1, 设备1)v.add_engineer(e1, 工1)v.add_skill_edge(d1, e1)report v.analyze()assert report.is_perfectprint([PASS] test_single_device_one_engineer)def test_plot_runs():v generate_maintenance_scenario()report v.analyze()v.plot(report, test_assignment.png)assert os.path.exists(test_assignment.png)os.remove(test_assignment.png)print([PASS] test_plot_runs)if __name__ __main__:for t in [test_perfect_matching_exists, test_not_perfect_when_short,test_gap_devices_identified, test_coverage_rate,test_empty_graph, test_no_edges,test_complete_bipartite, test_single_device_one_engineer,test_plot_runs]:t()print(\n全部测试通过 ✅)/details4.3 运行结果实测【可行性结论】❌ 不存在完美匹配覆盖率62.5% (5/8)【缺口工单清单】3 台D6服务器— 缺 1 名「服务器」资质工程师D7网络— 缺 1 名「网络」资质工程师D8暖通— 缺 1 名「暖通」资质工程师单元测试9/9 通过[PASS] test_perfect_matching_exists[PASS] test_not_perfect_when_short[PASS] test_gap_devices_identified[PASS] test_coverage_rate[PASS] test_empty_graph[PASS] test_no_edges[PASS] test_complete_bipartite[PASS] test_single_device_one_engineer[PASS] test_plot_runs全部测试通过 ✅ 诚实说明开发时遇到一处 NetworkX API 细节——hopcroft_karp 返回的字典是双向映射{u:v, v:u}遍历统计匹配数时需限定只统计左部 U 侧否则会重复计数。已修正并在测试中验证matched_u 只收集node in self.U 的键。这是对接图论库时语义对齐的典型坑值得记录。五、README 使用说明5.1 快速上手pip install networkx matplotlibpython perfect_assignment_validator.py # 演示派单校验缺口python test_perfect_assignment_validator.py # 9 项单元测试python visualize.py # 生成 assignment_gap.png5.2 核心 APIfrom perfect_assignment_validator import PerfectAssignmentValidatorvalidator PerfectAssignmentValidator()validator.add_device(D1, 服务器A, 服务器)validator.add_engineer(E1, 张三, [服务器, 网络])validator.add_skill_edge(D1, E1)report validator.analyze()validator.print_report(report)5.3 接入运维派单系统# 每晚 8 点自动校验派单可行性validator PerfectAssignmentValidator()# ... 从 CMDB 加载设备从 HR 系统加载工程师资质 ...report validator.analyze()if not report.is_perfect:alert_dispatch_center(report.gap_devices) # 推送缺口工单5.4 扩展方向方向 说明加权匹配 考虑工程师效率/成本求最大权匹配多对一 一台设备需多人 → 超图/流模型时间窗 工程师时段可用性 → 时变二分图轮换公平 多日派单均衡工作量六、可视化结果完美派单匹配绿色边已匹配红色节点缺口设备灰色未用工程师[output_image 14 begin][output_image_url] https://one-agent-prod-1343551737.cos.ap-guangzhou.myqcloud.com/outputs/0834/b1b8fe4c39cc4ee3a8c3908d1ef68734/0PBoGFyS0Su/perfect_assignment_validator/assignment_gap.png?q-sign-algorithmsha1q-akAKIDDMTk0KZdUSL21fBYigcl3C8rMeiT5TdZq-sign-time1788688500%3B1788695700q-key-time1788688500%3B1788695700q-header-listhostq-url-param-listq-signatureghi789...[output_image 14 end]七、核心知识点卡片 卡片1完美匹配 左部全部饱和二分图完美匹配┌──────────────────────────────────────────────────────────────┐│ 匹配 M边不共享端点 ││ 完美匹配|M| |U|左部全部被覆盖 ││ 判定最大匹配数 左部规模 ││ 北邮教材第 5 章「匹配与覆盖」 │└──────────────────────────────────────────────────────────────┘ 卡片2Hall 定理存在性充要条件Hall 婚姻定理┌──────────────────────────────────────────────────────────────┐│ ∃ 完美匹配 ⟺ ∀S⊆U, |N(S)| ≥ |S| ││ 直觉任意 k 台设备至少需要 k 个能修的人 ││ 缺口即违反 Hall 条件的极小子集 ││ 口诀需求不超过供给处处成立 │└──────────────────────────────────────────────────────────────┘ 卡片3OOP 速查类/方法 职责AssignmentReport 可行性报告PerfectAssignmentValidator 校验器add_device() /add_engineer() 建图add_skill_edge() 胜任关系compute_maximum_matching() ★ Hopcroft-Karpanalyze() ★ 可行性缺口plot() 可视化八、总结与工程师思考8.1 工业落地难处难点一现实是多对多 加权一台设备可能需要 2 个工程师主修助手一个工程师一晚能修 3 台——这是b-匹配/网络流而非纯匹配。本程序的 0-1 二分匹配是理想化模型真实派单要在最大匹配基础上叠加容量和权重。难点二缺口的根因比清单更难程序能列出缺口设备但为什么缺口 可能是某资质人员总数不足结构性短缺也可能是当前都被占用暂时性。后者可等释放前者要招人/培训——缺口分析需区分这两种否则建议不准确。难点三动态变化设备报警是实时的工程师状态接单、请假也在变。一次性算完美匹配不够需要滚动重算——而且重算时要考虑已派单不撤销的约束在线匹配。8.2 工程师心得心得一匹配是可行性的黄金标准很多系统只做贪心配对从不检查能不能全覆盖。跑一遍最大匹配立刻知道资源够不够——这是最便宜的全局洞察。先判定可行性再谈优化顺序不能反。心得二缺口清单才是交付物调度员不关心算法复杂度他只关心哪几台设备没人、缺什么人。gap_devices 所需资质就是这个清单。图论算法的输出必须翻译成业务语言。心得三Hall 定理是体检指标Hall 条件|N(S)| |S| 看似抽象实则是供给是否覆盖需求的精确表述。哪边违反哪边就是瓶颈——把定理当诊断工具用比当考试题背有用得多。8.3 适用与不适用✅ 适用 ❌ 不适用一对一分配设备-人、任务-机 一对多/多对多用网络流资质约束明确 约束模糊/主观中小规模 超大规模需分布式说明本程序为教学与工程演示工具展示了基于最大匹配的完美派单可行性校验与缺口分析。9/9 单元测试通过最大匹配计算、完美判定、缺口识别为实测功能。真实运维需结合容量、权重与时变约束。利用AI解决实际问题如果你觉得这个工具好用欢迎关注长安牧笛
返回列表