Стабильная сортировка, т.е. Минимально-разрушающая сортировка

Предположим, что у меня есть список вещей (числа, чтобы все было просто), и у меня есть функция, которую я хочу использовать для сортировки, используя SortBy. Например, следующий вид списка чисел по последней цифре:

SortBy[{301, 201}, Mod[#,10]&]

И обратите внимание, как два (из всех) этих чисел имеют одинаковую последнюю цифру. Поэтому не имеет значения, в каком порядке мы их возвращаем. В этом случае Mathematica возвращает их в обратном порядке. Как я могу обеспечить, чтобы все связи были нарушены в пользу того, как элементы были заказаны в исходном списке?

(Я знаю, что это немного тривиально, но я чувствую, что это время от времени возникает, поэтому я подумал, что было бы удобно получить его на StackOverflow. Я опубликую все, что придумаю, как ответ, если никто не бьет я к нему.)

Попытка сделать это более доступным для поиска: сортировать с минимальным возмущением, сортировать с наименьшим количеством свопов, настраивать разбиение на разделы, сортировать с дорогостоящей заменой, стабильную сортировку.

PS: Благодаря Nicholas за то, что это называется стабильной сортировкой. Это было на кончике моего языка! Здесь другая ссылка: http://planetmath.org/encyclopedia/StableSortingAlgorithm.html

Ответ 1

После того, как я спросил, мне дали удовлетворительное объяснение:

Короткий ответ: вы хотите SortBy[list, {f}] получить стабильный вид.

Длинный ответ:

SortBy[list, f] сортирует список в порядке, определенном путем применения f к каждому элементу списка, разбивая связи, используя каноническое упорядочение, объясненное в Sort. (Это вторая документированная заметка "Дополнительная информация" в документации для SortBy.)

SortBy[list, {f, g}] разрывает связи, используя порядок, определяемый применением g к каждому элементу.

Обратите внимание, что SortBy[list, f] совпадает с SortBy[list, {f, Identity}].

SortBy[list, {f}] не связывает разрыв (и дает стабильный вид), что вы хотите:

In[13]:= SortBy[{19, 301, 201, 502, 501, 101, 300}, {Mod[#, 10] &}]

Out[13]= {300, 301, 201, 501, 101, 502, 19}

Наконец, решение sakra SortBy[list, {f, tie++ &}] эффективно эквивалентно SortBy[list, {f}].

Ответ 2

Делает ли GatherBy то, что вы хотите?

Flatten[GatherBy[{301, 201, 502, 501, 101}, Mod[#, 10] &]]

Ответ 3

Существует вариант SortBy, который разрывает связи с помощью дополнительных функций упорядочения:

SortBy[list,{f1, f2, ...}]

Посредством подсчета связей вы можете получить стабильную сортировку:

Module[{tie = 0}, 
 SortBy[{19, 301, 201, 502, 501, 101, 300}, {Mod[#, 10] &, (tie++) &}]]

дает

{300, 301, 201, 501, 101, 502, 19}

Ответ 4

Это работает:

stableSortBy[list_, f_] := 
  SortBy[MapIndexed[List, list], {[email protected][#], Last[#]}&][[All,1]]

Но теперь я вижу rosettacode дает гораздо лучший способ сделать это:

stableSortBy[list_, f_] := list[[Ordering[f /@ list]]]

Итак, заказ - это ключ! Похоже, в документации Mathematica не упоминается об этом иногда важном различии Sort and Ordering.