it-swarm.com.ru

Массива чисел в JavaScript

В моем приложении мне нужно отсортировать большие массивы (от 100 000 до 1 000 000) случайных чисел.

Я использовал встроенную функцию array.sort(comparisonFunction), где функция сравнения выглядит следующим образом:

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

Это прекрасно работает, но я прочитал (например, родная сортировка JavaScript, выполняющая медленнее, чем реализованная сортировка слиянием и быстрая сортировка ), что есть более быстрые варианты, особенно если ваши требования удовлетворяют определенным условиям:

  1. Мне нужно только отсортировать числа (например, не объекты или буквенно-цифровые данные)
  2. Данные случайные (нет шансов, что они уже упорядочены)
  3. Сортировка не обязательно должна быть стабильной

Итак, какой самый быстрый (или достаточно близкий) алгоритм сортировки доступен в этих условиях?

И есть ли каноническая (или, по крайней мере, относительно идеальная) реализация JavaScript?

[Обновление]

Yikes ... два отрицательных голоса в течение 30 секунд после публикации! Итак, быстрое разъяснение - в связанном вопросе ОП требовал стабильной сортировки. Поскольку я не ... мне интересно, изменит ли это ответ (то есть, возможно, есть более быстрый вариант сортировки если вы заранее знаете, что ваши данные не будут предварительно отсортированы, и вы не нужна стабильная сортировка).

Возможно, ответ «нет», но вот почему я спрашиваю.

[ОБНОВЛЕНИЕ № 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");

5
mattstuehler

Существуют реализации сортировки, которые последовательно превосходят стандартный .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 сильно отличается от Chrome/Safari.

9
georg

Не нужно помечать это как ответ, так как это не javascript и не имеет проверки глубины интросорта для переключения на heapsort.

Пример быстрой сортировки C++. Он использует медиану 3, чтобы выбрать значение pivot, схему разделов Hoare, затем исключает средние значения == pivot (по крайней мере, один из них) и использует только рекурсию для меньшего раздела, возвращая цикл в больший раздел, чтобы ограничить сложность стека до O(log2(n)) худший случай. Наихудшая временная сложность все еще равна O (n ^ 2), но для этого потребуется медиана 3, чтобы многократно выбирать малые или большие значения, что является необычным паттерном. Сортированные или обратно отсортированные массивы не являются проблемой. Если все значения одинаковы, то сложность по времени равна O (n). Добавление проверки глубины для переключения на heapsort (делая это интросортом) ограничило бы временную сложность O (n log (n)), но с более высоким постоянным коэффициентом, зависящим от того, сколько используется путь к heapsort.

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 выполняется медленнее, чем реализованная сортировка слиянием и быстрая сортировка

1
rcgldr