Python数模笔记-NetworkX(4)最小生成树


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 程序说明

  1. 图的输入。本例为稀疏的有权无向图,使用 G.add_weighted_edges_from() 函数可以使用列表向图中添加多条赋权边,每个赋权边以元组 (node1,node2,weight) 表示。
  2. 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)条件最短路径

おすすめ

転載: blog.csdn.net/youcans/article/details/117150351