Если у вас есть 5 различных чисел, сколько сравнений вам потребуется, чтобы отсортировать их с помощью сортировки слиянием?
Количество сравнений с использованием сортировки слиянием
Ответы (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
Конечно, здесь скрывается какая-то совершенно правильная комбинаторная формула, но мне трудно оценить, что это может быть, ни аналитически, ни путем изучения чисел. У кого-нибудь есть предложения?
Что мешает вам написать сортировку слиянием, сохранить в ней счетчик количества сравнений и опробовать ее на всех перестановках [0,1,2,3,4]?
При сортировке слиянием двух списков длины 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].
Согласно Википедии: В худшем случае сортировка слиянием выполняет количество сравнений, равное до или чуть меньше (n ⌈lg n⌉ - 2^⌈lg n⌉ + 1)
Для сортировки всего пяти различных чисел максимальное количество сравнений, которое вы можете иметь, равно 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
Таким образом, общее количество сравнений будет восемь в худшем случае и семь в лучшем случае.