Big-O表示法什么时候失败?

时间:2009-06-02 18:57:22

标签: algorithm language-agnostic theory big-o

Big-O表示法[1]在实践中失败的一些例子是什么?

也就是说:算法的Big-O运行时间何时会预测算法A比算法B快,但实际上算法B在运行时会更快?

稍微宽泛一点:何时对算法性能不匹配的理论预测观察到运行时间?非Big-O预测可能基于搜索树中的平均/预期旋转次数,或排序算法中的比较次数,表示为因子乘以元素数。

澄清

尽管有些答案说,但Big-O符号 意味着预测算法性能。也就是说,它是一个有缺陷的工具:它只谈论渐近性能,并且模糊了常数因素。这样做有一个原因:它的意思是预测算法性能,而不依赖于您执行算法的计算机。

我想知道的是:这个工具的缺陷什么时候显示出来?我发现Big-O符号非常有用,但远非完美。什么是陷阱,边缘情况,陷阱?

我正在寻找的一个例子:使用Fibonacci堆而不是二进制堆运行Dijkstra的最短路径算法,得到O(m + n log n)时间对O((m + n)log n) ,对于n个顶点和m个边。你希望迟早会从斐波纳契堆中提速,但是说我的实验中速度增加从来没有实现过。

(实验证据,没有证明,表明在均匀随机边缘权重上运行的二进制堆花费O(1)时间而不是O(log n)时间;这是实验的一个重要问题。另一个是计数的婊子是对DecreaseKey的预期调用次数。

[1]实际上不是符号失败,而是符号所代表的概念,以及预测算法性能的理论方法。 < /抗学究气>

根据接受的答案

我接受了一个答案来强调我希望的那种答案。许多不同的答案同样存在:)我喜欢的答案是,它建议了Big-O表示法“失败”时的一般规则(当缓存未命中占据执行时间时),这也可能增加理解(在某种意义上)我不知道如何最好地表达ATM)。

18 个答案:

答案 0 :(得分:104)

它恰好在一种情况下失败:当人们试图将它用于某些不适合它的东西时。

它告诉你算法如何扩展。它没有告诉你它有多快。

Big-O表示法并不能告诉您在任何特定情况下哪种算法会更快。它只告诉你,对于足够大的输入,一个会比另一个更快。

答案 1 :(得分:27)

当N很小时,常数因子占主导地位。查找五个项目数组中的项目可能比在哈希表中查找项目更快。

答案 2 :(得分:17)

简短回答:当n很小时。当您只有三个目的地时,快速解决旅行商问题(但是,在万亿元素列表中找到最小数字可以持续一段时间,尽管这是O(n)。)

答案 3 :(得分:14)

规范示例是Quicksort,其最差时间为O(n ^ 2),而Heapsort为O(n logn)。然而,在实践中,Quicksort通常比Heapsort更快。为什么?有两个原因:

  • Quicksort中的每次迭代都比Heapsort简单得多。更重要的是,它可以通过简单的缓存策略轻松优化。

  • 最糟糕的情况很难被击中。

但恕我直言,这并不意味着'大O失败'无论如何。第一个因素(迭代时间)很容易纳入您的估算。毕竟,大O数应该乘以这个几乎恒定的事实。

如果你得到的是摊销数字而不是平均数,那么第二个因素会消失。他们可能更难估计,但要讲一个更完整的故事

答案 4 :(得分:9)

Big O失败的一个领域是内存访问模式。 Big O仅计算需要执行的操作 - 如果算法导致更多缓存未命中或需要从磁盘中分页的数据,则无法跟踪。对于小N,这些效应通常占主导地位。例如,由于存储器访问,通过100个整数的数组的线性搜索可能会通过100个整数的二叉树击败搜索,即使二叉树很可能需要较少的操作。每个树节点都会导致缓存未命中,而线性搜索将主要针对每次查找命中缓存。

答案 5 :(得分:9)

Big-O描述了算法的效率/复杂性,而不一定描述了给定代码块的实现的运行时间。这并不意味着Big-O失败了。它只是意味着它并不意味着预测运行时间。

