"Big O"符号的通俗易懂解释是什么?(这是一个关于IT技术的问题)

5359

我希望尽可能少使用正式定义和简单的数学语言。


68
摘要:算法复杂度的上限。请参考类似问题Big O,如何计算/近似?以获得良好的解释。 - Kosi2801
7
其他答案已经很好了,只有一个细节需要理解:O(log n)或类似的表示它取决于输入的“长度”或“大小”,而不是其本身的值。这可能很难理解,但非常重要。例如,在每次迭代中将算法拆分成两部分时就会出现这种情况。 - Harald Schilly
17
麻烦您观看麻省理工学院“计算机科学与编程导论”课程第八讲中关于算法复杂性的演讲,链接为http://www.youtube.com/watch?v=ewd7Lf2dr5Q。该讲座用简单易懂的例子对算法复杂性进行了解释,虽然并非全是简单英语,但仍能清晰地阐述相关概念。 - ivanjovanovic
18
大 O 表示算法在最大迭代次数下的最坏情况性能估计。它用于评估函数的表现。 - Paul Sweatte
37
作为一名自学编程者,解释“大O符号”: “大O符号”是用于描述算法时间复杂度的记号。它表示算法所需执行基本操作的数量,以输入大小的函数形式表示。例如,在最坏情况下,执行n个元素的线性搜索需要O(n)个操作。这个符号非常有用,因为它可以帮助我们比较和选择不同算法之间的效率。然而,它并不考虑实际运行时间,只关注输入大小对执行时间的影响。 - Soner Gönül
显示剩余5条评论
43个回答

2
TLDR: 大O表示算法的性能,用数学术语来解释。
较慢的算法往往以n的x次方或更多运行,具体取决于其深度,而较快的算法(例如二分搜索)以O(log n)运行,随着数据集变大,运行速度更快。可以使用n或不使用n等其他术语来解释大O(例如:O(1))。
可以通过查看算法中最复杂的行来计算大O。
对于小型或未排序的数据集,大O可能会令人惊讶,因为像二分搜索这样的n log n复杂度算法在较小或未排序的集合中可能很慢。有关线性搜索与二分搜索的简单运行示例,请查看我的JavaScript示例:https://codepen.io/serdarsenay/pen/XELWqN?editors=1011(下面写有算法)。
function lineerSearch() {
  init();
  var t = timer('lineerSearch benchmark');
  var input = this.event.target.value;
  for(var i = 0;i<unsortedhaystack.length - 1;i++) {
    if (unsortedhaystack[i] === input) {
      document.getElementById('result').innerHTML = 'result is... "' + unsortedhaystack[i] + '", on index: ' + i + ' of the unsorted array. Found' + ' within ' + i + ' iterations';
      console.log(document.getElementById('result').innerHTML);
      t.stop(); 
      return unsortedhaystack[i]; 
    }
  }
}

function binarySearch () {
  init();
  sortHaystack();
  var t = timer('binarySearch benchmark');
  var firstIndex = 0;
  var lastIndex = haystack.length-1;
  var input = this.event.target.value;

  //currently point in the half of the array
  var currentIndex = (haystack.length-1)/2 | 0;
  var iterations = 0;

  while (firstIndex <= lastIndex) {
    currentIndex = (firstIndex + lastIndex)/2 | 0;
    iterations++;
    if (haystack[currentIndex]  < input) {
      firstIndex = currentIndex + 1;
      //console.log(currentIndex + " added, fI:"+firstIndex+", lI: "+lastIndex);
    } else if (haystack[currentIndex] > input) {
      lastIndex = currentIndex - 1;
      //console.log(currentIndex + " substracted, fI:"+firstIndex+", lI: "+lastIndex);
    } else {
      document.getElementById('result').innerHTML = 'result is... "' + haystack[currentIndex] + '", on index: ' + currentIndex + ' of the sorted array. Found' + ' within ' + iterations + ' iterations';
      console.log(document.getElementById('result').innerHTML);
      t.stop(); 
      return true;
    }
  }
}

2
从(source)可以读到:
大O符号是一种数学符号,描述了当参数趋于特定值或无穷大时函数的极限行为。在计算机科学中,大O符号用于根据算法的运行时间或空间要求随着输入大小的增长而分类。
大O符号并不代表一个函数本身,而是set of functions一组具有特定渐近上界的函数;正如您可以从source中读到的那样:
大O符号根据函数的增长率来刻画函数:具有相同增长率的不同函数可以使用相同的O符号表示。
在计算机科学中,关于时间复杂度和空间复杂度的理论,人们通常可以将“大O符号”视为一种对算法的分类,针对其最坏情况下的时间和空间复杂度。例如,O(n)表示:

如果一个算法的时间/空间复杂度为O(n),则称该算法的运行时间/空间是线性的或O(n)。简而言之,这意味着随着输入规模的增加,其运行时间/空间最多呈线性增长(source)。

O(n log n)则表示:

如果T(n) = O(n log^k n)(其中k是正常数),则称该算法以准线性时间/空间运行;当k=1时,则称该算法以线性对数时间/空间运行(source)。

然而,通常这种轻松的措辞通常用于量化(针对最坏情况)一组算法在其输入大小增加方面与另一组算法的行为方式相比。要比较两类算法(例如,O(n log n)和O(n)),应分析两类算法在最坏情况下随着其输入大小(即n)的增加而表现如何;当n趋近于无穷大时分析n。

enter image description here

在上面的图像中,big-O表示绘制函数渐近最小上界之一,并且不是指集合O(f(n))
例如,在比较O(n log n)O(n)时,可以看到在某个输入后,O(n log n)(绿线)增长速度比O(n)(黄线)更快。这就是为什么(对于最坏情况),O(n)O(n log n)更可取的原因,因为您可以增加输入大小,而前者的增长率会比后者慢。

0

仅仅为了以快速简单的方式表达算法的复杂性。 大O符号存在的目的是解释任何给定算法的最佳、最坏和平均时间复杂度。它们只是可能问题实例大小的数值函数。

换句话说,精确地使用这些函数非常困难,因为它们往往会:

  • 存在太多的颠簸 - 例如二分查找算法通常在数组大小恰好为n = 2k-1(其中k是整数)时运行得更快,因为数组分区很好。这个细节并不特别重要,但它提醒我们任何算法的确切时间复杂度函数可能非常复杂,有一些上下颠簸,如图2.2所示。
  • 需要过多的详细说明 - 在最坏情况下计算RAM指令的确切数量需要将算法详细说明为完整的计算机程序。此外,精确答案取决于无趣的编码细节(例如,他使用了case语句还是嵌套ifs?)。执行像T(n)= 12754n2 + 4353n + 834lg2 n + 13546这样的精确最坏情况分析显然是非常困难的工作,但是与“时间随n的增长呈二次方增长”这一观察结果相比,它为我们提供了很少的额外信息。

使用大O符号以简单的上限和下限时间复杂度函数来交流,证明这是更容易的。大O符号通过忽略不影响算法比较的细节层次来简化我们的分析。 大O符号忽略了乘法常数之间的差异。在大O分析中,函数f(n)=2n和g(n)=n是相同的。

https://mimoza.marmara.edu.tr/~msakalli/cse706_12/SkienaTheAlgorithmDesignMan ual.pdf


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