簡體   English   中英

什么是 Array.prototype.sort() 時間復雜度?

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

根據 Mozilla 文檔:

排序的時間和空間復雜度無法保證,因為它取決於實現。

假設它不是O(n^2)至少安全嗎? 有沒有關於它是如何實施的更詳細的數據? 謝謝。

Firefox 使用合並排序 Chrome 從 70 版開始,使用稱為Timsort的合並排序和插入排序的混合

歸並排序的時間復雜度是O(n log n) 雖然規范沒有指定要使用的排序算法,但在任何嚴重的環境中,您可能會期望對較大的數組進行排序不會花費比O(n log n)更長的時間(因為如果這樣做,很容易更改為更快的算法,如歸並排序,或其他一些對數線性方法)。

雖然像合並排序這樣的比較排序具有O(n log n)的下限(即它們至少需要這么長時間才能完成),但 Timsort 利用了已排序數據的“運行”,因此具有O(n)

理論與實踐:理論上,理論與實踐沒有區別,但在實踐中卻有區別。

  • 理論:一切都清楚,但無濟於事;
  • 實踐:一切正常,但一切都不清楚;
  • 有時理論與實踐相遇:沒有什么是行得通的,沒有什么是清楚的。

Big O 表示法非常適合評估算法的可擴展性,但不提供直接比較算法實現之間性能的方法......

一個典型的例子是瀏覽器中 Array.sort() 的實現。 盡管 Timsort 比 Merge sort 具有更好的 Big O profile(參見https://www.bigocheatsheet.com/ ),但實證測試表明,Timsort 在 Chrome 的 V8 引擎中的實現明顯優於 Firefox 中 Merge sort 的實現.

下面的圖表分別顯示了兩組數據點:

  • 藍色數據是 Array.sort() 對 500 個隨機長度數組(從 100 到 500,000 個元素)隨機填充整數的測試用例的性能。 曲線以 N * Log( N ) 的形式顯示數據的中位數,虛線顯示 95% 的界限,同樣以 N * Log( N ) 的形式顯示。 也就是說,95% 的數據點位於這些曲線之間。
  • 橙色數據顯示了 Array.sort() 對於大多數排序數組的性能。 具體來說,數組中約 2% 的值被重新隨機化,然后再次應用 Array.sort()。 在這種情況下,實線和虛線是性能的線性度量,而不是對數。

此外,大 O 符號提供了從算法的可擴展性中期望的一般經驗法則,但沒有解決可變性問題。 Timsort 算法的 Chrome V8 實現在其執行方面比 Firefox 合並排序具有更大的可變性,盡管 Timsort 的 Big O 配置文件更好,但即使 Timsort 的最佳時間也不比合並排序的最差時間好。 冒着引發宗教戰爭的風險,這並不意味着 Timsort 比合並排序更糟糕,因為這可能只是 Firefox 的 JavaScript 實現更好的整體性能的一個例子。

Chrome Array.sort() 性能

Firefox Array.sort() 性能

上面圖表的數據是從我的 Acer Aspire E5-5775G 簽名版上的以下代碼生成的,它具有 Intel Core i5-7200U CPU @2.50GHz 和 8GB RAM。 然后將數據導入 Excel,分析 95% 的邊界范圍,然后繪制圖表。 為了便於視覺比較,圖表上的軸刻度已標准化。

  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 );

結論是,一個算法的性能,表面上是基於 Big O 符號的更好,有許多因素驅動其整體性能,更好的 Big O 並不一定轉化為更好的性能......

這取決於瀏覽器/引擎。

由於使用了 V8 v7.0和 Chrome 70 Timsort 算法。
其中,對於較小的數組,時間復雜度為 O(n),空間復雜度為 0(1)。 對於更大的數組,它的時間復雜度為 O(nlog(n)),空間復雜度為 O(n)。

舊版本的 V8 使用快速排序,它可以檢查小數組(最多 10 個元素)。 所以對於較小的數組,時間復雜度為 O(n^2),空間復雜度為 O(1)。
對於較大的數組,時間復雜度為 Θ(n log(n))(平均情況),空間復雜度為 O(log(n))

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM