2016-08-03 69 views
12

我已經實現了一個mergesort和一個快速排序來比較它們與本機JavaScript排序。對於快速排序,我嘗試使用此算法:view algorithm on youtube。兩種算法都使用盡可能少的內存,對於合併排序,每次遞歸調用都會傳遞一個輔助數組(以避免開銷),對於快速排序來說,起始和結束位置的位置也是如此。我正在使用排序來管理NodeJs應用程序中的大量數據。本機JavaScript排序執行比實施mergesort和quicksort慢

下面你有歸併,快速排序和本地JavaScript的排序和您可以測試性能

的問題是:爲什麼本地JavaScript執行速度較慢?

在我的情況下:

Chrome - 合併排序:措施:1997.920ms;快速排序:測量:1755.740ms; native:測量:4988.105ms
節點:合併排序:測量:2233.413ms;快速排序:測量:1876.055ms;本地:措施:6317.118ms

歸併排序

var length = 10000000; // ten millions; 
 
var arr = []; 
 
for (let i = length; i > 0; i--) { 
 
    // random array 
 
    arr.push(parseInt(Math.random() * 1000000000)); 
 
} 
 
var mergeSort = function(array) { 
 
    function merge(arr, aux, lo, mid, hi) { 
 
    for (var k = lo; k <= hi; k++) { 
 
     aux[k] = arr[k]; 
 
    } 
 

 
    var i = lo; 
 
    var j = mid + 1; 
 
    for (var k = lo; k <= hi; k++) { 
 
     if (i > mid) { 
 
     arr[k] = aux[j++]; 
 
     } else if (j > hi) { 
 
     arr[k] = aux[i++]; 
 
     } else if (aux[i] < aux[j]) { 
 
     arr[k] = aux[i++]; 
 
     } else { 
 
     arr[k] = aux[j++]; 
 
     } 
 
    } 
 
    } 
 

 
    function sort(array, aux, lo, hi) { 
 
    if (hi <= lo) return; 
 
    var mid = Math.floor(lo + (hi - lo)/2); 
 
    sort(array, aux, lo, mid); 
 
    sort(array, aux, mid + 1, hi); 
 

 
    merge(array, aux, lo, mid, hi); 
 
    } 
 

 
    function merge_sort(array) { 
 
    var aux = array.slice(0); 
 
    sort(array, aux, 0, array.length - 1); 
 
    return array; 
 
    } 
 

 
    return merge_sort(array); 
 
} 
 

 

 
console.time('measure'); 
 
mergeSort(arr); 
 
console.timeEnd('measure'); 
 
console.log(arr[0], arr[1]);

快速排序

var length = 10000000; // ten millions; 
 
var arr = []; 
 
for (let i = length; i > 0; i--) { 
 
    // random array 
 
    arr.push(parseInt(Math.random() * 1000000000)); 
 
} 
 

 
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; 
 
} 
 
arrLength = arr.length; 
 
console.time('measure'); 
 
quickSort(arr, 0, arrLength - 1, arrLength); 
 
console.log(arr[0], arr[1]); 
 
console.timeEnd('measure');

本地JavaScript排序

var length = 10000000; // ten millions; 
 
var arr = []; 
 
for (let i = length; i > 0; i--) { 
 
    // random array 
 
    arr.push(parseInt(Math.random() * 100000000)); 
 
} 
 

 
console.time('measure'); 
 
arr.sort(function compareNumbers(a, b) { 
 
    return a - b; 
 
}); 
 
console.timeEnd('measure'); 
 

 
console.log(arr[0], arr[1]);

+0

僅供參考,第一個是最慢的第二個最快的考慮我的電腦和隨機性:) – Lucio

+0

是,快速排序執行最快..所以原生js執行比你的電腦合併排序更好? –

+0

有趣。我在Firefox和Edge中檢查了這些。雖然本地排序仍然是最慢的,但他們三人在Firefox中的差異並不那麼大。在Edge中,第一個沒有完成,或者我放棄得太快。它似乎永遠不會完成。最後兩個在Edge中完成。 – jfriend00

回答

11

那麼,爲什麼本地排序較慢?在

https://github.com/v8/v8/blob/0c76b0ae850027006d5ec0d92449e449d996d3bb/src/js/array.js#L744

問題看代碼似乎是GetThirdIndex()。它在分區大小大於1000時被調用,並且我認爲它用於防止快速排序最壞情況的性能,但是開銷很大,因爲它創建內部數組對並對它們進行排序,並且這些對的排序可能導致進一步的遞歸調用GetThirdIndex()。這與遞歸調用相結合,這些調用涉及分區原始數組和分區內部數組對。

