1632
правки
Изменения
Timsort
,rollbackEdits.php mass rollback
'''Timsort''' — {{---}} гибридный алгоритм сортировки, сочетающий сортировку вставками и сортировку слияниемразличные подходы.
Данный алгоритм является относительно новым и был изобретен в 2002 году придуман Тимом Петерсом(в честь него и назван). На массивах данных, которые содержат упорядоченные подмассивы, алгоритм Тима Петерса показывает себя намного лучше других сортировок. В настоящее время '''Timsort''' является стандартным алгоритмом сортировки стандартной сортировкой в '''Python''' и '''GNU Octave''', реализован в '''OpenJDK 7''' и реализован в '''Android JDK 1.5'''. Чтобы понять почему — достаточно взглянуть на таблицу из Википедии:
== Основная идея алгоритма ==
Алгоритм '''Timsort''' состоит из нескольких частей:
* Начало.
* '''Шаг 1'''. Входной массив разделяется на подмассивы фиксированной длины, вычисляемой определённым образом.
* '''Шаг 2'''. Каждый подмассив сортируется [[Сортировка вставками | сортировкой вставками]], [[Сортировка пузырьком | сортировкой пузырьком]] или любой другой устойчивой сортировкой.
* '''Шаг 3'''. Отсортированные подмассивы объединяются в один массив с помощью модифицированной [[Сортировка слиянием | сортировки слиянием]].
* Конец.
== Алгоритм == === Обозначения === * <tex>n</tex> {{---}} размер входного массива.*<tex>\mathtt {run}</tex> Каждый {{---}} подмассив сортируется [httpво входном массиве, который обязан быть упорядоченным одним из двух способов:** строго по убыванию <tex>\mathtt {a_{i} > a_{i + 1} > \dots} </tex>. ** нестрого по возрастанию <tex>\mathtt {a_{i} \leqslant a_{i + 1} \leqslant \dots} </neerctex>.ifmo.ru * <tex>\mathtt {minrun} </wiki/index.php?title=%D0%A1%D0%BE%D1%80%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%BA%D0%B0_%D0%B2%D1%81%D1%82%D0%B0%D0%B2%D0%BA%D0%B0%D0%BC%D0%B8 сортировкой вставками]tex> {{---}} минимальный размер подмассива, описанного в предыдущем пункте.
===Шаг 1. Вычисление minrun===* Начало.* '''Шаг 0'''. Число <tex>\mathtt{minrun}</tex> определяется на основе <tex>n</tex>, исходя из следующих принципов:** Не должно быть слишком большим, поскольку к подмассиву размера <tex>\mathtt{minrun}</tex> будет в дальнейшем применена сортировка вставками (эффективна только на небольших массивах).** Отсортированные подмассивы собираются в единый массив с помощью модифицированной Оно не должно быть слишком маленьким, так как чем меньше подмассив, тем больше итераций слияния подмассивов придётся выполнить на последнем шаге алгоритма. Оптимальная величина для <tex> \mathtt{\dfrac{n}{minrun}} </tex> {{---}} ''степень двойки''. Это требование обусловлено тем, что алгоритм слияния подмассивов наиболее эффективно работает на подмассивах примерно равного размера.** Автором алгоритма было выбрано оптимальное значение, которое принадлежит диапазону <tex> [http32; 65) </tex> (подробнее о том, почему так, будет сказано ниже). ** Исключение:если <tex> n < 64 </tex>, тогда <tex> n = \mathtt{minrun} </neerctex> и '''Timsort''' превращается в сортировку вставками.ifmo* '''Шаг 1'''.ruБерем старшие 6 бит числа <tex>n</wiki/index.php?title=%D0%A1%D0%BE%D1%80%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%BA%D0%B0_%D1%81%D0%BB%D0%B8%D1%8F%D0%BD%D0%B8%D0%B5%D0%BC сортировки слиянием]tex> и добавляем единицу, если в оставшихся младших битах есть хотя бы один ненулевой.
Нетрудно понять, что после таких вычислений, <tex>\mathtt{\dfrac{{n}}{minrun}} </tex> будет равно степени двойки или немного меньше степени двойки.
* Конец.
'''int''' minRunLength(n):
flag = 0 <font color=green>// будет равно 1, если среди сдвинутых битов есть хотя бы один ненулевой</font>
'''while''' (n <tex> \geqslant</tex> 64)
flag |= n & 1
n >>= 1
'''return''' n + flag
== Алгоритм =Шаг 3. Слияние ===Нужно объединить полученные подмассивы для получения результирующего упорядоченного массива. Для достижения эффективности, нужно ''объединять подмассивы примерно равного размера'' и ''cохранять стабильность алгоритма''. [[Файл:Merge2mas.png|400px|right]]* Начало.* '''Шаг 0'''. Создается пустой стек пар <tex> < </tex> индекс начала подмассива, размер подмассива <tex> > </tex>.* '''Шаг 1'''. Берется первый упорядоченный подмассив.* '''Шаг 2'''. Добавляется в стек пара данных <tex> < </tex> индекс начала текущего подмассива, его размер <tex> > </tex>.* '''Шаг 3'''. Пусть <tex>X,Y,Z </tex> {{---}} длины верхних трех интервалов, которые лежат в стеке. Причем <tex>X</tex> {{---}} это последний элемент стека (если интервалов меньше трёх, проверяем лишь условия с оставшимися интервалами).* '''Шаг 4'''. Повторяем пока выражение (<tex>Z > X + Y \wedge Y > X</tex>) не станет истинным ** Если размер стека не меньше <tex>2</tex> и <tex>Y \leqslant X </tex> {{---}} сливаем <tex>X</tex> c <tex>Y</tex>. ** Если размер стека не меньше <tex>3</tex> и <tex>Z \leqslant X + Y</tex> {{---}} сливаем <tex>Y</tex> c <tex>\min(X,Z)</tex>. * '''Шаг 5'''. Переходим к шагу 2.* Конец
===Шаг №2. Разбиения на подмассивы и их сортировка=Модификация процедуры слияния подмассивов (Galloping Mode) ==На данном этапе у нас есть входной массив, его размер <tex>n</tex> и вычисленное число <tex>minrun</tex>. Алгоритм работы этого шага: [[ФайлРассмотрим процедуру слияния двух массивов:Массив.png|300px|thumb|right]]# Указатель текущего элемента ставится в начало входного массива.# Начиная с текущего элемента, идет поиск во входном массиве упорядоченного подмассива <tex>run</tex>. По определению, в <tex>run</tex> однозначно войдет текущий элемент и следующий за ним. Если получившийся подмассив упорядочен по убыванию — элементы переставляются так, чтобы они шли по возрастанию.# Если размер текущего <tex>run</tex> меньше <tex>minrun</tex>, тогда выбираются следующие за найденным подмассивом <tex>run</tex> элементы в количестве <tex> minrun - size(run) </tex>. Таким образом, на выходе будет получен подмассив размером большим или равный <tex>minrun</tex>, часть которого (в лучшем случае — он весь) упорядочена.# К данному подмассиву применяем сортировка вставками. Так как размер подмассива невелик и часть его уже упорядочена — сортировка работает эффективно. # Указатель текущего элемента ставится на следующий за подмассивом элемент. # Если конец входного массива не достигнут — переход к пункту 2, иначе — конец данного шага.
<tex>*B = {20000, 20001, 20002, \dots, 29999, 30000}</tex> Итак, нужно объединить полученные подмассивы для получения результирующего упорядоченного массива. Для достижения эффективности, объединение должно удовлетворять требованиям:# Объединять подмассивы примерно равного размера# Сохранить стабильность алгоритма (не делать бессмысленных перестановок). [[Файл:Слияние.png|right]]
==Доказательство времени работы алгоритма =Описание процедуры =Не сложно заметить, что чем меньше массивов, тем меньше произойдёт операций слияния, но чем их длины больше, тем дольше эти слияния===будут происходить. На малом количестве длинных массивов хорошо помогает вышеописанный метод '''Galloping Mode'''. Хоть он и не даёт асимптотического выигрыша, но уменьшает константу.Пусть <tex>*k</tex> Создается временный {{---}} число кусков, на которые разбился наш исходный массив в размере меньшего из сливаемых подмассивов, очевидно <tex>k</tex> = <tex> \left\lceil \mathtt{\dfrac{n}{minrun}} \right\rceil </tex>.
Главный факт, который нам понадобится для доказательства нужной оценки времени работы в <tex>*O(n \log{n})</tex> Меньший из подмассивов копируется во временный массив{{---}} это то, что сливаемые массивы '''всегда''' имеют примерно одинаковую длину. Можно сказать больше: пока <tex>k > 3</tex> сливаемые подмассивы будут именно одинаковой длины (данный факт хорошо виден на примере). Безусловно, после разбиения массива на блоки длиной <tex>\mathtt{minrun}</tex> последний блок может быть отличен от данного значения, но число элементов в нём не превосходит константы <tex>\mathtt{minrun}</tex>.
Мы выяснили, что при слиянии, длинна образовавшегося слитого массива увеличивается в <tex>\approx 2</tex> раза. Таким образом получаем, что каждый подмассив <tex>*\mathtt{run_i}</tex> Ставятся указатели текущей позиции на первые элементы большего может участвовать в не более <tex>O(\log{n})</tex> операций слияния, а значит и временного массивакаждый элемент будет задействован в сравниниях не более <tex>O(\log{n})</tex> раз. Элементов <tex>n</tex>, откуда получаем оценку в <tex>O(n\log{n})</tex>.
Также нужно сказать про [[Сортировка вставками | сортировку вставками]], которая используется для сортировки подмассивов <tex>\mathrm{run_i}</tex>: в нашем случае, алгоритм работает за <tex>O(\mathtt{minrun + inv})</tex>, где <tex>*\mathtt{inv}</tex> На каждом шаге рассматривается значение текущих {{---}} число обменов элементов входного массива, равное числу инверсий. C учетом значения <tex>k</tex> получим, что сортировка всех блоков может занять <tex>O(\mathtt{minrun + inv}) \cdot k = O(\mathtt{minrun + inv}) \cdot </tex><tex>\left\lceil \mathtt{\dfrac{n}{minrun}} \right\rceil </tex>. Что в большем и временном массиваххудшем случае <tex dpi>(\mathtt{inv = \dfrac{minrun(minrun - 1)}{2}} )</tex> может занимать <tex>O(\mathtt{n \cdot minrun}) </tex> времени. Откуда видно, берется меньший из нихчто константа <tex>\mathtt{minrun}</tex> играет немалое значение: при большом <tex>\mathtt{minrun}</tex> слияний будет меньше, копируется в новый отсортированный массива сортировки вставками будут выполняться долго. Указатель текущего элемента перемещается в массивеПричём эти функции растут с разной скоростью, из которого поэтому и ещё после экспериментов на различных значениях и был взят элементвыбран оптимальный диапазон {{---}} от <tex>32</tex> до <tex>64</tex>.
[[Категория: Дискретная математика и алгоритмы]]
[[Категория: Сортировка]][[Категория: Сортировкина сравнениях]]