Keyboard shortcuts

Press or to navigate between chapters

Press ? to show this help

Press Esc to hide this help

摊还分析(进阶)

复习

  • 最好、最坏与平均情况:同一算法在不同输入上耗时不同
  • 最坏与平均:最坏情况是一种保证,平均情况依赖输入假设
  • 时空权衡:用空间常常可以换时间

本章为进阶内容,零基础读者可以跳过,不影响后续阅读。

TL;DR

  • 摊还分析看的是一连串操作的平均代价
  • 偶尔一次昂贵,会被许多次廉价操作摊平
  • 它不依赖输入分布,和平均情况是两回事
  • 动态数组的扩容是经典例子

正文

  上一章我们把“最好、最坏、平均”分开了。这一章再介绍一种更精细的视角:摊还分析(amortized analysis)。它专门处理那种“平时很便宜,偶尔突然很贵”的操作。

  先看那个经典例子:动态数组

平时 O(1),偶尔 O(n)

  动态数组的容量是有限的。只要还有空位,往末尾追加一个元素就很便宜,是 O(1)

  可一旦装满了,就得扩容:申请一块更大的空间,把原来的元素全部复制过去,再追加新元素。这一次复制的代价,是 O(n)

  于是问题来了:这个数组的插入,到底是快还是慢?

  • 单独看最坏的一次,是 O(n)
  • 但它不是每次都很贵,只在装满时才贵一次

摊平来看

  关键在这里:如果我们每次扩容都把容量翻倍,那么每次昂贵的扩容之后,都要经过差不多同样多次的廉价插入,才会再次装满。

  把这一连串操作合起来平均一下,昂贵的复制成本,就被分摊到了中间那许多次廉价插入上。算下来,平均每次插入的代价仍然是 O(1)

  这就是摊还的含义:不是看某一次操作有多贵,而是看一长串操作的总代价,平均到每一次是多少。偶尔的巨额开销,被大量的廉价操作“摊”平了。

  可以这样记账:每次普通插入,除了完成插入,还悄悄“存”下一点信用;等扩容时,就用攒下的信用来支付那次昂贵的复制。总体一算,收支平衡。

它和“平均情况”不是一回事

  这里必须划清界限:摊还分析不等于平均情况分析。

  • 平均情况依赖对输入分布的假设:输入长什么样,是一次赌博
  • 摊还不依赖任何概率假设:它保证的是“任何一串操作的总代价,都落在这个界限内”

  换句话说,摊还是一种确定性的保证,而不是统计期望。哪怕你运气极差、每次都刚好触发扩容,把总代价平均下来,依然不会超过摊还的界限。

  所以,下次看到“单次 O(n)”和“摊还 O(1)”并存时,别觉得矛盾——它们说的是两件不同的事:一次有多坏,和一串平均有多好。

思考题 1

  动态数组单次插入最坏是 O(n),为什么还说它的插入摊还是 O(1)

思考题 2

  摊还分析和“平均情况”有什么不同?哪个更接近一种保证?

小结

知识点

  • 摊还分析看一长串操作的平均代价
  • 偶尔昂贵会被大量廉价操作摊平
  • 动态数组扩容是典型例子
  • 摊还是确定性保证,不依赖输入分布

参考资料

  1. Wikipedia(zh):摊还分析:估算一连串操作的平均代价
  2. Wikipedia(zh):动态数组:容量可自动扩展的数组

思考题答案(仅供参考)

思考题 1

  因为昂贵只在“扩容”那一次发生,而扩容后要经过许多次廉价插入才会再次扩容。把扩容成本分摊到这些廉价操作上,平均每次的代价仍是常数级。所以单次最坏是 O(n),摊还却是 O(1)

思考题 2

  平均情况依赖对输入分布的假设,是一种统计期望;摊还分析则不做任何概率假设,保证的是任意一串操作的总代价都在界限之内。因此,摊变更接近一种确定性的保证,而不是赌博式的期望。

协议

  本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。

封面图

设计师 | 南国微雪