相关笔记

概览

聚合分析(Aggregate Analysis)是摊还分析三种方法中最简单的一种。其核心思想是:对 个操作的序列确定一个总代价上界 ,则每个操作的摊还代价。聚合分析不区分不同操作类型的代价差异,将所有操作视为同一类来计算平均代价。本节通过**栈操作(MULTIPOP)二进制计数器(INCREMENT)**两个经典例子,展示聚合分析如何将朴素分析中的 上界降至


知识结构总览

graph TD
    A["16.1 聚合分析"] --> B["核心思想"]
    A --> C["经典例子"]
    A --> D["关键结论"]

    B --> B1["对n个操作求总代价上界T(n)"]
    B --> B2["摊还代价 = T(n)/n"]
    B --> B3["不区分操作类型"]

    C --> C1["栈操作 MULTIPOP"]
    C --> C2["二进制计数器 INCREMENT"]

    C1 --> C1a["朴素分析: O(n²)"]
    C1 --> C1b["聚合分析: O(n)"]
    C1 --> C1c["关键观察: POP次数 ≤ PUSH次数"]

    C2 --> C2a["朴素分析: O(nk)"]
    C2 --> C2b["聚合分析: O(n)"]
    C2 --> C2c["关键观察: 第i位翻转 ⌊n/2^i⌋ 次"]

    D --> D1["栈操作摊还代价: O(1)/次"]
    D --> D2["计数器摊还代价: O(1)/次"]

核心思想

核心思路

聚合分析的基本策略是:先求 个操作的总代价上界 ,再除以 得到每个操作的摊还代价。这种方法的关键在于找到一个紧凑的总代价上界,而不是简单地将每个操作的最坏情况代价相乘。聚合分析不涉及概率假设,因此其结论在最坏情况下仍然成立,这与平均情况分析有本质区别。

聚合分析(Aggregate Analysis)

设对一个数据结构执行 个操作的序列,若能证明这 个操作的总代价 的上界为 ,则每个操作的摊还代价(amortized cost)为 。聚合分析为所有操作分配相同的摊还代价,不区分不同类型的操作。

栈操作(MULTIPOP)

考虑一个栈 ,支持以下操作:

操作描述实际代价
PUSH(S, x)将元素 压入栈
POP(S)弹出栈顶元素
MULTIPOP(S, k)弹出栈顶 个元素

其中 为执行 MULTIPOP 时栈中的元素个数。

MULTIPOP 伪代码

MULTIPOP(S, k)
1  while not STACK-EMPTY(S) and k > 0
2      POP(S)
3      k = k - 1

朴素(最坏情况)分析

在一个由 个 PUSH、POP 和 MULTIPOP 操作组成的序列中,MULTIPOP 的最坏情况代价为 (当栈中有 个元素时执行 MULTIPOP)。因此, 个操作的总代价上界为

聚合分析

【聚合分析(POP次数不超过PUSH次数,总代价O(n))】 关键观察:一个对象(盘子)不能被弹出,除非它之前被压入。因此,在 个操作的序列中,POP 操作(包括 MULTIPOP 内部的 POP)的总次数不超过 PUSH 操作的总次数,而 PUSH 操作的总次数最多为

个操作中 PUSH 的次数为 ,POP(含 MULTIPOP 中的)的总次数为 ,则:

总代价

因此,每个操作的摊还代价

二进制计数器(INCREMENT)

考虑一个 位二进制计数器,用数组 表示,其中 是最低位。初始时所有位为 0。

INCREMENT 伪代码

INCREMENT(A, k)
1  i = 0
2  while i < k and A[i] == 1
3      A[i] = 0
4      i = i + 1
5  if i < k
6      A[i] = 1

INCREMENT 的代价等于翻转的位数(即第 2-4 行循环的迭代次数加上可能的第 6 行赋值)。

朴素分析

单次 INCREMENT 的最坏情况代价为 (当所有 位都为 1 时,需要翻转全部 位)。 次 INCREMENT 的总代价上界为

聚合分析

【聚合分析(第i位翻转⌊n/2^i⌋次,总翻转次数<2n)】 关键观察:考察第 次 INCREMENT 中被翻转的次数。

  • 每两次翻转中,一次是从 0 变为 1,一次是从 1 变为 0。
  • 从 0 变为 1 的频率:每经过 次 INCREMENT 才会翻转一次。
  • 因此, 次 INCREMENT 中被翻转 次。

总翻转次数:

因此, 次 INCREMENT 的总代价 ,每个操作的摊还代价

摊还代价(Amortized Cost)

在聚合分析中, 个操作序列的总代价为 ,则每个操作的摊还代价定义为 。摊还代价保证:在最坏情况下, 个操作中每个操作的平均代价不超过 。注意,摊还代价适用于操作的序列,而非单个操作。


补充理解与拓展

摊还分析 vs 平均情况分析

