什么是O(…),如何计算?


31

救命!我有一个问题需要分析算法或某些代码的Big-O。

  • 我不确定究竟是什么Big-O或Big-O与Big-Theta或与其他分析算法复杂性的方式之间的关系。

  • 我不确定Big-O是指运行代码的时间,还是所需的内存量(空间/时间折衷)。

  • 我有计算机科学作业,需要做一些循环,也许是递归算法,并为此设计出Big-O。

  • 我正在开发一个程序,我可以在两个数据结构或算法之间选择一个已知的Big-O,并且不确定要选择哪个。

我如何了解如何计算Big-O并将其应用到我的程序,作业或计算机科学常识中?

注意:此问题是社区确定的其他Big-O问题的规范重复目标。能够包含许多Big-O问题的大量有用信息的意图是广泛的。请不要使用如此广泛的事实来表示可以接受类似的问题。


1
请注意,这个问题正在meta上讨论。
enderland

2
可汗学院课程是一个很好的入门资源(CLRS的托马斯·科门是其中一位作家)。对于我作为CS毕业生来说,这也是一个很好的资源。 khanacademy.org/computing/computer-science/algorithms
Sudip Bhandari

3
给举报该问题的人员:请阅读问题底部的亚文字,并在举报或投票关闭之前点击该链接。

Answers:


22

O(...)指的是Big-O表示法,它是一种简单的方法,用于描述算法执行某项操作需要执行多少次操作。这称为时间复杂度

用Big-O表示法,算法的成本由其最昂贵的大量操作表示。如果算法采取步骤,它将表示为O(n 3)。计算列表中每个项目的算法将以O(n)时间(称为线性时间)运行。n3 + n2 + n

有关维基百科上的名称和经典示例的列表:常用功能的顺序

相关资料:


6
注意:大O本身并不度量时间或空间或任何特定事物。它只是一个函数(直到一个常数)的渐近增长的上限。该函数可以是算法的时间,空间等,取决于其输入长度,并且在CS上下文中最常见的是时间,但不是必须的。
恢复莫妮卡

23

渐近函数是什么?无论如何,渐近线是什么?

给定一个函数f(n),该函数描述当算法应用于大小为n的输入时算法消耗的资源量(CPU时间,RAM,磁盘空间等),我们最多定义三个渐近符号来描述其性能。大n

渐近线(或渐近函数)只是g(n)的其他某些函数(或关系),随着n越来越大,f(n)越来越接近,但从未达到。讨论渐近函数的好处是,即使f(n)的表达式极其复杂,它们通常也更易于讨论。渐近函数用作限制 f(n)上下的边界符号的一部分。

(注意:从这里使用的意义上来说,渐进函数仅在校正了一些恒定的非零因子后才接近原始函数,因为所有三个big-O /Θ/Ω表示法都忽略了它们的考虑。)

三种渐近边界符号是什么?它们有何不同?

这三种符号的用法如下:

f(n)= O(g(n))

其中f(n)是感兴趣的函数,而g(n)是您尝试近似f(n)的其他一些渐近函数。严格意义上,这不应该视为等式,而应正式说明f(n)相对于ng(n)相比增长多快,随着n变大。纯粹主义者通常会使用替代符号f(n)∈O(g(n))来强调符号O(g(n))实际上是具有相同增长率的函数的整个

Big-ϴ(θ)表示在f(n)增长到一个恒定因子之前相等(稍后会详细介绍)。它的行为类似于操作员的增长率。=

Big-O表示法描述了f(n)增长的上限。它的行为类似于操作员的增长率。

Big-Ω(Omega)表示随着f(n)的增长而下界。它的行为类似于操作员的增长率。

还有许多其他渐近符号,但是它们在计算机科学文献中的出现频率并不高。

大O表示法及其类似方法通常是比较时间复杂度的一种方法。

什么是时间复杂度?

