Timsort — различия между версиями

Материал из Викиконспекты
Перейти к: навигация, поиск
(Доказательство времени работы алгоритма)
Строка 1: Строка 1:
 
== Timsort ==
 
== Timsort ==
 
   
 
   
'''Timsort''' гибридный алгоритм сортировки, сочетающий сортировку вставками и сортировку слиянием.
+
'''Timsort''' {{---}} гибридный алгоритм сортировки, сочетающий сортировку вставками и сортировку слиянием.
  
Данный алгоритм был изобретен в 2002 году Тимом Петерсом(в честь него и назван). В настоящее время '''Timsort''' является стандартным алгоритмом сортировки в '''Python''', '''OpenJDK 7''' и реализован в '''Android JDK 1.5'''. Чтобы понять почему — достаточно взглянуть на таблицу из Википедии:
+
Данный алгоритм был изобретен в 2002 году Тимом Петерсом(в честь него и назван) и основывается на том, что в реальном мире сортируемые массивы данных часто содержат в себе упорядоченные подмассивы. На таким данных алгоритм Тима Петерса существенно быстрее многих других алгоритмов сортировки. В настоящее время '''Timsort''' является стандартной сортировкой в '''Python''', '''OpenJDK 7''' и реализован в '''Android JDK 1.5'''.
  
 
== Основная идея алгоритма ==
 
== Основная идея алгоритма ==
 +
Алгоритм '''Timsort''' состоит из нескольких шагов:
  
* По специальному алгоритму входной массив разделяется на подмассивы.
+
* '''Шаг №1''': по специальному алгоритму входной массив разделяется на подмассивы.
 
 
* Каждый подмассив сортируется [[Сортировка вставками | сортировкой вставками]] или [[Сортировка выбором | сортировкой выбором]].
 
 
 
* Отсортированные подмассивы собираются в единый массив с помощью модифицированной [[Сортировка слиянием | сортировки слиянием]].
 
  
 +
* '''Шаг №2''': каждый подмассив сортируется [[Сортировка вставками | сортировкой вставками]] или [[Сортировка выбором | сортировкой выбором]].
  
Данный алгоритм основывается на том, что в реальном мире сортируемые массивы данных часто содержат в себе упорядоченные подмассивы. На таких данных '''Timsort''' существенно быстрее многих дргугих алгоритмов сортировки.
+
* '''Шаг №3''': отсортированные подмассивы собираются в единый массив с помощью модифицированной [[Сортировка слиянием | сортировки слиянием]].
  
 
== Алгоритм ==
 
== Алгоритм ==
Строка 20: Строка 18:
 
===Используемые понятия и комментарии===
 
===Используемые понятия и комментарии===
  
* <tex>n</tex>   — размер входного массива.
+
* <tex>n</tex> {{---}} размер входного массива.
  
* <tex>run</tex> некоторый подмассив во входном массиве, который обязан быть упорядоченным одним из двух способов:
+
* <tex>run</tex> {{---}} некоторый подмассив во входном массиве, который обязан быть упорядоченным одним из двух способов:
 +
** строго по убыванию <tex> a_{i} > a_{i + 1} > ...  </tex>. 
 +
** нестрого по возрастанию <tex> a_{i} \le a_{i + 1} \le ...  </tex>. 
  
# строго по убыванию <tex> a_{i} > a_{i + 1} > ...  </tex>
+
<tex>minrun</tex> {{---}} минимальный размер подмассива, описанного в предыдущем пункте.   
# нестрого по возрастанию <tex> a_{i} \le a_{i + 1} \le ...  </tex>.   
 
  
*  <tex>minrun</tex> — минимальный размер подмассива, описанного в предыдущем пункте. 
 
 
Алгоритм '''Timsort''' состоит из нескольких шагов:
 
 
===Шаг №1. Вычисление minrun===
 
===Шаг №1. Вычисление minrun===
 
Число <tex>minrun</tex> определяется на основе <tex> n </tex>, исходя из принципов:
 
Число <tex>minrun</tex> определяется на основе <tex> n </tex>, исходя из принципов:
  
* Оно не должно быть слишком большим, поскольку к подмассиву размера <tex> minrun </tex> будет в дальнейшем применена сортировка вставками, а она эффективна только на небольших массивах.
+
* Не должно быть слишком большим, поскольку к подмассиву размера <tex> minrun </tex> будет в дальнейшем применена сортировка вставками (эффективна только на небольших массивах).
  