摊还分析与平均情况分析有本质区别:

  • 摊还分析不涉及概率假设。它考察的是对任意操作序列(包括最坏情况序列)的总代价上界,然后求平均。因此,摊还分析的结论在最坏情况下仍然成立。
  • 平均情况分析依赖于对输入分布的概率假设(如所有排列等概率),其结论只在假设的分布下成立,对于特定的最坏输入可能不成立。

这一区分由 Tarjan (1985) 在其开创性工作中明确阐述。Tarjan 指出:“摊还分析提供的是确定性的保证,而非概率性的保证。”

来源:CLRS Chapter 16; Tarjan, R. E. (1985). “Amortized Computational Complexity.” SIAM Journal on Computing, 14(2), 306-318.

聚合分析的局限性

聚合分析为所有操作分配相同的摊还代价,这意味着它无法区分不同操作类型的代价差异。例如,在栈操作中,PUSH 的实际代价为 1,而 MULTIPOP 的实际代价可能远大于 1,但聚合分析将它们的摊还代价都设为

这种”一刀切”的做法在以下场景中不够灵活:

  • 当不同操作类型的代价差异很大时,聚合分析可能无法给出紧凑的上界。
  • 当需要分析操作序列中特定操作的代价时,聚合分析无法提供细粒度的信息。

正是这些局限性,引出了记账方法(16.2节)和势能方法(16.3节)的动机。记账方法允许为不同操作分配不同的摊还代价,而势能方法则将”信用”与整个数据结构的状态关联起来,提供了最细粒度的分析能力。

来源:Tarjan, R. E. (1985). “Amortized Computational Complexity.” SIAM Journal on Computing, 14(2), 306-318.


易混淆点与辨析

摊还代价 ≠ 单个操作的最坏代价

摊还代价是 个操作的平均代价上界,不是单个操作的最坏代价。例如,单次 MULTIPOP 的最坏代价为 ,但其摊还代价仅为 。不能说”每次 MULTIPOP 的代价都是 “,而应该说” 个操作序列中,每个操作的平均代价为 ”。

摊还分析 ≠ 平均情况分析

摊还分析不涉及概率,它是对最坏情况操作序列的平均代价进行分析。平均情况分析则依赖于输入的概率分布假设。两者的结论性质完全不同:摊还分析提供确定性保证,平均情况分析提供概率性保证。

聚合分析的总代价上界必须紧凑

聚合分析的有效性依赖于找到一个紧凑的总代价上界 。如果简单地用每个操作的最坏代价乘以 (如栈操作得到 ),虽然正确但毫无意义。聚合分析的价值在于利用操作之间的相互约束(如 POP 次数不超过 PUSH 次数)来得到更紧凑的上界。


习题精选

题号题目描述难度
16.1-1若增加 MULTIPUSH(S, k) 操作,摊还代价是否仍为
16.1-2若二进制计数器增加 DECREMENT 操作,用聚合分析证明 次操作代价为
16.1-3 个操作代价为 (若 为 2 的幂),否则代价为 1,求总代价

视频学习指南

资源讲者/来源内容链接
MIT 6.006 Lecture 13Erik DemaineAmortized Analysis: Aggregate, Accounting, PotentialYouTube
CLRS Study Group社区Chapter 16 精读与习题讨论待补充

教材原文

教材原文(中文翻译)

16.1 聚合分析

在一系列 个操作中,最坏情况下代价最高的一种操作可能很昂贵,但聚合分析表明,该操作的高昂代价被其他操作的低廉代价所弥补。具体而言,如果我们能证明 个操作的总代价为 ,那么无论哪一种操作是最贵的,每个操作的摊还代价都是

栈操作

在本节的第一个例子中,我们分析对一个栈进行的操作。栈 支持以下操作:PUSH(S, x) 将元素 压入栈 中,代价为 POP(S) 弹出 的栈顶元素,代价为 。我们还引入一个新的操作 MULTIPOP(S, k),它弹出 的栈顶 个元素,如果 中元素不足 个则弹出全部元素。MULTIPOP 的代价为 ,其中 为 MULTIPOP 执行时栈中的元素个数。

虽然单个 MULTIPOP 操作的代价可能高达 ,但一个包含 个 PUSH、POP 和 MULTIPOP 操作的序列的总代价为 。原因在于,每个元素最多被弹出一次。由于 个操作中最多执行 次 PUSH,因此 POP 操作的总次数(包括 MULTIPOP 内部的 POP)最多为 。于是, 个操作的总代价为 ,每个操作的摊还代价为

二进制计数器

作为第二个例子,我们分析一个 位二进制计数器。我们用一个数组 来表示计数器,其中 是最低位。初始时所有位都为 0。INCREMENT 操作将计数器的值加 1。INCREMENT 的代价等于翻转的位数。

单次 INCREMENT 的最坏情况代价为 ,因此 次 INCREMENT 的朴素上界为 。然而,通过聚合分析,我们可以得到更紧凑的上界。第 次 INCREMENT 中被翻转 次。因此,总翻转次数为:

于是, 次 INCREMENT 的总代价为 ,每个操作的摊还代价为


参见Wiki


第16章-摊还分析 聚合分析