时间复杂度是时间T(n)的一个奇特术语,即算法根据其输入大小n执行函数所花费的时间。这可以通过实时量(例如秒),CPU指令数等来度量。通常假设该算法将在您的日常von Neumann体系结构计算机上运行。但是,当然,您可以使用时间复杂度来谈论更多奇异的计算系统,但情况可能有所不同!

使用Big-O表示法谈论空间复杂性也是很常见的。空间复杂度是完成算法所需的内存(存储)量,可能是RAM,磁盘等。

可能是一种算法较慢但使用较少的内存,而另一种算法较快但使用更多的内存。如果资源受到不同限制,则每种方法在不同情况下可能更合适。例如,嵌入式处理器可能具有有限的内存并倾向于使用较慢的算法,而数据中心中的服务器可能具有大量的内存并倾向于使用较快的算法。

计算大ϴ

计算算法的大重点是一个可以填满一本小本教科书或大约半学期的本科课程的主题:本节将介绍基础知识。

给定伪代码中的函数f(n)

int f(n) {
  int x = 0;
  for (int i = 1 to n) {
    for (int j = 1 to n) {
      ++x;
    }
  }
  return x;
}

时间的复杂度是多少?

外循环运行n次。每次外循环运行一次,内循环运行n次。这将运行时间设为T(n)= n 2

考虑第二个功能:

int g(n) {
  int x = 0;
  for (int k = 1 to 2) {
    for (int i = 1 to n) {
      for (int j = 1 to n) {
        ++x;
      }
    }
  }
  return x;
}

外循环运行两次。中间循环运行n次。每次中间循环运行一次,内部循环运行n次。这将运行时间设为T(n)= 2n 2

现在的问题是,两个函数的渐近运行时间是多少?

要计算这一点,我们执行两个步骤:

  • 删除常量。随着算法由于输入而增加时间,其他术语主导了运行时间,使其变得不重要。
  • 除去除最大期限外的所有词语。随着n达到无穷大,n 2迅速超过n

他们在这里的重点重点词,并简化为这些词