由於這些示例的測試數據是隨機數據,因此Relu的quicksort不需要GetThirdIndex()之類的東西。還有陣列中的「洞」檢查,但我認爲這並不重要。

到GetThirdIndex(一種替代)。將位數的一個替代中值:

http://en.wikipedia.org/wiki/Median_of_medians

合併排序是比用這些方法中使用,以防止最壞的情況快速排序更快,但它需要一個輔助陣列相同的大小或原始數組的一半大小。

內省排序,這是快速排序和堆排序的混合體,在切換到堆排序如果遞歸的層次太深將是一個替代:

http://en.wikipedia.org/wiki/Introsort

第二合併排序例子下面使用比較功能,作出公平的比較。它比原生版本快得多。在Chrome的情況下,比較功能不會影響整體時間。在Firefox的情況下,比較功能有更多的效果。在Firefox的情況下,本地版本因內存不足而失敗,所以我無法比較。

這些是自上而下的合併排序的一些更快的版本,原始的海報是「好奇」的,使用相互遞歸函數來避免複製和有點優化merge()(每比較兩個條件)。

結果與火狐(倍稍微變化)

native sort - failed for out of memory. 
Relu's merge sort - 1.8 seconds 
Relu's quick sort - 1.3 seconds 
optimized merge sort - 1.4 seconds 
optimized merge sort with compare - 1.8 seconds 

結果與鉻(倍稍微變化)

native sort - 5.3 seconds 
Relu's merge sort - 2.1 seconds 
Relu's quick sort - 1.8 seconds 
optimized merge sort - 1.6 seconds 
optimized merge sort with compare - 1.7 seconds 

歸併排序

var length = 10000000; // ten millions; 
 
var arr = []; 
 
for (let i = length; i > 0; i--) { 
 
    // random array 
 
    arr.push(parseInt(Math.random() * 1000000000)); 
 
} 
 
var mergeSort = function(array) { 
 
    function merge(arr, aux, lo, mid, hi) { 
 
    var i = lo; 
 
    var j = mid + 1; 
 
    var k = lo; 
 
    while(true){ 
 
     if(arr[i] <= arr[j]){ 
 
     aux[k++] = arr[i++]; 
 
     if(i > mid){ 
 
      do 
 
      aux[k++] = arr[j++]; 
 
      while(j <= hi); 
 
      break; 
 
     } 
 
     } else { 
 
     aux[k++] = arr[j++]; 
 
     if(j > hi){ 
 
      do 
 
      aux[k++] = arr[i++]; 
 
      while(i <= mid); 
 
      break; 
 
     } 
 
     } 
 
    } 
 
    } 
 

 
    function sortarrtoaux(arr, aux, lo, hi) { 
 
    if (hi < lo) return; 
 
    if (hi == lo){ 
 
     aux[lo] = arr[lo]; 
 
     return; 
 
    } 
 
    var mid = Math.floor(lo + (hi - lo)/2); 
 
    sortarrtoarr(arr, aux, lo, mid); 
 
    sortarrtoarr(arr, aux, mid + 1, hi); 
 
    merge(arr, aux, lo, mid, hi); 
 
    } 
 

 
    function sortarrtoarr(arr, aux, lo, hi) { 
 
    if (hi <= lo) return; 
 
    var mid = Math.floor(lo + (hi - lo)/2); 
 
    sortarrtoaux(arr, aux, lo, mid); 
 
    sortarrtoaux(arr, aux, mid + 1, hi); 
 
    merge(aux, arr, lo, mid, hi); 
 
    } 
 

 
    function merge_sort(arr) { 
 
    var aux = arr.slice(0); 
 
    sortarrtoarr(arr, aux, 0, arr.length - 1); 
 
    return arr; 
 
    } 
 

 
    return merge_sort(array); 
 
} 
 

 
console.time('measure'); 
 
mergeSort(arr); 
 
console.timeEnd('measure'); 
 
console.log(arr[0], arr[1]);

歸併排序與比較功能

var length = 10000000; // ten millions; 
 
var arr = []; 
 
for (let i = length; i > 0; i--) { 
 
    // random array 
 
    arr.push(parseInt(Math.random() * 1000000000)); 
 
} 
 
