如何批量排序JS数组(性能)

时间:2016-06-09 06:38:16

标签: javascript arrays algorithm sorting

我有一个JS应用程序需要做一个复杂的大型数组,然后显示它。使用内置的array.sort(cb)方法,我的数据最多可能需要1秒钟。这足以让我的UI变得笨拙。

因为UI的高度足以在屏幕上显示已排序数组的子集,其余部分位于滚动或分页下方,所以我有了一个想法。如果我制作了一个通过大型数组并快速排序的算法,前N个项目完全排序,但数组中的其余项目未完全排序,该怎么办?每次运行算法时,它都会从上到下对数组进行排序。

因此,我可以将处理分解为块并具有流畅的UI。在最初的几秒钟内,阵列将不会被完美地排序,但是缺陷将位于卷轴下方,因此它们不会被注意到。

我天真的解决方案是写自己的"选择排序"能够在N次比赛后休息并在之后恢复,但是"选择排序"是一个非常可怕的算法。更快的算法(根据我的理解)必须完成以保证前N个项目是稳定的。

有人知道现有的解决方案吗?我疯了吗?有什么建议吗?

更新

根据@moreON提出的想法,我写了一个自定义的QuickSort,一旦它具有所需的精度就会挽救。此数据的本机排序需要1秒。常规QuickSort大约需要250毫秒,这已经出乎意料地好了。在排序前100个项目后匆匆忙忙的QuickSort活跃了10ms,这要好得多。然后我可以再花250ms来完成排序,但这并不重要,因为用户已经在查看数据了。这将用户经历的延迟从1秒减少到10毫秒,这非常好。

//Init 1 million random integers into array
var arr1 = [];
var arr2 = [];
for(var i=0;i<1800000;i++) {
   var num = Math.floor(Math.random() * 1000000);
   arr1.push(num);
   arr2.push(num);
}
console.log(arr1);

//native sort
console.time("native sort");
arr1.sort(function(a,b) { return a-b; });
console.timeEnd("native sort"); //1sec
console.log(arr1);

//quicksort sort    Ref: https://www.nczonline.net/blog/2012/11/27/computer-science-in-javascript-quicksort/
function swap(arr, a, b) {
   var temp = arr[a];
   arr[a] = arr[b];
   arr[b] = temp;
}
function cmp(a,b) {
   return (a<b);
}
function partition(items, left, right) {
   var pivot = items[Math.floor((right + left) / 2)];
   var i = left;
   var j = right;

   while (i <= j) {
      while (cmp(items[i],pivot)) i++;
      while (cmp(pivot,items[j])) j--;
      if (i <= j) {
         swap(items, i, j);
         i++;
         j--;
      }
   }
   return i;
}
function quickSort(items, left, right, max) {    
   if(max && left-1 > max) return items; //bail out early if we have enough
   if (items.length > 1) {
      var index = partition(items, left, right);
      if (left < index - 1) quickSort(items, left, index - 1, max);
      if (index < right) quickSort(items, index, right, max);
   }
   return items;
}

//sort first 100
console.time("partial Quicksort");
arr2 = quickSort(arr2,0,arr2.length-1,100);
console.timeEnd("partial Quicksort"); //10ms
console.log(arr2);

//sort remainder
console.time("finishing Quicksort");
arr2 = quickSort(arr2,100,arr2.length-1); //250ms
console.timeEnd("finishing Quicksort");    
console.log(arr2);

4 个答案:

答案 0 :(得分:2)

如果您要汇总array,我认为可以在O(n)时间(https://en.wikipedia.org/wiki/Binary_heap#Building_a_heap)中完成,您可以按顺序提取每个N项目O(N log n)时间(n在您提取时变小)。

答案 1 :(得分:1)

这是我的解决方案的清理版本,它批量对大型数组进行排序,因此JS线程不会断断续续。在我的示例中,它需要1秒#!/bin/csh set line_num = "`grep -n 'grep' filename | tail -1 | grep -o '^[0-9]\+'`" @i = 0 foreach command ("`cat filename`") @i += 1 $command if($i == $line_num) then break endif end 并将其转换为五个单独的100ms操作。您将要根据您的数据智能地选择pageSize。更多页面将使最终排序花费更长时间,更少页面将使批次花费更长时间。

array.sort(cb)

答案 2 :(得分:0)

我认为你的问题归结为:

  

如何在大数组中找到前N个元素

这里有点回答:Find top N elements in an Array

这可以通过遍历列表一次,然后选择前N个元素来解决。 Θ(n)中。

请在此处查看:https://jsfiddle.net/jeeh4a8p/1/

function get_top_10(list) {
    var top10 = new Array(10).fill(0)
    for(i in list) {
        var smallest_in_top10 = Math.min.apply( Math, top10 )
        if(list[i] > smallest_in_top10) {
            top10.splice(top10.indexOf(smallest_in_top10),1)
            top10.push(list[i])
        }
    }
    return top10
}

console.log(get_top_10([1,2,3,4,5,6,7,8,9,10,11,12]))

var random_list = [];
for (var i = 0; i < 100; i++) {
    random_list.push(Math.round(Math.random() * 999999))
}

console.log(get_top_10(random_list))

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

答案 3 :(得分:-1)

首先,对绩效改进预期有一些看法。有效的排序算法是O(N * log2(N))。对于N = 1,000,000个项目,N * log2(N)~N * 20.我怀疑你在网页上尝试渲染的项目很多。

如果你只需要渲染前25行,选择排序将需要N * 25来对它们进行排序,所以它实际上会表现得更差,假设可比的恒定开销。

如果你想进一步试验这个,我能想到的一个算法是:维护PAGE_SIZE最小项的二叉树。通过对数据的单次传递继续更新它,在找到较小的项目时删除最大的项目。忽略重新平衡,它将使您N * log2(PAGE_SIZE)填充树并呈现您的第一页结果。