*  Оно не должно быть слишком маленьким, так как чем меньше подмассив тем больше итераций слияния подмассивов придётся выполнить на последнем шаге алгоритма. Оптимальная величина для <tex> n / minrun </tex> ''степень двойки''. Это требование обусловлено тем, что алгоритм слияния подмассивов наиболее эффективно работает на подмассивах примерно равного размера.
+
*  Оно не должно быть слишком маленьким, так как чем меньше подмассив {{---}} тем больше итераций слияния подмассивов придётся выполнить на последнем шаге алгоритма. Оптимальная величина для <tex> n / minrun </tex> {{---}} ''степень двойки''. Это требование обусловлено тем, что алгоритм слияния подмассивов наиболее эффективно работает на подмассивах примерно равного размера.
  
 
* Согласно авторским экспериментам:   
 
* Согласно авторским экспериментам:   
# При <tex> minrun > 256 </tex> нарушается пункт <tex>1</tex>.
+
** При <tex> minrun > 256 </tex> нарушается пункт <tex>1</tex>.
# При <tex> minrun < 8 </tex> нарушается пункт <tex>2</tex>.
+
** При <tex> minrun < 8 </tex> нарушается пункт <tex>2</tex>.
# Наиболее эффективные значения <tex> minrun </tex> из диапозона <tex> (32; 65) </tex>.
+
** Наиболее эффективные значения <tex> minrun </tex> из диапозона <tex> (32; 65) </tex>.
# Исключение — если <tex> n < 64 </tex>, тогда <tex> n = minrun </tex> и '''Timsort''' превращается в сортировку вставками.
+
** Исключение — если <tex> n < 64 </tex>, тогда <tex> n = minrun </tex> и '''Timsort''' превращается в сортировку вставками.
  
 
Таким образом, алгоритм расчета <tex> minrun </tex> не так уж сложен: берем старшие 6 бит числа <tex> n </tex> и добавляем единицу, если в оставшихся младших битах есть хотя бы один ненулевой.  
 
Таким образом, алгоритм расчета <tex> minrun </tex> не так уж сложен: берем старшие 6 бит числа <tex> n </tex> и добавляем единицу, если в оставшихся младших битах есть хотя бы один ненулевой.  
Строка 53: Строка 49:
 
   }
 
   }
  
