
1. 从数学建模到有向图一个被低估的“建模利器”如果你正在准备数学建模竞赛无论是国赛、美赛还是亚太杯你的工具箱里大概率已经装满了各种回归模型、优化算法和机器学习库。但有一个工具它结构简单、概念直观却能在解决诸如路径规划、网络流、状态转移、影响力传播等经典赛题时发挥出意想不到的威力——它就是有向图。很多初次接触建模的同学往往把“图”等同于“可视化图表”从而错过了这个强大的建模语言。实际上有向图是一种描述对象间定向关系的数学结构由“节点”和“有向边”构成。在Python中我们无需从头造轮子借助networkx、igraph等成熟库可以快速地将一个复杂的现实问题抽象为图模型并进行高效的分析与计算。我最初在准备2019年国赛C题机场出租车调度时就深刻体会到了有向图的妙用。题目涉及出租车在机场、市区不同区域间的流动与调度本质上就是一个典型的网络流问题。如果把每个候客区、下客区、道路交叉口看作节点把出租车的可行行驶路径看作有向边并赋予边以距离、时间、容量等属性一个复杂的调度优化问题就转化为了图上的最短路径或最大流问题。这种“转化”正是数学建模的核心思想之一用合适的数学结构刻画现实。本文将抛开枯燥的理论直接聚焦于如何用Python这把“瑞士军刀”在数学建模的实战中玩转有向图。我们会从环境搭建、基础建模、算法应用到一个完整的模拟赛题分析手把手带你掌握这套方法论。2. 环境准备与核心工具选型为什么是NetworkX工欲善其事必先利其器。在Python的图计算生态中主要有networkx、igraph、graph-tool几个选择。对于数学建模而言我的首选永远是NetworkX。原因很简单它足够简单、文档齐全、社区活跃并且与Python的科学计算栈NumPy, SciPy, pandas无缝集成。igraph性能更强但接口更偏C语言风格graph-tool功能强大但安装复杂依赖Boost库。数学建模竞赛时间紧迫我们需要的是能快速上手、稳定运行、方便调试的工具NetworkX完美符合这些要求。2.1 一步到位的环境配置很多同学在配置Python环境时容易踩坑尤其是包依赖和IDE选择。为了避免“请安装缺失的节点”这类错误我推荐使用Miniconda来管理环境并用VSCode作为编辑器。首先如果你还没有Python环境去Miniconda官网下载安装。安装后打开终端Windows用Anaconda Prompt或PowerShell创建一个专用于数学建模的虚拟环境conda create -n math_modeling python3.9 conda activate math_modeling选择Python 3.9是因为它在兼容性和稳定性上是一个很好的折中。接下来在这个环境中一次性安装我们所需的全部核心包pip install networkx matplotlib numpy scipy pandas jupyter注意这里没有一次性安装comfyui-m或其他不相关的包。网络热词中提到的“请先在你的 python 环境中运行 pip install -u --pre comfyui-m”很可能来自某个特定工作流的错误提示与通用数学建模无关请忽略。我们的环境应保持纯净只安装必要的、广泛使用的库。安装完成后你可以在VSCode中设置解释器路径到这个math_modeling环境然后新建一个Jupyter Notebook或Python文件开始工作。这种环境隔离的好处是你为不同项目如图论、机器学习、数值计算创建独立的环境避免包版本冲突这也是专业开发者的基本习惯。2.2 验证安装与绘制第一个有向图让我们写几行代码快速验证环境并感受一下NetworkX的便捷。这段代码不仅是为了画图更是为了理解有向图的基本元素。import networkx as nx import matplotlib.pyplot as plt # 创建一个有向图对象 G nx.DiGraph() # 添加节点可以一次加一个也可以加一个列表 G.add_node(A) # 节点A G.add_nodes_from([B, C, D]) # 节点B, C, D # 添加有向边从A指向B从B指向C以此类推 G.add_edge(A, B) G.add_edges_from([(B, C), (C, D), (D, A)]) # 再加一条边让图更复杂些 G.add_edge(A, C) # 绘制图形 pos nx.circular_layout(G) # 定义一个环形布局让节点排列美观 nx.draw(G, pos, with_labelsTrue, node_colorlightblue, node_size500, font_size12, font_weightbold, arrowsTrue, arrowsize20) plt.title(My First Directed Graph) plt.show()运行这段代码你会看到一个简单的有向图。DiGraph类代表有向图。add_edge(“A”, “B”)意味着一条从A到B的边方向至关重要。在建模时这个方向可以代表信息的流向、资金的转移、交通的单行道、任务的先后顺序等。layout布局决定了节点的摆放位置除了circular_layout还有spring_layout力导向布局更自然、shell_layout同心圆布局等在可视化复杂网络时非常有用。3. 数学建模中的有向图抽象如何把赛题“翻译”成图这是最关键的一步也是区分建模能力高下的地方。有向图建模的核心在于识别系统中的实体节点和实体间的定向关系有向边。我们结合几个典型赛题类型来看。3.1 类型一路径规划与最短路径问题典型赛题2016年国赛A题系泊系统设计、2022年国赛C题古代玻璃制品的成分分析中的物资运输子问题、2024年B题交通流量控制等。这类问题的本质是在一个网络中寻找最优最短、最快、最省的路径。建模抽象节点路径的关键点。如交通路口、城市、物流中心、检查点。有向边连接两个节点的可行路径方向代表通行方向单行道或主方向。边属性通常为权重weight代表距离、时间、成本或风险。这是算法计算的依据。Python实现示例假设我们要为一个小型物流网络建模目标是找到从仓库到各个配送点的最短运输时间。import networkx as nx # 创建有向图 logistics_net nx.DiGraph() # 添加节点仓库和配送点 nodes [Warehouse, Point_A, Point_B, Point_C, Point_D] logistics_net.add_nodes_from(nodes) # 添加有向边及权重运输时间单位小时 edges_with_weight [ (Warehouse, Point_A, 2.5), (Warehouse, Point_B, 1.8), (Point_A, Point_C, 1.2), (Point_B, Point_C, 0.9), (Point_B, Point_D, 1.5), (Point_C, Point_D, 0.7), # 注意有些路可能是单向的比如从D回C可能不允许或更耗时 (Point_D, Point_C, 1.0), ] logistics_net.add_weighted_edges_from(edges_with_weight) # 计算从仓库到所有点的最短路径长度 shortest_paths nx.single_source_dijkstra_path_length(logistics_net, sourceWarehouse) print(从仓库到各点的最短运输时间, shortest_paths) # 计算到Point_D的具体路径 path_to_D, distance_to_D nx.single_source_dijkstra_path(logistics_net, sourceWarehouse, targetPoint_D) print(f到Point_D的最短路径: {path_to_D}, 总耗时: {distance_to_D}小时)这里使用了Dijkstra算法nx.single_source_dijkstra_path它是解决非负权重图最短路径问题的标准算法。在建模论文中你不仅需要给出代码和结果更需要阐明为什么选择这个算法因为运输时间为正以及节点和边权重的实际物理意义。3.2 类型二排序、调度与拓扑排序问题典型赛题生产工序调度、课程安排、任务依赖关系分析如2000年国赛B题可能涉及的步骤优化。这类问题的核心是处理“先后”约束。建模抽象节点需要排序的单个任务、工序或事件。有向边表示依赖关系。如果任务A必须在任务B之前完成则添加一条从A指向B的边。目标找到一个线性序列拓扑序使得所有边的方向都得到尊重即所有依赖都被满足。Python实现示例一个简单的课程学习计划某些课程有先修要求。# 创建有向图表示课程依赖 course_graph nx.DiGraph() courses [高等数学, 线性代数, 概率论, 数据结构, 算法分析, 机器学习] course_graph.add_nodes_from(courses) # 定义先修关系元组 (先修课 后续课) prerequisites [ (高等数学, 概率论), (线性代数, 数据结构), (数据结构, 算法分析), (概率论, 机器学习), (线性代数, 机器学习), (算法分析, 机器学习), ] course_graph.add_edges_from(prerequisites) # 检查是否存在循环依赖即是否为一个有向无环图DAG if nx.is_directed_acyclic_graph(course_graph): print(课程依赖关系合理无循环。) # 进行拓扑排序得到一种可行的学习顺序 try: topological_order list(nx.topological_sort(course_graph)) print(一种可行的课程学习顺序为, topological_order) except nx.NetworkXError as e: print(排序出错, e) else: print(警告课程依赖关系存在循环无法安排学习计划) # 可以进一步用 nx.find_cycle 找出循环在哪里 cycle nx.find_cycle(course_graph, orientationoriginal) print(发现的循环依赖, cycle)nx.topological_sort是解决这类问题的利器。如果图中有环比如“机器学习”是先修“数据结构”的先修则排序失败。在实际建模中这可能意味着你设定的约束条件存在矛盾需要重新审视问题。3.3 类型三网络流与资源分配问题典型赛题2023年国赛A题定日镜场优化中的能量传输、供水管网、交通流量最大承载量、信息传播最大化等问题。目标是计算网络中可以承载的最大流量或最优流量分配。建模抽象节点资源的源点Source、汇点Sink以及中转站。有向边资源流动的通道。边属性容量capacity最大可通过量有时还有成本cost单位流量费用。目标在满足容量限制的前提下最大化从源点到汇点的总流量最大流问题或以最小成本输送指定流量最小费用最大流问题。Python实现示例一个简单的供水网络求从水厂到居民区的最大供水量。from networkx.algorithms.flow import shortest_augmenting_path # 创建有向图 flow_network nx.DiGraph() # 添加边并指定容量 (capacity) # 格式: (u, v, {capacity: c}) flow_edges [ (Water_Plant, Junction_1, {capacity: 10}), (Water_Plant, Junction_2, {capacity: 5}), (Junction_1, Junction_3, {capacity: 7}), (Junction_2, Junction_3, {capacity: 4}), (Junction_3, Residential_Area, {capacity: 8}), ] flow_network.add_edges_from(flow_edges) # 计算从源点水厂到汇点居民区的最大流 source Water_Plant sink Residential_Area max_flow_value, flow_dict nx.maximum_flow(flow_network, source, sink, flow_funcshortest_augmenting_path) print(f最大供水量为: {max_flow_value} 单位) print(各管道实际流量分配:) for u, neighbors in flow_dict.items(): for v, flow in neighbors.items(): if flow 0: print(f {u} - {v}: {flow})NetworkX的流算法模块功能强大。在这个例子中我们使用了shortest_augmenting_path作为最大流算法的实现。结果显示尽管管道总容量很大但受限于“Junction_3”到“Residential_Area”这段8单位的瓶颈最大流量只有8。这就是网络的“最小割”容量在论文中分析瓶颈是加分项。4. 进阶分析图论指标与算法在建模中的深度应用基础建模只是开始要想在论文中体现深度你需要利用图论中的各种指标和高级算法来分析你的模型。4.1 节点中心性分析识别关键枢纽在传播问题如谣言、疾病、创新扩散或基础设施网络如电力、交通中识别最重要的节点至关重要。NetworkX提供了多种中心性度量。度中心性一个节点连接的边数。在有向图中分为入度指向该节点的边数代表影响力/受欢迎程度和出度从该节点指出的边数代表活跃度/辐射能力。接近中心性节点到网络中所有其他节点平均最短距离的倒数。值越高说明该节点在信息传递中越不依赖于他人处于网络的中心位置。中介中心性衡量一个节点出现在其他节点对最短路径上的频率。值高的节点是网络中的“桥梁”或“枢纽”控制着信息流。应用场景在“人狗大作战”这类模拟对抗或资源争夺题中虽然原题可能是个游戏但抽象为对抗网络你可以用入度中心性找出被最多单位作为攻击目标的“核心阵地”用中介中心性找出连接我方不同部队的关键“交通要道”这些点需要重点布防。# 接续之前的 logistics_net 图 # 计算入度和出度 in_degrees dict(logistics_net.in_degree()) out_degrees dict(logistics_net.out_degree()) print(节点入度代表‘被依赖’程度:, in_degrees) print(节点出度代表‘辐射’能力:, out_degrees) # 计算中介中心性 (Betweenness Centrality) betweenness nx.betweenness_centrality(logistics_net, normalizedTrue) print(节点中介中心性‘桥梁’重要性:) for node, bc in sorted(betweenness.items(), keylambda item: item[1], reverseTrue): print(f {node}: {bc:.3f})4.2 社区发现与聚类挖掘网络内部结构许多复杂网络天然地形成若干个内部连接紧密、外部连接稀疏的“社区”。发现这些社区有助于理解系统的模块化结构。常用算法Louvain算法、Girvan-Newman算法等。NetworkX对部分算法有实现但对于大规模图python-louvain库更高效。建模应用在社交网络分析如2026亚太杯A题若涉及信息传播、供应链网络分解、城市功能分区等题目中社区发现可以帮助你将一个大问题分解为几个耦合度较低的子问题从而简化模型或实施分治策略。# 注意Louvain算法通常用于无向图。对于有向图有时可以忽略方向或使用专门算法。 # 这里以无向图为例展示概念 import networkx as nx # 安装 community 库: pip install python-louvain import community as community_louvain # 假设我们有一个合作网络图 G_undirected (无向) # partition community_louvain.best_partition(G_undirected) # print(社区划分结果:, partition) # 可以计算模块度 modularity community_louvain.modularity(partition, G_undirected)4.3 连通性与鲁棒性分析网络有多“结实”评估网络在节点或边失效情况下的表现是系统可靠性分析的核心。强连通分量在有向图中如果一个分量内的任意两个节点都可以互相到达则该分量称为强连通分量。分析SCC可以了解网络的内部凝聚力。点连通度/边连通度使网络变得不连通所需移除的最少节点数或边数。这个数值越高网络越健壮。建模应用在电力网络、通信网络设计中你需要评估关键线路或电站失效对整体系统的影响。通过模拟移除某个节点如G.remove_node(‘某枢纽’)再计算最大连通子图的大小或最短路径的平均变化可以量化该节点的重要性。# 计算强连通分量 strongly_connected_components list(nx.strongly_connected_components(logistics_net)) print(强连通分量内部互通的组:, strongly_connected_components) # 判断图的连通性对于有向图常用弱连通性即忽略方向后的连通性 is_weakly_connected nx.is_weakly_connected(logistics_net) print(f网络是否是弱连通的: {is_weakly_connected}) # 计算节点连通度需要图是连通的 if is_weakly_connected: # 注意node_connectivity 通常对无向图定义更明确对有向图有不同解释。 # 这里主要展示概念实际应用需查阅文档。 # connectivity nx.node_connectivity(logistics_net.to_undirected()) pass5. 实战演练模拟一个简化版赛题“城市应急物资配送网络优化”让我们综合运用以上知识模拟分析一个赛题。问题描述某城市有1个中央仓库、5个应急物资储备点以及若干条单向通行的主干道连接它们。已知每条道路的通行时间小时和最大运输车次容量次/天。在灾害发生后需要从中央仓库向各储备点快速调配物资。目标是1评估当前网络能否在12小时内将物资送达所有储备点2找出限制整体效率的关键瓶颈道路3如果预算允许升级一条道路将其容量提升50%应升级哪条5.1 问题抽象与模型构建我们首先定义节点和边。节点Central_Warehouse,Reserve_1, ...,Reserve_5。有向边代表单向主干道。边属性1time(通行时间)。边属性2capacity(每日最大运输车次假设每车次运送标准量物资)。我们构建这个网络。import networkx as nx import matplotlib.pyplot as plt G nx.DiGraph() nodes [Central_Warehouse, R1, R2, R3, R4, R5] G.add_nodes_from(nodes) # 添加边属性包括时间和容量 edges [ (Central_Warehouse, R1, {time: 2, capacity: 8}), (Central_Warehouse, R2, {time: 3, capacity: 6}), (R1, R3, {time: 1.5, capacity: 5}), (R2, R3, {time: 2, capacity: 7}), (R3, R4, {time: 2.5, capacity: 4}), (R3, R5, {time: 1, capacity: 10}), (R4, R5, {time: 1.5, capacity: 3}), # 注意这条是R4-R5 ] G.add_edges_from(edges) # 可视化 pos nx.spring_layout(G, seed42) plt.figure(figsize(10, 6)) nx.draw(G, pos, with_labelsTrue, node_colorlightgreen, node_size700, font_size10, font_weightbold, arrowsTrue) edge_labels nx.get_edge_attributes(G, time) nx.draw_networkx_edge_labels(G, pos, edge_labelsedge_labels, font_colorred) plt.title(城市应急物资配送网络边上数字为通行时间/小时) plt.show()5.2 任务一评估12小时可达性这本质上是单源最短路径问题。我们计算从中央仓库到每个储备点的最短时间。# 计算最短路径时间 shortest_times nx.single_source_dijkstra_path_length(G, sourceCentral_Warehouse, weighttime) print(从中央仓库到各储备点的最短时间小时:) for node, time in shortest_times.items(): print(f - {node}: {time:.1f} 小时) if time 12: print(f 结论可在12小时内送达。) else: print(f 警告无法在12小时内送达)如果所有时间都≤12小时则网络结构满足时间要求。否则需要报告哪些点超时并在论文中建议增设直连道路或提升相关道路等级。5.3 任务二识别关键瓶颈基于容量的最大流分析物资调配不仅要求快还要求量足。我们将中央仓库视为源点并假设有一个“超级汇点”连接所有储备点表示物资被接收计算整个网络的最大物资输送能力车次/天。同时通过分析最小割找出瓶颈边。# 为最大流计算添加一个超级汇点 G_flow G.copy() super_sink Super_Sink G_flow.add_node(super_sink) # 将每个储备点连接到超级汇点容量设为无穷大或一个很大的数表示接收能力无限 for reserve in [R1, R2, R3, R4, R5]: G_flow.add_edge(reserve, super_sink, capacityfloat(inf)) # 计算从中央仓库到超级汇点的最大流 source Central_Warehouse sink Super_Sink max_flow_value, flow_dict nx.maximum_flow(G_flow, source, sink, capacitycapacity) print(f\n网络每日最大物资输送能力车次: {max_flow_value}) # 简易瓶颈分析查看流量接近容量的边 print(\n高利用率边潜在瓶颈:) for u in flow_dict: for v, flow in flow_dict[u].items(): if u in G.nodes and v in G.nodes: # 只看原始网络中的边 edge_cap G[u][v].get(capacity, 0) if edge_cap 0: utilization flow / edge_cap if utilization 0.8: # 利用率超过80%视为瓶颈 print(f {u} - {v}: 流量{flow}/{edge_cap}, 利用率{utilization:.1%})5.4 任务三最优升级策略灵敏度分析我们需要评估升级哪条边容量提升50%能最大幅度地提升整体网络的最大流。一个直接的方法是进行“边升级模拟”。original_max_flow max_flow_value edges_to_upgrade list(G.edges()) best_upgrade None best_improvement 0 for (u, v) in edges_to_upgrade: # 临时创建新图升级当前边 G_temp G_flow.copy() original_cap G_temp[u][v].get(capacity, 0) G_temp[u][v][capacity] original_cap * 1.5 # 容量提升50% # 重新计算最大流 new_flow_value, _ nx.maximum_flow(G_temp, source, sink, capacitycapacity) improvement new_flow_value - original_max_flow if improvement best_improvement: best_improvement improvement best_upgrade (u, v) print(f\n最优升级道路: {best_upgrade[0]} - {best_upgrade[1]}) print(f升级后最大流提升: {best_improvement:.2f} 车次/天) print(f升级后总运力: {original_max_flow best_improvement:.2f} 车次/天)通过这个模拟我们可以定量地回答升级哪条路最有效。在论文中这部分内容可以放在“模型求解与结果分析”部分并配以图表说明。6. 论文写作要点与避坑指南将Python代码和分析结果转化为高质量的数学建模论文需要注意以下几点模型阐述清晰在“模型建立”部分用专业语言描述你的有向图模型。定义节点集合V、有向边集合E以及边权函数W代表时间、容量等。给出图的数学形式化定义G(V, E, W)。算法选择有理有据在“模型求解”部分说明你为何选择Dijkstra算法非负权重最短路径、最大流算法资源输送等。可以简要描述算法思想但不必粘贴代码细节。引用算法名称和使用的库如NetworkX。结果可视化与解读将网络图、最短路径树、流量分配图等用matplotlib绘制清晰放入论文。对结果进行深入解读例如“如图所示R3节点具有最高的中介中心性表明它是网络中最关键的枢纽一旦失效将严重影响整体配送效率。”灵敏度分析与模型检验就像我们做的升级模拟一样改变关键参数如容量、时间观察目标函数如最大流、最短时间的变化检验模型的稳定性和鲁棒性。这能极大提升论文的深度。代码与附录将完整、整洁、带有注释的Python代码放在附录中。确保代码可复现并注明使用的Python及库版本如Python 3.9, NetworkX 2.8.4。常见坑点混淆有向与无向务必根据实际问题决定使用DiGraph还是Graph。单向通行关系必须用有向边。权重属性名不一致使用算法时如dijkstra_path通过weight’time’参数明确指定使用哪一项作为权重。确保所有相关边都有该属性。忽略图的连通性在进行最短路径或流计算前检查源点和目标点是否在同一个连通分量内。否则算法会报错或返回无穷大。性能问题NetworkX适合中小规模图节点数万以内。如果模拟大规模网络如整个城市路网需要考虑使用igraph或更专业的图计算库并在论文中说明。有向图在数学建模中是一个极具表达力的工具它能将许多看似复杂的关系系统清晰地刻画出来。掌握用Python特别是NetworkX进行图建模、分析和可视化的全流程不仅能让你在比赛中快速构建模型原型更能为你的论文提供扎实的数值实验和直观的图形支撑。关键在于多练习尝试将过往的赛题用图论的角度重新审视你会发现一片新的天地。