var mergeSort = function(array, comparefn) { 
 
    function merge(arr, aux, lo, mid, hi, comparefn) { 
 
    var i = lo; 
 
    var j = mid + 1; 
 
    var k = lo; 
 
    while(true){ 
 
     var cmp = comparefn(arr[i], arr[j]); 
 
     if(cmp <= 0){ 
 
     aux[k++] = arr[i++]; 
 
     if(i > mid){ 
 
      do 
 
      aux[k++] = arr[j++]; 
 
      while(j <= hi); 
 
      break; 
 
     } 
 
     } else { 
 
     aux[k++] = arr[j++]; 
 
     if(j > hi){ 
 
      do 
 
      aux[k++] = arr[i++]; 
 
      while(i <= mid); 
 
      break; 
 
     } 
 
     } 
 
    } 
 
    } 
 

 
    function sortarrtoaux(arr, aux, lo, hi, comparefn) { 
 
    if (hi < lo) return; 
 
    if (hi == lo){ 
 
     aux[lo] = arr[lo]; 
 
     return; 
 
    } 
 
    var mid = Math.floor(lo + (hi - lo)/2); 
 
    sortarrtoarr(arr, aux, lo, mid, comparefn); 
 
    sortarrtoarr(arr, aux, mid + 1, hi, comparefn); 
 
    merge(arr, aux, lo, mid, hi, comparefn); 
 
    } 
 

 
    function sortarrtoarr(arr, aux, lo, hi, comparefn) { 
 
    if (hi <= lo) return; 
 
    var mid = Math.floor(lo + (hi - lo)/2); 
 
    sortarrtoaux(arr, aux, lo, mid, comparefn); 
 
    sortarrtoaux(arr, aux, mid + 1, hi, comparefn); 
 
    merge(aux, arr, lo, mid, hi, comparefn); 
 
    } 
 

 
    function merge_sort(arr, comparefn) { 
 
    var aux = arr.slice(0); 
 
    sortarrtoarr(arr, aux, 0, arr.length - 1, comparefn); 
 
    return arr; 
 
    } 
 

 
    return merge_sort(array, comparefn); 
 
} 
 

 
console.time('measure'); 
 
mergeSort(arr, function compareNumbers(a, b) { 
 
    return a - b; 
 
}); 
 
console.timeEnd('measure'); 
 
// check result 
 
for (let i = 1; i < length; i++) { 
 
    if(arr[i] < arr[i-1]){ 
 
     console.log('error'); 
 
     break; 
 
    } 
 
} 
 
console.log(arr[0], arr[1]);

邊注:本機排序並不穩定:

本地JavaScript排序 - 測試穩定性

var length = 100000; 
 
var arr = []; 
 
var j; 
 
for (let i = 0; i < length; i++) { 
 
    j = parseInt(Math.random() * 100); 
 
    arr[i] = [j, i]; 
 
} 
 

 
console.time('measure'); 
 
arr.sort(function compareNumbers(a, b) { 
 
    return a[0] - b[0]; 
 
}); 
 
console.timeEnd('measure'); 
 

 
for (let i = 1; i < length; i++) { 
 
    if((arr[i][0] == arr[i-1][0]) && 
 
     (arr[i][1] < arr[i-1][1])){ 
 
     console.log('not stable'); 
 
     console.log(arr[i-1][0], arr[i-1][1]); 
 
     console.log(arr[i ][0], arr[i ][1]); 
 
     break; 
 
    } 
 
}

本地JavaScript排序 - 變化比較使之穩定

var length = 100000; 
 
var arr = []; 
 
var j; 
 
for (let i = 0; i < length; i++) { 
 
    j = parseInt(Math.random() * 100); 
 
    arr[i] = [j, i]; 
 
} 
 

 
console.time('measure'); 
 
arr.sort(function compareNumbers(a, b) { 
 
    if(a[0] == b[0]) 
 
    return a[1] - b[1]; 
 
    return a[0] - b[0]; 
 
}); 
 
console.timeEnd('measure'); 
 

 
for (let i = 1; i < length; i++) { 
 
    if((arr[i][0] == arr[i-1][0]) && 
 
     (arr[i][1] < arr[i-1][1])){ 
 
     console.log('not stable'); 
 
     console.log(arr[i-1][0], arr[i-1][1]); 
 
     console.log(arr[i ][0], arr[i ][1]); 
 
     break; 
 
    } 
 
}
在你寫的順序

+5

這似乎甚至沒有試圖回答被問到的問題。 – zzzzBov

+0

「我想知道arr.sort()是否將數字轉換爲對象,儘管有一個比較函數應該可以處理數字。」 ---爲什麼不檢查? https://github.com/v8/v8/blob/master/src/js/array.js#L712 – zerkms

+0

@rcgldr不,我的意思是 - 你在假設V8是如何實現的,而不是檢查它。 「轉換可能會導致間接級別(如C中的指針),這會增加排序時間。」 ---這個說法沒有意義,因爲看到上面的鏈接。 – zerkms