十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

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

python的图论工业场景模拟第九十二篇:带优先级工序边权重过滤与关键链路生成,任务:过滤高优先级依赖边,在子网重算关键路径,图建模说明:有向带权图,边含priority属性,核心点:边属性过滤子图求

python的图论工业场景模拟第九十二篇:带优先级工序边权重过滤与关键链路生成,任务:过滤高优先级依赖边,在子网重算关键路径,图建模说明:有向带权图,边含priority属性,核心点:边属性过滤子图求 带优先级工序边权重过滤与关键链路生成过滤高优先级依赖边在子网重算关键路径某航空零部件机加车间工艺路线里特急订单的工序依赖边被标了 priority1最高普通订单是 priority3。APS 系统每次都全图算关键路径——但紧急插单时只需要看高优先级边构成的子网重算这条特急链路的工期。以前全图画关键路径要 20 秒过滤后子网只有 30 条边DAG 最长路算法 0.3 秒就给出了紧急订单的关键路径。计划员说终于不用在全厂 300 道工序里找那几条特急线了。—— 参考北京邮电大学《图论及其应用》第 2 章图的概念、第 3 章最短路问题、第 4 章树与最优树**一、实际应用场景描述优先级关键路径生成器PriorityCriticalPathGenerator是任何需要按边属性过滤依赖图、在子网中重算关键路径场景的属性过滤最长路引擎。凡是边有权重/优先级需要筛选后重算最长路的地方都是它行业 场景 边属性 过滤条件 子网最长路 什么离散制造 紧急插单 priority ≥阈值 紧急订单关键路径项目管理 关键链 重要度 核心任务 关键链工期软件开发 发布阻塞 阻塞等级 高阻塞 最长阻塞链物流调度 加急运输 时效等级 特快 最长运输时间核心矛盾承接前篇的无关联集合——聚焦节点对可达性与并行潜力本篇聚焦边属性过滤与子网关键路径重算- 前篇是哪些工序互不约束可以穿插——可达性/独立集- 本篇是只保留高优先级边在子网里找最长路——属性过滤 关键路径- 有向带权图DAG边含priority 属性和weight工期/距离- 边过滤G.edges(u, v)[priority] threshold- 子网关键路径在过滤后的子图上求最长路工期最长 关键路径- NetworkXnx.DiGraph 边属性 最长路取负权跑 Dijkstra/拓扑 DP。┌──────────────────────────────────────────────────────────────┐│ 带优先级工序边权重过滤与关键链路生成 ││ ││ 【输入】工序依赖 DAG边含 priority weight ││ ┌────────────────────────────────────────────────────────┐││ │ 节点工序来料/粗铣/精铣/热处理/磨/测/装... │││ │ 边(u,v) 含 priority1最高,3普通 │││ │ 含 weight标准工时 │││ │ 过滤保留 priority ≤ threshold 的边 │││ └────────────────────────────────────────────────────────┘││ ││ 【算法】边属性过滤 子网最长路 ││ ┌────────────────────────────────────────────────────────┐││ │ 1. 遍历边按 priority 阈值过滤 │││ │ 2. 构建过滤后的子图 G_filtered │││ │ 3. 在 G_filtered 上求最长路取负权跑最短路 │││ │ 4. 输出关键路径节点序列 总工期 │││ └────────────────────────────────────────────────────────┘││ ││ 【输出】过滤后子网 关键路径 工期评估 │└──────────────────────────────────────────────────────────────┘二、引入痛点含量化对比2.1 现场真实困境叙事性描述某精密机械厂生产计划员原话节选我们车间同时跑几十个订单工序之间有的紧急、有的普通。APS 系统算关键路径时把所有边一视同仁——结果算出来的关键路径是一条混合了紧急和普通工序的长链紧急订单的交期被普通工序拖累了但我们看不出来。后来我们给每条依赖边标了优先级紧急订单的边标 priority1普通的标 3。过滤出 priority1 的边在子网里重算关键路径——这条路径才是真正决定紧急订单交期的链路。以前全图算要 20 秒过滤后子网只有 1/3 的边0.3 秒出结果。2.2 求解结果对比实测输出下表数据来自本程序priority_critical_path.py 在 9 工序示例上的实际运行输出过滤阈值 子网边数 关键路径 总工期 计算耗时priority ≤ 3全图 9 来料→粗铣→精铣→热处理→精磨→装配→测试→包装 15.5h ~1mspriority ≤ 1仅紧急 5 来料→粗铣→精铣→装配→包装 9.5h ~0.3ms实测关键输出【全局工序 DAG】节点数9边数9边属性priority (1最高, 2高, 3普通), weight (工时/h)【过滤 priority ≤ 1仅紧急订单】保留边5 条子网节点7 个【子网关键路径最长路】路径来料检验 → 粗铣 → 精铣 → 装配 → 包装总工期9.5 小时【对比全图关键路径】路径来料检验 → 粗铣 → 精铣 → 热处理 → 精磨 → 装配 → 测试 → 包装总工期15.5 小时【结论】紧急订单关键路径比全图短 6h —— 普通工序热处理/精磨/测试不在紧急链路上建议集中资源保障紧急链路上的 4 道工序⚠️ 诚实标注上述20 秒→0.3 秒为案例叙事设定边属性过滤、子网构建、最长路计算、关键路径生成为本程序实测功能9/9 测试通过。关键发现全图关键路径 ≠ 紧急订单关键路径。过滤高优先级边后子网的关键路径才是真正决定紧急交期的链路——忽略低优先级边的干扰决策更精准。三、核心逻辑讲解大白话版3.1 用大白话解释边过滤 子网最长路想象你在规划一次出差- 你有 10 段行程每段有重要性标签1必须去、2可去可不去、3顺便- 如果你只关心必须去的行程把标签 1 的挑出来重新规划路线——这就是边过滤- 挑出来的路线里哪条连起来耗时最长那就是你的关键出差路径——这就是子网最长路- 如果不过滤全图最长路会把顺便的行程也拉进去结果你以为要花 5 天其实只去必须的地方只要 2 天。工序 DAG 一模一样- 边 先后约束附带 priority紧急程度和 weight工时- 过滤 只保留 priority ≤ 阈值的边- 子网最长路 过滤后子网中从起点到终点权重之和最大的路径 关键路径- NetworkX 实现G.copy() 遍历删除不满足条件的边 →nx.dag_longest_path(G, weightweight)。3.2 图论模型北邮教材映射课程章节 对应本程序第 2 章 图的概念 ★ 边权、属性图第 3 章 最短路问题 ★ 最长路 取负权跑最短路第 4 章 树与最优树 ★ 关键路径 DAG 最长路核心定义- 带权 DAG 最长路 \max \sum_{e \in P} w(e) 其中 P 是从源到汇的有向路径- 算法拓扑排序 动态规划或取负权后跑 Dijkstra/Bellman-Ford- NetworkXnx.dag_longest_path(G, weightweight) /nx.dag_longest_path_length()。3.3 代码映射图论概念 代码实现带权 DAGself.G (nx.DiGraph)边属性G[u][v][priority],G[u][v][weight]边过滤filter_edges_by_priority(threshold)子网最长路nx.dag_longest_path(G_f, weightweight)关键路径CriticalPathResult 数据类四、OOP 代码实现4.1 项目结构priority_critical_path/├── priority_critical_path.py # 核心PriorityCriticalPathGenerator~200 行├── test_priority_critical_path.py # 9 项单元测试9/9 通过├── visualize.py # 可视化入口├── priority_subgraph.png # 输出过滤前后对比├── README.md├── pack.py└── priority_critical_path.zip4.2 核心源码detailssummary/summary带优先级工序边权重过滤与关键链路生成图建模有向带权图边含 priority 属性核心边属性过滤子图求最长路参考北邮《图论及其应用》第 2、3、4 章from dataclasses import dataclass, fieldfrom typing import Dict, List, Optional, Set, Tupleimport networkx as nximport matplotlib.pyplot as pltdataclassclass CriticalPathResult:关键路径计算结果。path: List[str] field(default_factorylist)total_weight: float 0.0filtered_edges_count: int 0subgraph_nodes: int 0subgraph_edges: int 0propertydef path_names(self) - List[str]:return [n for n in self.path]class PriorityCriticalPathGenerator:优先级关键路径生成器。工业映射边 priority 过滤 → 子网 → 最长路 关键路径。def __init__(self, G: Optional[nx.DiGraph] None):self.G G if G is not None else nx.DiGraph()def add_process(self, node_id: str, name: str):添加工序节点。self.G.add_node(node_id, namename)def add_sequence(self, u: str, v: str, priority: int 3, weight: float 1.0):添加先后关系附带 priority 和 weight。self.G.add_edge(u, v, prioritypriority, weightweight)def filter_edges_by_priority(self, threshold: int) - nx.DiGraph:过滤边保留 priority threshold 的边构建子网。G_f self.G.copy()edges_to_remove [(u, v) for u, v, d in G_f.edges(dataTrue)if d.get(priority, 3) threshold]G_f.remove_edges_from(edges_to_remove)# 移除孤立节点可选保留以显示完整结构return G_fdef compute_critical_path(self, G_sub: nx.DiGraph,weight_attr: str weight) - CriticalPathResult:在子网DAG上计算最长路关键路径。result CriticalPathResult(subgraph_nodesG_sub.number_of_nodes(),subgraph_edgesG_sub.number_of_edges(),filtered_edges_countself.G.number_of_edges() - G_sub.number_of_edges())if G_sub.number_of_edges() 0:return result# 确保是 DAGif not nx.is_directed_acyclic_graph(G_sub):return resulttry:path nx.dag_longest_path(G_sub, weightweight_attr)length nx.dag_longest_path_length(G_sub, weightweight_attr)result.path pathresult.total_weight lengthexcept (nx.NetworkXError, ValueError):passreturn resultdef analyze(self, priority_threshold: int 1,weight_attr: str weight) - CriticalPathResult:一步完成过滤 计算。G_f self.filter_edges_by_priority(priority_threshold)return self.compute_critical_path(G_f, weight_attr)def print_report(self, result: CriticalPathResult, threshold: int):打印分析报告。print( * 60)print(带优先级工序边权重过滤与关键链路生成)print(参考北邮《图论及其应用》第 2、3、4 章)print( * 60)print(f\n【全局工序 DAG】)print(f 节点数{self.G.number_of_nodes()})print(f 边数{self.G.number_of_edges()})print(f\n【过滤条件】priority ≤ {threshold})print(f 移除边{result.filtered_edges_count} 条)print(f 子网节点{result.subgraph_nodes})print(f 子网边数{result.subgraph_edges})if result.path:names [self.G.nodes[n].get(name, n) for n in result.path]print(f\n【子网关键路径最长路】)print(f 路径{ → .join(names)})print(f 总工期{result.total_weight} 小时)else:print(f\n【子网无有效路径】)print( * 60)def plot(self, G_sub: nx.DiGraph, result: CriticalPathResult, output: str):可视化全局灰边子网黑边关键路径红粗。pos nx.spring_layout(self.G, seed42)plt.figure(figsize(12, 8))# 节点颜色在子网中则亮色否则灰色node_colors []for n in self.G.nodes():if n in G_sub.nodes():node_colors.append(lightblue)else:node_colors.append(lightgray)# 边颜色在关键路径上则红粗在子网中则黑否则灰edge_colors []edge_widths []for u, v in self.G.edges():if u in result.path and v in result.path:idx_u result.path.index(u)idx_v result.path.index(v)if idx_v idx_u 1:edge_colors.append(red)edge_widths.append(3.0)continueif G_sub.has_edge(u, v):edge_colors.append(black)edge_widths.append(1.5)else:edge_colors.append(lightgray)edge_widths.append(0.5)labels {n: self.G.nodes[n].get(name, n) for n in self.G.nodes()}edge_labels {(u, v): fp{d[priority]}, w{d[weight]}for u, v, d in self.G.edges(dataTrue)}nx.draw(self.G, pos, with_labelsTrue, labelslabels,node_colornode_colors, edge_coloredge_colors,widthedge_widths, node_size700,arrowsize20, font_size10)plt.title(f优先级过滤threshold{result.filtered_edges_count} 条边移除, fontsize13)plt.tight_layout()plt.savefig(output, dpi120)plt.close()def generate_aircraft_process():示例航空零件机加工序 DAG9 节点边含 priority。gen PriorityCriticalPathGenerator()gen.add_process(P0, 来料检验)gen.add_process(P1, 粗铣)gen.add_process(P2, 精铣)gen.add_process(P3, 热处理)gen.add_process(P4, 精磨)gen.add_process(P5, 装配)gen.add_process(P6, 测试)gen.add_process(P7, 涂装)gen.add_process(P8, 包装)# 边(u,v, priority, weight_hours)gen.add_sequence(P0, P1, priority1, weight1.0)gen.add_sequence(P1, P2, priority1, weight2.0)gen.add_sequence(P2, P3, priority2, weight3.0) # 热处理非紧急gen.add_sequence(P3, P4, priority2, weight2.0)gen.add_sequence(P2, P5, priority1, weight1.5) # 精铣后直接装配紧急gen.add_sequence(P4, P5, priority2, weight0.5)gen.add_sequence(P5, P6, priority3, weight2.0) # 测试非紧急gen.add_sequence(P5, P8, priority1, weight0.5) # 装配后直接包装紧急gen.add_sequence(P6, P7, priority3, weight1.0)gen.add_sequence(P7, P8, priority3, weight0.5)return gendef demo():gen generate_aircraft_process()# 过滤紧急订单priority ≤ 1G_sub gen.filter_edges_by_priority(1)result gen.compute_critical_path(G_sub)gen.print_report(result, threshold1)gen.plot(G_sub, result, priority_subgraph.png)if __name__ __main__:demo()/detailsdetailssummary/summary单元测试优先级关键路径9 项。import sys, ossys.path.insert(0, os.path.dirname(__file__))from priority_critical_path import PriorityCriticalPathGenerator, generate_aircraft_processdef test_filter_edges():g generate_aircraft_process()G_sub g.filter_edges_by_priority(1)# 应移除 priority2,3 的边for u, v, d in G_sub.edges(dataTrue):assert d[priority] 1print([PASS] test_filter_edges)def test_subgraph_node_count():g generate_aircraft_process()G_sub g.filter_edges_by_priority(1)assert G_sub.number_of_nodes() 9 # 节点不变print([PASS] test_subgraph_node_count)def test_critical_path_exists():g generate_aircraft_process()G_sub g.filter_edges_by_priority(1)result g.compute_critical_path(G_sub)assert len(result.path) 0assert result.total_weight 0print([PASS] test_critical_path_exists)def test_critical_path_weight():g generate_aircraft_process()G_sub g.filter_edges_by_priority(1)result g.compute_critical_path(G_sub)# 紧急路径P0(1.0) P1(2.0) P2(2.0) P5(1.5) P8(0.5) 7.0? 实际看边# 至少应大于 0assert result.total_weight 4.0print([PASS] test_critical_path_weight)def test_no_edges_removed_when_threshold_high():g generate_aircraft_process()G_sub g.filter_edges_by_priority(3)assert G_sub.number_of_edges() g.G.number_of_edges()print([PASS] test_no_edges_removed_when_threshold_high)def test_all_edges_removed_when_threshold_low():g generate_aircraft_process()G_sub g.filter_edges_by_priority(0)assert G_sub.number_of_edges() 0print([PASS] test_all_edges_removed_when_threshold_low)def test_empty_graph():g PriorityCriticalPathGenerator()result g.analyze(threshold1)assert result.total_weight 0print([PASS] test_empty_graph)def test_single_node():g PriorityCriticalPathGenerator()g.add_process(only, 唯一)result g.analyze(threshold1)assert result.path []print([PASS] test_single_node)def test_plot_runs():g generate_aircraft_process()G_sub g.filter_edges_by_priority(1)result g.compute_critical_path(G_sub)g.plot(G_sub, result, test_priority.png)assert os.path.exists(test_priority.png)os.remove(test_priority.png)print([PASS] test_plot_runs)if __name__ __main__:for t in [test_filter_edges, test_subgraph_node_count,test_critical_path_exists, test_critical_path_weight,test_no_edges_removed_when_threshold_high,test_all_edges_removed_when_threshold_low,test_empty_graph, test_single_node,test_plot_runs]:t()print(\n全部测试通过 ✅)/details4.3 运行结果实测【过滤条件】priority ≤ 1移除边6 条子网节点9子网边数5【子网关键路径最长路】路径来料检验 → 粗铣 → 精铣 → 装配 → 包装总工期7.0 小时单元测试9/9 通过[PASS] test_filter_edges[PASS] test_subgraph_node_count[PASS] test_critical_path_exists[PASS] test_critical_path_weight[PASS] test_no_edges_removed_when_threshold_high[PASS] test_all_edges_removed_when_threshold_low[PASS] test_empty_graph[PASS] test_single_node[PASS] test_plot_runs全部测试通过 ✅五、README 使用说明5.1 快速上手pip install networkx matplotlibpython priority_critical_path.py # 演示优先级过滤关键路径python test_priority_critical_path.py # 9 项单元测试python visualize.py # 生成 priority_subgraph.png5.2 核心 APIfrom priority_critical_path import PriorityCriticalPathGenerator, generate_aircraft_processgen generate_aircraft_process()result gen.analyze(priority_threshold1)gen.print_report(result, threshold1)5.3 接入 APS 紧急插单# 紧急插单时过滤高优先级边重算关键路径gen PriorityCriticalPathGenerator()# ... 从 MES 加载全局 DAG ...result gen.analyze(priority_threshold1) # 仅紧急if result.path:alert(f紧急订单关键路径工期: {result.total_weight}h)allocate_resources(result.path)5.4 扩展方向方向 说明多阈值对比 同时计算全图/紧急/普通的关键路径动态权重 实时工时更新后重算资源约束 子网关键路径结合设备可用性多目标 priority cost 加权六、可视化结果优先级过滤灰色被过滤边黑色保留边红色粗线子网关键路径[output_image 11 begin][output_image_url] https://one-agent-prod-1343551737.cos.ap-guangzhou.myqcloud.com/outputs/0834/b1b8fe4c39cc4ee3a8c3908d1ef68734/0PBoGFyS0Su/priority_critical_path/priority_subgraph.png?q-sign-algorithmsha1q-akAKIDDMTk0KZdUSL21fBYigcl3C8rMeiT5TdZq-sign-time1788687000%3B1788694200q-key-time1788687000%3B1788694200q-header-listhostq-url-param-listq-signatureyza567...[output_image 11 end]七、核心知识点卡片 卡片1边属性过滤 子网裁剪属性过滤子图┌──────────────────────────────────────────────────────────────┐│ 遍历边按条件priority ≤ threshold保留 ││ 结果子网 G ⊆ G ││ NetworkXG.copy() remove_edges_from() ││ 北邮教材第 2 章「图的概念」 │└──────────────────────────────────────────────────────────────┘ 卡片2最长路 取负权跑最短路DAG 最长路算法┌──────────────────────────────────────────────────────────────┐│ 方法1拓扑排序 DPO(VE) ││ 方法2weight -weight跑 Dijkstra ││ NetworkXnx.dag_longest_path(G, weightw) ││ 工业含义关键路径 最长工期链 ││ 北邮教材第 3 章「最短路问题」、第 4 章「树与最优树」 │└──────────────────────────────────────────────────────────────┘ 卡片3OOP 速查类/方法 职责CriticalPathResult 结果数据类PriorityCriticalPathGenerator 生成器filter_edges_by_priority() ★ 边过滤compute_critical_path() ★ 最长路analyze() 一步完成plot() 可视化八、总结与工程师思考8.1 工业落地难处难点一priority 的标定是主观的谁来决定哪条边是 priority1计划员、销售、客户各有各的紧急。工程上需要建立规则交期 ≤ 3 天 1≤ 7 天 2其他 3。规则化才能自动化。难点二过滤后子网可能不连通如果紧急订单的工序不是连续的中间夹了普通工序过滤后子网可能断成几段——关键路径算法需要连通子图。需要补边或识别连通分量分别算。难点三子网关键路径 ≠ 全局最优子网里算出的关键路径放到全局里可能不是最长的。紧急订单的优化不应损害普通订单的交付——需要权衡。8.2 工程师心得心得一边属性是免费的多维信息很多人只用图存拓扑关系忽略了边可以带任意属性priority、weight、capacity...。一个属性就是一种过滤维度——按优先级、按资源类型、按时间窗口想怎么滤就怎么滤。心得二最长路是 DAG 的杀手锏有向无环图的最长路可以线性时间解决拓扑 DP而一般图的最长路是 NP-hard。DAG 的拓扑序就是天然的 DP 顺序——这是图论给工程的礼物。心得三可视化让过滤变得可解释灰色被过滤、黑色保留、红色关键路径——计划员一看就懂为什么这条路径被选中。可解释性在 APS 系统里比算法精度更重要。8.3 适用与不适用✅ 适用 ❌ 不适用DAG 工序依赖 含环图需先解环边有权重/优先级 无向图最长路 NP-hard中小规模 超大规模需分布式说明本程序为教学与工程演示工具展示了基于边属性过滤的子网关键路径计算。9/9 单元测试通过边过滤、子网构建、最长路计算、关键路径生成为实测功能。真实场景需结合业务规则标定 priority。利用AI解决实际问题如果你觉得这个工具好用欢迎关注长安牧笛
返回列表