Сортировка Хана
Сортировка Хана (Yijie Han) — сложный алгоритм сортировки целых чисел со сложностью
, где — количество элементов для сортировки.Данная статья писалась на основе брошюры Хана, посвященной этой сортировке. Изложение материала в данной статье идет примерно в том же порядке, в каком она предоставлена в работе Хана.
Алгоритм
Алгоритм построен на основе экспоненциального поискового дерева (далее — Э.П.дерево) Андерсона (Andersson's exponential search tree). Сортировка происходит за счет вставки целых чисел в Э.П.дерево.
Andersson's exponential search tree
Э.П.дерево с
листьями состоит из корня и (0< <1) Э.П.поддеревьев, в каждом из которых листьев; каждое Э.П.поддерево является сыном корня . В этом дереве уровней. При нарушении баланса дерева, необходимо балансирование, которое требует времени при вставленных целых числах. Такое время достигается за счет вставки чисел группами, а не по одиночке, как изначально предлагает Андерссон.Необходимая информация
Определение: |
Контейнер — объект определенного типа, содержащий обрабатываемый элемент. Например __int32, __int64, и т.д. |
Определение: |
Алгоритм сортирующий | целых чисел из множества называется консервативным, если длина контейнера (число бит в контейнере), является . Если длина больше, то алгоритм неконсервативный.
Определение: |
Если мы сортируем целые числа из множества {0, 1, ..., | - 1} с длиной контейнера с >= 1, тогда мы сортируем с неконсервативным преимуществом .
Определение: |
Для множества min( Набор ) = min( : принадлежит ) max( ) = max( : принадлежит ) < если max( ) <= min( ) | определим
Уменьшение числа бит в числах
Один из способов ускорить сортировку — уменьшить число бит в числе. Один из способов уменьшить число бит в числе — использовать деление пополам (эту идею впервые подал van Emde Boas). Деление пополам заключается в том, что количество оставшихся бит в числе уменьшается в 2 раза. Это быстрый способ, требующий
памяти. Для своего дерева Андерссон использует хеширование, что позволяет сократить количество памяти до . Для того, чтобы еще ускорить алгоритм нам необходимо упаковать несколько чисел в один контейнер, чтобы затем за константное количество шагов произвести хэширование для всех чисел хранимых в контейнере. Для этого используется хэш функция для хэширования чисел в таблицу размера за константное время, без коллизий. Для этого используется хэш модифицированная функция авторства: Dierzfelbinger и Raman.Алгоритм: Пусть целое число
и пусть . Класс хэш функций из в определен как и для всех из .Данный алгоритм базируется на следующей лемме:
Номер один.
Лемма: |
Даны целые числа >= >= 0 и является подмножеством {0, ..., - 1}, содержащим элементов, и >= С . Функция принадлежащая может быть выбрана за время так, что количество коллизий |
Взяв
мы получим хэш функцию которая захэширует чисел из в таблицу размера без коллизий. Очевидно, что может быть посчитана для любого за константное время. Если мы упакуем несколько чисел в один контейнер так, что они разделены несколькими битами нулей, мы спокойно сможем применить ко всему контейнеру, а в результате все хэш значения для всех чисел в контейере были посчитаны. Заметим, что это возможно только потому, что в вычисление хэш знчения вовлечены только (mod ) и (div ).Такая хэш функция может быть найдена за
.Следует отметить, что несмотря на размер таблицы
, потребность в памяти не превышает потому, что хэширование используется только для уменьшения количества бит в числе.Signature sorting
В данной сортировке используется следующий алгоритм:
Предположим, что
чисел должны быть сортированы, и в каждом бит. Мы рассматриваем, что в каждом числе есть сегментов, в каждом из которых бит. Теперь мы применяем хэширование ко всем сегментам и получаем бит хэшированных значений для каждого числа. После сортировки на хэшированных значениях для всех начальных чисел начальная задача по сортировке чисел по бит в каждом стала задачей по сортировке чисел по бит в каждом.Так же, рассмотрим проблему последующего разделения. Пусть
, , ..., — чисел и — множество чисeл. Мы хотим разделить в наборов таких, что: < { } < < { } < ... < { } < . Т.к. мы используем signature sorting, до того как делать вышеописанное разделение, мы поделим биты в на сегментов и возьмем некоторые из них. Мы так же поделим биты для каждого числа из и оставим только один в каждом числе. По существу для каждого мы возьмем все сегментов. Если соответствующие сегменты и совпадают, то нам понадобится только один. Сегменты, которые мы берем для числа в , — сегмент, который выделяется из . Таким образом мы преобразуем начальную задачу о разделении чисел в бит в несколько задач на разделение с числами в бит.Пример:
= 3, = 5, = 7, = 10, S = {1, 4, 6, 8, 9, 13, 14}.
Мы разделим числа на 2 сегмента. Для
получим верхний сегмент 0, нижний 3; верхний 1, нижний 1; верхний 1, нижний 3; верхний 2, нижний 2. Для элементов из S получим: для 1: нижний 1 т.к. он выделяется из нижнего сегмента ; для 4 нижний 0; для 8 нижний 0; для 9 нижний 1; для 13 верхний 3; для 14 верхний 3. Теперь все верхние сегменты, нижние сегменты 1 и 3, нижние сегменты 4, 5, 6, 7, нижние сегменты 8, 9, 10 формируют 4 новые задачи на разделение.Сортировка на маленьких целых
Для лучшего понимания действия алгоритма и материала, изложенного в данной статье, в целом, ниже представлены несколько полезных лемм.
Номер два.
Лемма: |
целых чисел можно отсортировать в наборов , , ..., таким образом, что в каждом наборе чисел и < при < , за время и место с не консервативным преимуществом |
Доказательство: |
Доказательство данной леммы будет приведено далее в тексте статьи. |
Номер три.
Лемма: |
Выбор -ого наибольшего числа среди чисел упакованных в контейнеров может быть сделана за время и с использованием места. Конкретно медиана может быть так найдена. |
Доказательство: |
Так как мы можем делать попарное сравнение | чисел в одном контейнере с числами в другом и извлекать большие числа из одного контейнера и меньшие из другого за константное время, мы можем упаковать медианы из первого, второго, ..., -ого чисел из 5 контейнеров в один контейнер за константное время. Таким образом набор из медиан теперь содержится в контейнерах. Рекурсивно находим медиану в . Используя уберем хотя бы чисел среди . Затем упакуем оставшиеся из контейнеров в контейнеров и затем продолжим рекурсию.
Номер четыре.
Лемма: |
Если целых чисел, в сумме использующие бит, упакованы в один контейнер, тогда чисел в контейнерах могут быть отсортированы за время , с использованием места. |
Доказательство: |
Так как используется только Для контейнеров вне групп (которых бит в каждом контейнере для хранения чисел, мы можем использовать bucket sorting чтобы отсортировать все контейнеры. представляя каждый как число, что занимает времени и места. Потому, что мы используем бит на контейнер нам понадобится шаблонов для всех контейнеров. Затем поместим контейнеров с одинаковым шаблоном в одну группу. Для каждого шаблона останется не более контейнеров которые не смогут образовать группу. Поэтому не более контейнеров не смогут сформировать группу. Для каждой группы мы помещаем -е число во всех контейнерах в один. Таким образом мы берем -целых векторов и получаем -целых векторов где -ый вектор содержит -ое число из входящего вектора. Эта транспозиция может быть сделана за время , с использованием места. Для всех групп это занимает время , с использованием места. штук) мы просто разберем и соберем заново контейнеры. На это потребуется не более места и времени. После всего этого мы используем bucket sorting вновь для сортировки контейнеров. таким образом мы отсортируем все числа. |
Заметим, что когда
мы сортируем чисел в контейнеров за время , с использованием O(n/g) места. Выгода очевидна.Лемма пять.
Лемма: |
Если принять, что каждый контейнер содержит бит, и чисел, в каждом из которых бит, упакованы в один контейнер. Если каждое число имеет маркер, содержащий бит, и маркеров упакованы в один контейнер таким же образом , что и числа, тогда чисел в контейнерах могут быть отсортированы по их маркерам за время с использованием места.
(*): если число упаковано как -ое число в -ом контейнере для чисел, тогда маркер для упакован как -ый маркер в -ом контейнере для маркеров. |
Доказательство: |
Контейнеры для маркеров могут быть отсортированы с помощью bucket sort потому, что каждый контейнер использует | бит. Сортировка сгруппирует контейнеры для чисел как в четвертой лемме. Мы можем переместить каждую группу контейнеров для чисел.
Заметим, что сортирующие алгоритмы в четвертой и пятой леммах нестабильные. Хотя на их основе можно построить стабильные алгоритмы используя известный метод добавления адресных битов к каждому входящему числу.
Если у нас длина контейнеров больше, сортировка может быть ускорена, как показано в следующей лемме.
Лемма шесть.
Лемма: |
предположим, что каждый контейнер содержит бит, что чисел, в каждом из которых бит, упакованы в один контейнер, что каждое число имеет маркер, содержащий бит, и что маркеров упакованы в один контейнер тем же образом что и числа, тогда чисел в контейнерах могут быть отсортированы по своим маркерам за время , с использованием памяти. |
Доказательство: |
Заметим, что несмотря на то, что длина контейнера | бит всего бит используется для хранения упакованных чисел. Так же как в леммах четыре и пять мы сортируем контейнеры упакованных маркеров с помощью bucket sort. Для того, чтобы перемещать контейнеры чисел мы помещаем вместо контейнеров чисел в одну группу. Для транспозиции чисел в группе содержащей контейнеров мы сначала упаковываем контейнеров в контейнеров упаковывая контейнеров в один. Далее мы делаем транспозицию над контейнерами. Таким образом перемещение занимает всего времени для каждой группы и времени для всех чисел. После завершения транспозиции, мы далее распаковываем контейнеров в контейнеров.
Заметим, что если длина контейнера
и только бит используется для упаковки чисел в один контейнер, тогда выбор в лемме три может быть сделан за время и место , потому, что упаковка в доказатльстве леммы три теперь может быть сделана за время .Сортировка целых чисел в наборов
Постановка задачи и решение некоторых проблем:
Рассмотрим проблему сортировки
целых чисел из множества {0, 1, ..., - 1} в наборов как во второй лемме. Мы предполагаем, что в каждом контейнере бит и хранит число в бит. Поэтому неконсервативное преимущество . Мы так же предполагаем, что . Иначе мы можем использовать radix sort для сортировки за время и линейную память. Мы делим бит, используемых для представления каждого числа, в блоков. Таким образом каждый блок содержит как минимум бит. -ый блок содержит с -ого по -ый биты. Биты считаются с наименьшего бита начиная с нуля. Теперь у нас имеется -уровневый алгоритм, который работает следующим образом:На каждой стадии мы работаем с одним блоком бит. Назовем эти блоки маленькими числами (далее м.ч.) потому, что каждое м.ч. теперь содержит только
бит. Каждое число представлено и соотносится с м.ч., над которым мы работаем в данный момент. Положим, что нулевая стадия работает с самыми большим блоком (блок номер ). Предполагаем, что биты этих м.ч. упакованы в контейнеров с м.ч. упакованных в один контейнер. Мы пренебрегаем временем, потраченным на на эту упаковку, считая что она бесплатна. По третьей лемме мы можем найти медиану этих м.ч. за время и память . Пусть это найденная медиана. Тогда м.ч. могут быть разделены на не более чем три группы: , и . содержит м.ч. которые меньше , содержит м.ч. равные , содержит м.ч. большие . Так же мощность и <= . Мощность может быть любой. Пусть это набор чисел, у которых наибольший блок находится в . Тогда мы можем убрать убрать бит (наибольший блок) из каждого числа из из дальнейшего рассмотрения. Таким образом после первой стадии каждое число находится в наборе размера не большего половины размера начального набора или один из блоков в числе убран из дальнейшего рассмотрения. Так как в каждом числе только блоков, для каждого числа потребуется не более стадий чтобы поместить его в набор половинного размера. За стадий все числа будут отсортированы. Так как на каждой стадии мы работаем с контейнерами, то игнорируя время, необходимое на упаковку м.ч. в контейнеры и помещение м.ч. в нужный набор, мы затратим времени из-за стадий.