中科大算法分析与设计课程 PPT 精读
Published:
Note: 本文部分内容由 Gemini 3 Pro 生成,请注意鉴别。
课程资料和部分算法的可视化演示见附录。
- 分布式算法
- 生成树上的广播 Broadcast
- 生成树上的敛播 Convergecast
- Alg 2.2 构造生成树(若同步为 BFS)
- Alg 2.3 指定根时构造DFS生成树
- Alg 2.4 不指定根时构造DFS生成树
- 匿名环
- 异步环 LCR 算法:一个 $O(n^2)$ 算法
- 异步环 HS 算法:一个 $O(n\lg n)$ 算法
- 异步环:下界 $\Omega(n \log n)$
- 同步环 Non-uniform Alg:上界 $O(n)$
- 同步环 Uniform Alg:上界 $O(n)$
- 同步环 基于比较的算法:下界 $\Omega(n \log n)$
- 同步环 时间受限算法:下界 $\Omega(n \log n)$
- 因果关系
- Lamport 时间戳
- 向量时间戳
- 因果通信
- 概率算法
- 往年题例子
- 一、给出一个计算图的最小顶点覆盖的近似算法,并说明其时间复杂度及近似比。
- 二、设集合 S 和 T 中各有 n 个互不相同的元素,要求: (1)写一 Monte Carlo 算法判定 S 和 T 是否相等;(2)分析算法出错的概率;(3)算法是否有偏,若有偏,偏什么?
- 三、为什么说若一个 NPC 或者 NPH 问题在多项式时间内可解当且仅当 NP = P?
- 四、考察上界 O(n) 的同步环非均匀算法。修改该算法,使得每个阶段中可以有 k 条消息在转发,并分析时间复杂度和消息复杂度。
- 五、已知对于 MST 启发的 ∆TSP 问题有 $R_{MST}^\infty = 2$,请构造一个实例,使其渐近性能比为 2。
- 附录
分布式算法
生成树上的广播 Broadcast
1. 前置条件与目标
前提:假定网络中已经建立好了一棵生成树(Spanning Tree)。父子关系(
parent,children)已经确定。输入:根节点 $p_r$ 拥有消息 $M$。
输出:网络中所有节点都收到消息 $M$。
2. 算法逻辑 (Algorithm 2.1)
PPT 中给出了伪代码和状态转换描述,核心逻辑非常简单,就是“收到即转发”:
对于根节点 ($p_r$):
直接把消息 $M$ 发送给所有孩子节点 (
children).任务结束,进入终止状态 (
terminate).
对于非根节点 ($p_i$):
等待 (
upon receiving) 从父节点 (parent) 传来消息 $M$。一旦收到,立即将 $M$ 转发给自己的所有孩子节点。
任务结束,进入终止状态。
注意:物理上节点可能有很多连线,但逻辑上它只认生成树上的
parent和children进行通信。
3. 复杂度分析 (重点考点)
PPT 明确给出了消息复杂度和时间复杂度,并强调了该算法在同步和异步模型下表现一致。
A. 消息复杂度 (Message Complexity)
结论:$n-1$,即 $O(n)$。
原理:生成树有 $n$ 个节点,必然有 $n-1$ 条边。算法保证消息 $M$ 在树的每一条边上恰好传输一次(从父到子)。
对应之前的图解:这就是只数“发了几次包”。
B. 时间复杂度 (Time Complexity)
结论:$h$ (或 $d$),即树的高度 (Height/Depth),$O(h)$。
原理:广播是一层一层往下传的,最慢的那个节点(最坏情况)也就是树的最深处,所需时间取决于树有多深。
4. 证明逻辑 (数学归纳法)
PPT 花了很大篇幅(Lemma 2.1 和 Lemma 2.3)来证明时间复杂度。这里用到了你之前问的“容许执行” (Admissible Execution) 概念。
同步模型证明 (Lemma 2.1):
定义:时间按“轮” (Round) 算。
归纳法:
基础:距离根节点 1 的节点(根的孩子),在第 1 轮收到。
假设:距离为 $t-1$ 的节点在第 $t-1$ 轮收到。
推导:因为第 $t-1$ 轮父节点收到了,它会在下一轮发给孩子,所以距离为 $t$ 的节点一定在第 $t$ 轮收到。
结论:总时间 = 树的最大深度 $d$。
异步模型证明 (Lemma 2.3):
定义:异步系统中,消息延迟在 $(0, 1]$ 之间。为了算最坏情况(Time Complexity),我们假设每一步都用到最大延迟 $1$。
关键词:“至多在时刻 $t$ (at most at time t)”。
逻辑:和同步模型一样,也是通过归纳法证明。如果父节点最晚在 $t-1$ 收到,再加上传输延迟至多为 1,那么子节点最晚在 $t$ 收到。
总结:这张 PPT 在考什么?
如果这是一道考试题,通常会考察以下几点:
伪代码:让你写出基于生成树的广播算法(参考上面的步骤 2)。
计算复杂度:问你总共发了多少个消息($n-1$),最长花了多少时间(树高 $h$)。
证明:用数学归纳法证明为什么时间复杂度是 $O(h)$。一定要模仿 PPT 中的话术:“对距离 $t$ 使用归纳法…”。
生成树上的敛播 Convergecast
这段 PPT 内容讲解的是汇集(Convergecast),它是广播(Broadcast)的逆过程。如果说广播是“从上往下分发指令”,那么汇集就是“从下往上收集情报”。
为了帮助你应对考试,我将这段内容的逻辑、算法细节和考点提炼如下:
1. 核心概念:自底向上的“信息汇总”
方向:从叶子节点(Leaves)开始,向根节点(Root)流动。
目的:收集所有节点的信息,并在根节点汇总出一个全局结果(例如:求最大值、求和、计数、选举等)。
例子:PPT 中使用的例子是“求最大值 (Global Max)”。
每个节点都有一个初值 $x_i$。
根节点想要知道全网最大的那个 $x$ 是多少。
2. 算法逻辑(以求最大值为例)
这个算法的执行流程非常像公司的“层层汇报”:
启动者(Initiators):叶子节点。
因为叶子没有孩子,不需要等任何人。
叶子节点直接把自己的值 $x_i$ 发送给父亲。
中间层(Internal Nodes):阻塞等待(Blocking Wait)。
这是算法最关键的一步。
如果节点 $p_j$ 有 $k$ 个孩子,它必须收到所有 $k$ 个孩子的消息后,才能进行下一步。
计算:收到所有孩子的汇报值 ${v_{i1}, …, v_{ik}}$ 后,结合自己的值 $x_j$,计算局部最大值:$v_j = \max(x_j, v_{i1}, …, v_{ik})$。
上报:将算出的 $v_j$ 发送给自己的父亲。
终结者:根节点。
- 当根节点收到所有孩子的消息并计算出最大值后,算法结束,根节点得到了全局最大值。
3. 复杂度分析(必考点)
PPT 中给出的定理 Th 2.5 明确了其复杂度与广播完全一致,但原因不同:
Msg 复杂度:$n-1$ (即 $O(n)$)
原因:除了根节点(Root)以外,每一个节点都要向它的父节点发送恰好 1 条消息(汇报结果)。
全网 $n$ 个节点,也就意味着有 $n-1$ 次汇报发送。
时间复杂度:$d$ (即 $O(h)$, 树的高度)
- 原因:这也是由树的深度决定的。信息必须从最深的叶子节点(最底层),一层层处理、汇报,直到爬升到根节点。最长的路径就是树的高度 $d$。
4. 考试重难点
A. “阻塞等待”是考点
考试中可能会让你写伪代码或分析逻辑。一定要强调:非叶子节点必须等到收到“所有”孩子的消息才能上传。
错误写法:收到一个孩子消息就往上发一个。
后果:会导致根节点收到多次中间结果,导致消息复杂度爆炸(远大于 $n-1$),且破坏了算法的定义。
B. 广播与汇集的组合拳 (Broadcast + Convergecast)
PPT 最后提到“用途:初始化某一信息请求,然后用敛播响应”。这是一种非常经典的分布式模式:
Phase 1 (Broadcast): 根节点发令:“大家把手里的数据报上来!”(自顶向下)
Phase 2 (Convergecast): 节点收到命令后,开始采集数据并层层汇总提交。(自底向上)
C. 同步 vs 异步
PPT 提到“存在一个异步的敛播方法”。
在同步系统中:我们可以按轮次来,第 1 轮叶子发,第 2 轮上一层发……直到根。
在异步系统中:我们不需要时钟,依靠“收到所有孩子消息”这个事件来驱动算法运行。这再次证明了该算法的鲁棒性。
总结一张表
| 特性 | 广播 (Broadcast) | 汇集 (Convergecast) |
|---|---|---|
| 方向 | Root $\to$ Leaves (1对多) | Leaves $\to$ Root (多对1) |
| 触发条件 | 收到父节点消息 | 收到所有子节点消息 |
| Msg 复杂度 | $n-1$ | $n-1$ |
| 时间复杂度 | $d$ (树高) | $d$ (树高) |
| 典型应用 | 发布命令、更新配置 | 收集投票、求和、求最大值 |
Alg 2.2 构造生成树(若同步为 BFS)
这份 PPT 的内容主要围绕如何在一个没有预设结构的连通网络中,构造出一棵以特定节点 $p_r$ 为根的生成树。
它从最基础的 Flooding(泛洪)算法讲起,逐步演进到具体的生成树构造算法(Algorithm 2.2),并深入分析了其正确性、复杂性以及在同步/异步系统下的不同表现。
以下是 PPT 逻辑的详细梳理:
1. 基础铺垫:Flooding 算法 (泛洪)
在构造树之前,PPT 先介绍了最暴力的消息传播方式。
算法逻辑:
特殊节点 $p_r$ 发送消息 $M$ 给所有邻居。
当节点 $p_i$ 第一次收到 $M$ 时,把它转发给除了发送者以外的所有邻居。
复杂度分析:
Msg 复杂度:$O(m)$。因为每条信道(边)最多传输 2 次消息(两端各发一次),总数为 $2m - (n-1)$。
时间复杂度:$O(D)$,其中 $D$ 是网络直径。
2. 核心算法:构造生成树 (Algorithm 2.2)
这是本节的重点,是对 Flooding 算法的改进,增加了建立父子关系的逻辑。
A. 基本思想与握手机制
算法通过消息的确认机制来确定谁是谁的父亲,谁是谁的孩子。
确立关系 (<parent>):
如果 $p_i$ 收到的 $M$ 是它平生收到的第一个 $M$,那么发送方 $p_j$ 就是它的父亲 (Parent)。
$p_i$ 立即回信
<parent>给 $p_j$,并向其他邻居转发 $M$。
拒绝关系 (<reject>):
如果 $p_i$ 已经收到过 $M$(说明已经有爹了),又收到了别人发来的 $M$,它就回信
<reject>。这意味着那条边不是生成树上的边(非树边)。
终止条件:
- 节点必须等到它发出的所有 $M$ 都收到了回复(无论是
<parent>还是<reject>),算法才算在本地终止。
- 节点必须等到它发出的所有 $M$ 都收到了回复(无论是
B. 算法流程 (Code for $p_i$)
根节点:发送 $M$ 给所有邻居,设
parent = i(自己)。非根节点:
收到第一个 $M$ $\rightarrow$ 认爹,回
<parent>,转发 $M$。收到后续 $M$ $\rightarrow$ 回
<reject>。收到
<parent>$\rightarrow$ 把对方加入children集合。收到
<reject>$\rightarrow$ 把对方加入other集合。当
children$\cup$other包含了除 parent 外的所有邻居时 $\rightarrow$ Terminate。
3. 正确性证明 (Correctness)
PPT 用反证法证明了该算法确实能构造出一棵树。
连通性 (Connectivity):
- 证明:假设有节点不可达。由于网络物理上是连通的,必然存在一条边连接“可达集”和“不可达集”。可达的那个节点最终会发 $M$,不可达的节点收到后就会加入可达集。矛盾。
无环性 (Acyclicity):
- 证明:假设有环。父子关系意味着“父亲先收到 $M$,孩子后收到 $M$”。如果有环,意味着 $p_1$ 在自己第一次收到 $M$ 之前就已经收到了 $M$,这在时序上是不可能的。矛盾。
4. 同步 vs 异步模型的区别 (难点与考点)
这是 PPT 中非常关键的一个对比分析。
| 特性 | 同步模型 (Synchronous) | 异步模型 (Asynchronous) |
|---|---|---|
| 树的形状 | BFS 树 (广度优先搜索树) | 任意生成树 (Arbitrary Tree) |
| 原因 | 它是按“轮”传播的。第 $t$ 轮收到消息的节点,距离根必定是 $t$。路径一定是最短的。 | 消息速度快慢不一。一条“长链”上的消息可能传得比“短路”更快。 |
| 反例 | N/A | PPT 给出了 5 个顶点的完全图例子:消息可能沿着 $p_0 \to p_1 \to p_2 \to p_3 \to p_4$ 极速传播,形成一条链,尽管物理直径 $D=1$,但树高 $d=4$。 |
| 时间复杂度 | $O(D)$ | $O(D)$ (注意:这里的时间是指因果链时间,尽管树高可能是 $O(n)$,但最快的传播路径覆盖全网的时间仍受限于直径) |
| Msg 复杂度 | $O(m)$ | $O(m)$ |
5. 组合应用:请求与收集 (Request & Collect)
最后 PPT 将构造生成树(向下发)与汇集算法(向上收)结合起来。
总消息复杂度:$O(m + n)$。
构造树消耗 $O(m)$。
汇集消耗 $O(n)$。
总时间复杂度:
同步模型:$O(D)$。因为构造的是 BFS 树,树高 $d \approx D$。
异步模型:$O(n)$。因为构造出的树可能退化成链,树高 $d$ 可能达到 $n$。汇集需要从最底端爬上来,耗时 $O(n)$。
划重点:
握手过程:一定要搞清楚
<parent>和<reject>什么时候发,这是算法 2.2 的核心。异步的“反直觉”:异步构造出来的不是 BFS 树,这会导致树的高度 $d$ 可能远大于网络直径 $D$,进而影响后续在树上跑汇集算法的时间复杂度(从 $O(D)$ 恶化到 $O(n)$)。
常数因子:构造树比单纯 Flooding 稍微多一点点开销(因为要发确认回执),但在 $O$ 记号下都是 $O(m)$。
Alg 2.3 指定根时构造DFS生成树
这与之前的算法(Alg 2.2)有本质的区别。如果说 Alg 2.2 是“一群人同时喊话”(并行/BFS倾向),那么 Alg 2.3 就是“接力赛跑”或者“走迷宫”(串行/DFS)。
1. 核心差异:并行 vs. 串行
PPT 第 17 页明确指出了它与 Alg 2.2 的最大不同:
Alg 2.2 (构造任意/BFS树):收到消息后,向所有邻居转发。试图同时增加同一层的所有节点(像水波纹扩散)。
Alg 2.3 (构造 DFS 树):收到消息后,只向一个未访问过的邻居转发。每次只加一个节点(像一条贪吃蛇在游走)。
2. 算法逻辑:令牌传递 (Token Passing)
这个算法可以看作是一个“令牌(Token)”在网络中传递的过程。持有“令牌”(消息 $M$)的节点才有资格行动。
状态变量:
unexplored:还没联系过的邻居集合。这是 DFS 的核心,就像你走迷宫时手里拿的小本本,记着哪条路还没试过。
流程拆解:
收到 $M$(第一次,认爹):
如果你是第一次收到 $M$,发送者就是你的
parent。立即行动:查看
unexplored。如果有邻居:选一个 $P_k$,把 $M$ 传给它(继续深入)。
如果没有邻居(死胡同):给
parent回复<parent>(回溯)。
收到 $M$(非第一次,拒绝):
如果你已经有爹了,又收到 $M$,说明遇到了回边(Back-edge)。
立即行动:给发送者回复
<reject>。
收到反馈(
<parent>或<reject>):这是 Alg 2.2 没有的逻辑重点。因为是串行的,你必须等待刚才那个邻居给你反馈,才能决定下一步做什么。
如果收到
<parent>:说明那个邻居认你做爹了,把它加入children。后续动作:
看看
unexplored还有没有人?有:选下一个,发 $M$(尝试另一条路)。
没有(所有邻居都试过了):
如果你不是根:给你的
parent发<parent>(任务完成,把控制权交还给父亲,回溯)。如果你是根:Terminate(全网遍历结束)。
3. 复杂性分析(必考点)
PPT 第 20 页给出了结论,这里需要特别注意与之前算法的对比。
| 指标 | Alg 2.2 (泛洪/BFS) | Alg 2.3 (DFS) | 为什么? |
|---|---|---|---|
| 消息复杂度 | $O(m)$ | $O(m)$ | 每条边最多跑 4 次消息:去($M$)、回(Reject/Parent),双向各一次。 |
| 时间复杂度 | $O(D)$ (同步) | $O(m)$ | 这是巨大的性能下降! |
为什么时间复杂度是 $O(m)$?
因为是串行的。
Alg 2.2 是“千军万马避白袍”,所有节点同时跑。时间取决于路程(直径 $D$)。
Alg 2.3 是“孤胆英雄”,同一个时刻网络里只有一个 $M$ 在跑(逻辑上)。它要走遍几乎每一条边,试探每一个节点。所以时间与边数 $m$ 成正比。
注:PPT最后提到时间复杂度太差,可降至 $O(n)$,那是针对特殊优化的算法,但基础 DFS 就是 $O(m)$。
4. 考试中的“坑”与改进
PPT 提到了两个有趣的问题,可能是简答题的考点:
“如何改进使 msg 的复杂性不是 4m?”
思路:现在的逻辑是,即使我知道邻居 $P_j$ 是我的父亲,轮到我遍历邻居时,我可能还会傻乎乎地把 $M$ 发给 $P_j$,然后 $P_j$ 给我回
<reject>。改进:在初始化
unexplored时,或者在确定parent时,直接把parent从unexplored集合里踢出去。这样就不会“父子互探”浪费消息了。
引理 2.10:在异步模型下构造 DFS 树
考点:Alg 2.2 在异步下造不出 BFS 树,但 Alg 2.3 在异步下能造出 DFS 树。
原因:因为 Alg 2.3 严格依赖“应答-发送”的握手机制来控制流程。它不依赖时钟,而是依赖“收到回复”这个事件。无论消息飞多慢,节点都会死等回复,所以顺序绝对不会乱。
总结
Alg 2.3 就是一个由 unexplored 集合驱动的、严格串行的、带有回溯机制的令牌传递过程。记住“一次只发给一个邻居,等回复后再发下一个”,你就掌握了它的精髓。
Alg 2.4 不指定根时构造DFS生成树
这比之前的算法更进了一步:之前的算法假设大家已经有一个共同的领导(根 $p_r$),而现在的算法要求大家一边选领导,一边组建团队(构造树)。
这是一个典型的“优胜劣汰”过程。以下是核心知识点梳理:
1. 核心思想:竞选与吞并
因为没有指定的根,所以每个节点都可以自发醒来,试图成为根(Leader)。
ID 为王:每个节点都有一个唯一的 ID。ID 越大,优先级越高(素质分越高)。
扩散机制:节点试图将自己的 ID 像病毒一样传给邻居。
吞并规则:当两个不同的 ID 在某个节点相遇时,发生“战斗”。大 ID 吞并小 ID。
2. 三大冲突处理规则 (必考逻辑)
当节点 $P_i$(当前信奉领袖 $leader_i$)收到邻居 $P_j$ 发来的新领袖 ID $y$ 时,会进行比较:
| 情况 | 比较结果 | 含义 | 动作 (Action) |
|---|---|---|---|
| 1. 吞并 | $y > leader_i$ | 外来的和尚好念经。新领袖更强。 | 改换门庭: 1. 更新 leader 为 $y$。2. 认 $P_j$ 为父。 3. 重置 unexplored(关键!为了新领袖,要把所有邻居重新访问一遍)。4. 继续帮 $y$ 传播。 |
| 2. 忽略 | $y < leader_i$ | 哪来的小喽啰。我现在的领袖更强。 | 无视: 不理会 $y$,停止 $y$ 在这边的传播。等着自己的强领袖 $leader_i$ 的消息传过去覆盖它。 |
| 3. 环路 | $y = leader_i$ | 大水冲了龙王庙。是一家人。 | 回退: 说明遇到了回边(Back-edge)。发送 <already>(相当于 <reject>),告诉对方“我也归顺 $y$ 了,别传了”。 |
3. 算法流程 (Alg 2.4)
这个算法其实是 Alg 2.3 (DFS) 的多实例并发版。
初始化:醒来的节点把自己当成 Leader (
leader := id),开始尝试构建 DFS 树。被征服 (Upon receiving higher ID):
一旦收到比自己当前 Leader 更大的 ID,立马“叛变”。
特别注意:
unexplored被重置为all neighbors except Pj。这意味着之前的 DFS 进度作废,为了新 Leader,必须重新扫描邻居。
终止:
只有最终的真·Leader(全网 ID 最大的那个节点 $P_m$)会显式终止。
其他节点只是处于“等待命令”的状态(在算法文本中没有显式 terminate,但逻辑上它们已成为树的一部分)。
4. 复杂性分析 (性能代价)
因为存在“推倒重来”的过程,这个算法的开销比指定根的 DFS 大得多。
消息复杂度 (Msg Complexity):$O(n \cdot m)$
最坏情况下,每个节点都自发唤醒,试图构建一棵树。
每次构建 DFS 树需要 $O(m)$ 的消息。
$n$ 个节点轮番上阵(从小到大被吞并),总共就是 $n \times O(m) = O(nm)$。
注:PPT 中提到的 $O(pn^2)$ 是另一种基于节点数的估算,通常考试答 $O(nm)$ 更为准确且通用。
时间复杂度:$O(m)$
- 尽管消息量大,但从时间上看,最终那个最大的 ID ($P_m$) 像在走迷宫一样遍历全网,其路径长度受限于边数 $m$。
5. 正确性证明思路 (Lemma 2.12)
最终赢家:全网 ID 最大的节点 $P_m$ 最终一定会醒来(自发或被叫醒)。
无敌性:$P_m$ 的 ID 传播出去后,因为它是最大的,根据规则 ①,没有任何节点能拒绝它,也没有任何节点能覆盖它。
结果:最终所有节点的
leader都会变成 $P_m$,且形成的父子关系构成一棵以 $P_m$ 为根的 DFS 树。
总结
Alg 2.4 就是“谁大听谁的”。
如果不指定根,代价就是消息量暴增(从 $O(m)$ 涨到 $O(nm)$)。
考试坑点:当节点 $P_i$ 收到更大的 ID 时,不仅要换爹,还要重置
unexplored。这意味着它以前做过的探索工作全部白费,必须为新主子重新跑腿。
匿名环
这段 PPT(§3.2)深入探讨了匿名环(Anonymous Ring)中的核心理论限制,特别是“不可能三角”——在匿名、确定性、对称的网络中,无法完成领导者选举。
这部分内容是对刚才“破对称问题”的严谨数学形式化证明。以下是详细解读:
1. 环的形式化模型 (The Ring Model)
首先定义了我们研究的对象是什么:
结构:$n$ 个处理器 $P_0, \dots, P_{n-1}$ 围成一个圈。
方向与标号:
每个节点都有“左手”和“右手”。
左 (Left):顺时针方向,连接 $P_{i+1}$,信道标号为 1。
右 (Right):逆时针方向,连接 $P_{i-1}$,信道标号为 2。
注:下标运算都是模 $n$ 的(例如 $P{n-1}$ 的“左”是 $P_0$)。_
为什么研究环?:因为环是最简单的拓扑结构。如果在环上都做不到(下界),那么在更复杂的任意网络上也做不到。
2. 关键术语定义 (必考概念)
这里定义了三个维度的概念,考试中经常会让你辨析:
| 术语 | 定义 | 通俗理解 |
|---|---|---|
| 匿名 (Anonymous) | 处理器没有唯一标识符 (UID)。 | 大家都是克隆人,长得一模一样,代码一模一样。 |
| 一致性 (Uniform) | 算法不知道处理器总数 $n$。 | 代码里没有硬编码 if (n == 5) 这样的逻辑。同一份代码能在任意大小的环上跑。 |
| 非一致性 (Non-uniform) | 算法已知处理器总数 $n$。 | 代码里可以利用 $n$。比如针对 3 个人的环写一种代码,针对 4 个人的写另一种。 |
3. 不可能性定理 (Impossibility Theorem)
PPT 的核心结论是:对于环系统,不存在匿名的领导者选举算法。
为了证明这一点,PPT 采用了一种“杀鸡用牛刀”的反向策略——归约法:
逻辑链条:
如果我们在最有利的条件下(同步 + 非一致性/已知 $n$)都做不到。
那么在更困难的条件下(异步 + 一致性/未知 $n$)肯定更做不到。
证明目标:我们只需要证明“同步环系统中不存在匿名的、非一致性的领导者选举算法”即可。
4. 证明过程:对称性的诅咒
这个证明非常经典,利用了数学归纳法来证明所有节点永远保持“步调一致”。
引理 3.1 (Lemma 3.1)
在匿名同步环的所有容许执行中,对于每一轮 $k$,所有处理器的状态在第 $k$ 轮结束时都是完全相同的。
证明思路 (归纳法):
基础情况 ($k=0$):
- 一开始大家都是初始状态。因为是匿名的,也没有 UID,所以大家的初始状态 $S_0$ 是一模一样的。
归纳假设:
- 假设在第 $k-1$ 轮结束时,所有人的状态都一样,设为 $S_{k-1}$。
归纳步骤 (推导第 $k$ 轮):
因为大家状态 $S_{k-1}$ 一样,运行的代码也一样(匿名性质)。
所以,大家决定发出的消息也一样:所有人都向左发消息 $m_L$,向右发消息 $m_R$。
接收阶段:
每个人从左边收到的,是邻居发出的 $m_R$。
每个人从右边收到的,是邻居发出的 $m_L$。
状态转换:
每个人的输入是 ${ \text{收到 } m_R, \text{收到 } m_L }$。
每个人的当前状态是 $S_{k-1}$。
根据状态机函数 $NextState(S_{k-1}, \text{Input})$,大家算出来的新状态 $S_k$ 必然还是一模一样的。
定理 3.2 (结论)
根据引理,无论跑多少轮,所有节点的状态永远相同。
矛盾点:领导者选举算法要求最终有一个节点进入“Leader”状态,其他节点进入“Follower”状态。
推论:如果节点 A 变成了 Leader,那么根据对称性,节点 B、C、D… 也必须同时变成 Leader。
结果:大家都成了 Leader,这违反了“唯一性”,选举失败。
总结
这段 PPT 实际上判了匿名网络的“死刑”(在确定性算法下):
只要网络是匿名且对称(环)的,且是同步运行的,所有节点就像在这个“无限镜廊”里照镜子,你做什么动作,镜像就做什么动作,永远无法选出唯一的那个“王”。
要想打破这个僵局,必须引入:
唯一 ID(这就是为什么之前的 Alg 2.4/2.5 能成功)。
随机化(大家掷骰子,赌运气打破对称)。
非对称的拓扑结构(如果不是环,是对称性较差的图,可能不需要 ID)。
异步环 LCR 算法:一个 $O(n^2)$ 算法
这段 PPT 进入了异步环(Asynchronous Ring)的深水区,重点讲解了著名的 LCR 算法(由 Le Lann, Chang 和 Roberts 提出)。
这是分布式算法中“领导者选举”最基础、最经典的算法。为了让你能轻松理解,我将这段内容拆解为三个部分:前提设定、算法机制(大鱼吃小鱼)、以及复杂度分析(为什么最坏是 $O(n^2)$)。
1. 前提设定:告别匿名,引入 ID
上一节(§3.2)刚证明了“匿名环选不出领导”。既然匿名不行,这节课(§3.3)一开始就引入了唯一标识符(Unique ID)。
假设:每个处理器 $P_i$ 都有一个独特的 $id_i$。
一致性(Uniform)算法的新定义:
算法代码里不知道环的总人数 $n$。
只要给每个 ID 配上对应的状态机,算法就能跑。这意味代码是通用的,不管你是 3 个人的环还是 100 万人的环,代码逻辑都一样。
2. LCR 算法核心:大鱼吃小鱼
LCR 算法的思想非常简单粗暴,就是“优胜劣汰,强者生存”。这是一个单向通信算法(只向左发,从右收)。
规则逻辑:
发起(Start):每个节点醒来后,把自己的 ID 写在纸条上,传给左边的邻居。
筛选(Filter):当你收到右边传来的 ID(设为 $id_{recv}$)时,和自己的 ID($id_{self}$)比一比:
吞噬(Discard):如果 $id_{recv} < id_{self}$ —— “你比我弱,没资格当领导”。直接把纸条撕了,不转发。
转发(Forward):如果 $id_{recv} > id_{self}$ —— “你比我强,请通过”。把纸条传给左边的邻居。
加冕(Win):如果 $id_{recv} == id_{self}$ —— “我的纸条转了一圈回来了!”。说明我比环上所有人都强(否则早被截胡了)。宣布自己是 Leader。
终止(Terminate):新 Leader 产生后,发一个
<Leader>消息转一圈,通知其他人“选举结束,我是老大”。
3. 复杂度分析:为什么是 $O(n^2)$?
这是 PPT 的重点,也是考试计算题的高频考点。
A. 最好情况 (Best Case)
场景:ID 顺着传输方向递增排列(例如 $1 \to 2 \to 3 \to \dots \to n$)。
每个人的 ID 传给左边,立刻遇到比自己大的,立刻被吞噬。只有最大的那个 $n$ 能跑完一圈。
消息数:$O(n)$。
B. 最坏情况 (Worst Case)
PPT 第 18 页专门画图分析了这个情况。
场景:ID 逆序排列(或者说按传输方向递减)。
假设环是 $P_0, P_1, \dots, P_{n-1}$。
传输方向是 $P_0 \to P_{n-1} \to \dots \to P_1 \to P_0$(也就是 $P_0$ 发给 $P_{n-1}$,虽然 PPT 文字说是发给左邻居,但为了凑最坏情况,只要理解为每个 ID 都要跑很远才会被拦住)。
具体过程:
最大 ID ($n-1$):无人能挡,跑满全场 $n$ 步。
第二大 ID ($n-2$):它能跑过 $n-3, n-4 \dots 0$,直到撞上 $n-1$ 才被吃掉。跑了 $n-1$ 步。
第三大 ID ($n-3$):跑了 $n-2$ 步。
…
最小 ID (0):刚出门就被邻居吃掉,跑 1 步。
计算总和:
\[Total = n + (n-1) + (n-2) + \dots + 1 = \frac{n(n+1)}{2} \approx \Theta(n^2)\]
4. 总结
这段 PPT 实际上在告诉你:
LCR 算法是基于 ID 的,解决了匿名不可能性的问题。
它的逻辑是单向的“过滤”:只有比当前节点大的 ID 才能通过。
它的效率不够高:虽然平均情况下是 $O(n \log n)$(PPT 还没讲到,但通常会提到),但在最坏情况下会退化成 $O(n^2)$。
接下来的章节($O(n \log n)$ 算法):
因为 $O(n^2)$ 太慢了,后面的 HS 算法(Hirschberg-Sinclair)会引入双向通信和分阶段探测,把复杂度降到 $O(n \log n)$。这节课是给后面的优化算法做铺垫的“反面教材”。
异步环 HS 算法:一个 $O(n\lg n)$ 算法
这段内容介绍的是著名的 Hirschberg-Sinclair (HS) 算法。这是一个用于异步环形网络的领导者选举算法,它通过“更聪明”的消息传递策略,将消息复杂度从 LCR 算法的 $O(n^2)$ 降低到了 $O(n \log n)$。
以下是基于你提供的 PPT 内容整理的算法核心解析:
1. 核心思想与概念
改进思路:LCR 算法的问题在于每个节点都试图把 ID 传遍全网。HS 算法引入了阶段 (Phase) 和邻居 (Neighborhood) 的概念,采用“双向试探”的策略。
k-邻居 (k-neighborhood):
一个处理器 $P_i$ 的 k-邻居是一个集合,包含环上距离它至多为 $k$ 的所有节点。
集合大小为 $2k+1$ 个处理器(左边 $k$ 个 + 右边 $k$ 个 + 自己)。
分阶段执行:
算法按阶段 $l$ ($l=0, 1, 2, …$) 执行。
在第 $l$ 阶段,节点试图成为其 $2^l$-邻居 范围内的临时领袖 (Temporary Leader)。
只有在第 $l$ 阶段获胜(成为临时领袖)的节点,才有资格进入第 $l+1$ 阶段。这意味着随着阶段增加,竞争者(发起者)越来越少。
2. 算法详细流程 (Alg 3.1)
算法通过 probe(探测)和 reply(应答)两种消息来实现。
消息结构
probe 消息包含三个关键域:<probe, id, l, hop>。
id:发起者的标识符。l:当前阶段数。hop:跳步计数器,初始为 0,每转发一次加 1。
执行步骤
初始阶段 (Phase 0):
- 每个节点醒来后,向左和向右两个方向各发送一个距离为 $2^0=1$ 的
probe消息。
- 每个节点醒来后,向左和向右两个方向各发送一个距离为 $2^0=1$ 的
探测阶段 (Phase $l$) 的转发规则:
当节点收到来自邻居的
<probe, j, l, d>时,比较消息中的 $j$ 和自己的 $id$:如果 $j = id$:说明消息转了一圈回来了(且未被“没收”),终止算法,宣布自己是 Leader。
如果 $j < id$:说明发起者比自己弱(或者比路径上遇到的某个节点弱),没收 (Confiscate) 此消息,不再转发。
如果 $j > id$:说明发起者比自己强,继续判断跳数:
若
hop($d$) $< 2^l$:帮它转发到下一个邻居,hop加 1。若
hop($d$) $\ge 2^l$:说明已经到达了该阶段探测的边界(最后一个邻居)。此时不再向前发,而是向回发送reply消息给发起者。
晋级逻辑 (进入下一阶段):
发起者 $P_i$ 等待回复。
如果 $P_i$ 从左和右两个方向都收到了
reply,说明它在当前 $2^l$-邻域内是 ID 最大的。于是它成为该阶段的临时领袖,进入阶段 $l+1$,向两个方向发送距离为 $2^{l+1}$ 的新
probe。
3. 复杂性分析 (为什么是 $O(n \log n)$?)
正确性
最大 ID 的节点发出的
probe消息永远不会被没收(因为它比所有人都大)。它最终会通过所有阶段,直到消息绕环一周返回自己,成为唯一的 Leader。
消息复杂度证明
算法的总消息数 = 所有阶段的消息数之和。
单阶段消息上限:
- 在第 $l$ 阶段,每个发起者最多发送 $4 \times 2^l$ 条消息(向左去 $2^l$ 回 $2^l$ + 向右去 $2^l$ 回 $2^l$)。
发起者数量上限 (Lemma 3.3):
这是一个关键引理:在第 $k$ 阶段结束时,存活的临时 Leader 数量至多为 $n/(2^k+1)$。
原因:任何两个在第 $k$ 阶段存活的临时 Leader,它们的 $2^k$-邻域是不能重叠且还要有个缓冲区的(因为如果重叠或相邻,其中一个较小的 ID 就会被另一个较大的 ID 在探测时“击杀”)。所以每 $2^k+1$ 个节点里最多只有 1 个幸存者。
总消息数计算 (Theorem 3.4):
由于每次探测距离翻倍 ($2^l$),总阶段数约为 $\log n$。
第 $l$ 阶段的总消息数 $\approx$ (发起者数量) $\times$ (每人发的消息数)
\[\approx \frac{n}{2^{l-1}} \times (4 \cdot 2^l) = O(n)\]每一阶段的消息复杂度都是线性的 $O(n)$。
总共有 $\log n$ 个阶段,再加上最终 Leader 发送的 terminate 消息(共 $4n$ 条)。
因此,总消息复杂度 = $\sum O(n) = O(n \log n)$。
时间复杂度:
只盯着最终 Leader 看,因为它最终成为 Leader 了算法才能结束。
它从局部 Leader 走向最终 Leader,需要经过 $\lg(n − 1)$ 个阶段,在第 $l$ 个阶段, 它至少需要经过 $2*2l$ 个时刻才能确认自己还保持着局部 Leader 的身份,最终再发送自己是终极 Leader 的通知。
所以时间复杂度是 $\sum_{l=0}^{\lg(n−1)} 2 \cdot 2l + n = 3n − 4 = O(n)$。
总结
HS 算法通过指数级扩大探测范围 ($2^l$) 和 双向确认机制,快速淘汰了大量较小的 ID 节点,避免了 LCR 算法中每个节点都可能跑长途的情况,从而实现了 $O(n \log n)$ 的消息复杂度。
异步环:下界 $\Omega(n \log n)$
这段 PPT 的内容非常硬核,是在严格证明为什么在异步环中,任何基于一致性(Uniform)算法的领导者选举,其消息复杂度(Message Complexity)的下界(Lower Bound)一定是 $\Omega(n \log n)$。
这意味着:不管你设计多么聪明的算法(比如之前的 HS 算法已经做到了 $O(n \log n)$),你也无法做得比 $\Omega(n \log n)$ 更好(例如做到 $O(n)$ 是不可能的)。
证明采用了构造法和递归的思想。以下是通俗易懂的证明逻辑拆解:
1. 核心思路:构造“最坏情况” (Constructive Proof)
证明的目标是构造一个特定的环 $R$ 和一个特定的调度(Schedule),使得算法在这个环上必须发送足够多的消息。
策略:分治法逆向构造
拆解:假设我们已经知道对于大小为 $n/2$ 的环,最坏情况下至少要发 $M(n/2)$ 条消息。
拼接:我们把两个大小为 $n/2$ 的环($R_1$ 和 $R_2$)剪断,然后首尾相连拼成一个大小为 $n$ 的大环 $R$。
欺骗:因为算法是一致性的(不知道环的大小),且系统是异步的(消息可以无限延迟),我们可以先让 $R_1$ 和 $R_2$ 里的节点以为自己还在原来的小环里,各自跑完所有的“冤枉路”(耗费 $2 \times M(n/2)$)。
强迫通信:最后,我们连通这两个环的接口,迫使为了选举出全局唯一的 Leader,消息必须穿过接口传遍半个环,产生额外的 $\Theta(n)$ 消息。
最终得到递归公式:
\[M(n) = 2 M(n/2) + \Theta(n)\]这个递归式的解正是 $M(n) = \Theta(n \log n)$。
2. 关键概念:开调度 (Open Schedule)
定义:如果在某个调度 $\sigma$ 中,环上存在一条边 $e$,在这个调度执行期间,没有任何消息通过这条边(既没发过去也没发过来),那么这个调度叫开调度,边 $e$ 叫开边。
作用:开边就像是一个“切口”。因为没有消息通过它,我们可以随时把这个切口切开,把它和其他环的切口接起来,而不会破坏原本已经在环内部发生的事件逻辑(因为两边的节点本来就没有通过这条边交流)。
3. 证明三步曲 (The 3-Step Construction)
假设我们要证明大小为 $n$ 的环至少需要 $M(n)$ 的消息。我们假设归纳假设对 $n/2$ 成立。
Step 1: 各自为政 (The Independent Executions)
取两个大小为 $n/2$ 的环 $R_1$ 和 $R_2$。
让它们分别执行各自的“开调度” $\sigma_1$ 和 $\sigma_2$。
消息量:$R_1$ 贡献 $M(n/2)$, $R_2$ 贡献 $M(n/2)$。
状态:此时,我们将 $R_1$ 的开边 $e_1$ 和 $R_2$ 的开边 $e_2$ 剪开,用两条新边 $e_p$ 和 $e_q$ 将它们连成大环 $R$。因为 $\sigma_1$ 和 $\sigma_2$ 执行期间没有消息通过开边,所以节点根本不知道环已经变了。它们以为自己还在小环里。
Step 2: 保持静止 (Silent Configuration)
我们运行算法直到系统进入“静止”状态($\sigma_3$)。
所谓静止,就是除非有新的消息从外部(即通过连接边 $e_p$ 或 $e_q$)传进来,否则内部节点不再自发产生消息。
目的:把系统推到一个临界点,大家都在等消息。
Step 3: 强迫通信 (Forcing Communication)
这是最精彩的一步(Claim 3.8)。
假设全局最大的 Leader ID 在 $R_1$ 里。
此时 $R_2$ 里的所有节点都还不知道 Leader 是谁(因为 Step 1 里它们只看到了 $R_2$ 内部的局部 Leader)。
根据题目要求(所有节点必须知道 Leader ID),必须有消息从 $R_1$ 穿过连接边 ($e_p$ 或 $e_q$) 传给 $R_2$。
技巧:我们在 $e_p$ 和 $e_q$ 两条连接边中,只开放一条(比如 $e_p$),另一条继续保持“断开/阻塞”状态。
因为 $R_2$ 里的节点要得知 Leader,消息必须从 $e_p$ 进来,一路传遍 $R_2$ 的大部分节点。
这会迫使产生额外的 $\approx n/4$ 条消息($\Theta(n)$ 量级)。
此时,另一条边 $e_q$ 依然没有任何消息通过,所以对于大环 $R$ 来说,它依然是一个“开调度”($e_q$ 是开边)。这保证了归纳法可以继续向上推导到 $2n$。
总结
$\Omega(n \log n)$ 下界并非巧合,而是由递归结构决定的。
证明核心:利用异步延迟(让消息在切口处排队不发)和一致性(节点无法区分环的大小),我们可以把两个“被欺骗”的子环拼起来,让它们先白白浪费消息选出局部 Leader,最后再强迫它们通信选出全局 Leader。
考试重点:理解 $M(n) = 2M(n/2) + O(n)$ 这个递归关系的物理含义。
$2M(n/2)$: 两个子环内部的内耗。
$O(n)$: 跨环通信的额外开销。
同步环 Non-uniform Alg:上界 $O(n)$
在异步环中,我们已经证明了消息复杂度的下界是 $\Omega(n \log n)$。但是,一旦引入同步(Synchronous)假设(所有节点有时钟,按轮次 Step-by-Step 执行),游戏规则就变了。我们可以利用“时间”来传递信息,从而将消息复杂度降低到惊人的 $O(n)$。
以下是对这段 PPT 核心内容的详细解读,特别是那个 $O(n)$ 算法的原理及最后两个问题的答案。
1. 同步模型的潜力:用“沉默”传递信息
在异步系统中,如果没有收到消息,你不知道对方是“没发消息”还是“消息还在路上”。
但在同步系统中,如果在特定轮次没有收到消息,你可以确定地知道:“对方没发消息”。
这使得我们可以设计出“时间换消息”的策略:
上界(好消息):存在消息复杂度为 $O(n)$ 的算法。这是最优的(因为至少要通知一圈人,少于 $n$ 不可能)。
代价(坏消息):虽然省了消息,但时间复杂度可能会变得非常非常大,它不再只依赖于节点数 $n$,而是依赖于 ID 的数值大小。
2. 非均匀算法 (Non-uniform Algorithm) 详解
PPT 介绍的第一个算法是“非均匀”的,意味着所有节点必须知道环的大小 $n$,且必须同时启动。
算法逻辑:按 ID 值排队发言
这个算法的核心思想是:“谁 ID 小,谁先说话;没人说话,就进入下一轮。”
算法将时间划分为若干个 阶段 (Phase),每个阶段包含 $n$ 个轮次 (Round)。
Phase 0(第 $1$ 到 $n$ 轮):
只有 ID = 0 的节点有资格行动。
如果网络中有 ID 为 0 的节点,它发送消息转一圈,当选 Leader。
如果没有,所有人保持沉默,空转 $n$ 轮。
Phase 1(第 $n+1$ 到 $2n$ 轮):
只有 ID = 1 的节点有资格行动。
如果有,发消息,当选。
如果没有,继续沉默。
…
Phase i(第 $n \cdot i + 1$ 到 $n \cdot i + n$ 轮):
- 只有 ID = i 的节点有资格行动。
执行过程示例
假设环上有 3 个节点,ID 分别为 ${5, 10, 100}$,$n=3$。
Phase 0, 1, 2, 3, 4:这些阶段对应的 ID 都不存在。网络一片死寂,没有任何消息传输。大家只是在默默数轮次。
Phase 5:
轮到了 ID 为 5 的节点。
它发现“现在是 Phase 5,而且我的 ID 是 5”。
它发送一条消息
<Leader, 5>。这条消息花 $n$ 轮转一圈。
其他节点收到消息后,知道 Leader 产生了,算法终止。
3. 复杂度分析
这是这个算法最极端的特征:
消息复杂度 (Msg Complexity):$O(n)$
为什么? 因为在前面的所有空阶段,大家都在“沉默”,发送的消息数为 0。
只有当最小 ID 的那个节点醒来时,它发了一条消息,这条消息被转发 $n$ 次回到原点。
总消息数 = $n$。非常完美。
时间复杂度 (Time Complexity):$O(n \cdot \min(ID))$
为什么? 假设最小的 ID 是 $m$。
我们需要干等前 $m$ 个阶段(Phase 0 到 Phase $m-1$),每个阶段耗时 $n$ 轮。
总轮数 $\approx n \times (m+1)$。
缺陷:如果 ID 的数值非常大(比如最小 ID 是 $2^{64}$),即使 $n$ 很小,这个算法也要跑几十亿年。这在实际中是不可接受的,但在理论上证明了 $O(n)$ 消息是可能的。
4. 回答 PPT 最后的问题 (考点解析)
PPT 最后提出了两个关键问题,这是理解该算法设计的核心:
问题 ①:为什么 id 为 i 的节点要在 phase i 发 msg?
答案:为了利用时间同步来区分身份,从而避免显式的 ID 比较。
解析:在匿名或互不知晓 ID 的网络中,节点无法知道谁的 ID 最小。通过“约定”每个 ID 只能在特定的时间窗口(Phase)发送,我们实现了一种隐式的选举:
如果 Phase $i$ 有人发声,说明 $i$ 就是最小值(因为比 $i$ 小的 Phase 都没声音)。
如果大家乱发,就必须像 LCR 算法那样通过消息碰撞($O(n^2)$)来比大小。
这里是用等待时间换取了消息数量的减少。
问题 ②:为什么每个 phase 要 n 轮?
答案:为了保证较低优先级的节点能收到较高优先级节点的“胜选通知”,从而停止发送自己的消息。
解析:
假设最小 ID 是 $i$,次小 ID 是 $i+1$。
在 Phase $i$ 开始时,节点 $i$ 发出消息。这条消息要在环上走一圈通知所有人“Leader 已经产生了(是 $i$),大家可以洗洗睡了”,这需要 $n$ 轮。
如果 Phase $i$ 的长度小于 $n$(比如只有 $n/2$ 轮):
节点 $i$ 的消息才走到一半,Phase $i+1$ 就开始了。
此时节点 $i+1$ 还没收到通知,以为前面没人,它也会发出自己的消息。
这样网络里就有了多条消息,消息复杂度就超过 $O(n)$ 了。
因此,必须留足 $n$ 轮,确保高优先级的抑制信号能覆盖全网。
总结
这个算法是一个极端的理论模型:
优点:消息极少 ($n$)。
缺点:时间极长(依赖 ID 值),且需要知道 $n$(非一致性)。
下一步:PPT 后面提到的“均匀算法(弱同步)”将会尝试解决不知道 $n$ 或者不同时启动的问题,通常会通过指数增长的方式来逼近。
同步环 Uniform Alg:上界 $O(n)$
与之前的 Non-uniform 算法相比,它有两个重大突破:
无需知道环大小 $n$。
弱同步模型:节点可以在任意轮次醒来(自发或被叫醒),不需要像阅兵一样同时开始。
这个算法非常巧妙,它利用了 “变量速度” (Variable Speeds) 的概念——让小 ID 的消息跑得快,大 ID 的消息跑得慢。
以下是详细解读:
1. 核心思想:不同 ID,不同速度
这是整个算法的灵魂。
唤醒阶段 (Phase 1 / Wake-up Msg):
当一个节点刚醒来(不管是自然醒还是被叫醒),它发出的第一条消息叫“唤醒消息”。
速度:正常速度(每轮走 1 步)。
目的:赶紧去把所有还在睡觉的节点叫醒。如果一个节点在收到唤醒消息之前还没醒,那它就丧失了选举资格,只能当个“传声筒” (Relay)。只有那些自发醒来的才有资格当 Leader。
延迟阶段 (Phase 2 / Delayed Msg):
一旦消息到达了一个已经醒着的节点,它就变身了。
规则:源自 ID 为 $i$ 的消息,在每个节点转发前,必须滞留 $2^i - 1$ 轮。
速度:每 $2^i$ 轮才能走一步。
结果:ID 越小,$2^i$ 越小,跑得越快;ID 越大,$2^i$ 越大,跑得越慢(几乎是在爬)。
2. 算法流程 (Alg 3.2)
初始化:
waiting集合:用来存放那些正在“坐牢”(被延迟)的消息。asleep:标记是否醒来。
收到消息后:
如果之前在睡觉:醒来 (
asleep=false)。如果是被消息叫醒的,min=infinity(失去资格)。没收规则:
如果收到的 ID
m < min(比我见过的最小 ID 还小):说明这是个强力候选人,更新min=m,并把这个消息加入waiting列表开始延迟计时。如果收到的 ID
m > min:吞并(Swallow)。比我见过的最强者还弱,没必要传了。如果
m = id:我是 Leader!
发送规则:
- 检查
waiting里的消息。如果某个消息已经滞留够了 $2^m - 1$ 轮,把它放出来发给左邻居。
- 检查
3. 复杂性分析(为什么是 $O(n)$?)
证明过程非常繁琐,PPT把它分成了三类消息来算总账。
第一类消息 (唤醒消息):
这是在第一阶段(大家都还没醒透)跑的消息。
上限:$n$。
原因:每个节点最多只转发 1 个唤醒消息。因为一旦转发过,它就醒了,之后的都是第二阶段消息。
第二类消息 (早期淘汰赛):
定义:在最终 Leader 的消息进入第二阶段之前,其他输家发出的消息。
上限:$n$。
计算技巧:利用级数求和。因为 ID 为 $i$ 的消息每 $2^i$ 轮才发一次,所以在有限的 $n$ 轮里,它发的次数很少。所有 ID 加起来不超过 $n$。
第三类消息 (终局之战):
定义:在最终 Leader 的消息进入第二阶段之后,所有还在跑的消息。
上限:$2n$。
计算技巧:同样利用速度差异。Leader (ID=0) 跑得飞快,那些大 ID (ID=k) 还在慢吞吞地爬。在 Leader 跑完一圈的时间里,大 ID 根本跑不了几步。
公式推导结果显示总和 $\le 2n$。
总消息复杂度:$n + n + 2n = 4n$,即 $O(n)$。
4. 思考题解答
PPT 最后留了两个思考题,也是考试可能的简答题:
(1) 为何非唤醒 msg 要延迟 $2^i - 1$ 轮?
答案:为了制造速度差,从而抑制大 ID 的消息传播。
解析:如果不延迟,所有 ID 的消息都疯跑,那就是 $O(n^2)$(像 LCR 算法)。通过让大 ID 延迟指数级时间,我们可以保证小 ID(Leader)在跑完一圈宣布胜利之前,大 ID 们只跑了很短的距离就被 Leader 的消息“追尾”并消灭了。这直接把消息量从 $n^2$ 压到了 $n$。
(2) 如何修改算法 3.2 来改善时间复杂度?
现状:当前时间复杂度是 $O(n \cdot 2^{min_id})$。如果最小 ID 很大,时间会很久。
改进思路:
方案 1:引入 Relay(中继)状态
核心思路: “只有竞争者需要思考(延迟),路人只管传话(不延迟)。”
原算法的问题:
PPT 中提到,第二阶段的消息在每一个节点(无论该节点是否醒着,是否参与竞选)都要延迟 $2^i-1$ 轮。
这就好比:不仅候选人要犹豫,连负责送信的邮递员每走一步都要在原地等上 $2^i$ 分钟,这显然浪费时间。
改进逻辑:
我们将节点分为两类:
参与者 (Participant):自发唤醒的节点,试图成为 Leader。
中继者 (Relay):被唤醒的节点,或者已经淘汰的节点。
新规则:消息只有在经过参与者时才需要延迟 $2^i$ 轮(为了让更小的 ID 有机会追上来);而在经过中继者时,立刻转发(只消耗 1 轮传输时间)。
复杂度推导:
假设总节点数为 $n$,其中有 $N$ 个是自发唤醒的参与者($N \le n$)。
消息绕环一圈,会经过 $N$ 个参与者,和 $n-N$ 个中继者。
原时间:$n \times 2^i$ (所有 $n$ 个点都延迟)。
新时间:$N \times 2^i + (n - N) \times 1$。
当 $N$ 很小时(参与者少),提升非常明显。
方案 2:修改延迟函数 $f(id)$
核心思路: “不要以此消彼长,改用更温和的刹车(延迟)。”
这是一种典型的 时间-空间权衡。
原算法的权衡:
延迟函数 $f(id) = 2^{id}$ (指数级)。
效果:跑得慢的(大 ID)被跑得快的(小 ID)迅速消灭。
结果:消息极少 $O(n)$,但时间极慢 $O(n \cdot 2^{id})$。
改进逻辑:
如果我们把延迟函数改成线性的,比如 $f(id) = c \cdot id$。
时间复杂度:降低了。最坏时间变成了 $O(n \cdot id)$,比指数级快多了。
**代价:提高了 msg 复杂度”。
因为:延迟变短了 -> 大 ID 跑得快了 -> 在被小 ID 追上之前,大 ID 跑的距离更远了 -> 网络中被转发的消息总数增加了。
同步环 基于比较的算法:下界 $\Omega(n \log n)$
这一节的内容非常深入且理论化,旨在论证为什么“只要算法是基于比较的(Comparison-based),无论你怎么设计,其消息复杂度下界一定是 $\Omega(n \log n)$”。
这直接打破了之前同步算法可以做到 $O(n)$ 的幻想——那个 $O(n)$ 算法之所以能做到,是因为它作弊了(它利用了 ID 的数值来编码时间,不仅仅是比较 ID 大小)。如果我们禁止这种作弊行为,回到纯粹的比较(谁大谁小),那么 $\Omega(n \log n)$ 这个魔咒又回来了。
这段 PPT 充满了形式化定义和引理。为了帮助你理解,我将核心逻辑拆解为以下几个关键步骤:
1. 核心定义:什么是“基于比较的算法”?
PPT 花了很大篇幅(第 25-29 页)来定义这个概念。
直观理解:算法只关心 ID 的相对大小(谁比谁大),而不关心 ID 的具体数值(是 100 还是 1000)。
形式化定义:
序等价 (Order Equivalent):如果两个环 $R_1$ 和 $R_2$ 的节点 ID 排列顺序是一样的(例如 $R_1=[10, 50, 20]$,$R_2=[1, 5, 2]$,虽然数值不同,但 $10<20<50$ 和 $1<2<5$ 的相对关系一致),那么这两个环是“序等价”的。
行为相似 (Similar Behavior):如果两个环序等价,那么算法在这两个环上应该做出完全相同的动作(比如都在第 3 轮发消息,都在第 5 轮选出 Leader)。
基于比较:如果在所有序等价的环上,算法的行为都相似,那么这个算法就是基于比较的。
这为什么重要?
因为这限制了算法的能力。算法不能说“如果我的 ID 是 5,我就等 5 秒”。它只能说“如果我的 ID 比邻居小,我就闭嘴”。
2. 核心工具:k-邻居与主动轮 (k-neighborhood & Active Round)
为了证明下界,我们需要度量节点获得了多少信息。
k-邻居:节点 $P_i$ 左右各延伸 $k$ 步所看到的局部视野。
主动轮 (Active Round):
在同步系统中,很多轮次可能大家都在沉默(没有发消息)。
主动轮是指至少有一个节点发送消息的那些轮次。
关键点:信息只有在主动轮里才能传播。一个节点在经历 $k$ 个主动轮后,最多只能知道它 $k$-邻居范围内的信息(因为信息传播速度限制)。
Lemma 3.16 & 3.17 的含义:
这两个引理证明了一个直观的结论:如果两个节点(不管是在同一个环还是不同的环)看到的 $k$-邻居视野是一样的(或序等价的),那么在前 $k$ 个主动轮里,它们的行为必须是一模一样的。
3. 构造下界:高度对称的环 $S_n$
这是证明中最精彩的部分(第 38-43 页)。为了让算法“痛苦”,我们需要构造一个环,使得环上有很多节点的局部视野看起来是一样的。
构造方法:二进制逆序 (Bit Reversal)
对于节点 $i$ ($0 \le i < n$),其 ID 不是 $i$,而是 $rev(i)$。
$rev(i)$:把 $i$ 的二进制表示倒过来读。
例如 $n=8$ (3位二进制)。
$1$ 的二进制是
001-> 倒过来是100-> $rev(1) = 4$。$3$ 的二进制是
011-> 倒过来是110-> $rev(3) = 6$。
为什么用这个?
这个构造生成的环 $S_n$ 具有极高的自相似性。
- 引理 3.19 (PPT 未详述但被引用):在这个特殊的环 $S_n$ 中,对于任意小的 $k$(只要 $k \le n/8$),环上会有大量(超过 $n/k$ 个)节点的 $k$-邻居视野是序等价的。
4. 最终证明逻辑 (The Grand Finale)
结合上述所有工具,我们可以推导出 $\Omega(n \log n)$:
只要视野一样,行为就一样:在环 $S_n$ 上,因为有 $\approx n/k$ 个节点的 $k$-邻居视野看起来一样。
大家一起发消息:根据 Lemma 3.17,如果在第 $k$ 个主动轮里有一个节点发消息,那么这 $\approx n/k$ 个“克隆人”也会同时发消息。
计算总和:
在第 $1$ 个主动轮,至少 $\approx n/1$ 个消息。
在第 $2$ 个主动轮,至少 $\approx n/2$ 个消息。
…
在第 $k$ 个主动轮,至少 $\approx n/k$ 个消息。
求和:
\[Total \approx \sum_{k=1}^{n} \frac{n}{k} = n \sum_{k=1}^{n} \frac{1}{k} \approx n \ln n\]这就是著名的调和级数求和。
结论:总消息数至少是 $\Omega(n \log n)$。
总结
这段 PPT 实际上是在告诉我们:
没有捷径:如果你只能比较 ID 大小(不能利用 ID 数值做哈希或计时),那么你就无法打破对称性带来的冗余。
对称性的代价:在高度对称的环(如 $S_n$)中,很多节点会误以为自己处在相同的环境中,从而做出相同的动作(发消息),导致消息量爆炸。
$\Omega(n \log n)$ 是铁律:无论是异步还是“受限的同步”(基于比较或时间受限),这个下界都存在。只有利用 ID 数值的非均匀同步算法(上节课那个 $O(n)$)才能打破这个限制。
考试要点:
理解 基于比较 的定义(序等价)。
理解 主动轮 的概念(只有发消息的轮才算数)。
记住结论:基于比较的算法下界是 $\Omega(n \log n)$。
同步环 时间受限算法:下界 $\Omega(n \log n)$
这段 PPT 介绍了时间受限算法 (Time-Bounded Algorithm) 在同步环形网络中的消息复杂度下界。
核心结论是:即使算法的时间复杂度与 ID 无关(仅受限于环大小 $n$),只要它能解决领导者选举问题,它的消息复杂度下界依然是 $\Omega(n \log n)$。
1. 为什么需要这一节?
背景:前几节展示了一个消息复杂度 $O(n)$ 但时间复杂度依赖于 ID 大小的算法($O(n \cdot 2^{ID})$)。
问题:我们能否设计一个既省消息(接近 $O(n)$),又省时间(时间只跟 $n$ 有关,跟 ID 无关)的算法?
答案:不可能。 这一节就是证明这个“不可能”。它指出,只要你限制了时间(不让它随 ID 无限增长),你就必须付出 $\Omega(n \log n)$ 的消息代价。
2. 核心定义
时间受限 (Time-Bounded) [Def 3.4]:
算法的运行时间 $r(n)$ 只依赖于环的大小 $n$,而与具体的 ID 数值无关。
即使 ID 取自无限的自然数集合 $\mathbb{N}$,算法也必须在有限的 $r(n)$ 轮内终止。
基于 t-比较 (t-Comparison Based) [Def 3.5]:
如果一个算法在前 $t$ 轮的行为只取决于 ID 的相对大小(序等价),而与具体数值无关,那它就是“基于 t-比较”的。
直观理解:如果算法被迫在 $t$ 轮内结束,它就来不及利用 ID 的具体数值(比如利用 ID 来计时),只能退化成“比大小”的游戏。
3. 证明思路:Ramsey 定理的降维打击
证明的核心逻辑是“归约 (Reduction)”:
我们将“时间受限算法”归约为“基于比较的算法”。既然我们已经证明了基于比较的算法下界是 $\Omega(n \log n)$,那么时间受限算法也逃不掉。
证明步骤:
利用 Ramsey 定理 (Lemma 3.22):
Ramsey 定理:在足够大的集合中,总能找到一个高度有序的子集。
应用:虽然自然数集合 $\mathbb{N}$ 很大,算法 $A$ 可能会对不同的 ID 数值有不同的处理逻辑。但是,Ramsey 定理告诉我们,一定存在一个巨大的 ID 子集 $C_n$(大小为 $n^2 + 2n$),在这个子集里,算法 $A$ 的行为表现得像是一个在前 $r(n)$ 轮内“只会比大小”的算法(基于 $r(n)$-比较的算法)。
结果:对于来自 $C_n$ 的 ID,算法 $A$ 退化成了基于比较的算法。
映射与构造 (Theorem 3.23):
我们构造一个新的算法 $A’$。当 $A’$ 处理 ID $i$ 时,它模拟原算法 $A$ 处理 ID $c_i$($C_n$ 中的第 $i$ 个数)的行为。
因为 $A$ 在 $C_n$ 上表现得像基于比较的算法,所以 $A’$ 就是一个标准的基于比较的算法。
根据之前的 Theorem 3.18(基于比较的算法下界是 $\Omega(n \log n)$),$A’$ 至少发送 $\Omega(n \log n)$ 条消息。
因为 $A’$ 只是 $A$ 的一个映射,所以原算法 $A$ 在处理 $C_n$ 这个特定子集时,也至少发送 $\Omega(n \log n)$ 条消息。
4. 总结与启示
这段证明揭示了分布式算法中的一个深刻权衡(Trade-off):
如果要消息少 ($O(n)$):你必须利用 ID 的数值来换取时间(像之前的 $O(n \cdot 2^{ID})$ 算法),导致时间可能无限长。
如果要时间短 (只与 $n$ 相关):你不能充分利用 ID 的数值信息(被 Ramsey 定理限制),只能退化成比较 ID 大小,从而导致消息复杂度至少是 $\Omega(n \log n)$。
结论:在同步环中,时间效率和消息效率不可兼得。你要么忍受极慢的运行速度,要么忍受较高的通信开销。
因果关系
这段PPT的内容主要讲解了分布式系统中的因果关系(Causality),特别是著名的Happens-Before关系。这是理解分布式系统如何定序(Ordering)的基石。
以下是这段PPT的详细解读:
1. 为什么我们需要因果关系?(物理时钟不可靠)
PPT首先解释了为什么在分布式系统中,我们不能依赖物理时间(墙上时钟)来同步事件。
相对论影响(物理层面的不确定性):
举例张三和李四,即使使用了同步时钟,如果李四以接近光速移动,时间会发生相对论效应(时间膨胀)。当李四的表显示5点时,张三可能已经等了很久了。这说明相对速度不同会导致时钟漂移,物理时间不可靠。
计算机系统的不确定性(系统层面的不确定性):
现代计算机非常复杂,存在CPU竞争、中断(Interrupts)、页错误等机制。这意味着即使两个机器收到相同的信号(如赛跑的小旗),它们的执行时间也是无法预料的,不可能指望它们在物理上的“同一时刻”做出反应。
结论: 我们无法在物理时间上观察到一个全局一致的状态。因此,必须寻找另一种机制来定序,即因果相关(Causality)。
2. 基本定义
为了描述因果关系,PPT定义了以下符号:
P:处理器(节点)的集合 ${P_1, P_2, \dots, P_n}$。
E:所有事件的集合。$E_p$ 表示在处理器 $p$ 上发生的所有事件。
定序(Ordering):
本地序 ($<_p$):在同一个节点 $p$ 上,事件是有先后顺序的(全序)。
消息序 ($<_m$):如果 $e_1$ 发送消息,$e_2$ 接收该消息,则 $e_1$ 一定在 $e_2$ 之前发生。
3. Happens-Before 关系 ($\rightarrow$ 或 $<_H$)
这是分布式系统中最核心的概念(通常称为Lamport因果关系)。它通过传递闭包将本地序和消息序结合起来。
Happens-Before 关系遵循三条规则:
规则 1(本地顺序): 如果 $e_1$ 和 $e_2$ 在同一个处理器上,且 $e_1$ 先于 $e_2$ 执行,则 $e_1 \rightarrow e_2$。
规则 2(消息传递): 如果 $e_1$ 发送消息 $m$,而 $e_2$ 接收该消息 $m$,则 $e_1 \rightarrow e_2$。
规则 3(传递性): 如果 $e_1 \rightarrow e_2$ 且 $e_2 \rightarrow e_3$,则 $e_1 \rightarrow e_3$。
重要特性:
偏序关系: $<_H$ 是一种偏序关系。这意味着并不是所有的事件都可以比较。
并发(Concurrency): 如果两个事件 $e$ 和 $e’$,既没有 $e \rightarrow e’$,也没有 $e’ \rightarrow e$,则称这两个事件是并发的。它们之间没有因果依赖。
4. H-DAG(Happens-Before 有向无环图)
为了形式化描述,我们可以将这种关系画成一个有向无环图(DAG):
顶点(Nodes): 事件集合 $E$。
边(Edges): 如果存在本地执行顺序 ($<_p$) 或消息传递关系 ($<_m$),则画一条有向边。
总结: 这段内容的核心思想是,在没有完美物理时钟的分布式系统中,我们利用“消息发送一定早于接收”这一公理,构建了一个逻辑上的时间顺序(Happens-Before),用来代替不准确的物理时间。
Lamport 时间戳
这段 PPT(§4.2.1)介绍的是分布式系统中大名鼎鼎的 Lamport 逻辑时钟(Lamport Timestamps)。
它是为了解决上一节提到的物理时钟不可靠问题而诞生的。它的目标很简单:既然物理时间靠不住,那我们就造一个“逻辑时间”,只要能体现出事件的先后因果顺序(Happens-Before)就行。
以下是核心知识点拆解:
1. 为什么要设计这个算法?
目标:把松散的“偏序关系”(Partial Order,即上一节的 H 关系)变成严格的“全序关系”(Total Order)。
作用:比如在资源分配(互斥锁)中,如果不分个绝对的先来后到,就会乱套。我们需要给每一个事件都打上一个独一无二的标签,让大家排好队。
2. 算法核心逻辑(必考点)
Lamport 算法不依赖绝对时间,而是依赖计数器。
三个角色:
节点(进程):每个人手里有一个计数器
my_TS(初始化为 0)。事件:发生事件 $e$,就给它贴个标签
e.TS。消息:发消息 $m$ 时,把当前的时间戳
m.TS带在身上传出去。
计数规则(Slide 18):
这是算法的灵魂,像两人对表一样:
发生本地事件:计数器加 1 (
my_TS++)。发送消息:先加 1,然后把新值贴在消息上发出去。
接收消息(最关键):
当你收到一条消息,上面写着时间
m.TS。你看一眼自己的表
my_TS。同步逻辑:取两者中的最大值
max(m.TS, my_TS)。推进一步:在最大值的基础上再加 1。
通俗理解:如果收到的信是“2025年”写的,而你以为现在是“2024年”,那你必须立马把日历翻到“2026年”,以保证你收到信的时间肯定晚于写信的时间。
Initially:my_TS=0; On event e: if ( e是接收消息m ) then my_TS = max ( m.TS, my_TS ); //取msg时戳和节点时戳的较大者作为新时戳 my_TS++; e.TS=my_TS; //给事件e打时戳 if ( e是发送消息m ) then m.TS=my_TS; //给消息m打时戳
3. 因果性保证(Slide 19)
这个算法保证了一个性质:
若 $e_1 \rightarrow e_2$(因果上 $e_1$ 在先),则 $TS(e_1) < TS(e_2)$。
- 因为本地事件会 +1,发消息到收消息也会至少 +1,所以时间戳一定会增长,不会倒流。
4. 解决“撞车”:扩展的全序(Slide 20)
问题:如果有两个互不相干的节点(并发事件),它们可能刚好都把计数器加到了 5。这时候谁在前谁在后?
解决方案:“拼爹”(拼节点 ID)。
改进后的时间戳格式:
时间.ID(如 PPT 中的4.3)。比较规则:先比时间戳大小;如果时间戳一样,再比节点 ID 大小。
PPT 里的例子(事件 $e_8$):
节点 ID = 3。
当前自己的
my_TS= 1。收到的消息
m.TS= 3。计算:
max(3, 1) + 1 = 4。最终结果:时间是 4,ID 是 3 $\rightarrow$ 标记为 4.3。
总结
Lamport 时间戳的精髓在于:它不代表真实的物理时间,它只代表“逻辑上的进度”。
通过 max + 1 的操作,它强行把所有因果相关的事件拉开距离,保证了“果”的时间戳一定大于“因”。
向量时间戳
这部分 PPT (§4.2.2) 主要介绍了 向量时间戳 (Vector Timestamp, VT)。它的出现是为了解决 Lamport 时间戳无法准确判定两个事件是否“并发”的缺陷,并用于检测分布式系统中的“违反因果关系”错误。
以下是这段 PPT 的详细解读:
1. 为什么我们需要向量时间戳?(Lamport 的缺陷)
PPT 开篇指出 Lamport 时间戳有一个致命的逻辑不对称性:
如果事件 $e1$ 因果上先于 $e2$ ($e1 \rightarrow e2$),那么 $e1$ 的时间戳一定小于 $e2$。
但是,如果 $e1$ 的时间戳小于 $e2$,不能推导出 $e1$ 因果上先于 $e2$。它们可能是互不相关的并发事件。
因此,仅靠 Lamport 时间戳,我们无法画出准确的事件关系图,也无法判定两个事件是否真的有因果联系。
2. 核心案例:对象迁移中的“违反因果”
PPT 使用了一个生动的例子来说明如果无法捕捉因果关系,会发生什么错误:
场景:
迁移:P1 把对象 O 发送给 P2 (消息 M1)。
通知:P1 告诉 P3 “对象 O 现在在 P2 那里” (消息 M2)。
请求:P3 收到通知后,向 P2 请求访问对象 O (消息 M3)。
故障 (Bug):
在网络中,消息 M3 (请求) 比 消息 M1 (对象本身) 先到达 了 P2。
P2 收到请求时,对象 O 还没运到。于是 P2 报错说“查无此对象”。
本质原因:
在因果上,M1 应该先于 M3 ($M1 \rightarrow M3$)。
但在 P2 的物理接收顺序上,M3 先于 M1 ($r(M3) < r(M1)$)。这就是违反因果序。
3. 解决方案:向量时间戳 (VT)
为了检测这种错误,我们需要一种机制,满足:当且仅当 $e1.VT < e2.VT$ 时,事件 $e1$ 因果先于 $e2$。
3.1 什么是向量时间戳?
它不再是一个整数,而是一个整数数组。
数组长度等于系统中节点的个数。
VT[i] = k表示:当前节点知道在节点i上已经发生了k个事件。
3.2 算法实现
每个节点维护一个局部向量 my_VT,初始全为 0。
发生本地事件:把自己位置的计数器加 1 (
my_VT[self]++)。发送消息:把自己位置的计数器加 1,把自己的
my_VT附在消息上发出去。接收消息:
对比消息里的向量
m.VT和自己的my_VT。对每一位取最大值:
my_VT[i] = max(m.VT[i], my_VT[i])。把自己位置的计数器加 1。
Initially:my_VT=[0,…,0];
On event e:
if ( e是消息m的接收者 ) then
for i=1 to M do //向量时戳的每个分量只增不减
my_VT[ i ] = max( m.VT[i], my_VT[i] );
my_VT[ self ]++; //设变量self是本节点的名字
e.VT=my_VT; //给事件e打时戳
if ( e是消息m的发送者 ) then
m.VT=my_VT; //给消息m打时戳
4. 如何判定关系?(核心逻辑)
有了向量,我们就可以精确判定两个事件的关系:
因果先后 ($e1 \rightarrow e2$):如果 V1 的每一位都小于等于 V2 的对应位,且至少有一位严格小于,则 V1 因果先于 V2。
并发 ($e1 || e2$):如果 V1 既不小于 V2,V2 也不小于 V1(即有的位 V1 大,有的位 V2 大),则两者并发。这是 Lamport 时间戳做不到的。
5. 回到案例:如何检测错误?
PPT 最后展示了如何用 VT 解决之前的对象迁移 Bug:
P2 的视角:
P2 先收到了 P3 的请求 M3,其时间戳为
(3, 0, 3)。后收到了 P1 的对象 M1,其时间戳为
(1, 0, 0)。
检测逻辑:
比较向量:
(1, 0, 0) < (3, 0, 3)。结论:M1 在因果上必须先于 M3。
但实际接收顺序是 M3 先到。P2 一看 M3 的时间戳,发现它依赖的 M1 还没到(违反了因果序),系统就可以通过缓存 M3 等待 M1 到达来避免报错。
总结
这部分 PPT 讲述了利用向量时间戳来捕捉分布式系统中的并发关系和因果依赖,从而能够检测并处理由于网络延迟导致的消息乱序(违反因果关系)问题。
因果通信
这段 PPT (§4.2.3) 主要讲解了分布式系统中的因果通信 (Causality Communication)。它的核心目的是解决网络中消息乱序到达的问题,确保应用层接收消息的顺序符合“因果关系”。
简单来说,如果消息 A 导致了消息 B(即 A $\rightarrow$ B),那么接收者必须先处理 A,再处理 B,即使网络先把 B 送到了。
以下是详细解读:
1. 核心理念:抑制与缓冲
问题:网络传输是不可控的,处理器无法选择消息到达物理网卡的顺序。
解决:我们不能改变“到达顺序”,但可以改变“传递(Deliver)给应用程序的顺序”。
手段:如果发现一个消息来得太早(它的“因”还没到),就把它先缓冲(Buffer) 起来,也就是抑制(Delay) 它的传递,直到它所有的“前驱”消息都到齐了,再按顺序提交给应用。
对比 FIFO:
FIFO(如 TCP)只保证同一个发送者的消息有序(利用序列号)。
因果通信要保证整个系统中所有节点的因果有序(利用向量时间戳)。
2. 核心数据结构
为了实现这一点,算法在每个节点 $P$ 上维护了两个关键数组:
earliest[1..M]:这是一个“预估表”。
earliest[k]记录了本地节点认为节点 $k$ 下一个将要交付的消息的时间戳下界。换句话说,它代表了本地节点目前对全局进度的“期待值”。
blocked[1..M]:- 这是一个“候车室”。
blocked[k]是一个队列,用来存放从节点 $k$ 收到的、但还不能立即交付的乱序消息。
- 这是一个“候车室”。
3. 算法流程 (Alg. Causal Msg delivery)
当节点收到一个来自 $p$ 的消息 $m$ 时,执行以下步骤:
接收与入队:
首先更新
earliest[p](更新对 $p$ 的期待值)。无条件将消息 $m$ 放入
blocked[p]队列中等待检查。
安全检查 (死循环检查):
检查所有阻塞队列,看有没有消息变成了“安全”状态(即非阻塞)。
什么是安全?
对于队列 $k$ 里的头消息 $m’$,我们需要检查除 $k$ 和自己以外的所有其他节点 $i$。
检查条件:
not_earliest(earliest[i], earliest[k], i)。直观解释:我们检查消息 $m’$ 所携带的向量时间戳。如果 $m’$ 依赖于节点 $i$ 的某个事件(由 $m’.TS[i]$ 表示),而本地记录的
earliest[i]显示那个事件还没发生(或者消息还没传过来),那么 $m’$ 就不安全,必须继续等。只有当所有依赖都满足了,消息才会被移出队列,Deliver 给应用。
更新进度:
- 一旦消息 $m’$ 被交付,就要更新
earliest[k],通常是加 1(向量加法),表示“我已经处理完这个了,准备接收下一个”。
- 一旦消息 $m’$ 被交付,就要更新
4. 实例演示 (P1, P2, P3 场景)
PPT 第 38-39 页展示了一个经典的违反因果序后被纠正的例子:
场景:
$P_1$ 发送消息 $m_2$(时间戳 1,1,0),它依赖于 $P_2$ 的某个事件(中间位是 1)。
$P_2$ 发送消息 $m_1$(时间戳 0,1,0)。
在因果上,$m_1 \rightarrow m_2$。但在 $P_3$ 这边,$m_2$ 先到了。
第一步:$m_2$ 先到 (乱序)
$P_3$ 收到 $m_2 (1,1,0)$。
检查安全:$m_2$ 要求 $P_2$ 的分量是 1。但 $P_3$ 本地记录的
earliest[2]是(0,1,0)(注:这里PPT示例数值可能有些混淆,核心意思是本地只知道 $P_2$ 的初始状态,不知道它发过消息)。判定:$P_2$ 上有一个更早的消息还没到(即 $m_1$)。
动作:将 $m_2$ 锁在
blocked[1]队列里,不交付。
第二步:$m_1$ 终于到了
$P_3$ 收到 $m_1 (0,1,0)$。
检查安全:$m_1$ 不依赖其他未达消息。
动作:Deliver $m_1$。
更新:更新
earliest[2],表示 $P_2$ 的进度推进了。回头检查:现在再看
blocked[1]里的 $m_2$。因为 $m_1$ 已经处理过,本地关于 $P_2$ 的状态已经更新,$m_2$ 的依赖被满足了。动作:Deliver $m_2$。
5. 潜在问题
死锁风险:如果某个节点长时间不发消息,其他节点的
earliest数组就无法更新,导致依赖该节点的其他消息一直被卡在阻塞队列里。解决:通常这种算法用于组播 (Multicast),或者节点需要定期发送心跳包来更新时间戳。
概率算法
数字概率算法
这段内容主要讲述了数字概率算法(Numerical Probabilistic Algorithms)的原理及其在三个主要领域的应用:$\pi$值的计算、数值积分以及概率计数(包括集合势的估计和去重计数)。
1. $\pi$ 值的计算 (蒙特卡洛方法)
这是蒙特卡洛算法最经典的入门案例。
原理:
在一个边长为 $2r$ 的正方形内画一个半径为 $r$ 的内切圆。
正方形面积 $S_{square} = 4r^2$
内切圆面积 $S_{circle} = \pi r^2$
圆与正方形的面积比为 $\pi / 4$
算法过程 (Darts):
假设向这个正方形随机投掷 $n$ 支飞镖,假设击中正方形内任意一点的概率相等。
随机产生点 $(x, y)$,其中 $0 \le x, y \le 1$(为了简化,只看第一象限)。
判断点是否在圆内:如果 $x^2 + y^2 \le 1$,则计数 $k$ 加 1。
最终 $\pi$ 的近似值为 $4k/n$。
精度:实验表明,随着投掷次数 $n$ 的增加,计算结果越接近真实值。例如 $n=10$ 亿时,可以精确到小数点后 4 位。
2. 数值积分 (计算定积分)
概率算法常用于计算定积分 $\int_0^1 f(x) dx$,即求曲线 $y=f(x)$ 下方的面积。
算法 1: 投点法 (Hit-or-Miss)
原理:类似于求 $\pi$。在单位正方形内随机投点,计算落入函数曲线 $y=f(x)$ 下方(即阴影部分)的点的比例。
公式:$Integral \approx k/n$。
误差:如果 $n \ge I(1-I)/\epsilon^2\delta$,则绝对误差超过 $\epsilon$ 的概率不超过 $\delta$。
算法 2: 平均值法 (Crude Monte Carlo)
原理:在积分区间 $[a, b]$ 上随机均匀取点,求这些点上的函数值 $f(x)$ 的算术平均值,再乘以区间宽度 $(b-a)$。
对比:
Crude算法通常比Hit-or-Miss更有效,方差更小。
与确定性算法 (如梯形法则) 的对比:
低维情况:在同样的精度下,确定性算法(如梯形法)通常需要的迭代次数更少。
高维优势:确定性算法在处理多重积分(高维)时,采样点随维数呈指数增长;而蒙特卡洛算法对维数不敏感,非常适合 4 维或更高维度的积分计算。
特殊函数:对于波动极大的函数(如 $sin^2(100! \pi x)$),梯形法则可能完全失效(返回0),而概率算法能给出更好的近似。
3. 概率计数 (Probabilistic Counting)
这部分解决了当数据量极大(如海量集合)时,如何估计集合大小的问题。
生日悖论 (The Birthday Paradox)
问题:随机选出 25 个人,至少有两个人生日相同的概率是多少?
结论:直觉上认为概率很小,但实际上概率约为 57%(大于 50%)。
启示:这利用了“碰撞”原理,可以通过第一次发生重复采样时的样本量来反推总集合的大小。
估计集合的势 (Set Cardinality)
算法 SetCount:对集合 $X$ 进行有放回的随机均匀采样,直到出现第一个重复元素。设此时共采样了 $k$ 次。
估算公式:集合大小 $|X| \approx 2k^2/\pi$。
多重集合中不同元素计数 (Distinct Elements / WordCount)
场景:统计莎士比亚全集中有多少个不同的单词。由于内存限制,不能存储所有单词。
算法思路 (LogLog/HyperLogLog 的前身):
使用哈希函数 $h(x)$ 将单词映射为二进制串。
对于每个单词,找到其哈希值中第一个为 1 的位的位置(记为 $\pi(h(x), 1)$)。
维护一个位数组 $y$,将对应位置置为 1。
最终返回数组 $y$ 中第一个为 0 的位置(记为 $k$)。
原理:
如果算法返回 $k=4$,说明哈希值以
1,01,001开头的单词都出现过,但以0001开头的单词没出现过。出现以
0001开头(概率 $1/16$)的事件没发生,说明总单词数 $n$ 大概不超过 $2^4$。估算结果:互不相同的单词数 $n$ 大致在 $2^{k-2}$ 到 $2^k$ 之间。更精确的估计是 $n \approx 2^k / 1.54703$。
优势:空间复杂度仅为 $O(\lg N)$,远优于排序法的 $O(N)$。
Sherwood 算法
课件详细介绍了 Sherwood 算法 的概念、原理以及具体的应用实例(特别是随机预处理技术)。
Sherwood 算法是一类概率算法,它的核心目标不是为了求近似解,而是为了消除确定性算法中“最好情况”和“最坏情况”之间的差异,使得算法的执行时间相对均匀,不再依赖于具体的输入实例。
以下是对课件内容的详细解析和总结:
1. Sherwood 算法的核心思想
背景:很多确定性算法(如快速排序、二叉搜索树操作)在处理某些特定输入(“坏实例”)时,效率会非常低(最坏情况复杂度),但在平均情况下效率很高。
目的:引入随机性,将算法在“坏实例”上的高耗时风险平摊到所有实例上。
效果:
不再有“最坏情况的实例”,只有“最坏情况的随机数选择”(但这发生的概率极低)。
虽然平均执行时间可能会因为引入了随机数生成等操作而略微增加,但它保证了算法性能的稳定性。
2. 随机预处理 (Stochastic Preprocessing)
这是实现 Sherwood 算法的一种通用技术。
基本原理:
变换 ($u$):将原始输入实例 $x$,利用一个随机数 $r$,通过函数 $u(x, r)$ 变换为一个随机实例 $y$。这个 $y$ 应该服从均匀分布。
求解 ($f$):使用原有的确定性算法 $f$ 来求解随机实例 $y$,得到结果 $s = f(y)$。
还原 ($v$):利用函数 $v(r, s)$,将 $y$ 的解 $s$ 还原为原实例 $x$ 的解。
算法流程 (伪代码 RH(x)):
1. 获取输入 x 的规模 n。 2. 随机选择一个元素 r (随机种子)。 3. y = u(x, r); // 将 x 转化为随机实例 y 4. s = f(y); // 用确定性算法求 y 的解 5. return v(r, s); // 还原为 x 的解即 $f(x) = v(r, f(u(x, r)))$ 。
3. 具体应用实例
实例 1:选择和排序 (Selection and Sorting)
场景:比如快速排序(Quicksort),如果输入数组已经是排好序的,效率会退化到 $O(n^2)$。
Sherwood 策略:在排序前,先调用
Shuffle函数(洗牌算法)。做法:随机打乱数组中元素的顺序。这样无论原始输入是什么样(即使是恶意的最坏序列),经过打乱后都变成了随机排列,从而保证快速排序能以 $O(n \log n)$ 的平均时间运行。
实例 2:离散对数计算 (Discrete Logarithm)
问题:已知 $a = g^x \mod p$,求 $x$。
难点:简单的确定性算法在某些特定 $a$ 值下运行很快,但在另一些值下运行极慢(最坏情况)。我们希望执行时间与具体的 $a$ 无关。
Sherwood 改造 (
dlogRH):随机化:随机选一个整数 $r$。
构造新实例:计算 $b = g^r \mod p$,然后计算 $c = b \cdot a \mod p$。
- 原理推导:$c = g^r \cdot g^x = g^{r+x} \mod p$。
求解:用确定性算法求 $c$ 的离散对数 $y$。此时 $y = r + x$。
还原:计算 $x = (y - r) \mod (p-1)$。
意义:通过乘以 $g^r$,将求解 $a$ 的对数转化为了求解随机数 $c$ 的对数,平滑了计算时间。
实例 3:加密计算 (隐私保护)
课件最后提出了一个非常有意思的应用场景——外包计算。
场景:你需要计算 $f(x)$,但计算能力不足,或者没有高效算法。别人有计算能力,你却不想让他知道你的原始数据 $x$。
方法:
你自己做一步简单的随机变换 $y = u(x, r)$(加密)。
把 $y$ 发送给拥有计算能力的人(云服务器/第三方)。
对方计算 $f(y)$ 并返回结果。对方只看到了随机的 $y$,无法反推 $x$。
你自己计算 $v(r, f(y))$ 得到最终结果 $f(x)$。
条件:这要求 $u(x, r)$ 的分布独立于 $x$,即起到“盲化”数据的作用。
实例 4:有序静态链表搜索
问题背景:
数据结构:静态链表
val[1..n]和ptr[1..n],其中val存储数值,ptr存储下一个元素的索引。表中的元素是有序的,即val[head] ≤ val[ptr[head]] ≤ ...。搜索问题:给定一个关键字
x,在链表中找到其索引i,使得val[i] = x。挑战:虽然链表有序,但因为是链式存储,不能直接像数组那样进行 $O(\log n)$ 的二分查找。如果直接顺序查找,最坏时间复杂度是 $\Omega(n)$。
算法对比
课件中展示了三种不同思路的算法:
$O(n)$ 的确定性算法 (Sequential Search)
算法 A (
A(x)):从链表头head开始,顺着ptr指针逐个比较,直到找到x。性能:
最坏时间 $w_A(n) \propto n$。
平均时间 $m_A(n) \propto n/2$。
缺点:如果目标元素在链表尾部,效率极低。
$O(n)$ 的概率算法 (Sherwood Algorithm)
算法 D (
D(x)):引入随机性来打破“从头开始”的僵局。随机选取数组中的一个索引
i。比较
x和val[i](记为y):Case 1 ($x < y$):如果
x比随机点小,说明x在前面。只能老老实实从头head开始搜。Case 2 ($x > y$):如果
x比随机点大,说明x在后面。可以从随机点ptr[i]开始往后搜(跳过了前面一段,节省了时间)。Case 3 ($x = y$):运气爆棚,直接找到了。
性能:
最坏时间 $w_D(n) \propto n$(比如随机选到了第一个元素,或者 x 就是最后一个元素)。
平均时间 $m_D(n) \approx n/3$。
优势:平均性能优于单纯的确定性算法 A。
平均时间为 $O(\sqrt{n})$ 的确定性算法
算法 B (
B(x)):尝试通过预处理来加速。取前 $\sqrt{n}$ 个物理位置上的元素(即
val[1..√n]),把它们当作随机抽样点。在这 $\sqrt{n}$ 个点中,找到不超过
x的最大值y,记下其下标i。从
i开始调用Search继续往后找。
原理:这相当于把链表分成了 $\sqrt{n}$ 个段,先通过采样快速定位到
x所在的“段”,然后再在段内进行顺序查找。性能:
采样消耗 $\sqrt{n}$。
段内查找平均消耗 $n / \sqrt{n} = \sqrt{n}$。
总平均时间 $m_B(n) \propto 2\sqrt{n}$。
本质:这其实利用了
val数组中物理存储的随机性来模拟索引结构。
核心结论
确定性算法 A:最简单,但受限于链表结构,平均性能一般 ($n/2$)。
Sherwood 算法 D:通过引入随机起点,不仅消除了对特定最坏实例的依赖,还将平均查找长度降低到了 $n/3$。
优化算法 B:利用“采样+分段”的思想,打破了线性查找的限制,将平均时间复杂度从 $O(n)$ 降低到了 $O(\sqrt{n})$。
总结
Sherwood 算法通过“随机预处理”技术,主动打破输入数据的特定模式,将算法的性能与输入数据的具体值解耦。它是一种“以退为进”的策略:虽然稍微增加了平均处理时间,但彻底消除了最坏情况发生的可能性,使算法更加健壮。
Las Vegas 算法:8 皇后问题
以下是关于 Las Vegas 算法 及其在 8 皇后问题 (8-Queens Problem) 中应用的详细解析,基于你提供的课件内容。
1. Las Vegas 算法 vs Sherwood 算法
课件开头对比了这两类概率算法,这是理解它们各自定位的关键。
Las Vegas (LV):
特点:绝不给错解。要么给出正确解,要么根本不给解(失败/僵局)。
效率:通常能获得比确定性算法更高效的平均性能。
时间上界:可能不存在。因为可能一直随机失败,一直重试,理论上最坏时间是无穷大。
失败处理:一旦失败(陷入僵局),通常的做法是从头再来(重新运行同一实例),因为下一次随机决策可能就会避开死胡同。成功的概率随着尝试时间的增加而增加。
Sherwood:
特点:总是给出正确解,目的是消除最坏情况。
效率:平均性能一般不比确定性算法优(主要是为了稳定性)。
时间上界:可以计算出一个确定的执行时间上界。
2. Las Vegas 算法的一般形式与性能分析
函数定义:
LV(x, y, success),其中success为 true 表示找到解,false 表示失败。关键指标:
$p(x)$:算法一次运行成功的概率。
$s(x)$:成功时的期望时间。
$e(x)$:失败时的期望时间。
顽固算法 (Obstinate):
策略:一直调用
LV直到success为真。期望时间 $t(x)$ 公式:
\[t(x) = s(x) + \frac{1-p(x)}{p(x)} e(x)\]解读:总时间 = 一次成功的时间 + (平均失败次数 $\times$ 一次失败的时间)。要最小化 $t(x)$,需要在成功率 $p(x)$ 和单次运行时间之间做折中。有时候降低 $p(x)$(快速失败)反而能减少总时间,因为 $e(x)$ 变小了。
3. 经典案例:8 皇后问题
8 皇后问题要求在 $8 \times 8$ 的棋盘上放置 8 个皇后,使其互不攻击(行、列、对角线均无冲突)。
A. 传统回溯法 (Backtracking)
策略:从第一行开始逐行放置。如果当前行找不到安全位置,就回退到上一行,移动上一行皇后的位置。
效率:平均需要搜索约 114 个结点才能找到一个解。
B. 纯 Las Vegas 算法 (QueensLv)
策略 (贪心 + 随机):
逐行放置皇后。
对于第 $k+1$ 行,计算所有安全位置(
nb个)。如果
nb > 0:在这些安全位置中随机均匀选择一个放置皇后。如果
nb = 0:死局。当前尝试失败,算法终止(返回 false)。
性能:
成功率 $p \approx 0.1293$(约 13%)。
期望节点数 $t(x) \approx 55.93$。
结论:比纯回溯法(114 个结点)快了一倍左右。
C. 混合策略 (Hybrid / 折中算法)
这是课件的重点改进方案。
问题:纯 LV 算法一旦失败就得从头再来(太消极);纯回溯法一步步试错太慢(太乐观)。
改进思路:先用 LV 算法随机放置前 $k$ 个皇后(
stepVegas),剩下的 $8-k$ 个皇后用回溯法放置。- 如果随机放置的前 $k$ 个位置太烂,导致后面无解,回溯法会返回失败,然后整个算法重新随机前 $k$ 个。
关键参数
stepVegas:预先随机放置几个皇后最好?实验数据 (8 皇后):当
stepVegas = 2或3时,期望节点数降至约 28~29,远优于纯回溯 (114) 和纯 LV (56)。实验数据 (12 皇后):当
stepVegas = 5时效果最好,时间从纯回溯的 125ms 降至 37ms。实验数据 (20 皇后):混合算法找一个解比确定性回溯快约 1000 倍!
结论:通常一半略少的皇后使用随机放置,剩余的使用回溯法,效果最佳。
4. 总结
Las Vegas 算法在解决如 N 皇后这类解分布不均匀且搜索空间巨大的问题时非常有效。通过引入随机性,它能跳过复杂的搜索树分支。而将 Las Vegas 与确定性回溯结合(混合策略),则能通过随机性快速深入搜索树的深层,同时利用回溯法的完备性来搜索局部解,从而获得极大的性能提升。
Las Vegas 算法:模 $p$ 平方根
这段PPT(§4.2)主要讲解了数论中的模 $p$ 平方根问题,以及在 $p$ 为奇素数时如何求解平方根,特别是针对计算困难的情况($p \equiv 1 \pmod 4$)提出了一种 Las Vegas 概率算法。
以下是详细的知识点拆解:
1. 基本概念与定义 (Slide 84-87)
首先定义了什么是“模 $p$ 的平方根”:
二次剩余 (Quadratic Residue):设 $p$ 是奇素数,对于整数 $x \in [1, p-1]$,如果存在整数 $y$ 使得 $y^2 \equiv x \pmod p$,则称 $x$ 是模 $p$ 的二次剩余。
平方根:上述的 $y$ 即为 $x$ 模 $p$ 的平方根。
非二次剩余:如果不存在这样的 $y$,则 $x$ 是非二次剩余。
核心定理:
数量定理:
任何一个二次剩余 $x$ 恰好有两个 不同的平方根。
这两个根互为相反数(模 $p$ 意义下),即如果 $y$ 是根,则 $p-y$ 也是根。
分布定理:
- 在 $[1, p-1]$ 中,恰有一半的数是二次剩余,另一半是非二次剩余。
判定定理 (欧拉判别法):
$x$ 是模 $p$ 的二次剩余 $\iff x^{\frac{p-1}{2}} \equiv 1 \pmod p$。
$x$ 是非二次剩余 $\iff x^{\frac{p-1}{2}} \equiv -1 \pmod p$。
2. 计算平方根的难题 (Slide 88)
虽然判定 $x$ 是否有根很容易(用欧拉判别法),但求出根 $y$ 却不一定容易。这取决于 $p$ 的形式:
情况 1:$p \equiv 3 \pmod 4$
- 这种情况很容易,有直接的公式求解(PPT中略过,通常是 $y \equiv \pm x^{(p+1)/4}$)。
情况 2:$p \equiv 1 \pmod 4$
没有有效的确定性算法。
必须使用 Las Vegas 概率算法。
3. Las Vegas 求解算法 (Slide 89-93)
针对 $p \equiv 1 \pmod 4$ 的困难情况,PPT 介绍了一种基于扩域运算的概率算法。
A. 算法核心思想
算法类似于复数运算,构造了一个扩域 $\mathbb{Z}_p[\sqrt{x}]$。
定义二元组 $(a, b)$ 表示数 $a + b\sqrt{x}$。
定义乘法规则:
\[(a, b) \times (c, d) = (ac + xbd, ad + bc)\]注:这其实就是 $(a+b\sqrt{x})(c+d\sqrt{x})$ 展开后的实部和虚部,其中 $\sqrt{x}^2$ 替换为 $x$。
B. 数学推导 (Why it works)
我们需要求 $y$ 使得 $y^2 \equiv x$。
算法随机选取一个数 $a$,然后计算 $(a + \sqrt{x})^{\frac{p-1}{2}}$。
设计算结果为 $c + d\sqrt{x}$(即二元组 $(c, d)$)。
根据推导:
将未知的真根 $y$ 代入 $\sqrt{x}$ 的位置(因为 $y^2 \equiv x$)。
根据费马小定理,$z^{p-1} \equiv 1$,所以 $z^{\frac{p-1}{2}} \equiv \pm 1$。
我们有两个方程:
$c + dy \equiv (a + y)^{\frac{p-1}{2}} \equiv \pm 1 \pmod p$
$c - dy \equiv (a - y)^{\frac{p-1}{2}} \equiv \pm 1 \pmod p$
关键点:如果 $(a+y)$ 和 $(a-y)$ 的勒让德符号不同(即一个是二次剩余,一个不是),那么上述两个式子右边一个是 $1$,一个是 $-1$。
两式相减:
\[(c + dy) - (c - dy) \equiv 1 - (-1) \implies 2dy \equiv 2 \implies dy \equiv 1 \pmod p\]或者反过来得到 $dy \equiv -1$。
只要 $d \neq 0$,我们就可以通过求逆元算出 $y = \pm d^{-1} \pmod p$。
C. 算法流程 (rootLV)
随机选择:随机选一个整数 $a \in [1, p-1]$。
极小概率直接命中:如果 $a^2 \equiv x \pmod p$,那 $a$ 就是根,直接返回。
计算:计算 $(a, 1)^{\frac{p-1}{2}}$,得到结果 $(c, d)$。这里用到快速幂模运算。
判断:
如果 $d = 0$,说明随机选的 $a$ 不好(导致 $c+dy$ 和 $c-dy$ 同号),失败 (Success = false)。
如果 $d \neq 0$,说明成功。解线性方程求出 $y$(即计算 $d$ 模 $p$ 的逆元)。
概率:算法成功的概率接近 0.5。如果失败,重新选一个 $a$ 再试一次即可。平均调用 2 次就能找到解。
总结
这段内容展示了数论在算法设计中的应用。对于模 $p$ 平方根问题,利用 $x$ 是二次剩余这一性质,通过构造扩域并在其中进行指数运算,可以将“求根问题”转化为“以 50% 概率解线性方程”的问题。这就是典型的 Las Vegas 算法:要么给出正确解,要么报告失败(然后重试),绝不给错误解。
Las Vegas 算法:整数的因数分解
以下是关于整数因数分解及其Dixon 算法的详细解析:
1. 整数因数分解问题概述
目标:给定一个大于 1 的合数 $n$,找到它的一个非平凡因数(即不是 1 和 $n$ 本身的因数)。
分解过程:
素性检测 (
prime(n)):首先判定 $n$ 是否为素数(常用 Monte Carlo 算法)。分裂 (
split(n)):如果 $n$ 是合数,找到它的一个因子。
朴素算法:从 2 到 $\sqrt{n}$ 逐一试除。
最坏时间复杂度:$O(\sqrt{n})$。对于大整数(如 50 位十进制数),计算时间是指数级的,无法接受。
结论:目前没有多项式时间算法能分解 $n$。
2. 数学基础:模 $n$ 的二次剩余
Dixon 算法依赖于数论中的一个重要性质。
定义:如果 $x^2 \equiv y \pmod n$,则 $y$ 是 $x$ 模 $n$ 的平方,或者说 $x$ 是 $y$ 模 $n$ 的平方根。
合数的性质:
如果 $n = p \times q$($p, q$ 为不同奇素数),则每个二次剩余恰好有 4 个 平方根。
这意味着对于同一个 $y$,可能存在 $a$ 和 $b$ 使得 $a^2 \equiv b^2 \pmod n$,但 $a \not\equiv \pm b \pmod n$。
核心原理:
如果我们能找到两个数 $a$ 和 $b$,满足:
$a^2 \equiv b^2 \pmod n$
$a \not\equiv \pm b \pmod n$
那么 $n$ 一定整除 $(a^2 - b^2) = (a+b)(a-b)$。
此时,$\gcd(a+b, n)$ 就能给出 $n$ 的一个非平凡因子。
3. Dixon 因数分解算法详解
Dixon 算法通过随机寻找“平滑数”来构造满足上述核心原理的 $a$ 和 $b$。
关键概念:$k$-平滑 ($k$-smooth)
如果一个整数的所有素因子都包含在前 $k$ 个素数之中,则称该整数为 $k$-平滑。例如,前 3 个素数为 2, 3, 5。那么 $12 = 2^2 \times 3$ 是 3-平滑的,而 $35 = 5 \times 7$ 不是。
算法步骤
Step 1: 收集关系式 (Collection Phase)
随机选择整数 $x \in [1, n-1]$。
计算 $y = x^2 \pmod n$。
检查 $y$ 是否为 $k$-平滑。如果是,将 $(x, y)$ 这一对及其因数分解记录下来。
目标是收集 $k+1$ 个这样的关系式。
Step 2: 线性代数消元 (Linear Algebra Phase)
对于收集到的每个 $y_i$,我们只关心其素因子的指数是奇数还是偶数。构造一个 $(k+1) \times k$ 的 0-1 矩阵。
因为行数 ($k+1$) 大于列数 ($k$),根据线性代数原理,这些行在模 2 意义下必然 线性相关。
使用高斯消元法找到这些线性相关的行,这意味着这些行的对应 $y_i$ 乘积是一个完全平方数。
Step 3: 计算因子 (Factor Computation)
令 $a$ 为相关行对应 $x_i$ 的乘积。
令 $b$ 为相关行对应 $y_i$ 的乘积开平方(因为 $y_i$ 乘积的素因子指数均为偶数,开方很容易)。
此时满足 $a^2 \equiv b^2 \pmod n$。
计算 $\gcd(a+b, n)$。如果结果不是 1 或 $n$,则找到了非平凡因子。
4. 实例演示 (n = 2537)
设置:$k=7$,前 7 个素数为 ${2, 3, 5, 7, 11, 13, 17}$。
Step 1:收集到 8 个 $x$,它们的平方 $x^2 \pmod n$ 都是 7-平滑的。
- 例如:$1003^2 \equiv 1584 \equiv 2^4 \cdot 3^2 \cdot 11 \pmod{2537}$。
Step 2:发现第 1, 4, 8 个方程对应 $y$ 的乘积是完全平方数。
- $y_1 \cdot y_4 \cdot y_8 = (2^4 \cdot 3^2 \cdot 11) \cdot (2^4 \cdot 11) \cdot (3^4) = 2^8 \cdot 3^6 \cdot 11^2 = (2^4 \cdot 3^3 \cdot 11)^2$。
Step 3:
$a = x_1 \cdot x_4 \cdot x_8 = 1003 \cdot 2419 \cdot 2200 \pmod{2537}$。
$b = \sqrt{y_1 y_4 y_8} = 2^4 \cdot 3^3 \cdot 11$。
计算 $\gcd(a+b, 2537) = 43$。成功分解!
5. 性能分析
$k$ 的选择:这是一个权衡。
$k$ 越大,随机选的 $x^2 \pmod n$ 越容易是 $k$-平滑的(容易收集),但后续矩阵处理越慢。
$k$ 越小,矩阵小,但很难找到 $k$-平滑数。
最佳实践:通常取 $k \approx \exp(\sqrt{\ln n \ln \ln n})$。
复杂度:Dixon 算法期望时间复杂度约为 $O(\exp(\sqrt{2 \ln n \ln \ln n}))$,比朴素算法快得多,但仍是亚指数级的。
Monte Carlo 算法
以下是关于 Monte Carlo (蒙特卡洛) 算法 的核心概念、误差放大机制以及有偏算法的详细解析。
1. Monte Carlo 算法概述
Monte Carlo (MC) 算法是一类概率算法。与确定性算法不同,它不能保证总是给出正确的答案。
特性:它偶尔会犯错,且出错时没有警告信息。
目标:无论针对何种输入实例,它都能以高概率找到正确解。
适用场景:当无法找到有效的确定性算法来获取正确答案时使用。
2. 核心定义
为了衡量 MC 算法的质量,引入了以下定义:
p-正确 (p-correct):如果一个 MC 算法返回正确解的概率不小于 $p$(其中 $1/2 < p < 1$),则称该算法是 $p$-正确的。
- 优势 (Advantage):算法的优势定义为 $p - 1/2$。只要 $p > 0.5$,算法就比瞎猜(50%概率)要好。
一致性 (Consistent):如果一个 MC 算法对同一个实例绝不给出两个不同的正确解(即正确解是唯一的),则称该算法是一致的(或相容的)。
3. 一般 MC 算法的精度提升:多数投票法
对于一个一致的、$p$-正确的算法($p>0.5$),我们可以通过重复调用并取出现频数最高的解来提高其正确率。
原理:既然算法正确的概率大于错误概率,重复多次后,正确答案出现的次数在统计上应该最多。
示例 (MC3):
假设有一个 75%-correct 的算法。
重复运行 3 次,设结果为 $t, u, v$。
如果其中有两个或三个结果相同,就返回那个结果。
效果:正确率从 75% 提升到了约 84% (27/32)。
定理:无论算法的优势 $\epsilon$ ($p = 0.5 + \epsilon$) 多么微小,只要 $\epsilon > 0$,就可以通过重复调用足够多的次数,将错误概率 $\delta$ 降到任意小。
- 代价:对于优势很小的算法,可能需要调用很多次(例如数百次)才能达到理想的精度。
4. 有偏算法 (Biased Algorithms):更高效的提升
对于某些特定的 MC 算法,我们不需要“少数服从多数”,只需要“一次成功”即可确认答案。这类算法提升精度的效率远高于普通算法。
A. 偏真算法 (True-biased)
主要用于判定问题(返回 True/False)。
定义:
当算法返回 True 时,解总是正确的。
当算法返回 False 时,解可能是错误的。
策略:重复运行多次。只要有一次返回 True,就断定结果为 True;只有所有次都返回 False,才认为结果是 False。
效率:不需要统计频数。重复 6 次即可将 55% 的正确率提升到 99%。
B. 偏 $y_0$ 算法 ($y_0$-biased)
这是偏真算法的推广形式,不再局限于判定问题。
定义:存在一个特定解 $y_0$(通常代表某种“成功”或“发现因子”的状态)。
如果算法返回的结果是 $y_0$,则它总是正确的。
如果算法返回非 $y_0$,则有可能是错误的。
正确性证明:
如果实例 $x$ 不属于特定子集 $X$,算法总是返回正确解(此时正确解就是 $y_0$)。
如果实例 $x$ 属于 $X$(正确解是 $y_0$),算法可能返回错误解(非 $y_0$)。
因此,只要返回了 $y_0$,它一定是真解。
策略:重复调用 $k$ 次,优先返回 $y_0$。如果某次结果为 $y_0$,则直接返回 $y_0$。
误差降低:重复 $k$ 次后,错误概率迅速降低为 $(1-p)^k$。例如 $p=0.55$,重复 4 次即可达到 95% 的正确率。
总结对比
| 算法类型 | 一致的 p-正确算法 (普通) | 偏真 / 偏 y0 算法 (有偏) |
|---|---|---|
| 判决机制 | 多数投票 (返回频数最高的解) | 优先命中 (一旦出现 $y_0$/True 即返回) |
| 收敛速度 | 较慢 (需要数百次重复来显著提升微小优势) | 极快 (指数级降低错误率,几次重复即可) |
| 前提条件 | $p > 1/2$ (优势必须存在) | $p > 0$ (只要有非零概率返回 $y_0$ 即可) |
Monte Carlo 算法:主元素问题
1. 问题定义
主元素:在一个包含 $n$ 个元素的数组 $T[1..n]$ 中,如果某个元素 $x$ 的出现次数严格大于 $n/2$,则称 $x$ 为主元素。
- 注:如果数组中存在主元素,那么它一定是唯一的。
2. 基础算法 maj(T)
这是一个简单的概率算法,试图通过随机采样来找到主元素。
算法流程:
随机采样:从数组中随机选择一个下标 $i$ ($1 \le i \le n$)。
计数验证:令候选元素 $x = T[i]$,遍历整个数组统计 $x$ 出现的次数 $k$。
判定:如果 $k > n/2$,返回
true;否则返回false。
算法性质分析:
偏真性 (True-biased):
如果算法返回
true,说明找到了一个出现次数确实超过一半的元素,结论绝对正确。如果算法返回
false,数组可能仍然含有主元素,只是刚才随机运气不好,没抽中它而已。
正确率:
如果数组确实有主元素,因为主元素占比超过一半,所以随机抽中它的概率 $p > 1/2$。
因此,该算法是一个 $1/2$-正确 的算法(单次失败概率 $< 1/2$)。
3. 算法改进:重复调用 (maj2 和 majMC)
单次运行 50% 的错误率(漏报率)在实际应用中是无法接受的。利用 Monte Carlo 算法的特性,我们可以通过独立重复试验来指数级降低错误率。
A. maj2(T):重复两次
逻辑:先调用一次
maj(T),如果成功则返回;如果失败,再调用一次。概率分析(假设存在主元素):
第一次就选中的概率:$p > 1/2$。
第一次没选中,第二次选中的概率:$(1-p)p$。
总成功率:$p + (1-p)p = 1 - (1-p)^2 > 1 - (1/2)^2 = 3/4$。
即:重复两次,正确率提升至 75% 以上。
B. majMC(T, ε):重复 k 次
为了满足任意给定的错误率上限 $\epsilon$(例如 $\epsilon = 0.001$),我们可以动态计算需要的重复次数。
重复次数 $k$:
由于 $k$ 次独立采样均失败的概率小于 $2^{-k}$,为了使 $2^{-k} < \epsilon$,我们需要取 $k = \lceil \lg(1/\epsilon) \rceil$。
算法流程:
循环 $k$ 次,每次都随机采一个样并验证。只要有一次验证成功,就返回
true。如果 $k$ 次全败,才返回false。时间复杂度:
单次验证需要遍历数组,耗时 $O(n)$。
重复 $k$ 次,总时间复杂度为 $O(n \lg(1/\epsilon))$。
4. 总结
这是一个典型的 Monte Carlo 算法:它通过牺牲确定性(存在极小的概率找不到解)来换取实现的简单性。
有偏性:它是单侧错误的,只会出现“有主元素但没找到”(False Negative),绝不会出现“没有主元素却说有”(False Positive)。
对比:课件最后提到,其实对于主元素问题,存在时间复杂度为 $O(n)$ 的确定性算法(如 Boyer-Moore 投票算法)。这里使用 MC 算法主要是为了作为教学案例,展示概率算法如何通过重复执行来提高可靠性。
Monte Carlo 算法:素数测定
课件详细介绍了素数判定(Primality Testing)的演变过程,从简单的概率算法到 Fermat 测试,再到更健壮的 Miller-Rabin 算法。
1. 简单的概率算法与 Fermat 小定理
背景:直接判定一个大数 $n$ 是否为素数很难。
Fermat 小定理:如果 $n$ 是素数,那么对于任意 $a \in [1, n-1]$,都有 $a^{n-1} \equiv 1 \pmod n$。
Fermat 测试:
随机选一个 $a$,计算 $a^{n-1} \pmod n$。
如果结果不是 1,那么 $n$ 肯定是合数(正确)。
如果结果是 1,那么 $n$ 可能是素数(也可能是伪素数)。
缺陷:存在Carmichael 数(如 561),它们虽然是合数,但对几乎所有的底数 $a$ 都满足费马小定理。对于这些数,Fermat 测试会失效,无法通过重复测试来任意降低错误率。
2. Miller-Rabin 测试 (强伪素数测试)
为了解决 Carmichael 数的问题,引入了更强的测试条件。
A. 强伪素数定义
对于奇数 $n$,设 $n-1 = 2^s \cdot t$(其中 $t$ 为奇数)。
如果 $n$ 是素数,那么对于任意 $a \in [2, n-2]$,必然满足以下两个条件之一:
$a^t \equiv 1 \pmod n$
存在某个 $r \in [0, s-1]$,使得 $a^{2^r \cdot t} \equiv n-1 \equiv -1 \pmod n$
如果 $n$ 是合数但满足上述条件,则称 $n$ 为以 $a$ 为底的强伪素数。
B. 算法逻辑 (MillRab)
将 $n-1$ 分解为 $2^s \cdot t$。
随机选择 $a$。
计算 $x = a^t \pmod n$。
如果 $x=1$ 或 $x=n-1$,则通过测试(可能为素数)。
重复做平方运算 $x = x^2 \pmod n$ 最多 $s-1$ 次。如果中间出现了 $n-1$,则通过测试。
如果直到最后都没出现 $n-1$,则返回
false(肯定是合数)。
C. 错误率与优势
定理:对于任意大于 4 的奇合数 $n$,其强伪证据(即能骗过算法的 $a$)的数量不超过总数的 1/4。
结论:Miller-Rabin 算法是一个 3/4-正确 的算法。
只要随机选 $a$,它判定合数为合数的概率至少是 75%。
这比 Fermat 测试强得多,因为不存在类似 Carmichael 数那样的“无敌合数”。
3. 重复调用 (RepeatMillRob)
策略:独立重复运行 Miller-Rabin 测试 $k$ 次。
正确率:
如果 $n$ 是合数,连续 $k$ 次都碰巧选到强伪证据(骗过算法)的概率小于 $(1/4)^k = 4^{-k}$。
取 $k=10$,错误率小于百万分之一。
取 $k=150$,错误率小于 $4^{-150}$,这在实际上比计算机硬件出错的概率还要低。
时间复杂度:
主要是模幂运算。每次测试耗时 $O(\lg^3 n)$(如果是朴素乘法)。
总时间为 $O(k \lg^3 n)$。对于上千位的数字,计算速度完全可以接受。
总结
Miller-Rabin 算法是目前工业界(如 RSA 加密密钥生成)最常用的素数判定算法。它虽然是概率算法(偏假),但通过少量的重复测试,可以将错误率降低到工程上可以忽略不计的程度,且效率远高于确定性算法。
Monte Carlo 算法:矩阵乘法验证
这段 PPT (§5.3) 介绍了一个经典的 Monte Carlo 算法(通常被称为 Freivalds 算法),用于快速验证两个矩阵相乘的结果是否正确。
这是一个极其巧妙的算法,展示了概率算法如何将计算复杂度从 $O(n^3)$ 降低到 $O(n^2)$。
1. 问题背景:矩阵乘法验证
输入:三个 $n \times n$ 的矩阵 $A, B, C$。
问题:判定 $A \times B$ 是否等于 $C$?
传统做法:
直接计算 $A \times B$,然后逐个元素对比结果与 $C$ 是否一致。
代价:矩阵乘法的时间复杂度是 $O(n^3)$(即便用 Strassen 算法也是 $O(n^{2.81})$)。当 $n$ 非常大时,计算代价极高。
新思路:使用 Monte Carlo 算法,在 $O(n^2)$ 时间内解决问题,但要接受极小的出错概率。
2. 核心算法:引入随机向量
算法的核心在于不直接计算矩阵乘积,而是引入一个随机的 $0/1$ 向量 $X$,将矩阵等式验证转化为向量等式验证。
判定依据:将判定 $AB = C$ 改为判定 $X(AB) = XC$。
结合律的妙用 (The Trick):
如果我们先算 $AB$,那复杂度还是 $O(n^3)$。
利用结合律,我们可以先算 $XA$,再算 $(XA)B$。
计算顺序:
计算 $R_1 = X \times A$ —— 向量乘矩阵,复杂度 $O(n^2)$。
计算 $R_2 = R_1 \times B$ —— 向量乘矩阵,复杂度 $O(n^2)$。
计算 $R_3 = X \times C$ —— 向量乘矩阵,复杂度 $O(n^2)$。
总复杂度:$O(n^2)$。
3. 算法流程 (goodproduct)
随机化:生成一个长度为 $n$ 的随机向量 $X$,每个元素随机取 0 或 1。
验证:检查是否满足 $(XA)B = XC$。
输出:
如果相等,返回
true(可能 $AB=C$,也可能出错了)。如果不等,返回
false(肯定 $AB \neq C$)。
4. 误差分析与算法性质
PPT 通过一个具体的数值例子(Slide 141-142)展示了两种情况:
漏判 (False Positive):$AB \neq C$,但选到了特定的 $X$(如 $X=(1,1,0)$),导致 $XAB$ 恰好等于 $XC$。算法错误地返回了
true。正确识别:换一个 $X$(如 $X=(0,1,1)$),就能检测出 $AB \neq C$。
偏假算法 (False-biased / One-sided Error):
若返回
false:说明 $XAB \neq XC$,则必然有 $AB \neq C$。结论绝对正确。若返回
true:说明 $XAB = XC$,但这不能保证 $AB = C$(存在 50% 的概率是误判)。单次错误率:如果 $AB \neq C$,单次随机测试出错(没检测出来)的概率 $\le 1/2$。
5. 精度改进 (RepeatGoodProduct)
为了降低那 50% 的错误率,我们采用重复测试的方法。
策略:重复运行 $k$ 次算法,每次使用独立的随机向量 $X$。
逻辑:
只要有一次发现不等(返回
false),就立即断定 $AB \neq C$。如果 $k$ 次都返回
true,才敢说 $AB = C$。
错误率:连续 $k$ 次都“倒霉”没检测出错误的概率是 $1/2^k$。
当 $k=20$ 时,出错概率小于 百万分之一。
此时总时间复杂度为 $O(k n^2)$。
总结
对于大矩阵验证,$O(k n^2)$ 远远快于 $O(n^3)$。这个算法完美体现了 Monte Carlo 算法的哲学:用极小的、可控的错误概率,换取巨大的计算速度提升。
往年题例子
一、给出一个计算图的最小顶点覆盖的近似算法,并说明其时间复杂度及近似比。
VertexSet approxVertexCover ( Graph g ) {
cset=∅;
e1=g.e;
while (e1 != ∅) {
从e1中任取一条边(u,v);
cset=cset∪{u,v};
从e1中删去与u和v相关联的所有边;
}
return c
}
时间复杂度分析
算法需要遍历所有边,若用邻接表表示,时间复杂度为 $O(V + E)$。
近似比证明
算法选出的所有边 $(u, v)$ 组成了一个边集 $M$(正好是一个匹配)。对于 $M$ 中的每一条边,算法都将它的两个端点加入了 $A(G)$。所以 $A(G) = 2 \cdot |M|$。
考虑最优顶点覆盖 $OPT(G)$。根据顶点覆盖的定义,$OPT(G)$ 必须覆盖图中的每一条边,当然也包括 $M$ 中的边。因为 $M$ 是一个匹配,其中的边两两互不相连,所以 $OPT(G)$ 必须为 $M$ 中的每一条边至少选择一个顶点才能覆盖住它们,因此 $OPT(G) \ge |M|$。
联立得 $\dfrac{A(G)}{OPT(G)} \le 2$。
二、设集合 S 和 T 中各有 n 个互不相同的元素,要求: (1)写一 Monte Carlo 算法判定 S 和 T 是否相等;(2)分析算法出错的概率;(3)算法是否有偏,若有偏,偏什么?
这是一个非常经典的随机化算法问题,通常使用多项式恒等测试(Polynomial Identity Testing)的思路来解决。
以下是基于蒙特卡洛(Monte Carlo)方法的解答:
(1) 蒙特卡洛算法设计
我们要利用多项式的性质:如果两个集合相等,那么以这两个集合元素为根构成的多项式应该是恒等的。
算法流程:
定义域选择:选择一个足够大的素数 $q$(建议 $q \gg n$,例如 $q > n^2$ 或更大,以降低冲突概率)。
随机采样:从整数域 ${0, 1, 2, \dots, q-1}$ 中均匀随机地选取一个整数 $r$。
多项式求值:
计算集合 $S$ 对应的多项式在 $r$ 处的值:$V_S = \prod_{s \in S} (r - s) \pmod q$
计算集合 $T$ 对应的多项式在 $r$ 处的值:$V_T = \prod_{t \in T} (r - t) \pmod q$
判定:
如果 $V_S \neq V_T$,返回 FALSE(判定 $S \neq T$)。
如果 $V_S = V_T$,返回 TRUE(判定 $S = T$)。
(2) 算法出错概率分析
我们需要分两种情况讨论:
情况一:当 $S = T$ 时(即两集合确实相等)
此时对于任意的 $x$,多项式 $\prod(x-s)$ 和 $\prod(x-t)$ 是完全相同的。
因此 $V_S$ 必定等于 $V_T$。
算法总是返回 TRUE。
出错概率为 0。
情况二:当 $S \neq T$ 时(即两集合不相等)
构造差值多项式 $Q(x) = \prod_{s \in S} (x - s) - \prod_{t \in T} (x - t)$。
由于 $S$ 和 $T$ 中各有 $n$ 个元素,$Q(x)$ 是一个最高次数不超过 $n$ 的非零多项式(因为 $S \neq T$,根据多项式的唯一分解定理,这两个多项式不恒等)。
根据代数基本定理,一个 $n$ 次非零多项式在域 $\mathbb{F}_q$ 上至多有 $n$ 个根。
算法出错(即产生“假阳性”,误报 $S=T$)的条件是:我们随机选取的 $r$ 恰好是 $Q(x)$ 的一个根,即 $Q(r) \equiv 0 \pmod q$。
随机选取的 $r$ 共有 $q$ 种可能,而坏的 $r$(根)至多有 $n$ 个。
出错概率:
\[P(\text{Error}) = P(V_S = V_T \mid S \neq T) \le \frac{n}{q}\]
结论:算法的出错概率至多为 $\frac{n}{q}$。只要 $q$ 选得足够大(例如 $q \approx 2n$ 或更大),出错概率就可以控制在很小的范围内。
(3) 算法是否有偏?若有偏,偏什么?
根据上一题 PPT 中的定义逻辑:
偏真 (True-biased):当返回 TRUE 时解总是正确的(即没有假阳性,只有假阴性)。
偏假 (False-biased):当返回 FALSE 时解总是正确的(即没有假阴性,只有假阳性)。
分析本算法:
当算法返回 FALSE ($V_S \neq V_T$) 时:
这说明两个多项式在 $r$ 点的值确实不同,因此两个集合一定不相等。
$\implies$ 返回 FALSE 总是正确的。
当算法返回 TRUE ($V_S = V_T$) 时:
这可能是因为 $S=T$,但也可能是 $S \neq T$ 却运气不好撞到了多项式的交点(根)。
$\implies$ 返回 TRUE 时可能是错误的。
结论:
该算法是 偏假的 (False-biased)。
(注:这里的“偏假”是指算法在输出 False 结果时是绝对可信的,或者说算法的错误只会出现在将 False 的实例误判为 True 的方向上。这对应于标准复杂度类中的 co-RP 算法。)
三、为什么说若一个 NPC 或者 NPH 问题在多项式时间内可解当且仅当 NP = P?
充分性(若问题多项式可解 $\rightarrow P=NP$):
根据定义,NP-Hard(或 NPC)问题是所有 NP 问题中最难的问题。对于任意一个 NP 问题 $L$,都存在一个多项式时间的归约算法将其转化为该 NP-Hard 问题。
如果这个 NP-Hard 问题存在多项式时间的求解算法,那么对于任何 $NP$ 问题,我们都可以先通过归约将其转化为该问题,再进行求解。整个过程的时间复杂度仍为多项式级别。
这意味着 $NP \subseteq P$。由于 $P \subseteq NP$ 总是成立,因此可得 $P=NP$。
必要性(若 $P=NP \rightarrow$ 问题多项式可解):
若 $P=NP$,则所有 NP 问题都可以在多项式时间内解决。
对于 NPC 问题,因其属于 NP,故显然可解。
对于常见的 NP-Hard 优化问题,它们通常可以通过多项式次调用其对应的 NPC 判定版本来解决(例如通过二分法确定最优解)。因此,在 $P=NP$ 的前提下,这类 NP-Hard 问题也是多项式时间可解的。
四、考察上界 O(n) 的同步环非均匀算法。修改该算法,使得每个阶段中可以有 k 条消息在转发,并分析时间复杂度和消息复杂度。
这是一道关于同步环非均匀算法(Synchronous Ring Non-uniform Algorithm) 改进的经典题目。
原算法(上界为 $O(n)$ 的算法)极其保守,每个阶段(Phase)只允许 1 个特定的 ID 候选人发起消息,导致消息复杂度极低($O(n)$),但时间复杂度极高(依赖于最小 ID 的值,即 $O(n \cdot m)$)。
将算法修改为 “每个阶段处理 $k$ 个候选人”(即批量处理 ID),是典型的时间与消息复杂度的权衡(Time-Message Trade-off)。
1. 算法修改方案
原算法中,第 $i$ 阶段(Phase $i$)专门用于检测 ID 为 $i$ 的节点。
修改后的算法将 ID 空间划分为大小为 $k$ 的块。
阶段划分:算法按阶段 $p = 0, 1, 2, \dots$ 运行。
候选人范围:第 $p$ 阶段负责检测 ID 在范围 $[p \cdot k, (p+1) \cdot k - 1]$ 内的节点。
节点行为:
在第 $p$ 阶段开始时,如果一个节点的 ID 属于当前阶段的范围,它就作为候选人发起一个包含自己 ID 的消息。
转发规则(抑制策略):
当节点收到一个消息时,将消息中的 ID 与自己的 ID 进行比较。
只有当 收到 ID < 自身 ID 时,才将消息转发给下一个邻居(保持求最小 ID 的逻辑)。
否则,吞并(丢弃)该消息。
终止:如果一个节点收到了自己的 ID,它宣布当选 Leader,算法终止。
阶段时长:每个阶段持续 $n$ 轮,以确保消息能完整绕环一周。
2. 复杂度分析
假设环上节点的最小 ID 为 $m$。
时间复杂度:$O(n \cdot m/k)$
分析:
算法会一直运行空阶段,直到进入包含最小 ID $m$ 的那个阶段。
包含 $m$ 的阶段序号为 $p = \lfloor m / k \rfloor$。
在该阶段之前,有 $\lfloor m / k \rfloor$ 个阶段是空的(无人发起消息,网络保持沉默)。
每个阶段耗时 $n$ 轮。
计算:
\[\text{Total Time} \approx (\lfloor \frac{m}{k} \rfloor + 1) \times n \approx O(\frac{n \cdot m}{k})\]结论:时间复杂度相比原算法降低了 $k$ 倍。
消息复杂度:$O(n \cdot k)$
分析:
在前 $p$ 个空阶段中,没有任何消息发送(消息数为 0)。
在第 $p$ 阶段(活跃阶段),ID 在范围 $[p \cdot k, (p+1) \cdot k - 1]$ 内的所有节点都会发起消息。
最坏情况下,该范围内可能存在 $k$ 个节点(即 $m, m+1, \dots, m+k-1$ 都在环上)。
在这个阶段内,这就变成了一个简单的 LCR 算法(在 $n$ 个节点的环上,有 $k$ 个发起者)。
最坏情况构造:假设这 $k$ 个节点的 ID 在环上是逆序排列的(沿着消息发送方向,先遇到大 ID,再遇到小 ID)。
最小的 ID $m$ 会跑满一圈($n$ 步)。
次小的 ID 会跑很远直到遇到最小 ID。
…
最大的候选 ID 也可能跑很远。
每个消息最多跑 $n$ 步,共有 $k$ 个消息。
计算:
\[\text{Total Messages} \le k \times n = O(n \cdot k)\]结论:消息复杂度随着 $k$ 的增加而线性增加。
3. 总结
修改后的算法展示了分布式计算中经典的权衡 (Trade-off):
通过引入参数 $k$,我们牺牲了消息复杂度(从 $O(n)$ 增加到 $O(nk)$),换取了时间复杂度的显著降低(从 $O(nm)$ 降低到 $O(nm/k)$)。
当 $k=1$ 时,退化回原算法。
当 $k=n$(或 $k$ 覆盖所有 ID)时,变成标准的 LCR 算法(时间 $O(n)$,消息 $O(n^2)$)。
五、已知对于 MST 启发的 ∆TSP 问题有 $R_{MST}^\infty = 2$,请构造一个实例,使其渐近性能比为 2。
这是一个关于度量旅行商问题($\Delta\text{TSP}$)中 最小生成树(MST)算法 最坏情况性能比的经典构造实例。该算法的渐近性能比 $R_{\infty}^{\text{MST}} = 2$。
以下是该实例的标准化数学描述与分析:
1. 实例构造 (Instance Construction)
考虑一个包含 $n$ 个顶点的完全图 $G = (V, E)$,其中 $n$ 较大。顶点集合定义如下:
中心顶点:$O$
外围顶点:$L = {L_1, L_2, \dots, L_{n-1}}$,这些顶点在逻辑上排列成一个环。
距离函数 $d(u, v)$
定义各点对之间的度量距离,使其满足三角不等式:
辐射边:$d(O, L_i) = 1$,对于所有 $i = 1, \dots, n-1$。
环邻边:$d(L_i, L_{i+1}) = 1$(其中 $L_n \equiv L_1$),即环中相邻的叶顶点距离为 1。
其余边:$d(L_i, L_j) = 2$,对于所有非相邻的叶顶点对(即 $|i-j| \pmod{n-1} \neq 1$)。
2. 算法步骤与成本计算
2.1 最小生成树 (MST) 的计算
由于所有连接 $O$ 与 $L_i$ 的边权均为最小(等于 1),且连接所有叶顶点的边权至少为 1。
构造:MST 是以 $O$ 为中心的星形图(Star Graph),包含边 ${(O, L_i) \mid i=1, \dots, n-1}$。
MST 成本:
\[W(\text{MST}) = \sum_{i=1}^{n-1} d(O, L_i) = n - 1\]
2.2 启发式游程 (MST 加倍与捷径)
算法通过加倍 MST 的边构造欧拉回路,再通过“跳过”重复顶点(Shortcut)生成 TSP 游程。
欧拉游程:$O \to L_1 \to O \to L_2 \to O \dots \to L_{n-1} \to O$。其总长度为 $2 \times W(\text{MST}) = 2(n-1)$。
构造坏情况捷径:
我们可以选择访问叶顶点的顺序 $\pi$,使得没有任何两个连续访问的叶顶点在原环中是相邻的。
例如,在 $n$ 足够大时,选取 $\pi$ 使得 $d(L_{\pi(k)}, L_{\pi(k+1)}) = 2$。
启发式游程路径:$O \to L_{\pi(1)} \to L_{\pi(2)} \to \dots \to L_{\pi(n-1)} \to O$。
近似解成本:
\[C_{\text{MST}} = d(O, L_{\pi(1)}) + \sum_{k=1}^{n-2} d(L_{\pi(k)}, L_{\pi(k+1)}) + d(L_{\pi(n-1)}, O)\] \[C_{\text{MST}} = 1 + (n-2) \times 2 + 1 = 2n - 2\]
3. 最优解 (Optimal Tour)
一个接近最优的 TSP 游程是沿着外围环移动,最后通过中心点 $O$ 返回。
最优路径示例:$L_1 \to L_2 \to \dots \to L_{n-1} \to O \to L_1$。
最优解成本:
\[C_{\text{OPT}} = (n-2) \times 1 \text{ (相邻叶顶点边)} + 1 \text{ (从 } L_{n-1} \text{ 到 } O) + 1 \text{ (从 } O \text{ 到 } L_1)\] \[C_{\text{OPT}} = n\]
4. 性能比分析 (Approximation Ratio)
计算启发式算法结果与最优解的比率:
\[R_n = \frac{C_{\text{MST}}}{C_{\text{OPT}}} = \frac{2n - 2}{n} = 2 - \frac{2}{n}\]当顶点数 $n$ 趋于无穷大时:
\[\lim_{n \to \infty} R_n = 2\]严格度量空间的修正:
如果需要避免 $d(L_i, L_k) = d(L_i, L_j) + d(L_j, L_k)$ 的等式情况,可以将相邻叶顶点的距离设为 $1 + \epsilon$。此时比率将变为 $\frac{2n-2}{n + (n-2)\epsilon}$,在 $\epsilon \to 0$ 且 $n \to \infty$ 时依然收敛于 2。
