Метод Шелла, быстрая сортировка

Метод Шелла


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

Более подробно, на первом этапе группируются элементы входного набора с достаточно большим шагом. Например, выбираются все 1000-е элементы, т.е. создаются группы:
группа 1:   1, 1001, 2001, 3001 и т.д.
группа 2:   2, 1002, 2002, 3002 и т.д.
группа 3:   3, 1003, 2003, 3003 и т.д.
. . . . . . . . . . . . . . . . . . . . .
группа 1000:  1000, 2000, 3000 и т.д.
Внутри каждой группы выполняется обычная сортировка вставками, что эффективно за счет небольшого числа элементов в группе.
На втором этапе выполняется группировка уже с меньшим шагом, например - все сотые элементы. В каждой группе опять выполняется обычная сортировка вставками, которая эффективна за счет того, что после первого этапа в каждой группе набор данных будет уже частично отсортирован.
На третьем этапе элементы группируются с еще меньшим шагом, например – все десятые элементы. Выполняется сортировка, группировка с еще меньшим шагом и т.д.
На последнем этапе сначала выполняется группировка с шагом 1, создающая единственный набор данных размерности  n, а затем - сортировка практически отсортированного набора.
Пример. Исходный набор: 15 – 33 – 42 – 07 – 12 - 19
Выполняем группировку с шагом 3, создаем три группы по 2 элемента и сортируем каждую из них отдельно:
группа 1:  15 – 07   =>  07 – 15 (1 сравнение, 1 пересылка)
группа 2:  33 – 12   =>  12 – 33 (1 сравнение, 1 пересылка)
группа 3:  42 – 19   =>  19 – 42 (1 сравнение, 1 пересылка)
Новый набор чисел:  07 – 15 – 12 – 33 – 19 – 42
Группировка с меньшим шагом 2 дает 2 группы по 3 элемента, которые сортируются отдельно:
группа 1:  07 – 12 – 19  =>  уже упорядочена (2 сравнения, 0 пересылок)
группа 2:  15 – 33 – 42  =>  уже упорядочена (2 сравнения, 0 пересылок)
Новый набор чисел:  07 – 12 – 19 – 15 – 33 – 42
Последняя группировка с шагом 1 дает сам набор чисел; к нему применяется сортировка вставками с 5-ю сравнениями и только одной пересылкой, после чего получаем искомый результат.
Итого – 12 сравнений и 4 пересылки, что в общем-то не лучше чем у простых методов. Однако, здесь надо учесть два фактора.
Фактор 1 (общий). Улучшенные методы показывают свою эффективность именно для больших наборов данных (сотни, тысячи и т.д. элементов). Для очень малых наборов (как в примере) они могут давать даже худшие результаты.
Фактор 2 (специфический). Эффективность метода Шелла существенно зависит от выбора последовательности шагов группировки. Эта последовательность обязательно должна быть убывающей, а последний шаг обязательно равен 1. В настоящее время неизвестна наилучшая последовательность шагов, обеспечивающая наименьшую трудоемкость. На основе многочисленных экспериментов установлено, что число шагов группировки надо выбирать по формуле  [(log 2 n)] – 1, где  скобки [ ] используются для обозначения целой части числа, а в качестве самих последовательностей рекомендуется один из следующих наборов (обращаю внимание: для удобства восприятия шаги даются в обратном порядке):
1, 3, 5, 9, 17, 33, . . .  (общая формула:  tk = (2* tk-1) –1 )
1, 3, 7, 15, 31, 63, 127, 255, 511, 1023, 2047, 4095, 8191, 16383, 32767 . . . (общая формула:  tk = (2* tk-1) +1, а еще проще – (2k – 1)).
В соответствии с этими рекомендациями, в предыдущем примере надо взять лишь 2 шага группировки со значениями 3 и 1. В этом случае потребуется лишь 8 сравнений и 5 пересылок.
Оценка трудоемкости метода Шелла выражается соотношением  O(n1,2), что лучше чем у простейших методов, особенно при больших  n.

Метод быстрой сортировки


Данный метод в настоящее время считается наиболее быстрым универсальным методом сортировки. Как ни странно, он является обобщением самого плохого из простейших методов – обменного метода. Эффективность метода достигается тем, что перестановка применяется не для соседних элементов, а отстоящих друг от друга на приличном расстоянии.
Более конкретно, алгоритм быстрой сортировки заключается в следующем.

Пример. Пусть исходный набор включает 11 чисел:  13-42-28-17-09-25-47-31-39-15-20. Основные шаги сортировки:

Итого для данного примера потребовалось 22 сравнения и 6 пересылок.
В целом, оценка трудоемкости метода быстрой сортировки является типичной для улучшенных методов и выражается соотношением  (n*log 2 n)/6. Отсюда следует, что данный метод неэффективен при малых  n (десятки или сотни элементов), но с ростом  n  его эффективность резко растет, и при очень больших  n  метод дает наилучшие показатели среди всех универсальных методов сортировки.
К сожалению, есть одна ситуация, когда быстрая сортировка теряет свою эффективность и становится пропорциональной  n2, т.е. опускается до уровня простых методов. Эта ситуация связана с правилом выбора опорного элемента. Эффективность метода сильно зависит от выбора опорного элемента, и использование простейшего способа выбора (серединный элемент массива) часто приводит к падению эффективности метода. Это связано с тем, что каждое разделение массива на две половины в идеале должно давать примерно равное число элементов слева и справа от опорного элемента (принцип дихотомии!). Если опорный элемент близок к минимальному или максимальному, после попарных перестановок будут получены существенно неравномерные наборы. Если подобная ситуация возникает на каждом шаге работы алгоритма, общая эффективность резко падает. Для устранения этого недостатка надо уметь правильно выбирать опорный элемент.
Наилучшее правило выбора опорного элемента – это так называемая медиана. Медиана – это средний элемент массива не по расположению, а по значению. В приведенном выше примере медианой является число 25, которое также было и серединным элементом (честно говоря, пример был подобран специально). К сожалению, поиск медианы в массиве является задачей, сопоставимой по трудоемкости с самой сортировкой, поэтому были предложены другие, более простые правила выбора опорного элемента.
На практике хорошо показал себя следующий способ: выбрать случайно в массиве три элемента и взять в качестве опорного средний из них. Этот способ очень прост в реализации, т.к. требует только двух сравнений, но, конечно, он может обеспечивать хорошие показатели только в среднем, и не гарантирует идеальное поведение алгоритма абсолютно для ЛЮБЫХ входных данных.
Есть и другие усовершенствования базового алгоритма быстрой сортировки. Среди них:

Комбинация всех этих методов известна как метод Синглтона [7].
Что касается программной реализации базового алгоритма, то можно заметить его принципиальную особенность: разделение массива на 2 половины, разделение каждой половины на свои половины и т.д. При каждом разделении приходится запоминать правую половину (конечно, не сами элементы, а лишь индексы левой и правой границы) и возвращаться к ней после полной обработки левой половины. Все это как нельзя лучше соответствует рекурсивному принципу обработки, и поэтому быстрая сортировка проще всего реализуется рекурсивно. Конечно, при необходимости можно включить явное использование стека для запоминания левой и правой границы подмассива, аналогично нерекурсивной реализации процедур обхода дерева.