O(n)算法在计算时间方面是否可以超过O(n ^ 2)?

时间:2014-03-23 17:14:55

标签: big-o complexity-theory time-complexity

假设我有两种算法:

for (int i = 0; i < n; i++) {
  for (int j = 0; j < n; j++) {
    //do something in constant time
  }
}

这自然是O(n^2)。假设我也有:

for (int i = 0; i < 100; i++) {
  for (int j = 0; j < n; j++) {
    //do something in constant time
  }
}

这是O(n) + O(n) + O(n) + O(n) + ... O(n) + = O(n)

似乎即使我的第二个算法是O(n),也需要更长的时间。有人可以扩展吗?我提出它是因为我经常看到算法,例如,他们将首先执行排序步骤或类似的事情,并且在确定总复杂度时,它只是限制算法的最复杂元素。

6 个答案:

答案 0 :(得分:98)

渐近复杂度(大O和大Theta所代表的)完全忽略了所涉及的常数因素 - 它仅用于指示随着输入的大小变大,运行时间将如何变化。

对于某些给定的Θ(n)Θ(n2)算法肯定可能需要比n算法更长的时间 - n这将会发生真正取决于涉及的算法 - 对于您的具体示例,n < 100就是这种情况,忽略了两者之间不同的优化可能性。

对于分别花费Θ(n)Θ(n2)时间的任何两个给定算法,您可能会看到的是:

  • Θ(n)较小时,n算法较慢,Θ(n2)一个因n增加而变慢 (如果Θ(n)更复杂,即具有更高的常数因子),则会发生这种情况,或
  • Θ(n2)总是慢一些。

尽管可能 Θ(n)算法可能更慢,但Θ(n2)一个,然后再Θ(n)一个,依此类推{ {1}}增加,直到n变得非常大,从那时起n一个总是会变慢,尽管它不太可能发生。

稍微更多的数学术语:

假设Θ(n2)算法对某些Θ(n2)进行cn2次操作。

c算法对某些Θ(n)进行dn次操作。

这与the formal definition一致,因为我们可以假设这适用于大于0的d(即对于所有n)以及运行时间所在的两个函数是一样的。

根据您的示例,如果您要说nc = 1,那么d = 100算法会慢到Θ(n),此时n = 100 Θ(n2) 1}}算法会变慢。

(由WolframAlpha提供)

符号说明:

技术上,big-O只是一个上限,这意味着您可以说O(1)算法(或者实际上任何算法花费O(n2)或更少的时间)也需要O(n2)。因此我改为使用big-Theta(Θ)符号,这只是一个紧密的约束。有关详细信息,请参阅the formal definitions

Big-O经常被非正式地视为或被教导成为一个紧张的界限,所以你可能已经基本上使用了big-Theta而不知道它。

如果我们只谈论一个上限(根据big-O的正式定义),那就更像是“任何事情” - O(n)一个可以更快,{{ 1}}一个可以更快或者它们可以花费相同的时间(渐近) - 关于比较算法的大O,通常无法得出特别有意义的结论,一个只能比如说,给定一些算法的大O,该算法不会花费那么多的时间(渐近)。

答案 1 :(得分:19)

,O(n)算法在运行时间方面可以超过O(n 2 )算法。这个发生在常数因子(我们在大O表示法中省略)很大时。例如,在上面的代码中,O(n)算法将具有很大的常数因子。因此,它将比在O(n 2 )中运行的算法表现更差,因为n

这里,n = 100是交叉点。因此,当任务可以在O(n)和O(n 2 )中执行时线性算法的常数因子多于二次算法,因此我们倾向于选择运算时间较差的算法。 例如,在对数组进行排序时,我们会切换到较小数组的插入排序,即使合并排序或快速排序运行渐进式更快。这是因为插入排序的常数因子比合并/快速排序小,并且运行速度更快。

答案 2 :(得分:17)

O(n)并不意味着比较不同算法的相对速度。它们用于测量当输入大小增加时运行时间增加的速度。例如,

  • O(n)表示如果n乘以1000,则运行时间大致乘以1000.
  • O(n^2)表示如果n乘以1000,则运行大致乘以1000000。

因此,当n足够大时,任何O(n)算法都会超过O(n^2)算法。它并不代表任何固定n的内容。

答案 3 :(得分:5)

长话短说,是的,它可以。 O的定义基于O(f(x)) < O(g(x))暗示g(x)明确需要花费更多时间才能运行f(x)给定足够大的x。< / p>

例如,一个众所周知的事实是,对于小值,合并排序优于插入排序(如果我没记错的话,那应该适用于小于n的{​​{1}}

答案 4 :(得分:3)

是。 O()仅表示渐近复杂度。如果线性算法具有相同的足够大的线性减速常数(例如,如果循环的核心运行时间长10倍,那么它的二次型会慢一些),线性算法可以像二次曲线一样慢。

O() - 符号只是一个推断,虽然是一个非常好的。

答案 5 :(得分:3)

唯一的保证就是 - 无论常数因素 - 足够大n,O(n)算法花费的操作少于O(n ^ 2)一个。

作为一个例子,让我们计算OPs整洁的例子中的操作。 他的两个算法只有一行不同:

for (int i = 0; i < n; i++) {       (* A, the O(n*n) algorithm. *)

VS。

for (int i = 0; i < 100; i++) {     (* B, the O(n) algorithm. *)

由于他的其他程序是相同的,实际运行时间的差异将由这两行决定。

  • 对于n = 100,两条线都进行100次迭代,因此A和B在n = 100处执行完全相同。
  • 对于n <100,例如,n = 10,A仅进行10次迭代,而B进行100次。显然A更快。
  • 对于n> 100,例如,n = 1000。现在A的循环进行1000次迭代,而B循环仍然进行固定的100次迭代。显然A比较慢。

当然,要使O(n)算法更快,n的大小取决于常数因子。如果您将B中的常量100更改为1000,则截止值也会更改为1000.