提交 b2d99a48 编写于 作者: W wizardforcel

2.8~2.9

上级 8af3a809
......@@ -262,7 +262,7 @@ def reachable_nodes(G, start):
+ 如果节点已在`seen`中,我们返回到步骤 1。
+ 否则,我们将节点添加到`seen`,并将其邻居添加到栈。
栈为空时,我们无法再到达任何节点,所以我们终止了循环并返回。
当栈为空时,我们无法再到达任何节点,所以我们终止了循环并返回。
例如,我们可以找到从节点`0`可到达的,完全图中的所有节点:
......@@ -276,7 +276,7 @@ def reachable_nodes(G, start):
下一次循环中,`pop`返回栈中的最后一个元素,即节点`9.`因此,节点`9`被添加到`seen`,并且其邻居被添加到栈。
请注意,同一个节点在堆栈中可能会出现多次;实际上,具有`k`个邻居的节点将添加到堆`k`次。稍后我们将寻找方法,来使此算法更有效率。
请注意,同一个节点在栈中可能会出现多次;实际上,具有`k`个邻居的节点将添加到`k`次。稍后我们将寻找方法,来使此算法更有效率。
我们可以使用`reachable_nodes`来编写`is_connected`
......@@ -405,3 +405,82 @@ ys = [prob_connected(n, p) for p in ps]
对于较大的`n`值,图(?)展示了类似的结果。随着`n`的增加,临界值越来越小,转变越来越突然。
这些实验与 Erdős 和 Rényi 在其论文中证明的结果一致。
## 2.8 图的算法分析
这一章中,我提出了一个检查图是否连通的算法;在接下来的几章中,我们将再次看到更多的图的算法。并且我们要分析这些算法的性能,了解它们的运行时间如何随着图大小的增加而增长。
如果你还不熟悉算法分析,在你继续之前,你应该阅读附录一。
图算法的增长级别通常表示为顶点数量`n`,以及边数量`m`的函数。
作为一个例子,我们分析从前面的`reachable_nodes`
```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
```
每次循环,我们从栈中弹出一个节点;默认情况下,`pop`删除并返回列表的最后一个元素,这是一个常数时间的操作。
接下来我们检查节点是否被已访问,这是一个集合,所以检查成员是常数时间。
如果节点还没有访问,我们添加它是常量时间,然后将邻居添加到栈中,这相对于邻居数量是线性的。
为了使用`n``m`表达运行时间,我们可以将每个节点添加到`seen``stack`的总次数加起来。
每个节点只添加一次,所以添加的总数为`n`
但是节点可能多次被添加到栈,具体取决于它们有多少邻居。如果节点具有`k`个邻居,则它会被添加到栈`k`次。当然,如果它有`k`个邻居,那意味着它拥有`k`个边。
所以添加到栈的总数是边的数量`m`的两倍,由于我们考虑每个边两次。
因此,这个函数的增长级别为`O(n + m)`,我们可以说,即运行时间与`n``m`成正比,以较大者为准。
如果我们知道`n``m`之间的关系,我们可以简化这个表达式。例如,在完全图中,边数是`n(n-1)/ 2`,它属于`O(n^2)`。所以对于一个完全图,`reachable_nodes`是二次于`n`的。
## 2.9 练习
本章的代码在`chap02.ipynb`中,它是本书的仓库中的 Jupyter 笔记本。使用此代码的更多信息,请参阅第(?)节。
练习 1:启动`chap02.ipynb`并运行代码。笔记本中嵌入了一些简单的练习,你可能想尝试一下。
练习 2:我们分析了`reachable_nodes`的性能,并将其分类为`O(n + m)`,其中`n`是节点数,`m`是边数。继续分析,`is_connected`的增长级别是什么?
```py
def is_connected(G):
start = next(G.nodes_iter())
reachable = reachable_nodes(G, start)
return len(reachable) == len(G)
```
练习 3 :在我实现`reachable_nodes`时,你可能很困惑,因为向栈中添加所有邻居而不检查它们是否已访问,明显是低效的。编写一个该函数的版本,在将邻居添加到栈之前检查它们。这个“优化”是否改变了增长级别?它是否使函数更快?
> 译者注:在弹出节点时将其添加到`seen`,在遍历邻居时检查它们是否已访问。
练习 4:
实际上有两种 ER 图。我们在本章中生成的一种,`G(n,p)`的特征是两个参数,节点数量和节点之间的边的概率。
一种替代定义表示为`G(n,m)`,也以两个参数为特征:节点数`n`和边数`m`。在这个定义中,边数是固定的,但它们的位置是随机的。
使用这个替代定义,重复这一章的实验。这里是几个如何处理它的建议:
1. 编写一个名为`m_pairs`的函数,该函数接受节点列表和边数`m`,并返回随机选择的`m`个边。一个简单的方法是,生成所有可能的边的列表,并使用`random.sample`
2. 编写一个名为`make_m_graph`的函数,接受`n``m`,并返回`n`个节点和`m`个边的随机图。
3. 创建一个`prob_connected`的版本,使用`make_m_graph`而不是`make_random_graph`
4. 计算一系列`m`值的连通概率。
与第一类 ER 图的结果相比,该实验的结果如何?
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册