简体   繁体   English

什么是 Array.prototype.sort() 时间复杂度?

[英]What is Array.prototype.sort() time complexity?

According to Mozilla documentation :根据 Mozilla 文档:

The time and space complexity of the sort cannot be guaranteed as it depends on the implementation.排序的时间和空间复杂度无法保证,因为它取决于实现。

Is it at least safe to assume that it's not O(n^2) ?假设它不是O(n^2)至少安全吗? Is there anywhere more detailed data about how it's implemented ?有没有关于它是如何实施的更详细的数据? Thanks.谢谢。

Firefox uses merge sort . Firefox 使用合并排序 Chrome, as of version 70, uses a hybrid of merge sort and insertion sort called Timsort . Chrome 从 70 版开始,使用称为Timsort的合并排序和插入排序的混合

The time complexity of merge sort is O(n log n) .归并排序的时间复杂度是O(n log n) While the specification does not specify the sorting algorithm to use, in any serious environment, you can probably expect that sorting larger arrays does not take longer than O(n log n) (because if it did, it would be easy to change to a much faster algorithm like merge sort, or some other log-linear method).虽然规范没有指定要使用的排序算法,但在任何严重的环境中,您可能会期望对较大的数组进行排序不会花费比O(n log n)更长的时间(因为如果这样做,很容易更改为更快的算法,如归并排序,或其他一些对数线性方法)。

While comparison sorts like merge sort have a lower bound of O(n log n) (ie they take at least this long to complete), Timsort takes advantages of "runs" of already ordered data and so has a lower bound of O(n) .虽然像合并排序这样的比较排序具有O(n log n)的下限(即它们至少需要这么长时间才能完成),但 Timsort 利用了已排序数据的“运行”,因此具有O(n)

Theory and practice: In theory there is no difference between theory and practice, but in practice there is.理论与实践:理论上,理论与实践没有区别,但在实践中却有区别。

  • Theory: everything is clear, but nothing works;理论:一切都清楚,但无济于事;
  • Practice: everything works, but nothing is clear;实践:一切正常,但一切都不清楚;
  • Sometimes theory meets practice: nothing works and nothing is clear.有时理论与实践相遇:没有什么是行得通的,没有什么是清楚的。

Big O notation is great for assessing the scalability of an algorithm, but does not provide a means of direct comparison of performance between implementations of an algorithm... Big O 表示法非常适合评估算法的可扩展性,但不提供直接比较算法实现之间性能的方法......

