Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
OpenDocCN
think-comp-2e-zh
提交
b2d99a48
T
think-comp-2e-zh
项目概览
OpenDocCN
/
think-comp-2e-zh
大约 1 年 前同步成功
通知
0
Star
16
Fork
7
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
T
think-comp-2e-zh
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
提交
b2d99a48
编写于
10月 30, 2017
作者:
W
wizardforcel
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
2.8~2.9
上级
8af3a809
变更
1
隐藏空白更改
内联
并排
Showing
1 changed file
with
81 addition
and
2 deletion
+81
-2
2.md
2.md
+81
-2
未找到文件。
2.md
浏览文件 @
b2d99a48
...
...
@@ -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.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录