2016-11-21 25 views
1

私のアプリケーションでは、大きな配列(100,000〜1,000,000)の乱数をソートする必要があります。JavaScriptで大きな(ish)配列をソートする最も速い方法は何ですか

私はcomparisonFunctionは次のようになり array.sort(comparisonFunction)に建て使用してきた

:特にあなたの場合は、より高速な選択肢があること

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

これはうまく動作しますが、私が読んだ(例えば、Native JavaScript sort performing slower than implemented mergesort and quicksortを)要件は、特定の条件を満たし:

  1. 私は数字だけ(例えば、オブジェクトではなく、または英数字データ)をソートする必要が
  2. データであるランダム(それはすでに注文したということはチャンスがない)
  3. ソートが

が安定している必要はありません - 最速のものです(または近いです十分な)ソートアルゴリズムは、これらの状況で利用可能ですか?

そして、正規の(または少なくとも比較的理想的な)JavaScript実装がありますか?

[UPDATE]

む〜... 2投稿の30秒以内に投票ダウン!したがって、簡単な説明 - リンクされた質問では、OPは安定した並べ替えが必要でした。あなたがデータをあらかじめソートされていないことがわかっていれば、答えが変わるかどうかは分かりません(つまり、おそらくもっと速いソートオプションがあるかもしれません)。安定した分類)。

おそらく答えは「いいえ」ですが、それが私が求めている理由です。

[UPDATE#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");

+6

*」...私は、はるかに高速の選択肢があることを読みました... "*どこ? –

+2

ネイティブJavaScriptの '.sort()'は安定している必要はありません。私はJavaScriptのソートがどんな有意なサイズの配列のネイティブなソートよりも速かったならば、私は驚くだろうが、特定の入力制約では、基数ソートは試してみる価値があるかもしれない。 – Pointy

+1

@Liam:上記は明らかに、安定した並べ替えを必要としないことを示しています。 (あなたが数字を並べ替えるのであれば、安定しているか不安定であるか区別することはできません...) –

答えて

5

あります一貫して株.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>
は、私の周りに持っている別のブラウザからいくつかのタイミングです(チャクラ誰が?):

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エンジンはクローム/サファリとは非常に異なっています。

+0

私はiceweaselを使用していますが、私はtimsortで227.418ms、Array#Sortで172.212msを得ます。 – acontell

+2

@acontell:yep、強く依存エンジンで、アップデートを参照してください。 – georg

+0

私はこのFiddle(https://jsfiddle.net/uqq54ho8/2/)をまとめました。これはネイティブソートをそのtimsortと比較しています。 5,000,000の乱数の場合、結果は次のとおりです。* timsort:1050ms native:2536ms quick:754ms *。 [Mozilla/5.0(Macintosh; Intel Mac OS X 10_12_1)AppleWebKit/537.36(GeckoのようなKHTML)Chrome/54.0.2840.98 Safari/537.36] – mattstuehler

1

これはjavascriptではないため、答えとしてマークする必要はありません。また、heapsortに切り替えるためのintrosortの深さチェックもありません。

例C++クイックソート。ピボット値、Hoareパーティションスキームを選択するために3の中央値を使用し、中間値==ピボット(これらのうちの少なくとも1つ)を除外し、小さなパーティションでのみ再帰を使用し、大きなパーティションをループバックしてスタックの複雑さをO (log2(n))最悪の場合。最悪の場合の時間の複雑さは依然としてO(n^2)ですが、これは小数または大きな値を繰り返し選択するために中央値3を必要とします。並べ替えられた配列や逆順の配列は問題になりません。すべての値が同じ場合、時間の複雑さはO(n)です。 heapsortに切り替えるための深さチェックを追加することで、時間の複雑さが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; 
     } 
    } 
} 

スペースが問題でない場合は、ここではマージソートが良いかもしれ:

Native JavaScript sort performing slower than implemented mergesort and quicksort

関連する問題