算法的摊销分析是什么?

95

5
可能适合放在 http://programmers.stackexchange.com 上。 - lanzz
2
@lanzz 或许现在应该属于cs.stackexchange.com - nbro
一个关于常摊时间含义的好帖子。 - RBT
7个回答

97

摊销分析不是简单地将调用次数乘以一个调用的最坏情况。

例如,对于动态数组在需要时会翻倍大小,常规的渐近分析只会得出添加一个项的成本为O(n),因为它可能需要增长并复制所有元素到新数组。摊销分析考虑到为了增长,必须添加n/2个项而不引起增长自上次增长以来,因此添加一个项实际上只需要O(1)(O(n)的成本摊销在n/2个操作上)。

摊销分析与“平均性能”不同-摊销分析提供了一个硬性保证,即如果执行了这么多操作,性能将做什么。


4
摊销分析考虑到为了使容器增长,必须在上一次扩容之后添加n/2个元素而不导致容器增长。因此,添加一个元素实际上只需要O(1)的时间复杂度(O(n)的代价摊销在n/2个操作中)。 - Aleksandr Hovhannisyan
@AleksandrH 有特定的部分吗? - harold
2
是的,如果没有解释数字的来源,就很难理解数学内容了。 - Aleksandr Hovhannisyan

49

有很多关于“什么”的答案,但是没有关于“为什么”的答案。

正如其他人所说,渐近分析是关于给定操作在大数据集上的性能缩放的方式。摊销分析是关于所有大数据集上操作的性能平均值如何缩放的方式。摊销分析从不给出比渐近分析更差的界限,有时会给出更好的界限。

如果您关心较长作业的总运行时间,那么摊销分析的更好界限可能是您关心的问题。这就是为什么脚本语言(例如)经常愿意通过某个因素来增加数组和哈希表,即使这是一项昂贵的操作。(增长可以是 O(n)操作,但摊销是 O(1),因为它不经常执行。)

如果您正在进行实时编程(单个操作必须在可预测时间内完成),那么摊销分析的更好边界就无关紧要了。如果您没有及时完成操作,不能及时返回并调整链锯,那么平均速度是否快就不重要了...

哪一个对您的情况最重要取决于您的编程问题究竟是什么。


3
"增长操作可以是O(n)的,但摊销后是O(1),因为你很少这样做。"我认为这个陈述确实需要一个严谨的数学证明。" - nbro
如果你正在进行实时编程,你应该更加精确并解释清楚为什么那段话应该被视为“真实”。 - nbro
2
@nbro 你为什么认为“应该”?这个问题询问摊销分析与渐近分析的区别以及何时使用每种方法。它链接到解释如何执行它们的文章。因此,数学分析似乎是多余的。至于实时编程,我已经解释了。实时编程是指必须在可预测时间内完成各个操作的编程。一个典型的例子是嵌入式编程,需要定期监视某些内容,例如控制机械。对于这种情况,偶尔出现的缓慢操作是不可接受的。 - btilly

27

渐近分析

这个术语指的是在算法操作的数据(即输入)“足够大,使得进一步增加其大小不会改变结论”的假设下进行的算法性能分析。虽然不需要指定输入的确切大小(我们只需要上限),但必须指定数据集本身。

需要注意的是,到目前为止,我们只谈到了分析的方法;我们还没有具体说明我们正在分析哪些量(时间复杂度?空间复杂度?),也没有说明我们感兴趣的是哪个指标(最坏情况?最好情况?平均情况?)。

实际上,渐近分析通常指的是算法的上界时间复杂度,即总运行时间的最坏情况性能,用大O符号表示(例如,排序算法可能是 O(nlogn) )。

摊销分析

这个术语指的是基于针对最坏情况场景的特定操作序列来分析算法性能,尽管摊销分析并不指明被测量的是哪个量(仍然没有说明是时间复杂度还是空间复杂度),但它确实表明了度量指标是最坏情况性能。为了执行这种分析,我们需要指定输入的大小,但不需要做出任何关于其形式的假设。

用通俗易懂的话来说,摊销分析是选择一个任意的输入大小,然后“播放”算法。每当必须做出依赖于输入的决策时,都采取最坏的路径¹。算法完成后,将计算出的复杂度除以输入的大小即可得出最终结果。

