Почему моя сортировка Shell такая медленная?

Я пытаюсь реализовать кучу алгоритмов сортировки в JavaScript, и я не могу понять, почему моя сортировка оболочки такая медленная. Это в 6 раз медленнее, чем моя сортировка слиянием, и лишь немного быстрее, чем моя сортировка вставками. Я видел другую реализацию в Интернете, но я больше сосредоточен на том, чтобы сделать ее понятной и читаемой (поскольку у меня есть блог для нубов), а более быстрая реализация слишком лаконична для моих целей. Любые мысли о том, как я могу сохранить общий план, но заставить его двигаться быстрее?

  var shellSort = function( list ) {
    var gapSize = Math.floor( list.length / 2 );

    while( gapSize > 0 ) {
      _shellInsertionSort( list, gapSize );
      gapSize = Math.floor( gapSize / 2 );
    }

    return list;
  };

  function _shellInsertionSort( list, gapSize ) {
    var temp, i, j;

    for( i = gapSize; i < list.length; i += gapSize ) {
      j = i;
      while( j > 0 && list[ j - gapSize ] > list[j] ) {
        temp = list[j];
        list[j] = list[ j - gapSize ];
        list[ j - gapSize ] = temp;
        j -= gapSize;
      }
    }
  };

Моя сортировка слиянием:

  var mergeSort = function( list ) {
    if ( list.length <= 1 ) {
      return list;
    }

    var left = [],
        right = [],
        middle = Math.floor( list.length / 2 ),
        i;

    for( i = 0; i < middle; i++ ) {
      left.push( list[i] );
    }

    for( ; i < list.length; i++ ) {
      right.push( list[i] );
    }

    left = mergeSort( left );
    right = mergeSort( right );

    return _merge( left, right );
  };

  function _merge( left, right ) {
    var result = [];

    // Should be able to just get rid of arguments in while loop
    while( left.length || right.length ) {
      if( left.length > 0 && right.length > 0 ) {
        if( left[0] <= right[0] ) {
          result.push( left.shift() );
        } else {
          result.push( right.shift() );
        }
      } 
      else if( left.length ) {
        return result.concat( left );
      } 
      else {
        return result.concat( right );
      }
    }
  }

Мои тесты:

  var testSpeed = function( testSize, rounds ) {
    var testArrays = [],
        algorithms = Array.prototype.slice.call( arguments, 2 ),
        results = [],
        i, j;

    for( i = 0; i < rounds; i++ ) {
      testArrays[i] = [];
      for( j = 0; j < testSize; j++ ) {
        testArrays[i].push( Math.ceil( Math.random() * testSize ));
      }
    }

    for( i = 0; i < algorithms.length; i++ ) {
      for( j = 0; j < rounds; j++ ) {
        if( !results[i] ) {
          results[i] = [];
        }
        results[i].push( testAlgorithm( algorithms[i], testArrays[j] ));
      }
    }

    return results;
  };

  var testAlgorithm = function( algorithm, set ) {
    var clone = set.slice(),
        start = new Date().getTime(),
        end;

    algorithm( clone );

    end = new Date().getTime();

    return end - start;
  };

person EmptyArsenal    schedule 28.02.2014    source источник
comment
Это может лучше подходить для codereview.stackexchange.com.   -  person Josh Mein    schedule 01.03.2014
comment
Сортировка Шелла является квадратичной в худшем случае с вашей последовательностью пробелов, так почему вы думаете, что она не должна быть медленнее, чем сортировка слиянием?   -  person Niklas B.    schedule 01.03.2014
comment
Хотя я совсем не проверял ваш код полностью (и codereview, вероятно, является лучшим местом, чтобы попросить о проверке кода), я считаю, что используемая вами последовательность пробелов не очень хороша; в отличие от других последовательностей пробелов, он имеет временную сложность в наихудшем случае O (N ^ 2).   -  person rici    schedule 01.03.2014
comment
@NiklasB.: Это зависит от последовательности интервалов, которую вы используете. См. Sedgewick 1996 (citeseerx.ist.psu.edu/viewdoc/ summary?doi=10.1.1.101.9061) для обзора результатов сложности.   -  person rici    schedule 01.03.2014
comment
@rici Я принял во внимание последовательность пробелов, которая здесь выбрана особенно неудачно. Но даже с хорошей последовательностью пробелов вы вряд ли асимптотически превзойдете сортировку слиянием.   -  person Niklas B.    schedule 01.03.2014
comment
Спасибо за обратную связь и указание на codereview. Я не специалист по CS, и точное вычисление O не в моих силах. Я использовал последовательность гэпов Марчина Чиуры [704, 301, 132...], но производительность была хуже, вероятно, из-за моей реализации.   -  person EmptyArsenal    schedule 01.03.2014


Ответы (1)


Я собрал ваши алгоритмы вместе, сделал функцию, которая просто сэмплирует их, записывает время (с помощью millis() вместо того, как вы его тестируете, чего я не понимаю) и выводит среднее время. Сортировка оболочки составляет 0,02 миллисекунды для массива из 100 чисел.. не принимая во внимание, что большинство миллисекундных записей сортируются с помощью сортировки оболочки (поскольку иногда это 0). Я сделал то же самое с сортировкой слиянием, и это 0,21 миллисекунды. Надеюсь, это ответит на это https://www.khanacademy.org/computer-programming/sort-algorithms/6199294078746624

person sean mccarren    schedule 14.02.2015