===Шаг №2. Разбиения на подмассивы и их сортировка===
+
===Шаг №2. Алгоритм разбиения на подмассивы и их сортировка===
На данном этапе у нас есть входной массив, его размер <tex>n</tex> и вычисленное число <tex>minrun</tex>. Алгоритм работы этого шага: [[Файл:MinrunExample.png‎ |300px|thumb|right]]
+
На данном этапе у нас есть входной массив, его размер <tex>n</tex> и вычисленное число <tex>minrun</tex>. Обратим внимание, что если данные изначального массива достаточно близки к случайным, то размер упорядоченных подмассивов близок к <tex>minrun</tex>,. Но если в изначальных данных были упорядоченные диапазоны, то упорядоченные подмассивы могут иметь размер, превышающий <tex>minrun</tex>. [[Файл:MinrunExample.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>, часть которого (в лучшем случае он весь) упорядочена.
+
* Начиная с текущего элемента, идет поиск во входном массиве упорядоченного подмассива <tex>run</tex>. По определению, в <tex>run</tex> однозначно войдет текущий элемент и следующий за ним. Если получившийся подмассив упорядочен по убыванию {{---}} элементы переставляются так, чтобы они шли по возрастанию.
# К данному подмассиву применяем сортировка вставками. Так как размер подмассива невелик и часть его уже упорядочена сортировка работает эффективно.  
+
* Если размер текущего <tex>run</tex> меньше <tex>minrun</tex>, тогда выбираются следующие за найденным подмассивом <tex>run</tex> элементы в количестве <tex> minrun - size(run) </tex>. Таким образом, на выходе будет получен подмассив размером большим или равный <tex>minrun</tex>, часть которого (в лучшем случае {{---}} он весь) упорядочена.
# Указатель текущего элемента ставится на следующий за подмассивом элемент.  
+
* К данному подмассиву применяем сортировка вставками. Так как размер подмассива невелик и часть его уже упорядочена {{---}} сортировка работает эффективно.  
# Если конец входного массива не достигнут переход к пункту 2, иначе — конец данного шага.
+
* Указатель текущего элемента ставится на следующий за подмассивом элемент.  
 +
* Если конец входного массива не достигнут {{---}} переход к пункту 2
 +
* Конец.
  
 
===Шаг №3. Слияние===
 
===Шаг №3. Слияние===
Если данные изначального массива достаточно близки к случайным, то размер упорядоченных подмассивов близок к <tex>minrun</tex>. Если в изначальных данных были упорядоченные диапазоны, то упорядоченные подмассивы могут иметь размер, превышающий <tex>minrun</tex>.
+
Нужно объединить полученные подмассивы для получения результирующего упорядоченного массива. Для достижения эффективности, объединение должно ''объединять подмассивы примерно равного размера'' и ''cохранять стабильность алгоритма (не делать бессмысленных перестановок)''.  
 
+
[[Файл:Merge2mas.png|400px|right]]
* Итак, нужно объединить полученные подмассивы для получения результирующего упорядоченного массива. Для достижения эффективности, объединение должно удовлетворять требованиям:
+
* Начало.
# Объединять подмассивы примерно равного размера
+
* Создается пустой стек пар <tex> < </tex>индекс начала подмассива<tex> > </tex> {{---}} <tex> < </tex>размер подмассива<tex> > </tex>.
# Сохранить стабильность алгоритма (не делать бессмысленных перестановок). [[Файл:Merge2mas.png|400px|right]]
 
 
 
Алгоритм шага №3:
 
* Создается пустой стек пар <индекс начала подмассива> <tex>-</tex> <размер подмассива>.
 
 
* Берется первый упорядоченный подмассив.
 
* Берется первый упорядоченный подмассив.
* Добавляется в стек пара данных <индекс начала текущего подмассива> <tex>-</tex> <его размер>.
+
* Добавляется в стек пара данных <tex> < </tex>индекс начала текущего подмассива<tex> > </tex> {{---}} <tex> < </tex>его размер<tex> > </tex>.
* Затем правило следующее. Пусть <tex>X,Y,Z - </tex> длины верхних трех интервалов, которые лежат в стеке. Причем <tex>X</tex> это последний элемент стека.  
+
* Пусть <tex>X,Y,Z </tex> {{---}} длины верхних трех интервалов, которые лежат в стеке. Причем <tex>X</tex> {{---}} это последний элемент стека.  
# Повторяем пока выражение (<tex>Z > X + Y</tex> && <tex>Y > X</tex>) не станет истинным  
+
* Повторяем пока выражение (<tex>Z > X + Y</tex> && <tex>Y > X</tex>) не станет истинным  
# Если количество элементов в стеке не меньше 3 и <tex>Z \leqslant X + Y</tex> сливаем <tex>Y</tex> c <tex>min(X,Z)</tex>. Иначе Если <tex>Y \leqslant X </tex> сливаем <tex>X</tex> c <tex>Y</tex>.  
+
** Если размер стека не меньше 3 и <tex>Z \leqslant X + Y</tex> {{---}} сливаем <tex>Y</tex> c <tex>min(X,Z)</tex>.  
# Возвращаемся в п.1.
+
** Иначе Если <tex>Y \leqslant X </tex> {{---}} сливаем <tex>X</tex> c <tex>Y</tex>.  
 +
** Возвращаемся в п.6.
 +
* Конец
  
Основная цель этой процедуры сохранение баланса. Изменения будут выглядеть как на картинке, а значит и размеры подмассивов в стеке эффективны для дальнейшей сортировки слиянием.
+
Основная цель этой процедуры {{---}} сохранение баланса. Изменения будут выглядеть как на картинке, а значит и размеры подмассивов в стеке эффективны для дальнейшей сортировки слиянием.
  
 
===Описание процедуры слияния===
 
===Описание процедуры слияния===
Строка 107: Строка 103:
 
* На каждой операции копирования элемента из временного или большего подмассива в результирующий запоминается, из какого именно подмассива был элемент.
 
* На каждой операции копирования элемента из временного или большего подмассива в результирующий запоминается, из какого именно подмассива был элемент.
  
* Если уже некоторое количество элементов (в данной реализации алгоритма это число равно 7) было взято из одного и того же массива предполагается, что и дальше придётся брать данные из него. Чтобы подтвердить эту идею, алгоритм переходит в режим '''«галопа»''', то есть перемещается по массиву-претенденту на поставку следующей большой порции данных бинарным поиском (массив упорядочен) текущего элемента из второго соединяемого массива.
+
* Если уже некоторое количество элементов (в данной реализации алгоритма это число равно 7) было взято из одного и того же массива {{---}} предполагается, что и дальше придётся брать данные из него. Чтобы подтвердить эту идею, алгоритм переходит в режим '''«галопа»''', то есть перемещается по массиву-претенденту на поставку следующей большой порции данных бинарным поиском (массив упорядочен) текущего элемента из второго соединяемого массива.
  
 
* В момент, когда данные из текущего массива-поставщика больше не подходят (или был достигнут конец массива), данные копируются целиком.
 
* В момент, когда данные из текущего массива-поставщика больше не подходят (или был достигнут конец массива), данные копируются целиком.
Строка 115: Строка 111:
  
 
== Доказательство времени работы алгоритма ==
 
== Доказательство времени работы алгоритма ==
Главный факт, который нам понадобится для доказательства нужной оценки времени работы в <tex>О(nlog(n))</tex> - это то, что сливаемые массивы '''всегда''' имеют примерно одинаковую длинну. То есть подмассивы <tex>run_1</tex>, <tex>run_2</tex> такие, что <tex>run_1.size \gg run_2.size</tex> будут слиты только тогда, когда размеры соответствующих подмассивов, в которые они войдут будут примерно равны. Например, если на вход подан массив <tex>64, 32, 16, 8, 4, 2, 1</tex>, то, временно забыв про понятие <tex>minrun</tex>, '''Timsort''' сделает следующие слияния:
+
Главный факт, который нам понадобится для доказательства нужной оценки времени работы в <tex>О(nlog(n))</tex> {{---}} это то, что сливаемые массивы '''всегда''' имеют примерно одинаковую длинну. То есть подмассивы <tex>run_1</tex>, <tex>run_2</tex> такие, что <tex>run_1.size \gg run_2.size</tex> будут слиты только тогда, когда размеры соответствующих подмассивов, в которые они войдут будут примерно равны. Например, если на вход подан массив <tex>64, 32, 16, 8, 4, 2, 1</tex>, то, временно забыв про понятие <tex>minrun</tex>, '''Timsort''' сделает следующие слияния:
 
# 64, 32, 16, 8, 4, 2, 1, 1
 
# 64, 32, 16, 8, 4, 2, 1, 1
 
# 64, 32, 16, 8, 4, 2, 2
 
# 64, 32, 16, 8, 4, 2, 2

Версия 16:50, 9 июня 2013

Timsort

Timsort — гибридный алгоритм сортировки, сочетающий сортировку вставками и сортировку слиянием.

Данный алгоритм был изобретен в 2002 году Тимом Петерсом(в честь него и назван) и основывается на том, что в реальном мире сортируемые массивы данных часто содержат в себе упорядоченные подмассивы. На таким данных алгоритм Тима Петерса существенно быстрее многих других алгоритмов сортировки. В настоящее время Timsort является стандартной сортировкой в Python, OpenJDK 7 и реализован в Android JDK 1.5.

Основная идея алгоритма

Алгоритм Timsort состоит из нескольких шагов:

  • Шаг №1: по специальному алгоритму входной массив разделяется на подмассивы.
  • Шаг №3: отсортированные подмассивы собираются в единый массив с помощью модифицированной сортировки слиянием.

Алгоритм

Используемые понятия и комментарии

  • [math]n[/math] — размер входного массива.
  • [math]run[/math] — некоторый подмассив во входном массиве, который обязан быть упорядоченным одним из двух способов:
    • строго по убыванию [math] a_{i} \gt a_{i + 1} \gt ... [/math].
    • нестрого по возрастанию [math] a_{i} \le a_{i + 1} \le ... [/math].
  • [math]minrun[/math] — минимальный размер подмассива, описанного в предыдущем пункте.

Шаг №1. Вычисление minrun

Число [math]minrun[/math] определяется на основе [math] n [/math], исходя из принципов:

  • Не должно быть слишком большим, поскольку к подмассиву размера [math] minrun [/math] будет в дальнейшем применена сортировка вставками (эффективна только на небольших массивах).
  • Оно не должно быть слишком маленьким, так как чем меньше подмассив — тем больше итераций слияния подмассивов придётся выполнить на последнем шаге алгоритма. Оптимальная величина для [math] n / minrun [/math]степень двойки. Это требование обусловлено тем, что алгоритм слияния подмассивов наиболее эффективно работает на подмассивах примерно равного размера.
  • Согласно авторским экспериментам:
    • При [math] minrun \gt 256 [/math] нарушается пункт [math]1[/math].
    • При [math] minrun \lt 8 [/math] нарушается пункт [math]2[/math].
    • Наиболее эффективные значения [math] minrun [/math] из диапозона [math] (32; 65) [/math].
    • Исключение — если [math] n \lt 64 [/math], тогда [math] n = minrun [/math] и Timsort превращается в сортировку вставками.

Таким образом, алгоритм расчета [math] minrun [/math] не так уж сложен: берем старшие 6 бит числа [math] n [/math] и добавляем единицу, если в оставшихся младших битах есть хотя бы один ненулевой.

 int GetMinrun(int n) {
     int flag = 0;           /* станет 1 если среди сдвинутых битов есть хотя бы 1 ненулевой */
     while (n >= 64) {
         flag |= n & 1;
         n >>= 1;
     }
     return n + flag;
 }

Шаг №2. Алгоритм разбиения на подмассивы и их сортировка

На данном этапе у нас есть входной массив, его размер [math]n[/math] и вычисленное число [math]minrun[/math]. Обратим внимание, что если данные изначального массива достаточно близки к случайным, то размер упорядоченных подмассивов близок к [math]minrun[/math],. Но если в изначальных данных были упорядоченные диапазоны, то упорядоченные подмассивы могут иметь размер, превышающий [math]minrun[/math].
MinrunExample.png
  • Начало.
  • Указатель текущего элемента ставится в начало входного массива.
  • Начиная с текущего элемента, идет поиск во входном массиве упорядоченного подмассива [math]run[/math]. По определению, в [math]run[/math] однозначно войдет текущий элемент и следующий за ним. Если получившийся подмассив упорядочен по убыванию — элементы переставляются так, чтобы они шли по возрастанию.
  • Если размер текущего [math]run[/math] меньше [math]minrun[/math], тогда выбираются следующие за найденным подмассивом [math]run[/math] элементы в количестве [math] minrun - size(run) [/math]. Таким образом, на выходе будет получен подмассив размером большим или равный [math]minrun[/math], часть которого (в лучшем случае — он весь) упорядочена.
  • К данному подмассиву применяем сортировка вставками. Так как размер подмассива невелик и часть его уже упорядочена — сортировка работает эффективно.
  • Указатель текущего элемента ставится на следующий за подмассивом элемент.
  • Если конец входного массива не достигнут — переход к пункту 2
  • Конец.

Шаг №3. Слияние

Нужно объединить полученные подмассивы для получения результирующего упорядоченного массива. Для достижения эффективности, объединение должно объединять подмассивы примерно равного размера и cохранять стабильность алгоритма (не делать бессмысленных перестановок).

Merge2mas.png
  • Начало.
  • Создается пустой стек пар [math] \lt [/math]индекс начала подмассива[math] \gt [/math][math] \lt [/math]размер подмассива[math] \gt [/math].
  • Берется первый упорядоченный подмассив.
  • Добавляется в стек пара данных [math] \lt [/math]индекс начала текущего подмассива[math] \gt [/math][math] \lt [/math]его размер[math] \gt [/math].
  • Пусть [math]X,Y,Z [/math] — длины верхних трех интервалов, которые лежат в стеке. Причем [math]X[/math] — это последний элемент стека.
  • Повторяем пока выражение ([math]Z \gt X + Y[/math] && [math]Y \gt X[/math]) не станет истинным
    • Если размер стека не меньше 3 и [math]Z \leqslant X + Y[/math] — сливаем [math]Y[/math] c [math]min(X,Z)[/math].
    • Иначе Если [math]Y \leqslant X [/math] — сливаем [math]X[/math] c [math]Y[/math].
    • Возвращаемся в п.6.
  • Конец

Основная цель этой процедуры — сохранение баланса. Изменения будут выглядеть как на картинке, а значит и размеры подмассивов в стеке эффективны для дальнейшей сортировки слиянием.

Описание процедуры слияния

  • Создается временный массив в размере меньшего из сливаемых подмассивов.
  • Меньший из подмассивов копируется во временный массив, но надо учесть, что если меньший подмассив [math]-[/math] правый, то ответ (результат сливания) формируется справа налево. Дабы избежать данной путаницы, лучше копировать всегда левый подмассив во временный. На скорости это практически не отразится.
  • Ставятся указатели текущей позиции на первые элементы большего и временного массива.
  • На каждом шаге рассматривается значение текущих элементов в большем и временном массивах, берется меньший из них, копируется в новый

отсортированный массив. Указатель текущего элемента перемещается в массиве, из которого был взят элемент.

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

Модификация процедуры слияния подмассивов (Galloping Mode)

Рассмотрим процедуру слияния двух массивов:

[math]A = {1, 2, 3, ..., 9999, 10000}[/math]

[math]B = {20000, 20001, 20002, ..., 29999, 30000}[/math]

Вышеуказанная процедура для них сработает, но каждый раз на её четвёртом пункте нужно будет выполнить одно сравнение и одно копирование. В итоге 10000 сравнений и 10000 копирований. Алгоритм Timsort предлагает в этом месте модификацию, которая получила называет «галоп». Алгоритм следующий:

  • Начинается процедура слияния.
  • На каждой операции копирования элемента из временного или большего подмассива в результирующий запоминается, из какого именно подмассива был элемент.
  • Если уже некоторое количество элементов (в данной реализации алгоритма это число равно 7) было взято из одного и того же массива — предполагается, что и дальше придётся брать данные из него. Чтобы подтвердить эту идею, алгоритм переходит в режим «галопа», то есть перемещается по массиву-претенденту на поставку следующей большой порции данных бинарным поиском (массив упорядочен) текущего элемента из второго соединяемого массива.
  • В момент, когда данные из текущего массива-поставщика больше не подходят (или был достигнут конец массива), данные копируются целиком.

Для вышеописанных массивов [math] A, B [/math] алгоритм выглядит следующим образом: Первые 7 итераций сравниваются числа [math]1, 2, 3, 4, 5, 6, 7[/math] из массива [math]A[/math] с числом [math]20000[/math], так как [math]20000[/math] больше, то элементы массива [math]A[/math] копируются в результирующий. Начиная со следующей итерации алгоритм переходит в режим «галопа»: сравнивает с числом [math]20000[/math] последовательно элементы [math]8, 10, 14, 22, 38, 7+2^{i - 1}, ..., 10000 [/math] массива [math]A[/math]. ([math] \thicksim\log_{2}(n)[/math] сравнений). После того как конец массива [math]A[/math] достигнут и известно, что он весь меньше [math]B[/math], нужные данные из массива [math]A[/math] копируются в результирующий.

Доказательство времени работы алгоритма

Главный факт, который нам понадобится для доказательства нужной оценки времени работы в [math]О(nlog(n))[/math] — это то, что сливаемые массивы всегда имеют примерно одинаковую длинну. То есть подмассивы [math]run_1[/math], [math]run_2[/math] такие, что [math]run_1.size \gg run_2.size[/math] будут слиты только тогда, когда размеры соответствующих подмассивов, в которые они войдут будут примерно равны. Например, если на вход подан массив [math]64, 32, 16, 8, 4, 2, 1[/math], то, временно забыв про понятие [math]minrun[/math], Timsort сделает следующие слияния:

  1. 64, 32, 16, 8, 4, 2, 1, 1
  2. 64, 32, 16, 8, 4, 2, 2
  3. 64, 32, 16, 8, 4, 4
  4. 64, 32, 16, 8, 8
  5. 64, 32, 16, 16
  6. 64, 32, 32
  7. 64, 64
  8. 128

При сливании двух подмассивов [math]run_1[/math],[math]run_2[/math] будет произведенно [math]O(run_1.size + run_2.size)[/math] операций сравнения. Результирующий подмассив [math]run_{12}[/math] будет удовлетворять выражению [math]run_{12}.size \approx 2run_1.size \approx 2run_2.size [/math]. Таким образом, каждый подмассив [math]run_i[/math] может поучаствовать в не более [math]O(log(n))[/math] операций слияния, а значит и каждый элемент будет задействован в сравниниях не более [math]O(log(n))[/math] раз. Элементов [math]n[/math], откуда и получаем соответствующую оценку в [math]O(nlog(n))[/math]

Источники

  • Peter McIlroy "Optimistic Sorting and Information Theoretic Complexity", Proceedings of the Fourth Annual ACM-SIAM Symposium on Discrete Algorithms, ISBN 0-89871-313-7, Chapter 53, pp 467-474, January 1993.
  • Magnus Lie Hetland Python Algorithms: Mastering Basic Algorithms in the Python Language. — Apress, 2010. — 336 с.