¹注:精确来说,这是理论上最差的情况。如果您有一个向量,在其容量用尽时每次动态扩大两倍,“最坏情况”并不意味着假定它将在每次插入时都需要扩大,因为插入是按序列处理的。我们可以(实际上必须)使用已知的状态在数学上消除尽可能多的“更糟糕”的情况,即使输入仍然未知。

最重要的区别

渐进分析与分摊分析的关键区别在于前者取决于输入本身,而后者取决于算法执行的操作序列。

因此:

  • 渐进分析允许我们断言,在给定趋近于 N 的最好/最坏/平均情况输入时,算法的复杂度受某个函数 F(N)的限制--其中 N 是一个变量
  • 分摊分析允许我们断言,在给定未知特征但已知大小 N 的输入时,算法的复杂度不会更差,而是受到一个函数 F(N) 的值的限制--其中 N 是已知值

7
以上答案说明了为什么人们不应该盲目地点赞高排名者的长篇回答。 - btilly
2
@btilly:如果您的反馈是可操作的,那将更有用——也就是说,您能否给我一个确切的想法,这个答案到底有什么问题,以及如何改进它? - Jon
7
从哪里开始?你错误地定义了这两个术语,并提供了很多错误的澄清细节。以随机示例为例,摊销分析并不总是最坏情况。否则,我们就不能说在动态调整大小的哈希表中插入的摊销性能是“O(1)”了。 - btilly
@btilly《算法导论》第451页说:“分摊分析可以保证每个操作的平均性能在最坏情况下。” - Glen Selle
1
@GlenSelle 摊销分析是一种数学技术。它可以用于多种目的,包括最坏情况性能。然而,它不一定是最坏情况。在你的情况下,显然它被用于最坏情况。在哈希的情况下,它没有被用于最坏情况。 - btilly

17

这个问题的简洁定义来自于《算法导论》中的分摊分析章节的第一句话:

分摊分析中,执行数据结构操作所需的时间是对进行的所有操作进行平均。

我们通过渐进分析来表示程序增长的复杂度——也就是通过一个函数限制程序的增长,并定义该函数的最坏、最好或平均情况。

但是,在只有一个情况会导致程序复杂度达到峰值的情况下,使用渐进分析可能会误导人们,而实际上程序并不需要太多计算。

因此,即使单个操作可能很昂贵,在一系列操作中平均成本更有意义。 这就是分摊分析!

分摊分析是用于计算复杂度的另一种方法,它是渐进技术的替代品。它可以帮助我们以实用性为基础计算出更真实的复杂度,以便比较和决定两个或多个算法之间的差异。


5
我目前找到的最好的算法摊销分析参考资料,是在算法导论这本书的第17章“摊销分析”中。所有内容都在那里,比 Stack Overflow 帖子中能找到的解释要好得多。你可以在任何体面大学的图书馆里找到这本书。

是的。从提到的书中阅读摊销算法的内容更好,最终让人清晰明了。 - Rajesh Mappu

2
常规渐近分析是指针对问题规模的函数,渐进地分析单个操作的性能。O()符号表示了一种渐近分析方法。
摊销分析(也是一种渐近分析)则关注于多个操作在共享数据结构上的总性能。
不同之处在于,摊销分析通常证明M次操作所需计算的总量具有比单个操作最坏情况下M倍更好的性能保证。
例如,在大小为N的splay tree上进行单个操作可能需要O(N)时间。然而,在大小为N的树上进行M次操作的序列被限制在O(M(1+log N)+N log N)时间内,大约每个操作需要O(log N)的时间。但请注意,摊销分析比“平均情况”分析严格得多:它证明了任何可能的操作序列都满足其渐近最坏情况。

1
摊销分析处理的是例程运行次数的总成本以及可以从中获得的好处。例如,在未排序的n个项目的数组中搜索单个匹配项可能需要最多n次比较,因此其复杂度为O(n)。然而,如果我们知道同一数组将被搜索m次,则重复执行总任务的复杂度将为O(m*n)。但是,如果我们提前对数组进行排序,则成本为O(n log(n)),对于已排序的数组,连续搜索仅需要O(log(n))。因此,采用这种方法的m个元素的总摊销成本为O(n*log(n)+m*log(n))。如果m>=n,则通过预先排序相比不排序的O(n^2),这等同于O(n log(n))。因此,摊销成本更便宜。
简而言之,通过早期稍微多花一点钱,我们可以节省很多后期的成本。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接