数学建模社区-数学中国

标题: Python小白的数学建模课-图论的基本概念 [打印本页]

作者: 1047521767    时间: 2021-10-30 21:36
标题: Python小白的数学建模课-图论的基本概念
Python小白的数学建模课-图论的基本概念9 i! t7 e5 h- L& l# j) K7 {9 M

7 M  H' \. G- p7 }7 [/ T
! B6 }1 K% `: y6 d" g, p: F' m1. 图论1.1 图论是什么( o7 e' O  C5 Y/ i0 D8 D
图论〔Graph Theory〕以图为研究对象,是离散数学的重要内容。图论不仅与拓扑学、计算机数据结构和算法密切相关,而且正在成为机器学习的关键技术。" l- t# k) x% a

$ _" q) e  W2 b; R7 O2 v4 C: t* A图论中所说的图,不是指图形图像(image)或地图(map),而是指由顶点(vertex)和连接顶点的边(edge)所构成的关系结构。
# o! d% ?- A+ h( ]$ e! `* [* _% ~4 w8 t9 N' H+ ^; H
图提供了一种处理关系和交互等抽象概念的更好的方法,它还提供了直观的视觉方式来思考这些概念。
' {* `' l( W+ [' y  q: i
; t% Y2 j- `+ }- W* i1.2 NetworkX 工具包
. B4 z5 E" K+ K* ONetworkX 是基于 Python 语言的图论与复杂网络工具包,用于创建、操作和研究复杂网络的结构、动力学和功能。
* J7 Q# g5 z; m4 L$ G, W" D: B7 {# L: y  b2 l
NetworkX 可以以标准和非标准的数据格式描述图与网络,生成图与网络,分析网络结构,构建网络模型,设计网络算法,绘制网络图形。
- ^. i" m) O5 |7 E
. }1 Y* i& F7 L" TNetworkX 提供了图形的类、对象、图形生成器、网络生成器、绘图工具,内置了常用的图论和网络分析算法,可以进行图和网络的建模、分析和仿真。
' Y3 y! {# b3 X9 ]/ g1 p
8 _& n9 Q$ l& U+ H  u% NNetworkX 的功能非常强大和庞杂,所涉及内容远远、远远地超出了数学建模的范围,甚至于很难进行系统的概括。本系列结合数学建模的应用需求,来介绍 NetworkX 图论与复杂网络工具包的基本功能和典型算法。
9 `0 |* o- i: H, T8 {  F9 C7 H3 J3 B+ v& N' c& ^9 n/ W
1 g' z; n" k8 D* g
2、图、顶点和边的创建与基本操作
6 O3 V7 X) S# b! l+ m/ E

图由顶点和连接顶点的边构成,但与顶点的位置、边的曲直长短无关。

Networkx 支持创建简单无向图、有向图和多重图;内置许多标准的图论算法,节点可为任意数据;支持任意的边值维度,功能丰富,简单易用。

2.1 图的基本概念5 D7 Q; Z* N3 G! X
图(Graph):图是由若干顶点和连接顶点的边所构成关系结构。
/ Y4 I5 H8 F8 P7 ]; T顶点(Node):图中的点称为顶点,也称节点。, r7 C' l. G2 e/ z  |: n2 T( |
边(Edge):顶点之间的连线,称为边。
: D# X% O' l/ `9 F) a平行边(Parallel edge):起点相同、终点也相同的两条边称为平行边。
( Y$ L7 O8 I1 u循环(Cycle):起点和终点重合的边称为循环。' r5 |. |/ f. @( u3 R
有向图(Digraph):图中的每条边都带有方向,称为有向图。6 |3 D9 T2 j& o% |/ |0 n* D
无向图(Undirected graph):图中的每条边都没有方向,称为无向图。
4 q- M' Y( d: M* W  c+ n: F赋权图(Weighted graph):图中的每条边都有一个或多个对应的参数,称为赋权图。该参数称为这条边的权,权可以用来表示两点间的距离、时间、费用。
" o2 `% w8 g7 B* c. D; @度(Degree):与顶点相连的边的数量,称为该顶点的度。
) a- {! d$ C5 |# @+ m9 M1 q/ R
' b- j& G6 C8 z4 s4 |! O9 }2.2 图、顶点和边的操作8 N7 ^0 R% ^. c! T, [" B
Networkx很容易创建图、向图中添加顶点和边、从图中删除顶点和边,也可以查看、删除顶点和边的属性。
( {% C: M( L! J7 Z1 A
) S, ]6 q7 b4 O, Y+ {9 A* L3 G2.2.1 图的创建Graph() 类、DiGraph() 类、MultiGraph() 类和 MultiDiGraph() 类分别用来创建:无向图、有向图、多图和有向多图。定义和例程如下:  e4 @  s* R- D* {2 X* G
  ?  _! R! v4 \7 {
class Graph(incoming_graph_data=None, **attr)
: h8 ?7 }  A) i; Oimport networkx as nx  # 导入 NetworkX 工具包
1 Q8 s" k) t& W5 X( f, a
/ x+ q2 u3 Y$ C& G9 |% I4 S# 创建 图( y5 z! U3 _) I" Z
G1 = nx.Graph()  # 创建:空的 无向图$ L) \& C. x( ^% e! p0 t% i
G2 = nx.DiGraph()  #创建:空的 有向图! s' H: G4 C1 V: l1 b6 e2 ]
G3 = nx.MultiGraph()  #创建:空的 多图& `: {+ _" I5 o6 `
G4 = nx.MultiDiGraph()  #创建:空的 有向多图4 u% N3 {+ f7 l* ?! |7 t  h

' j+ |7 n3 G+ {" T# L  z6 Y; m0 Z$ z0 t5 {7 N' u
2.2.2 顶点的添加、删除和查看) H8 [& T. P+ S4 L- i9 v% ~

图的每个顶点都有唯一的标签属性(label),可以用整数或字符类型表示,顶点还可以自定义任意属性。

顶点的常用操作:添加顶点,删除顶点,定义顶点属性,查看顶点和顶点属性。定义和例程如下:


" ?* n+ g( y: X2 X, [! l3 O  RGraph.add_node(node_for_adding, **attr)* A/ g% S4 m" M6 O9 V* r& L
Graph.add_nodes_from(nodes_for_adding, **attr)9 s6 I5 t2 @. H$ v" R2 F% Q$ o
Graph.remove_node(n)8 p  I8 b+ P7 p7 r7 V' K
Graph.remove_nodes_from(nodes), `1 J) c& B$ [6 D6 r0 @

* {6 x" ~7 r9 {9 J- l( t2 |# 顶点(node)的操作
; t" k( A( H, x* K9 e  N# 向图中添加顶点' d6 W/ P: @. y, W& M' M
G1.add_node(1)  # 向 G1 添加顶点 15 F9 H' K5 p+ T# P8 F# R7 |- Y
G1.add_node(1, name='n1', weight=1.0)  # 添加顶点 1,定义 name, weight 属性# x& d& b" `+ k  {4 F
G1.add_node(2, date='May-16') # 添加顶点 2,定义 time 属性, t$ Z% o2 N2 X) F" m
G1.add_nodes_from([3, 0, 6], dist=1)  # 添加多个顶点,并定义属性
% J* B9 U, f. kG1.add_nodes_from(range(10, 15))  # 向图 G1 添加顶点 10~14
% t1 q) f) X) o7 \0 x& I, ~: \. B' E1 l+ H* J! G3 p
# 查看顶点和顶点属性
3 s5 D5 A1 h; h8 @print(G1.nodes())  # 查看顶点列表3 J/ b! n& `6 S, K4 B( @, a
# [1, 2, 3, 0, 6, 10, 11, 12, 13, 14]
: Y$ f& s1 ?7 Z* H. X( a% gprint(G1._node)  # 查看顶点属性
3 O/ a& F9 x0 [4 Q& z* S+ \# {1: {'name': 'n1', 'weight': 1.0}, 2: {'date': 'May-16'}, 3: {'dist': 1}, 0: {'dist': 1}, 6: {'dist': 1}, 10: {}, 11: {}, 12: {}, 13: {}, 14: {}}
. u; M$ t: f: R# z1 `' h% C9 N: L9 A8 w
# 从图中删除顶点" Y, o8 j$ H+ `/ o
G1.remove_node(1)  # 删除顶点
0 J1 |7 `7 n7 t4 W5 k4 EG1.remove_nodes_from([1, 11, 13, 14])  # 通过顶点标签的 list 删除多个顶点' d  J8 F# S- I% i- B
print(G1.nodes())  # 查看顶点
: w, ~# }$ J7 u- H8 T# [2, 3, 0, 6, 10, 12]  # 顶点列表
4 L6 D$ p3 P7 ^! x2 ^% {+ ~2.2.3 边的添加、删除和查看

边是两个顶点之间的连接,在 NetworkX 中 边是由对应顶点的名字的元组组成 e=(node1,node2)。边可以设置权重、关系等属性。

边的常用操作:添加边,删除边,定义边的属性,查看边和边的属性。向图中添加边时,如果边的顶点是图中不存在的,则自动向图中添加该顶点。

Graph.add_edge(u_of_edge, v_of_edge, **attr)
  t3 b3 j- f2 L) F, \( ~Graph.add_edges_from(ebunch_to_add, **attr)
' M- {: s7 \3 _/ ]; kGraph.add_weighted_edges_from(ebunch_to_add, weight=‘weight’, **attr)9 ^8 G- o1 @3 v+ f6 d- k% D
  t2 G6 V3 f  m$ h& I3 w" @
# 边(edge)的操作
0 N( f& Y1 h* I7 N5 y0 d: l# Q# 向图中添加边" L8 O& d8 V% E; v9 \1 t/ \
G1.add_edge(1,5)  # 向 G1 添加边,并自动添加图中没有的顶点
' [0 U( V6 j  L# ~G1.add_edge(0,10, weight=2.7)  # 向 G1 添加边,并设置边的属性
8 p2 \5 q; x) FG1.add_edges_from([(1,2,{'weight':0}), (2,3,{'color':'blue'})])  # 向图中添加边,并设置属性
6 m2 V1 E3 d7 U* R1 R  p+ G: C5 ^7 ^0 GG1.add_edges_from([(3,6),(1,2),(6,7),(5,10),(0,1)])  # 向图中添加多条边
+ ^. {0 `# A, Y2 `8 n/ O1 KG1.add_weighted_edges_from([(1,2,3.6),[6,12,0.5]])  # 向图中添加多条赋权边: (node1,node2,weight)/ Y+ j$ L4 [8 V) z2 c; X
print(G1.nodes())  # 查看顶点6 G; y4 v9 _1 G& X' E. l' z
# [2, 3, 0, 6, 10, 12, 1, 5, 7]  # 自动添加了图中没有的顶点
, T8 {7 X4 G; i( y6 W' ]
( e- _3 e) E' u  g7 r# 从图中删除边7 `" Y4 t/ o- x( F& ?% K- _% `
G1.remove_edge(0,1)  # 从图中删除边 0-1
- H- W5 _: \+ \4 \# f: r, iG1.remove_edges_from([(2,3),(1,5),(6,7)])  # 从图中删除多条边
4 Y! v0 W. v3 i3 P, _7 A7 f0 k* X( f, r- b
# 查看 边和边的属性  m2 |$ O5 b; C) F* o$ {
print(G1.edges)  # 查看所有的边
5 ~# D! B4 \3 Z8 a  E6 ?9 D# k[(2, 1), (3, 6), (0, 10), (6, 12), (10, 5)]
6 T# T/ `# L# ~/ C# c3 |. E# ?print(G1.get_edge_data(1,2))  # 查看指定边的属性, E  K8 }6 M( r8 i
# {'weight': 3.6}
, A+ o. \% X  D/ X3 Q/ fprint(G1[1][2])  # 查看指定边的属性
6 q) g9 ^& Z6 n: `# {'weight': 3.6}& x3 N3 i( C9 h
print(G1.edges(data=True))  # 查看所有边的属性6 W/ Q8 v4 C+ a. d& C! e
# [(2, 1, {'weight': 3.6}), (3, 6, {}), (0, 10, {'weight': 2.7}), (6, 12, {'weight': 0.5}), (10, 5, {})]
. r. X* b$ B' v1 O7 l
! W2 ^- t0 `  J2 y& P7 q( m2.2.4 查看图、顶点和边的信息
1 l% {. |) [6 _$ l
5 ^; ^# K1 C0 ~- I8 d& o0 r5 h# 查看图、顶点和边的信息
& A' V3 t( u; w" m& c* c# B9 k" ^/ ]print(G1.nodes)  # 返回所有的顶点 [node1,...]- L2 G8 N4 N/ c! o2 @
# [2, 3, 0, 6, 10, 12, 1, 5, 7]
( K& t) m+ [6 _' {; e/ O% @print(G1.edges)  # 返回所有的边 [(node1,node2),...]) f; E8 U# }. d; w# j
# [(2, 1), (3, 6), (0, 10), (6, 12), (10, 5)]4 ]4 N7 \4 U, H$ Q8 L/ j
print(G1.degree)  # 返回各顶点的度 [(node1,degree1),...]
& G9 S! Q8 P' Z' z# [(2, 1), (3, 1), (0, 1), (6, 2), (10, 2), (12, 1), (1, 1), (5, 1), (7, 0)]
5 L, V  V2 b/ W0 i( uprint(G1.number_of_nodes())  # 返回顶点的数量+ `0 ?, n. J; j& h  M
# 9
3 ^! d1 e+ s5 C7 t% w. iprint(G1.number_of_edges())  # 返回边的数量! n9 a5 Z3 u; k& S! ^. x9 Z
# 5
0 F6 O# ]" Q0 @3 [. ?" R% S* J9 v6 S; fprint(G1[10])  # 返回与指定顶点相邻的所有顶点的属性4 y6 [8 P; F( s: J: L* e( f
# {0: {'weight': 2.7}, 5: {}}, s% Z9 ~9 J1 A+ b- w/ k+ J
print(G1.adj[10])  # 返回与指定顶点相邻的所有顶点的属性
2 f! V- r: b2 X1 G# {0: {'weight': 2.7}, 5: {}}. v" j; d7 U, P
print(G1[1][2])  # 返回指定边的属性
0 v* e- v4 F7 W" P# {'weight': 3.6}4 b8 X3 x# ^; q4 H
print(G1.adj[1][2])  # 返回指定边的属性- O1 K+ q' V: T4 N( L
# {'weight': 3.6}
! M7 J' n& z) nprint(G1.degree(10))  # 返回指定顶点的度) k4 t8 v* Z) d* t, f0 F
# 2  I! C2 T" |1 g5 M

+ V" f8 [  a" W  G+ qprint('nx.info:',nx.info(G1))  # 返回图的基本信息
+ b+ r) _# w0 `8 L+ P3 Yprint('nx.degree:',nx.degree(G1))  # 返回图中各顶点的度! o. H1 t! Q: I. N7 u3 ~
print('nx.density:',nx.degree_histogram(G1))  # 返回图中度的分布! d, f1 q& K# R7 V1 ]% @( V  |
print('nx.pagerank:',nx.pagerank(G1))  # 返回图中各顶点的频率分布) }$ V& _# b' v: W
& V" y% _/ S2 p8 T5 T* Q
; V* a: o) S4 [' Z+ B9 }- t
' x9 R( M, K& m

+ W* i( y% H: H+ p+ C, [$ ^% O" w/ ~/ V$ e+ [- R  W: E
2.3 图的属性和方法图的方法
; y: ?4 R/ @' L: U( d( X  z& T& w% h5 ^
方法                                                说明
0 ^0 _% ]5 S) `, D, o' G2 |G.has_node(n)                                当图 G 中包括顶点 n 时返回 True
! A( T7 g0 `1 nG.has_edge(u, v)                        当图 G 中包括边 (u,v) 时返回 True
- ^$ f/ `  B0 |; _2 yG.number_of_nodes()                        返回 图 G 中的顶点的数量6 E# F0 [; I4 _3 A3 d. r
G.number_of_edges()                        返回 图 G 中的边的数量
1 D! d5 C3 c- H- J& c5 qG.number_of_selfloops()                返回 图 G 中的自循环边的数量
0 t* _- m: I* h' t8 GG.degree([nbunch, weight])                返回 图 G 中的全部顶点或指定顶点的度  v9 o: z/ ~! |. y2 R
G.selfloop_edges([data, default])        返回 图 G 中的全部的自循环边
! Z: Z# z' b& B8 Z0 n1 V- mG.subgraph([nodes])                        从图 G1中抽取顶点[nodes]及对应边构成的子图+ D8 D2 X, ^. K8 a+ {5 U. P
union(G1,G2)                                合并图 G1、G2
# m, `+ Y% q8 ]6 Rnx.info(G)                                        返回图的基本信息) G) C1 ^' f; W' P; O. H: ?
nx.degree(G)                                返回图中各顶点的度
& C; |) Z6 h- Bnx.degree_histogram(G)                返回图中度的分布# l& x1 g' U+ X4 o$ q! S& }
nx.pagerank(G)                                返回图中各顶点的频率分布
5 \4 _5 Y- X+ W, Q& @* jnx.add_star(G,[nodes],**attr)        向图 G 添加星形网络
) X! u  u: l! t# h7 v* ?7 _nx.add_path(G,[nodes],**attr)        向图 G 添加一条路径1 \. ?6 Q) g3 y( ?2 m
nx.add_cycle(G,[nodes],**attr)        向图 G 添加闭合路径& ?) [) [# J, n
( B, R: Z; x( ~2 U* o" v, a2 a9 e
& s0 I: ~; g+ N, i9 M. w; s# x
例程:! s4 W. f% E* J4 `
G1.clear() # 清空图G1! d" a3 Y- ~% k4 n8 U; V
nx.add_star(G1, [1, 2, 3, 4, 5], weight=1)  # 添加星形网络:以第一个顶点为中心2 D& G9 _) c2 k
# [(1, 2), (1, 3), (1, 4), (1, 5)]
! a, w' Q$ x9 M2 A$ L6 \0 @6 Enx.add_path(G1, [5, 6, 8, 9, 10], weight=2)  # 添加路径:顺序连接 n个节点的 n-1条边
, D/ I- x- j& U4 m  ^$ Q; y* }# [(5, 6), (6, 8), (8, 9), (9, 10)]9 y, M$ I7 I' r5 v1 ~; |! P4 g
nx.add_cycle(G1, [7, 8, 9, 10, 12], weight=3)  # 添加闭合回路:循环连接 n个节点的 n 条边  Y) ~2 J. X3 L/ R3 h
# [(7, 8), (7, 12), (8, 9), (9, 10), (10, 12)]8 z2 Z1 Q: \& W+ r
print(G1.nodes)  # 返回所有的顶点 [node1,...]
6 A; \$ |0 _, l' @nx.draw_networkx(G1)
$ g1 b7 {5 `  c) w/ i+ b! xplt.show()
$ G( R1 }5 B5 r5 e, Z3 u* f* N& u, y$ f5 V
G2 = G1.subgraph([1, 2, 3, 8, 9, 10])1 T- x: I4 f2 f
G3 = G1.subgraph([4, 5, 6, 7])' h' s% g: i  @5 f
G = nx.union(G2, G3)
# }- t+ y# ?( Wprint(G.nodes)  # 返回所有的顶点 [node1,...]2 ?& b9 g, q3 d* }& c( A. _
# [1, 2, 3, 8, 9, 10, 4, 5, 6, 7]
2 d8 Q0 C8 |5 u  w$ E4 v
$ ?* R! ~. e4 x7 N/ z3 m6 G4 g4 q* u5 K
3、图的绘制与分析3.1 图的绘制$ r  [; H' u+ _. o5 c* R
可视化是图论和网络问题中很重要的内容。NetworkX 在 Matplotlib、Graphviz 等图形工具包的基础上,提供了丰富的绘图功能。
, f3 R6 w" ^  W* R# c" g
# R0 n" ~$ Q- Q. m. n' v本系列拟对图和网络的可视化作一个专题,在此只简单介绍基于 Matplotlib 的基本绘图函数。基本绘图函数使用字典提供的位置将节点放置在散点图上,或者使用布局函数计算位置。
- V3 V( \' b3 i& U
& M  `% H: S: b& k9 Z$ ^$ M/ H+ q方法                                                                        说明
+ e3 b3 J1 F/ [draw(G[,pos,ax])                                                基于 Matplotlib 绘制 图 G
+ t) |. v- P1 G/ Z2 X: {0 \draw_networkx(G[, pos, arrows, with_labels])        基于 Matplotlib 绘制 图 G1 [- P7 T5 [. }" w2 @6 v) H
draw_networkx_nodes(G, pos[, nodelist, . . . ])        绘制图 G 的顶点) }0 g" J- o2 M) i
draw_networkx_edges(G, pos[, edgelist, . . . ])        绘制图 G 的边+ O  F1 t/ J6 d# L& m
draw_networkx_labels(G, pos[, labels, . . . ])            绘制顶点的标签1 M! A5 ^0 {$ r2 a, w- A3 e
draw_networkx_edge_labels(G, pos[, . . . ])                绘制边的标签3 f* `. s4 j4 D' {+ D& g( S
( ?& c! ^. ]( v+ ]
) G$ i, f* K  {+ _. Z( ]: b
其中,nx.draw() 和 nx.draw_networkx() 是最基本的绘图函数,并可以通过自定义函数属性或其它绘图函数设置不同的绘图要求。
6 W2 F6 y: B7 G5 C1 R* k/ r& }

draw(G, pos=None, ax=None, **kwds)

draw_networkx(G, pos=None, arrows=True, with_labels=True, **kwds)


: g# w) e* F" V2 m# m* y, o常用的属性定义如下:
( @* p7 J& Z  e  |' @- ~6 S; g( j) Y' O. k* O0 a* M9 d4 D
‘node_size’:指定节点的尺寸大小,默认300/ [# Q" \0 a) n' Q; m0 |
‘node_color’:指定节点的颜色,默认红色
( h: X* m4 ^' `4 r' G# Z+ |& g‘node_shape’:节点的形状,默认圆形
, h: n- Y- i9 c# O) C( c( N/ K/ c. R'‘alpha’:透明度,默认1.0,不透明2 _& b! D# I0 x" Q7 z; ?
‘width’:边的宽度,默认1.0
4 |5 [  F# b' v( x/ x‘edge_color’:边的颜色,默认黑色, \+ i0 M4 x+ V; o
‘style’:边的样式,可选 ‘solid’、‘dashed’、‘dotted’、‘dashdot’
8 C; C# d3 h4 q& r9 y, i‘with_labels’:节点是否带标签,默认True6 z9 v# t5 o  _, \/ ]
‘font_size’:节点标签字体大小,默认127 [5 _; r" e  n" _  \1 W9 O( w
‘font_color’:节点标签字体颜色,默认黑色2 X$ y. x2 Y  u. A# ?. s; F5 Z
/ F4 \. ]" U/ X* a& W3 t
( a9 v! J+ ]( t: q; C; ^) m% i
3.2 图的分析NetwotkX 提供了图论函数对图的结构进行分析
* h/ ?& l4 g! g5 I% N  L8 I子图5 F' l( K! a; ~
连通子图5 r4 z1 z& o) v' K- ^

: z* V( K8 j0 A
( a" d, k' ~1 Y) t6 U- K
5 e5 z7 e, O6 Q! t9 j( v6 S" h8 k
% J( z( W7 E1 ]) f% s0 t  ~1 M1 [5 M4 V





欢迎光临 数学建模社区-数学中国 (http://www.madio.net/) Powered by Discuz! X2.5