查看this question的答案,了解Big-O的绝佳定义。

答案 6 :(得分:8)

  1. 对于大多数算法,存在“平均情况”和“最坏情况”。如果您的数据通常属于“最坏情况”的情况,那么另一种算法可能在理论上在平均情况下效率较低,可能对您的数据更有效。

  2. 某些算法也具有您的数据可以利用的最佳案例。例如,一些排序算法具有可怕的理论效率,但如果数据已经排序(或几乎如此),则实际上非常快。另一种算法虽然理论上在一般情况下更快,但可能无法利用数据已经排序并且在实践中表现更差的事实。

  3. 对于非常小的数据集,有时候由于“k”值较大,理论效率较高的算法实际上可能效率较低。

答案 7 :(得分:7)

一个例子(我不是专家)是线性编程的单纯形算法在任意输入上具有指数最坏情况的复杂性,即使它们在实践中表现良好。一个有趣的解决方案是考虑“平滑的复杂性”,它通过查看任意输入的小随机扰动来混合最坏情况和平均情况性能。

Spielman and Teng (2004)能够证明阴影顶点单纯形算法具有多项式平滑复杂度。

答案 8 :(得分:4)

这在某种程度上取决于Big-O测量的内容 - 当它是最糟糕的情况时,它通常会“失败”,因为运行时性能将比Big-O建议的要好得多。如果是平均情况,那可能会更糟。

如果算法的输入数据具有某些先验信息,则Big-O表示法通常“失败”。通常,Big-O表示法指的是最坏的情况复杂性 - 如果数据完全随机或完全非随机,通常会发生这种情况。

例如,如果您将数据提供给已分析的算法并且big-o基于随机数据,但您的数据具有非常明确的结构,则结果时间可能比预期快得多。出于同样的原因,如果您正在测量平均复杂度,并且您提供的数据非常随机化,那么该算法可能会比预期的更糟糕。

答案 9 :(得分:4)

Big O 说,例如算法A比算法B运行得更快。可以说,当输入增长时,算法A使用的时间或空间以与算法B不同的速率增长。但是,对于任何特定的输入大小,大O表示法都没有说明一种算法相对于另一种算法的性能。

例如,A可能每次操作较慢,但是具有比B更好的大O.B对于较小的输入更有效,但是如果数据大小增加,则会有一些截止点,其中A变得更快。 Big-O本身并没有说明截止点在哪里。

答案 10 :(得分:3)

  1. 小N - 对于今天的电脑来说,100可能太小而无法担心。
  2. 隐藏乘数 - IE合并与快速排序。
  3. 病理案例 - 再次,合并与快速

答案 11 :(得分:3)

一般的答案是Big-O允许你通过隐藏常数因素而变得非常草率。正如问题中所提到的,Fibonacci Heaps的使用就是一个例子。 Fibonacci Heaps do 具有很好的渐近运行时间,但实际上常量因子太大而无法用于现实生活中遇到的数据集大小。

Fibonacci Heap通常用于证明图相关算法渐近复杂度的良好下界。

另一个类似的例子是矩阵乘法的Coppersmith-Winograd algorithm。它是目前用于矩阵乘法的最快已知渐近运行时间的算法,O(n 2.376 )。但是,它的常数因素实在太大而不适用。与Fibonacci Heaps一样,它经常被用作其他算法的构建块来证明理论时间界限。

答案 12 :(得分:2)

Big-Oh表示法失败的一个广泛领域是数据量超过可用RAM量时。

使用排序作为一个例子,在最佳情况下,排序所需的时间量不受比较或交换次数(其中分别有O(n log n)和O(n))的支配。 )。时间量由磁盘操作数量决定:块写入和块读取。

为了更好地分析处理超过可用RAM的数据的算法,I / O模型诞生了,您可以计算磁盘读取的数量。在那里,您考虑三个参数:

  • 元素数量N;
  • 内存量(RAM),M(可以在内存中的元素数量);和
  • 磁盘块的大小,B(每块的元素数量)。

值得注意的是磁盘空间量不足;这被视为无限。典型的额外假设是M> 1。乙 2

