提交 b83ea5de 编写于 作者: W wizardforcel

2.4~2.5

上级 d3e8f19a
......@@ -36,7 +36,7 @@
> 图 2.2:表示城市和高速公路的无向图
为了表示图,我们将使用一个名为 NetworkX 的包,它是 Python 中最常用的网络库。可以在 <https://networkx.github.io/> 上阅读更多信息,但是我们之后会解释。
为了表示图,我们将使用一个名为 NetworkX 的包,它是 Python 中最常用的网络库。可以在 <https://networkx.github.io/> 上阅读更多信息,但是我们之后会解释。
我们可以通过导入 NetworkX 和实例化`nx.DiGraph`来创建有向图:
......@@ -46,7 +46,7 @@ import networkx as nx
G = nx.DiGraph()
```
通常将 NetworkX 导入为`nx`。此时,`G`是一个`DiGraph`对象,不包含节点和边。我们可以使用`add_node`方法添加节点:
通常将 NetworkX 导入为`nx`。此时,`G`是一个`DiGraph`对象,不包含节点和边。我们可以使用`add_node`方法添加节点:
```py
......@@ -175,3 +175,130 @@ Erdős 和 Rényi 研究了这些随机图的属性;其令人惊奇的结果
Erdős 和 Rényi 表明,这个临界值是`p* = lnn / n`,其中`n`是节点数。如果`p < p*`,随机图`G(n, p)`不太可能连通,并且如果`p > p*`,则很可能连通。
为了测试这个说法,我们将开发算法来生成随机图,并检查它们是否连通。
## 2.4 生成图
![](img/2-3.png)
我将首先生成一个完整的图,这是一个图,其中每个节点都彼此连接。
这是一个生成器函数,它接收节点列表并枚举所有不同的偶对。如果你不熟悉生成器函数,你可能需要阅读附录?,然后回来。
```py
def all_pairs(nodes):
for i, u in enumerate(nodes):
for j, v in enumerate(nodes):
if i>j:
yield u, v
```
你可以使用`all_pairs`来构造一个完全图。
```py
def make_complete_graph(n):
G = nx.Graph()
nodes = range(n)
G.add_nodes_from(nodes)
G.add_edges_from(all_pairs(nodes))
return G
```
`make_complete_graph`接受节点数`n`,并返回一个新的`Graph`,拥有`n`个节点,所有节点之间都有边。
以下代码生成了一个包含 10 个节点的完全图,并绘制出来。
```py
complete = make_complete_graph(10)
nx.draw_circular(complete,
node_color=COLORS[2],
node_size=1000,
with_labels=True)
```
图(?)显示了结果。不久之后,我们将修改此代码来生成 ER 图,但首先我们将开发函数来检查图是否是连通的。
## 2.5 连通图
如果每个节点到每个其他节点都存在路径,这个图就是连通图。请见<http://en.wikipedia.org/wiki/Connectivity_(graph_theory)>
对于许多涉及图的应用,检查图是否连通是很有用的。幸运的是,有一个简单的算法。
你可以从任何节点起步,并检查是否可以到达所有其他节点。如果你可以到达一个节点`v`,你可以到达`v`的任何一个邻居,他们是`v`通过边连接的任何节点。
`Graph`类提供了一个称为`neighbors`的方法,返回给定节点的邻居列表。例如,在上一节中我们生成的完全图中:
```py
>>> complete.neighbors(0)
[1, 2, 3, 4, 5, 6, 7, 8, 9]
```
假设我们从节点`s`起步。我们可以将`s`标记为“已访问”,然后我们可以标记它的邻居。然后我们标记邻居的邻居,依此类推,直到你无法再到达任何节点。如果访问了所有节点,则图是连通图。
以下是 Python 中的样子:
```py
def reachable_nodes(G, start):
seen = set()
stack = [start]
while stack:
node = stack.pop()
if node not in seen:
seen.add(node)
stack.extend(G.neighbors(node))
return seen
```
`reachable_nodes`接受`Graph`和起始节点`start`,并返回可以从`start`到达的节点集合,他们。
最初,已访问的集合是空的,我们创建一个名为`stack`的列表,跟踪我们发现但尚未处理的节点。最开始,栈包含单个节点`start`
现在,每次在循环中,我们:
+ 从栈中删除一个节点。
+ 如果节点已在`seen`中,我们返回到步骤 1。
+ 否则,我们将节点添加到`seen`,并将其邻居添加到栈。
当堆栈为空时,我们无法再到达任何节点,所以我们终止了循环并返回。
例如,我们可以找到从节点`0`可到达的,完全图中的所有节点:
```py
>>> reachable_nodes(complete, 0)
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
```
最初,栈包含节点`0``seen`是空的。第一次循环中,节点`0`添加到了`seen`,所有其他节点添加到了栈中(因为它们都是节点`0`的邻居)。
下一次循环中,`pop`返回栈中的最后一个元素,即节点`9.`因此,节点`9`被添加到`seen`,并且其邻居被添加到栈。
请注意,同一个节点在堆栈中可能会出现多次;实际上,具有`k`个邻居的节点将添加到堆栈`k`次。稍后我们将寻找方法,来使此算法更有效率。
我们可以使用`reachable_nodes`来编写`is_connected`
```py
def is_connected(G):
start = next(G.nodes_iter())
reachable = reachable_nodes(G, start)
return len(reachable) == len(G)
```
`is_connected`通过调用`nodes_iter`来选择一个起始节点,`node_iter`返回一个迭代器对象,并将结果传递给`next`,返回第一个节点。
`reachable`获取了一组节点,它们可以从`start`到达。如果这个集合的大小与图的大小相同,那意味着我们可以访问所有节点,也就是这个图是连通的。
一个完全图是连通的,毫不奇怪:
```py
>>> is_connected(complete)
True
```
下一节中,我们会生成 ER 图,并检查它们是否是连通的。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册