T(N)= N 2 ∈Θ(N 2
T(N)= 2 2 ∈Θ(N 2

如果我们有另一个具有多个术语的算法,则可以使用相同的规则对其进行简化:

T(n)= 2n 2 + 4n + 7∈ϴ(n 2

所有这些算法的关键是我们关注最大的项并删除常量。我们不是在看实际的运行时间,而是相对的复杂性

计算Big-Ω和Big-O

首先要注意的是,在非正式文学中,“ Big-O”通常被视为Big-Θ 的同义词,这也许是因为希腊字母的输入方式比较棘手。因此,如果突然有人要求您提供算法的Big-O,他们可能会想要Big-Θ。

现在,如果您确实确实想按照前面定义的形式来计算Big-Ω和Big-O ,则会遇到一个主要问题:对于任何给定的函数,都有无限多个Big-Ω和Big-O描述!这就像问小于或等于42的数字是多少。有很多可能性。

对于具有T(n)∈ϴ(n 2的算法,以下任何形式上正确有效的陈述:

  • T(n)∈O(n 2
  • T(n)∈O(n 3
  • T(n)∈O(n 5
  • T(n)∈O(n 12345 ×e n
  • T(n)∈Ω(n 2
  • T(n)∈Ω(n)
  • T(n)∈Ω(log(n))
  • T(n)∈Ω(log(log(n)))
  • T(n)∈Ω(1)

但是陈述T(n)∈O(n)T(n)∈Ω(n 3是不正确的。

什么是相对复杂度?有哪些类别的算法?

如果我们比较两种不同的算法,随着输入到无穷大,它们的复杂性通常会增加。如果我们查看不同类型的算法,它们可能会保持相对不变(例如,相差一个常数)或可能会有很大差异。这就是执行Big-O分析的原因:确定算法在大输入下是否会合理执行。

算法类别细分如下:

  • Θ(1)-恒定 例如,选择列表中的第一个数字将始终花费相同的时间。

  • Θ(n)-线性 例如,迭代列表将始终花费与列表大小n成正比的时间。

  • Θ(log(n))-对数(基数通常无关紧要)。在每个步骤中划分输入空间的算法(例如二进制搜索)就是示例。

  • Θ(n×log(n))-线性乘以对数(“ linearithmic”)。这些算法通常在分割和征服(log(n))的同时仍迭代(n)所有输入。许多流行的排序算法(合并排序,Timsort)都属于此类。

  • Θ(n m)-多项式(n升为任意常数m)。这是一个非常常见的复杂性类,通常在嵌套循环中找到。

  • Θ(m n)-指数(任何常数m升至n)。许多递归和图形算法都属于此类。

  • Θ(n!)-阶乘 某些图形和组合算法是阶乘复杂性。

这与最佳/平均/最坏情况有关吗?

不可以 。Big-O及其表示法家族谈论一种特定的数学函数。它们是用来帮助表征算法效率的数学工具,但是最佳/平均/最坏情况的概念与此处所述的增长率理论无关。

要谈论一种算法的Big-O,必须致力于一种算法的特定数学模型,该模型仅具有一个参数n,无论用什么意义,该参数都应描述输入的“大小”。但是在现实世界中,输入的结构不仅仅是长度。如果这是一个排序算法,我可以养活的字符串"abcdef""fedcba""dbafce"。它们的长度均为6,但是其中一个已经排序,一个已经反转,最后一个只是一个随机的杂物。如果输入已经排序,则某些排序算法(例如Timsort)会更好地工作。但是,如何将这种不均匀性纳入数学模型呢?

典型的方法是简单地假设输入来自某种随机的概率分布。然后,您对长度为的所有输入求平均算法的复杂度n。这为您提供了算法的平均情况复杂度模型。在这里,您可以像往常一样使用Big-O /Θ/Ω表示法来描述平均情况下的行为。

但是,如果您担心拒绝服务攻击,那么您可能会更加悲观。在这种情况下,可以更安全地假设唯一的输入是那些会给您的算法带来最大痛苦的输入。这为您提供了算法的最坏情况复杂度模型。之后,您可以讨论最坏情况模型的Big-O /Θ/Ω等。

同样,您也可以将兴趣完全集中在算法遇到最少麻烦的输入上,以得出最佳情况模型,然后查看Big-O /Θ/Ω等。


这是一个很好的答案,但是Big-O和Big-Ω与最坏和最好的情况无关。它们是上限和下限,但是它们都可以应用于任何一种情况,例如,在最佳情况下,插入排序的时间复杂度为ϴ(n)(Big-O和Big-Ω),而在最坏情况下为时间复杂度ϴ(n²)(Big-O和Big-Ω)。
保罗

另外,除空算法外,任何算法都Ω(1)处于最佳,最差和平均情况下,但这是因为这是一个下限,并且与该算法的实际最佳情况无关。
保罗

大事不管与最佳或最坏情况无关 –这是一个常见的误解。它们仅仅是说明确定性函数与另一个函数相比是更快,更慢还是以相同的速度增长的方式。最好/最坏情况的概念仅在您开始谈论实算法的时间/空间复杂性时才存在,在这种情况下,会出现不确定性因素,您需要考虑输入的概率分布。即使这样,最好/最坏的情况仍在与big-O / Omega 正交的轴上。
Rufflewind

@Rufflewind如果您认为可以更好地描述它,请继续。您确实意识到这是社区Wiki的答案,对不对?

“首先要注意的是,在非正式文学中,“ Big-O”通常被视为Big-Θ的同义词。”->不仅在非正式文学中。我回到大学的第一个学期关于复杂性,就告诉我们Big-O的定义,就好像它是Big-Θ。(我们的教科书使用了这个定义!)当我进入“复杂性II”时,这让我感到非常困惑,我发现两者并不相同。
T. Sar-恢复莫妮卡'18
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.