继续排序示例,您通常喜欢在I / O情况下进行合并排序:将元素划分为大小为θ(M)的块,并将它们排序在内存中(例如,快速排序)。然后,通过从每个块读取第一个块到内存中来合并它们的θ(M / B),将所有元素填充到堆中,并重复选择最小元素,直到您选择了它们中的B。将此新合并块写出并继续。如果你耗尽了其中一个读入内存的块,请从同一个块读取一个新块并将其放入堆中。

(所有表达式都应该被视为大θ)。您形成N / M排序的块然后合并。合并N / M次的log(base M / B);每次读取和写入所有N / B块时,都需要N / B *(基准M / B的N / M)时间。

您可以分析内存中的排序算法(经过适当修改以包括块读取和块写入),并且看到它们的效率远远低于我提供的合并排序。

这些知识是由Arge和Brodal(http://daimi.au.dk/~large/ioS08/)的I / O算法课程提供的;我还进行了验证理论的实验:一旦超过内存,堆排序就会“几乎无限”。快速排序变得难以忍受地慢,合并排序几乎非常缓慢,I / O效率合并排序表现良好(最好的一堆)。

答案 13 :(得分:2)

我见过一些情况,随着数据集的增长,算法的复杂性变得不如内存访问模式重要。在某些情况下,使用智能算法导航大型数据结构会导致更多的页面错误或缓存未命中,而不是使用更糟糕的大O的算法。

对于小n,两种算法可以比较。随着n的增加,更智能的算法表现更好。但是,在某些时候,n增长到足以使系统屈服于内存压力,在这种情况下,“更糟糕”的算法实际上可能表现得更好,因为常量基本上是重置的。

但这并不是特别有趣。当你达到这个反转点时,两种算法的性能通常是不可接受的,你必须找到一种新的算法,它具有更友好的内存访问模式和更好的大O复杂性。

答案 14 :(得分:1)

这个问题就像在问:“一个人的智商何时在实践中失败?”很明显,拥有高智商并不意味着你会在生活中取得成功,智商低并不意味着你会死亡。然而,我们将智商作为评估潜力的一种手段来衡量,即使它不是绝对的。

在算法中,Big-Oh表示法为您提供算法的IQ。这并不一定意味着算法对于您的特定情况将表现最佳,但是有一些数学基础表明该算法具有一些良好的潜力。如果Big-Oh表示法足以衡量性能,那么你会看到更多,并且运行时测试更少。

将Big-Oh视为一个范围而不是一个或多或少的具体指标。最佳案例场景和最坏情况场景以及介于两者之间的大量场景。根据Big-Oh范围内的算法选择算法,但不要将符号作为衡量性能的绝对值。

答案 15 :(得分:1)

当您的数据不适合模型时,big-o表示法仍然有效,但您会看到最佳和最差情况下的重叠。

此外,某些操作针对线性数据访问与随机数据访问进行了调整,因此,如果调用它的方法从设计更改,则一个算法虽然在周期方面优越,但可能会非常慢。同样,如果算法导致页面/缓存未命中由于其访问内存的方式,Big-O将无法准确估计运行流程的成本。

显然,正如我已经忘记的那样,当N很小时

答案 16 :(得分:0)

简短的回答:当你开始使用大量内存时,总是在现代硬件上。教科书假设内存访问是统一的,而不再是。您当然可以对非统一访问模型进行Big O分析,但这有点复杂。

小n个案例很明显但并不有趣:足够快就足够了。

实际上,在使用Delphi,Java,C#和Smalltalk中的标准集合时,我遇到了几百万个对象的问题。对于较小的那些,主导因素被证明是哈希函数或比较

答案 17 :(得分:0)

Robert Sedgewick在他的Coursera课程中讨论了关于算法分析的大O符号的缺点。他称之为特别令人震惊的例子银河算法,因为虽然它们的复杂性类别比它们的前辈更好,但它需要输入天文尺寸才能在实践中显示。

https://www.cs.princeton.edu/~rs/talks/AlgsMasses.pdf