Как работает сортировка вставками слиянием?

В настоящее время я изучаю алгоритмы сортировки и нашел сортировку слиянием. Я почти ничего не смог найти для него, кроме нескольких статей и ссылок на книги. Итак, этот алгоритм был открыт Лестером Фордом-младшим и Селмером Джонсоном. Частично это описано здесь: http://www2.warwick.ac.uk/fac/sci/dcs/teaching/material/cs341/FJ.pdf

Моя проблема сейчас заключается в том, чтобы понять, как работает часть вставки, и, кроме того, какая последовательность чисел 1, 3, 5, 11, упомянутая в объяснении, как вставить. Это выглядит так знакомо, но я просто не могу вспомнить, что это было.

То, что у меня есть на данный момент, выглядит так:

//pointer to array, array size, element size, compare function pointer
void sort(void *data, size_t n, size_t s, int (*fcomp)(void*, void*))
{
  if(!data) return;
  if(n < 2 || s == 0) return;

  size_t i = 0, j = 0, k = 0, l = 0, r = 0, m = 0;

  void *be = malloc((n/2)*s); //elements greater in pair comparison
  void *le = malloc((n/2 + n%2)*s);//elements lesser in pair comparison
  void *mc = malloc(n*s); //main chain

  //compare pair-wise K_1:K_2, ... , K_N:K_N-1
  for(i = 0; i < n; i+=2)
  {
    if(fcomp(voidAdd(data, s, i), voidAdd(data, s, i+1)) >= 0)
    {
      //element at i bigger than i+1 so, put it in be and i+1 in le
      memcpy(voidAdd(be, s, k++), voidAdd(data, s, i), s);
      memcpy(voidAdd(le, s, j++), voidAdd(data, s, i+1), s);
    }
    else
    {
      //element i+1 bigger than i so put it in be and i in le
      memcpy(voidAdd(be, s, k++), voidAdd(data, s, i+1), s);
      memcpy(voidAdd(le, s, j++), voidAdd(data, s, i), s);
    }
  }

  sort(be, n/2, s, fcomp); //recursivly repeat process for bigger elements
  /*
  now we have chain a_1, ..., a_n/2 and b_1, ..., b_n/2 with a_i > b_i and
  a_1 < ... a_n/2
  */

  memcpy(mc, le, s); //insert b_1 into the main-chain
  memcpy(voidAdd(mc, s, 1), be, (n/2)*s); //copy a_1, ... a_n/2 in main chain
  //now we have b_1, a_1, ..., a_n/2 as main chain

  //start insertion here
  j = n/2 + 1;
  for(i = 1; i < n/2; i++)
  {
    k = ...;//number from sequence 1, 3, 5, 11, ...
  }

  memcpy(data, mc, n*s);
  free(mc);
  free(be);
  free(le);

}

Из того, что написано в связанном pdf, в него нужно вставить b_3, b_2, b_5, b_4... в основную цепочку теперь с бинарной вставкой, но я не уверен, как именно это сделать и откуда они берут эти числа .


person rfreytag    schedule 03.01.2015    source источник
comment
Числа объясняются в вашем PDF-файле и имеют форму t(k) = (2^(k+1) + (-1)^k)/3, где ^ — оператор степени.   -  person orlp    schedule 03.01.2015
comment
вау, как я этого не заметил... спасибо, что указали на это!   -  person rfreytag    schedule 03.01.2015
comment
это может помочь: youtube.com/watch?v=XaqR3G_NVoo ^^   -  person albttx    schedule 12.01.2016
comment
@PandaCool Осторожно: сортировка слиянием и сортировка слиянием-вставками - это разные алгоритмы: p   -  person Morwenn    schedule 12.01.2016


Ответы (1)


Я фактически реализовал этот алгоритм на C++ на этой неделе и смог понять, как работает часть вставки. Не хочу повторяться, поэтому процитирую самого себя:

Чтобы выполнить минимальное количество сравнений, нам нужно принять во внимание следующее наблюдение о бинарном поиске: максимальное количество сравнений, необходимое для выполнения бинарного поиска в отсортированной последовательности, одинаково, когда количество элементов равно 2 ^ n и когда это 2^(n+1)−1. Например, поиск элемента в отсортированной последовательности из 8 или 15 элементов требует одинакового количества сравнений.

По сути, после вставки первого элемента pend в основную цепочку алгоритм берет самый дальний элемент pend, для которого необходимо выполнить 2 сравнения. : вам нужно 2 сравнения, чтобы вставить менее 4 элементов, поэтому мы берем b3 в документе, потому что мы можем вставить его в {b1, a1, a2}. Затем мы знаем, что b2 < a2, поэтому мы можем вставить a2 в основную цепочку, которая будет либо {b1, a1}, либо {b1, a1, b2}, что означает, что мы вставляем его в цепочку не более чем из 3 элементов, поэтому нам нужно не более 2 сравнений, чтобы вставить его. Затем нам нужен элемент, который можно будет вставить не более чем с 3 сравнениями, поэтому его нужно вставить в основную цепочку, состоящую не более чем из 7 элементов: у нас есть b5 < a5, поэтому мы можем вставить b5 в {b1, a1, b2, a2, a3, b3, a4}, которая является основной цепочкой. из 7 элементов и т.д...

Следующий pend b для выбора всегда будет соответствовать элементу, который вы можете вставить в основную цепочку, размер которой равен 2^n - 1. Кнуту удалось найти формулу генерации, заданную @orlp: t(k) = (2^(k+1) + (-1)^k)/3. Сгенерированные числа соответствуют числам Джейкобсталя; серия растет так быстро, что вы можете просто кэшировать их, 66-е число Якобсталя даже не помещается в 64-битное целое число. После того, как вы вставили такой элемент bk, вы можете вставить в обратном порядке все элементы bk, для которых k меньше текущего числа Якобсталя. Если у вас остались элементы pend в конце сортировки, но ни один из них не имеет индекса, соответствующего числу Якобсталя, просто вставьте их в основную цепочку; порядок вставки не должен иметь значения, поскольку количество сравнений, необходимых для вставки любого из них, должно быть одинаковым независимо от порядка вставки.

person Morwenn    schedule 10.01.2016
comment
Хорошо, спасибо! Моя самая большая проблема с пониманием того, как это работает, заключалась в том, как синхронизировать порядок подсписков, как описано в книге. Я посмотрю и посмотрю, как вы решили это. - person rfreytag; 12.01.2016
comment
@pfannkuchen_gesicht По сути, я создал списки элементов основной цепочки и список узлов pend, где каждый узел содержит элемент и итератор к более высокому элементу в списке основной цепочки. . Это позволяет узнать верхнюю границу вставки всякий раз, когда вам нужно вставить элемент pend. - person Morwenn; 12.01.2016