簡體   English   中英

在 JavaScript 中對大型(ish)數字數組進行排序的最快方法是什么

[英]What is the fastest way to sort a large(ish) array of numbers in JavaScript

在我的應用程序中,我需要對隨機數的大型數組(介於 100,000 和 1,000,000 之間)進行排序。

我一直在使用內置的array.sort(comparisonFunction) ,其中array.sort(comparisonFunction)看起來像這樣:

function comparisonFunction(a,b) {
    return a-b;
}

這工作得很好,但我讀過(例如,本機 JavaScript 排序執行比實現的合並排序和快速排序慢)有更快的選擇,特別是如果您的要求滿足某些條件:

  1. 我只需要對數字進行排序(例如,不是對象或字母數字數據)
  2. 數據是隨機的(不可能已經訂購了)
  3. 排序不需要穩定

那么 - 在這些情況下可用的最快(或足夠接近)的排序算法是什么?

而且,是否有規范的(或至少是相對理想的)JavaScript 實現?

[更新]

因此,快速澄清一下 - 在鏈接的問題中,OP 需要穩定的排序。 因為我沒有 - 我想知道這是否會改變答案(即,如果您事先知道您的數據不會被預先排序,並且您不需要穩定的排序,那么也許有一個更快的排序選項可用)。

也許答案是“不”,但這就是我問的原因。

[更新#2]

這是快速排序的一個實現,除非我犯了一個錯誤 - 輕松擊敗本機排序功能:

 function comparisonFunction(a, b) { return a - b; } function quickSort(arr, leftPos, rightPos, arrLength) { let initialLeftPos = leftPos; let initialRightPos = rightPos; let direction = true; let pivot = rightPos; while ((leftPos - rightPos) < 0) { if (direction) { if (arr[pivot] < arr[leftPos]) { quickSort.swap(arr, pivot, leftPos); pivot = leftPos; rightPos--; direction = !direction; } else leftPos++; } else { if (arr[pivot] <= arr[rightPos]) { rightPos--; } else { quickSort.swap(arr, pivot, rightPos); leftPos++; pivot = rightPos; direction = !direction; } } } if (pivot - 1 > initialLeftPos) { quickSort(arr, initialLeftPos, pivot - 1, arrLength); } if (pivot + 1 < initialRightPos) { quickSort(arr, pivot + 1, initialRightPos, arrLength); } } quickSort.swap = (arr, el1, el2) => { let swapedElem = arr[el1]; arr[el1] = arr[el2]; arr[el2] = swapedElem; } var i, arr1, arr2, length; length = 1000000; arr1 = []; arr2 = []; for (i = 0; i < length; i++) { arr1.push(Math.random()); arr2.push(Math.random()); } console.time("nativeSort"); arr1.sort(comparisonFunction); console.timeEnd("nativeSort"); console.time("quickSort"); quickSort(arr2, 0, length - 1, length); console.timeEnd("quickSort");

有一些排序實現始終擊敗股票.sort (至少 V8), node-timsort就是其中之一。 示例:

 var SIZE = 1 << 20; var a = [], b = []; for(var i = 0; i < SIZE; i++) { var r = (Math.random() * 10000) >>> 0; a.push(r); b.push(r); } console.log(navigator.userAgent); console.time("timsort"); timsort.sort(a, (x, y) => x - y); console.timeEnd("timsort"); console.time("Array#sort"); b.sort((x, y) => x - y); console.timeEnd("Array#sort");
 <script src="https://rawgithub.com/mziccard/node-timsort/master/build/timsort.js"></script>

以下是我周圍不同瀏覽器的一些時間(Chakra 任何人?):

Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.113 Safari/537.36
timsort: 256.120ms
Array#sort: 341.595ms

Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/602.2.14 (KHTML, like Gecko) Version/10.0.1 Safari/602.2.14
timsort: 189.795ms
Array#sort: 245.725ms

Mozilla/5.0 (Macintosh; Intel Mac OS X 10.11; rv:51.0) Gecko/20100101 Firefox/51.0
timsort: 402.230ms
Array#sort: 187.900ms

因此,FF 引擎與 Chrome/Safari 非常不同。

無需將此標記為答案,因為它不是 javascript,並且沒有 introsort 的深度檢查來切換到堆排序。

示例 C++ 快速排序。 它使用 3 的中位數來選擇樞軸值,Hoare 分區方案,然后排除中間值 == 樞軸(這可能會或可能不會改善時間,因為值 == 樞軸可以在分區步驟中的任何位置結束),並且只在較小的分區,在較大的分區上循環以將堆棧復雜度限制為 O(log2(n)) 最壞情況。 最壞情況下的時間復雜度仍然是 O(n^2),但這需要 3 的中位數來重復選擇小值或大值,這是一種不尋常的模式。 排序或反向排序的數組不是問題。 如果所有值都相同,則時間復雜度為 O(n)。 添加深度檢查以切換到堆排序(使其成為內排序)會將時間復雜度限制為 O(n log(n)),但具有更高的常數因子,具體取決於使用了多少堆排序路徑。

void QuickSort(uint32_t a[], size_t lo, size_t hi) {
    while(lo < hi){
        size_t i = lo, j = (lo+hi)/2, k = hi;
        uint32_t p;
        if (a[k] < a[i])            // median of 3
            std::swap(a[k], a[i]);
        if (a[j] < a[i])
            std::swap(a[j], a[i]);
        if (a[k] < a[j])
            std::swap(a[k], a[j]);
        p = a[j];
        i--;                        // Hoare partition
        k++;
        while (1) {
            while (a[++i] < p);
            while (a[--k] > p);
            if (i >= k)
                break;
            std::swap(a[i], a[k]);
        }
        i = k++;
        while(i > lo && a[i] == p)  // exclude middle values == pivot
            i--;
        while(k < hi && a[k] == p)
            k++;
        // recurse on smaller part, loop on larger part
        if((i - lo) <= (hi - k)){
            QuickSort(a, lo, i);
            lo = k;
        } else {
            QuickSort(a, k, hi);
            hi = i;
        }
    }
}

如果空間不是問題,那么這里的合並排序可能會更好:

原生 JavaScript 排序的執行速度比實現的歸並排序和快速排序慢

如果只是排序數字,並再次假設空間不是問題,基數排序將是最快的。

對數字數組進行排序的最快方法是將其轉換為TypedArray然后調用其sort函數。 類型化數組的排序函數默認是數值型的,比帶有比較函數的Array.sort()快得多。

 const a = Array.from({length: 1<<20}, Math.random) const b = Array.from(a) function comparisonFunction(a,b){ return ab } console.time("nativeSort") a.sort(comparisonFunction) console.timeEnd("nativeSort") console.time("typedSort") let typedArray = Float32Array.from(b) typedArray.sort() console.timeEnd("typedSort")

暫無
暫無

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

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