Количество сравнений с использованием сортировки слиянием

Если у вас есть 5 различных чисел, сколько сравнений вам потребуется, чтобы отсортировать их с помощью сортировки слиянием?


person DarthVader    schedule 04.10.2009    source источник
comment
Ну, я студент, но это не вопрос домашнего задания. Просто любопытно. O(nlogn) — наихудший случай сортировки слиянием. что дает 5 * 2,3 = 11 сравнений, но когда я делаю это на бумаге, я получаю лучшие результаты, поэтому мне было любопытно. Сколько сравнений нам нужно, чтобы отсортировать это в худшем случае?   -  person DarthVader    schedule 04.10.2009
comment
Худшим случаем было бы сравнение каждого числа с любым другим числом, равным 10.   -  person Zed    schedule 04.10.2009
comment
То же, что пузырьковая сортировка, сортировка вставками или сортировка выбором. можете ли вы дать последовательность из 5 чисел для худшего случая?   -  person DarthVader    schedule 04.10.2009
comment
Напишите сортировку слиянием, сгенерируйте все перестановки, и все будет готово.   -  person sdcvvc    schedule 04.10.2009
comment
Обозначение Big O описывает только верхнюю границу. f(n) ∈ O(n·log n) означает, что независимо от того, насколько велико n, поведение f(n) с некоторой конкретной точки всегда меньше, чем n·log n.   -  person Gumbo    schedule 04.10.2009
comment
Обозначение Big O описывает рост, оно ничего не говорит ни о количестве операций, с которыми вы закончите, ни о том, сколько времени они займут. Таким образом, O(nlogn) вполне может превзойти O(1), если 1 означает 1 неделю, а другой алгоритм O(nlogn) занимает всего несколько минут.   -  person Lasse V. Karlsen    schedule 05.10.2009


Ответы (6)


Я нахожу этот вопрос интересным, поэтому решил тщательно его изучить (с небольшими экспериментами с Python).

Я скачал mergesort.py с здесь и изменил его, добавив аргумент cmp для функция сравнения. Затем:

import collections
import itertools
import mergesort
import sys

class CountingComparator(object):
  def __init__(self):
    self.count = 0
  def __call__(self, a, b):
    self.count += 1
    return cmp(a, b)

ms_histo = collections.defaultdict(int)

for perm in itertools.permutations(range(int(sys.argv[1]))):
  cc = CountingComparator()
  lperm = list(perm)
  mergesort.mergesort(lperm, cmp=cc)
  ms_histo[cc.count] += 1

for c in sorted(ms_histo):
  print "%d %2d" % (c, ms_histo[c])

В результате получилась простая гистограмма (начиная с длины 4, как я делал для разработки и отладки):

4  8
5 16

Для проблемы, как указано, с длиной 5 вместо 4 я получаю:

5  4
6 20
7 48
8 48

и длиной 6 (и более широкий формат ;-):

7    8
8   56
9  176
10 288
11 192

Наконец, с длиной 7 (и даже более широким форматом ;-):

 9   16
10  128
11  480
12 1216
13 1920
14 1280

Конечно, здесь скрывается какая-то совершенно правильная комбинаторная формула, но мне трудно оценить, что это может быть, ни аналитически, ни путем изучения чисел. У кого-нибудь есть предложения?

person Alex Martelli    schedule 04.10.2009
comment
хорошо сделано. Я очень ценю ваше любопытство и интерес к теме. Глядя на результаты, вы можете видеть, что количество сравнений больше n и меньше 2n. Wiki предлагает: в худшем случае сортировка слиянием выполняет количество сравнений, равное или немного меньшее, чем (n ⌈lg n⌉ - 2⌈lg n⌉ + 1), что находится между (n lg n - n + 1) и (n lg n + n + O(lg n)). [1] - person DarthVader; 05.10.2009

Что мешает вам написать сортировку слиянием, сохранить в ней счетчик количества сравнений и опробовать ее на всех перестановках [0,1,2,3,4]?

person MAK    schedule 04.10.2009
comment
Мне нравится ваш ответ, у меня просто нет времени его кодировать. Я посмотрел на эти апплеты сортировки, и некоторые из них просто неправильные, некоторые просто картинки. - person DarthVader; 04.10.2009
comment
Сортировка слиянием на самом деле не занимает так много времени, как вы, вероятно, думаете. В Python он довольно короткий (и я думаю, что еще короче во многих функциональных языках), и базовое решение C/C++/Java также не должно быть слишком длинным. - person MAK; 05.10.2009

