Skip to content

摊还分析

Abstract

  • Amortized 摊还分析:求操作序列中所有操作的平均时间,评价操作的代价。用于特定操作非常慢,但一般操作很快的算法。

摊还分析产生一个平均代价的上界,比最坏情况估计要好。

摊还分析不使用概率知识。

聚合分析

聚合分析就是求 \(n\) 个操作序列的总代价上限 \(T(n)\),然后得到平均代价 \(T(n)/n\)

聚合分析的例子有:

  • MultiPop(S, k):从栈顶弹出 \(k\) 个元素
    • 如果对这个操作使用最坏情况分析,会得到 \(O(n^2)\) 的复杂度。
    • 但我们知道栈中的元素最多会被弹出一次,所以对 \(n\) 个操作的总代价是 \(O(n)\),平均代价是 \(O(1)\)
  • 二进制计数器递增 INCREMENT:每次递增计数器
    • 如果进行最坏情况分析,最坏情况可能需要反转所有的位,得到复杂度 \(O(nk)\)
    • 但是我们知道,每次递增计数器,只有最低位需要反转,下一位每两次递增需要反转一次,以此类推。对等比数列进行求和可以得到总代价是 \(O(n)\),从而平均代价是 \(O(1)\)

很朴素的一种分析方法。

核算法

核算法计算每个操作的代价和信用,用前面操作的信用来支付后面操作的代价。核算法要求摊还代价满足:\(\sum \hat{c}_i \geq \sum c_i\)

例子:

  • MultiPop
    • Push 操作:实际代价 1,产生信用 1
    • Pop 操作:实际代价 1,消耗信用 1
    • MultiPop 操作:实际代价 \(k\),消耗信用 \(k\)
    • 将信用和代价合并,得到 Push 操作摊还代价是 2,其余两个操作摊还代价是 0。
  • 双栈实现队列:
    • 入队操作:实际代价 1,产生信用 2。摊还代价 3。
    • 出队操作:总是产生信用 2
      • 如果栈 2 不为空,直接出栈,实际代价 1。摊还代价 3。
      • 如果栈 2 为空,将栈 1 全部出栈到栈 2,实际代价 \(2n + 1\)。每次移动元素消耗 2 个信用,共消耗 \(2n\) 个信用。摊还代价 \(2n + 1 - 2n + 2 = 3\)
    • 综上,所有操作的摊还代价都是 3。

势能法

势能法将特定结构与势能关联,操作引起势能的变化。我们通过势能变化来计算摊还代价:\(\sum \hat{c}_i = \sum c_i + \sum \Phi_i - \Phi_{\text{init}}\)

例子:

  • MultiPop 中,使用栈的大小作为势能。
动态数组的扩张

动态数组插入的最坏情况是 \(O(n)\)(创建新数组并拷贝 \(n\) 个元素),简单分析得出插入操作上界为 \(n*O(n)=O(n^2)\)。然而并不是每次插入都是 \(O(n)\),这个上界并不好。

采用摊还分析,计算如下:

\[ \frac{(1+1+1+\dots)+(1+2+4+\dots)}{n}=\frac{n+\lfloor\log_2(n-1)\rfloor+1}{n}\leq3 \]

得到插入的平均时间复杂度为 \(O(1)\)

红黑树的插入
回顾红黑树