Python数模笔记-NetworkX(4)最小生成树
1、生成树和最小生成树1.1 生成树连通的无圈图称为树,就是不包含循环的回路的连通图。对于无向连通图,生成树(Spanning tree)是原图的极小连通子图,它包含原图中的所有 n 个顶点,并且有保持图连通的最少的边,即只有足以构成一棵树的 n-1 条边。生成树满足:(1)包含连通图中所有的顶点;(2)任意两顶点之间有且仅有一条通路。因此,生成树中边的数量 = 顶点数 - 1。对于非连通无向图,
1、生成树和最小生成树
1.1 生成树
连通的无圈图称为树,就是不包含循环的回路的连通图。
对于无向连通图,生成树(Spanning tree)是原图的极小连通子图,它包含原图中的所有 n 个顶点,并且有保持图连通的最少的边,即只有足以构成一棵树的 n-1 条边。
生成树满足:
- (1)包含连通图中所有的顶点;
- (2)任意两顶点之间有且仅有一条通路。
因此,生成树中边的数量 = 顶点数 - 1。
对于非连通无向图, 遍历每个连通分量中的顶点集合所经过的边是多颗生成树,这些连通分量的生成树构成非连通图的生成森林 。
欢迎关注 Youcans 原创系列,数模笔记每周更新
Python数模笔记-PuLP库
Python数模笔记-StatsModels统计回归
Python数模笔记-Sklearn
Python数模笔记-NetworkX
Python数模笔记-模拟退火算法
1.2 最小生成树和最大生成树
遍历连通图的方式有多种,也就是说对于一张连通图,可能有多种不同的生成树。
带权的连通图的生成树中各条边的权重之和最小的生成树,称为最小生成树(minimum spanning tree,MST),也称最小权重生成树。
对应地,各条边的权重之和最大的生成树,称为最大生成树。
生成树和最小生成树有许多重要的应用。例如在若干城市之间铺设通信线路,使任意两个城市之间都可以通信,要使铺设线路的总费用最低,就需要找到最小生成树。
2、最小生成树算法
构造最小生成树的算法很多,通常是基于MST性质,从空树开始,按照贪心法逐步选择并加入 n-1 条安全边(不产生回路),最终生成一棵最小生成树。
最小生成树的典型算法有普里姆算法(Prim算法)和克鲁斯卡算法(Kruskal算法)。
2.1 普里姆算法(Prim算法)
Prim 算法以顶点为基础构造最小生成树:从某一个顶点 s 开始,每次选择剩余的代价最小的边所对应的顶点,加入到最小生成树的顶点集合中,逐步扩充直到包含整个连通网的所有顶点,可以称为“加点法”。Prim算法中每个顶点只与连通图连接一次,因此不用考虑在加入顶点的过程中是否会形成回路。
Prim 算法中图的存贮结构采用邻接矩阵,使用一个顶点集合 u 构造最小生成树。由于不断向集合u中加点,还需要建立一个辅助数组来同步更新最小代价边的信息。
Prim 算法每次选择顶点时,都需要进行排序,但每次都只需要对一部分边进行排序。Prim 算法的时间复杂度为 O(n*n),与边的数量无关,适用于边很多的稠密图。采用堆实现优先队列来维护最小点,可以将Prim算法的时间复杂度降低到 O(mlogn),称为Prim_heap 算法,但该算法的空间消耗很大。
2.2 克鲁斯卡算法(Kruskal算法)
Kruskal 算法以边为基础构造最小生成树:初始边数为 0,每次选择一条满足条件的最小代价边,加入到边集合中,逐步扩充直到包含整个生成树,可以称为“加边法”。Kruskal 算法是利用避圈思想,每次找到不使图构成回路的代价最小的边。
Kruskal 算法中图的存贮结构采用边集数组,权值相等的边在数组中的排列次序是任意的。
Kruskal算法开始就要对所有的边进行排序,之后还需要对所有边应用 Union-Find算法,但不再需要排序。Kruskal 算法的时间复杂度为 O(mlogm),主要是对边排序的时间复杂度,适用于边较少的稀疏图。
3、NetworkX 的最小生成树算法
3.1 最小/最大生成树函数
函数 | 功能 |
---|---|
minimum_spanning_tree(G[, weight,…]) | 计算无向图上的最小生成树 |
maximum_spanning_tree(G[, weight,…]) | 计算无向图上的最大生成树 |
minimum_spanning_edges(G[, algorithm,…]) | 计算无向加权图最小生成树的边 |
maximum_spanning_edges(G[, algorithm,…]) | 计算无向加权图最大生成树的边 |
3.2 minimum_spanning_tree() 使用说明
minimum_spanning_tree(G, weight=‘weight’, algorithm=‘kruskal’, ignore_nan=False)
minimum_spanning_edges(G, algorithm=‘kruskal’, weight=‘weight’, keys=True, data=True, ignore_nan=False)
minimum_spanning_tree() 用于计算无向连通图的最小生成树(森林)。minimum_spanning_edges() 用于计算无向连通图的最小生成树(森林)的边。
对于连通无向图,计算最小生成树;对于非连通无向图,计算最小生成森林。
主要参数:
- G(undirected graph):无向图。
- weight(str):指定用作计算权重的边属性。
- algorithm(string):计算最小生成树的算法,可选项为 ‘kruskal’、‘prim’ 或 ‘boruvka’。默认算法为 ‘kruskal’。
- data(bool):指定返回值是否包括边的权值。
- ignore_nan(bool) :在边的权重为 Nan 时产生异常。
返回值:
- minimum_spanning_tree() 的返回值是最小生成树,类型为<class ‘networkx.classes.graph.Graph’> 。
- minimum_spanning_edges() 的返回值是最小生成树的构成边,类型为<class ‘generator’>。
3.3 minimum_spanning_tree() 算法使用例程
本案例问题来自:司守奎、孙兆亮,数学建模算法与应用(第2版),P48,例4.5,国防工业出版社。
例:最小生成树问题。已知如图的有权无向图,求最小生成树。
# networkX_E4.py
# Demo of minimum spanning tree(MST) with NetworkX
# Copyright 2021 YouCans, XUPT
# Crated:2021-05-21
import matplotlib.pyplot as plt # 导入 Matplotlib 工具包
import networkx as nx # 导入 NetworkX 工具包
G = nx.Graph() # 创建:空的 无向图
G.add_weighted_edges_from([(1,2,50),(1,3,60),(2,4,65),(2,5,40),(3,4,52),
(3,7,45),(4,5,50),(4,6,30),(4,7,42),(5,6,70)]) # 向图中添加多条赋权边: (node1,node2,weight)
T = nx.minimum_spanning_tree(G) # 返回包括最小生成树的图
print(T.nodes) # [1, 2, 3, 4, 5, 7, 6]
print(T.edges) # [(1,2), (2,5), (3,7), (4,6), (4,7), (4,5)]
print(sorted(T.edges)) # [(1,2), (2,5), (3,7), (4,5), (4,6), (4,7)]
print(sorted(T.edges(data=True))) # data=True 表示返回值包括边的权重
# [(1,2,{'weight':50}), (2,5,{'weight':40}), (3,7,{'weight':45}), (4,5,{'weight':50}), (4,6,{'weight':30}), (4,7,{'weight':42})]
mst1 = nx.tree.minimum_spanning_edges(G, algorithm="kruskal") # 返回值 带权的边
print(list(mst1))
# [(4,6,{'weight':30}), (2,5,{'weight':40}), (4,7,{'weight':42}), (3,7,{'weight':45}), (1,2,{'weight':50}), (4,5,{'weight':50})]
mst2 = nx.tree.minimum_spanning_edges(G, algorithm="prim",data=False) # data=False 表示返回值不带权
print(list(mst2))
# [(1,2), (2,5), (5,4), (4,6), (4,7), (7,3)]
pos={1:(2.5,10),2:(0,5),3:(7.5,10),4:(5,5),5:(2.5,0),6:(7.5,0),7:(10,5)} # 指定顶点位置
nx.draw(G, pos, with_labels=True, alpha=0.8) # 绘制无向图
labels = nx.get_edge_attributes(G,'weight') # YouCans, XUPT
nx.draw_networkx_edge_labels(G,pos,edge_labels=labels, font_color='c') # 显示边的权值
nx.draw_networkx_edges(G,pos,edgelist=T.edges,edge_color='r',width=4) # 设置指定边的颜色
plt.show()
3.4 程序运行结果
[1, 2, 3, 4, 5, 7, 6]
[(1, 2), (2, 5), (3, 7), (4, 6), (4, 7), (4, 5)]
[(1, 2), (2, 5), (3, 7), (4, 5), (4, 6), (4, 7)]
[(1, 2, {'weight': 50}), (2, 5, {'weight': 40}), (3, 7, {'weight': 45}), (4, 5, {'weight': 50}), (4, 6, {'weight': 30}), (4, 7, {'weight': 42})]
[(4, 6, {'weight': 30}), (2, 5, {'weight': 40}), (4, 7, {'weight': 42}), (3, 7, {'weight': 45}), (1, 2, {'weight': 50}), (4, 5, {'weight': 50})]
[(1, 2), (2, 5), (5, 4), (4, 6), (4, 7), (7, 3)]
3.5 程序说明
- 图的输入。本例为稀疏的有权无向图,使用 G.add_weighted_edges_from() 函数可以使用列表向图中添加多条赋权边,每个赋权边以元组 (node1,node2,weight) 表示。
- nx.minimum_spanning_tree() 和 nx.tree.minimum_spanning_edges() 都可以计算最小生成树,参数设置和属性也基本一致。区别主要在于返回值:nx.minimum_spanning_tree() 返回值是最小生成树构成的图(‘Graph’),需要用 T.edges() 调用对应的最小生成树的边。nx.tree.minimum_spanning_edges() 返回值是最小生成树的构成边(‘generator’),需要用 list() 转换为列表数据。
版权说明:
YouCans 原创作品:Python 数模笔记
本文内容及例程为作者原创,并非转载书籍或网络内容。
本文中案例问题来自:司守奎、孙兆亮,数学建模算法与应用(第2版),国防工业出版社
YouCans 原创作品,转载需标注原始链接。
Copyright 2021 YouCans, XUPT
Crated:2021-05-21
欢迎关注 Youcans 原创系列,每周更新数模笔记
Python数模笔记-PuLP库(1)线性规划入门
Python数模笔记-PuLP库(2)线性规划进阶
Python数模笔记-PuLP库(3)线性规划实例
Python数模笔记-Scipy库(1)线性规划问题
Python数模笔记-StatsModels 统计回归(1)简介
Python数模笔记-StatsModels 统计回归(2)线性回归
Python数模笔记-StatsModels 统计回归(3)模型数据的准备
Python数模笔记-StatsModels 统计回归(4)可视化
Python数模笔记-Sklearn (1)介绍
Python数模笔记-Sklearn (2)聚类分析
Python数模笔记-Sklearn (3)主成分分析
Python数模笔记-Sklearn (4)线性回归
Python数模笔记-Sklearn (5)支持向量机
Python数模笔记-模拟退火算法(1)多变量函数优化
Python数模笔记-模拟退火算法(2)约束条件的处理
Python数模笔记-模拟退火算法(3)整数规划问题
Python数模笔记-模拟退火算法(4)旅行商问题
Python数模笔记-NetworkX(1)图的操作
Python数模笔记-NetworkX(2)最短路径
Python数模笔记-NetworkX(3)条件最短路径
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)