При сортировке слиянием двух списков длины L1 и L2 я полагаю, что наихудшее количество сравнений — это L1+L2-1.

  • Изначально у вас есть пять 1-длинных списков.
  • Вы можете объединить две пары списков с помощью 2 сравнений, в результате чего получатся списки длиной 2, 2 и 1.
  • Затем вы можете объединить длинный список 2 и 1 не более чем с другими сравнениями 1+2-1 = 2, получив длинный список 2 и 3.
  • Наконец, вы объединяете эти списки не более чем с 2+3-1 = 4 сравнениями.

Так что я думаю, что ответ 8.

Эта последовательность чисел приводит к приведенному выше: [2], [4], [1], [3], [5] -> [2,4], [1,3], [5] -> [2, 4], [1,3,5] -> [1,2,3,4,5]

Изменить:

Вот наивная реализация Erlang. Исходя из этого, количество сравнений равно 5,6,7 или 8 для перестановок 1..5.

-module(mergesort).

-compile(export_all).


test() ->
  lists:sort([{sort(L),L} || L <- permutations()]).

sort([]) -> {0, []};
sort([_] = L) -> {0, L};
sort(L) -> 
  {L1, L2} = lists:split(length(L) div 2, L),
  {C1, SL1} = sort(L1), {C2, SL2} = sort(L2),
  {C3, RL} = merge(SL1, SL2, [], 0),
  {C1+C2+C3, RL}.

merge([], L2, Merged, Comps) -> {Comps, Merged ++ L2};
merge(L1, [], Merged, Comps) -> {Comps, Merged ++ L1};
merge([H1|T1], [H2|_] = L2, Merged, Comps) when H1 < H2 -> merge(T1, L2, Merged ++[H1], Comps + 1);
merge(L1, [H2|T2], Merged, Comps) -> merge(L1, T2, Merged ++[H2], Comps + 1).


permutations() ->
  L = lists:seq(1,5),
  [[A,B,C,D,E] || A <- L, B <- L, C <- L, D <- L, E <- L, A =/= B, A =/= C, A =/= D, A =/= E, B =/= C, B =/= D, B =/= E, C =/= D, C =/= E, D =/= E].
person Zed    schedule 04.10.2009

http://www.sorting-algorithms.com/

person Makach    schedule 04.10.2009

Согласно Википедии: В худшем случае сортировка слиянием выполняет количество сравнений, равное до или чуть меньше (n ⌈lg n⌉ - 2^⌈lg n⌉ + 1)

person SteinNorheim    schedule 04.10.2009
comment
Я прочитал это, я просто хотел увидеть номер. тогда я могу сказать: 5 * 3 - 2 * 3 + 1 = 10. Мне также было любопытно узнать о таком экземпляре чисел. - person DarthVader; 04.10.2009
comment
Поскольку ⌈lg 5⌉ равно 2, ответ будет 5*2-2^2+1 = 7. Это имеет смысл, если вы будете следовать алгоритму, описанному в статье. Если исходная последовательность 2,4,1,3,5, сравнения будут в порядке появления: (2,4) (2,1) (3,5) (1,3) (2,3) (4,3) (4,5) - person SteinNorheim; 05.10.2009
comment
формула предлагает потолок, а не пол. следовательно, log5 = 3 - person DarthVader; 05.10.2009
comment
На самом деле, я допустил ошибку, предполагая, что lg относится к натуральному логарифму. ln(5)=1,609... Но, конечно, логарифм по основанию 2 имеет больше смысла в этом контексте. - person SteinNorheim; 05.10.2009
comment
stackoverflow.com/questions/12346054/ спрашивает об этой формуле и о том, откуда она взялась. У моего ответа есть доказательство. - person MvG; 10.09.2012

Для сортировки всего пяти различных чисел максимальное количество сравнений, которое вы можете иметь, равно 8, а минимальное количество сравнений равно 7. Вот почему: -

Предположим, что массив представляет собой a,b,c,d,e

разделить рекурсивно: a,b,c и d,e

разделить рекурсивно: a,b&c и d&e

разделить рекурсивно: a&b&c и d&e

Теперь слияние, которое потребует сравнения-

a & b : одно сравнение для формирования a,b

a,b и c : два сравнения для формирования a,b,c

d & e : одно сравнение с формой d,e

a,b,c и d,e : четыре сравнения в худшем случае или три сравнения id d является наибольшим элементом массива для формирования a,b,c,d,e

Таким образом, общее количество сравнений будет восемь в худшем случае и семь в лучшем случае.

person Chahat Ahuja    schedule 08.02.2016