Case in point is the implementation of Array.sort() within browsers.一个典型的例子是浏览器中 Array.sort() 的实现。 Despite Timsort having a better Big O profile than Merge sort (see https://www.bigocheatsheet.com/ ), empirical testing shows that the implementation of Timsort in Chrome's V8 engine is clearly outperformed on average by the implementation of Merge sort in Firefox.尽管 Timsort 比 Merge sort 具有更好的 Big O profile(参见https://www.bigocheatsheet.com/ ),但实证测试表明,Timsort 在 Chrome 的 V8 引擎中的实现明显优于 Firefox 中 Merge sort 的实现.

The charts below each show two sets of data points:下面的图表分别显示了两组数据点:

  • The blue data is the performance of Array.sort() for 500 test cases of random length arrays (from 100 to 500,000 elements) randomly filled with integers.蓝色数据是 Array.sort() 对 500 个随机长度数组(从 100 到 500,000 个元素)随机填充整数的测试用例的性能。 The curved line shows the median of the data in the form of N * Log( N ), and the dotted curves show the 95% bounds, again in the form of N * Log( N ).曲线以 N * Log( N ) 的形式显示数据的中位数,虚线显示 95% 的界限,同样以 N * Log( N ) 的形式显示。 That is to say, 95% of the data points lie between these curves.也就是说,95% 的数据点位于这些曲线之间。
  • The orange data shows the performance of Array.sort() for a mostly sorted array.橙色数据显示了 Array.sort() 对于大多数排序数组的性能。 Specifically, ~2% of the values in the array are re-randomized and then Array.sort() is applied again.具体来说,数组中约 2% 的值被重新随机化,然后再次应用 Array.sort()。 In this case, the solid and dotted lines are linear measures of performance, not logarithmatic.在这种情况下,实线和虚线是性能的线性度量,而不是对数。

Furthermore, Big O notation provides a general rule of thumb to expect from the scalability of the algorithm, but does not address the variability.此外,大 O 符号提供了从算法的可扩展性中期望的一般经验法则,但没有解决可变性问题。 The Chrome V8 implementation of the Timsort algorithm has a wider variability in its execution than the Firefox Merge sort, and despite Timsort's better Big O profile, even Timsort's best times are not better than the Merge sort's worst times. Timsort 算法的 Chrome V8 实现在其执行方面比 Firefox 合并排序具有更大的可变性,尽管 Timsort 的 Big O 配置文件更好,但即使 Timsort 的最佳时间也不比合并排序的最差时间好。 At the risk of starting a religious war, this does not mean that the Timsort is worse than Merge sort, as this could simply be a case of better overall performance by Firefox's implementation of JavaScript.冒着引发宗教战争的风险,这并不意味着 Timsort 比合并排序更糟糕,因为这可能只是 Firefox 的 JavaScript 实现更好的整体性能的一个例子。

Chrome Array.sort() 性能

Firefox Array.sort() 性能

The data for the charts above was generated from the following code on my Acer Aspire E5-5775G Signature Edition having an Intel Core i5-7200U CPU @2.50GHz and 8GB of RAM.上面图表的数据是从我的 Acer Aspire E5-5775G 签名版上的以下代码生成的,它具有 Intel Core i5-7200U CPU @2.50GHz 和 8GB RAM。 The data was then imported into Excel, analyzed for the 95% bounding range, and then charted.然后将数据导入 Excel,分析 95% 的边界范围,然后绘制图表。 The axes scales on the charts are normalized for ease of visual comparison.为了便于视觉比较,图表上的轴刻度已标准化。

  function generateDataPoints( qtyOfTests, arrayRange, valueRange, nearlySortedChange ) {
  
    let loadingTheArray = [];
    let randomSortMetrics = [];
    let nearlySortedMetrics = [];
    
    for ( let testNo = 0; testNo < qtyOfTests; testNo++ ) {
      if ( testNo % 10 === 0 ) console.log( testNo );
      
      // Random determine the size of the array given the range, and then
      // randomly fill the array with values.
      let testArray = [];      
      let testArrayLen = Math.round( Math.random() * ( arrayRange.hi - arrayRange.lo ) ) + arrayRange.lo;
      
      start = performance.now();
      for ( let v = 0; v < testArrayLen; v++ ) {
        testArray[ v ] = Math.round( Math.random() * ( valueRange.hi - valueRange.lo ) ) + valueRange.lo;
      }
      end = performance.now();
      
      loadingTheArray[ testNo ] = { x: testArrayLen, y: Math.floor( end - start ) };
      
      // Perform the sort and capture the result.
      start = performance.now();
        testArray.sort( (a, b ) => a - b );
      end = performance.now();
      
      randomSortMetrics[ testNo ] = { x: testArrayLen, y: Math.floor( end - start ) };
      
      // Now, let's change a portion of the sorted values and sort again.
      let qtyOfValuesToChange = testArrayLen * nearlySortedChange;
      for ( let i = 0; i < qtyOfValuesToChange; i++ ) {
        let v = Math.round( Math.random() * testArrayLen );
        testArray[ v ] = Math.round( Math.random() * ( valueRange.hi - valueRange.lo ) ) + valueRange.lo;
      }
      
      start = performance.now();
        testArray.sort( (a, b ) => a - b );
      end = performance.now();
      
      nearlySortedMetrics[ testNo ] = { x: testArrayLen, y: Math.floor( end - start ) };

    }
    
    return  [ loadingTheArray, randomSortMetrics, nearlySortedMetrics ];
    
  }
  
  // Let's start running tests!
  let arraySizeRange = { lo: 100, hi: 500000 };
  let valueRange = { lo: 0, hi: 2 ** 32 - 1 };
  let results = generateDataPoints( 500, arraySizeRange, valueRange, 0.02 );
  
  
  let tabFormat = 'No Of Elements\tTime to Load Array\tFull Sort\tMostly Sorted\n';
  for ( let i = 0; i < results[0].length; i++ ) {
    tabFormat += `${results[0][i].x}\t${results[0][i].y}\t${results[1][i].y}\t${results[2][i].y}\n`;
  }
  console.log( tabFormat );

The takeaway is that the performance of an algorithm, ostensibly being better based on Big O notation, has many factors that drive its overall performance, and a better Big O does not necessarily translate to better performance...结论是,一个算法的性能,表面上是基于 Big O 符号的更好,有许多因素驱动其整体性能,更好的 Big O 并不一定转化为更好的性能......

It depends on the browser/engine.这取决于浏览器/引擎。

Since V8 v7.0 and Chrome 70 Timsort algorithm is used.由于使用了 V8 v7.0和 Chrome 70 Timsort 算法。
Which, for smaller arrays, has a time complexity of O(n) and space complexity of 0(1).其中,对于较小的数组,时间复杂度为 O(n),空间复杂度为 0(1)。 And for larger arrays, it has a time complexity of O(nlog(n)) and space complexity of O(n).对于更大的数组,它的时间复杂度为 O(nlog(n)),空间复杂度为 O(n)。

The older version of V8 uses Quick Sort, and it has a check for small arrays (up to 10 elements).旧版本的 V8 使用快速排序,它可以检查小数组(最多 10 个元素)。 So for smaller arrays, the time complexity is O(n^2), and space complexity is O(1).所以对于较小的数组,时间复杂度为 O(n^2),空间复杂度为 O(1)。
For larger arrays, time complexity is Θ(n log(n)) (average case), and space complexity is O(log(n))对于较大的数组,时间复杂度为 Θ(n log(n))(平均情况),空间复杂度为 O(log(n))

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM