Сортировка Хана
Сортировка Хана (Yijie Han) — сложный алгоритм сортировки целых чисел со сложностью , где — количество элементов для сортировки.
Содержание
Алгоритм
Алгоритм построен на основе экспоненциального поискового дерева (далее — Э.П.дерево) Андерсона (Andersson's exponential search tree). Сортировка происходит за счет вставки целых чисел в Э.П.дерево.
Andersson's exponential search tree
Э.П.дерево с листьями состоит из корня и (0<<1) Э.П.поддеревьев, в каждом из которых листьев; каждое Э.П.поддерево является сыном корня . В этом дереве уровней. При нарушении баланса дерева, необходимо балансирование, которое требует времени при вставленных целых числах. Такое время достигается за счет вставки чисел группами, а не по одиночке, как изначально предлагает Андерссон.
Необходимая информация
| Определение: |
| Контейнер — объект определенного типа, содержащий обрабатываемый элемент. Например __int32, __int64, и т.д. |
| Определение: |
| Алгоритм сортирующий целых чисел из множества {0, 1, ..., - 1} называется консервативным, если длина контейнера (число бит в контейнере), является Если длина больше, то алгоритм не консервативный. |
| Определение: |
| Для множества определим
min() = min(: принадлежит ) max() = max(: принадлежит ) Набор < если max() <= min() |
Уменьшение числа бит в числах
Один из способов ускорить сортировку — уменьшить число бит в числе. Один из способов уменьшить число бит в числе — использовать деление пополам (эту идею впервые подал van Emde Boas). Деление пополам заключается в том, что количество оставшихся бит в числе уменьшается в 2 раза. Это быстрый способ, требующий памяти. Для своего дерева Андерссон использует хеширование, что позволяет сократить количество памяти до . Для того, чтобы еще ускорить алгоритм нам необходимо упаковать несколько чисел в один контейнер, чтобы затем за константное количество шагов произвести хэширование для всех чисел хранимых в контейнере. Для этого используется хэш функция для хэширования чисел в таблицу размера за константное время, без коллизий. Для этого используется хэш модифицированная функция авторства: Dierzfelbinger и Raman.
Алгоритм: Пусть целое число >= 0 и пусть = {0, ..., - 1}. Класс хэш функций из в {0, ..., - 1} определен как = {| 0 < < , и нечетно} и для всех из : mod div
Данный алгоритм базируется на следующей лемме:
| Лемма: |
Даны целые числа >= >= 0 и является подмножеством {0, ..., - 1}, содержащим элементов, и >= С. Функция принадлежащая может быть выбрана за время так, что количество коллизий |
Взяв мы получим хэш функцию которая захэширует чисел из в таблицу размера без коллизий. Очевидно, что может быть посчитана для любого за константное время. Если мы упакуем несколько чисел в один контейнер так, что они разделены несколькими битами нулей, мы спокойно сможем применить ко всему контейнеру, а в результате все хэш значения для всех чисел в контейере были посчитаны. Заметим, что это возможно только потому, что в вычисление хэш знчения вовлечены только (mod ) и (div ).
Такая хэш функция может быть найдена за .
Следует отметить, что несмотря на размер таблицы , потребность в памяти не превышает потому, что хэширование используется только для уменьшения количества бит в числе.
Signature sorting
В данной сортировке используется следующий алгоритм:
Предположим, что чисел должны быть сортированы, и в каждом бит. Мы рассматриваем, что в каждом числе есть сегментов, в каждом из которых бит. Теперь мы применяем хэширование ко всем сегментам и получаем бит хэшированных значений для каждого числа. После сортировки на хэшированных значениях для всех начальных чисел начальная задача по сортировке чисел по бит в каждом стала задачей по сортировке чисел по бит в каждом.
Так же, рассмотрим проблему последующего разделения. Пусть , , ..., — чисел и — множество чисeл. Мы хотим разделить в наборов таких, что: < {} < < {} < ... < {} < . Т.к. мы используем signature sorting, до того как делать вышеописанное разделение, мы поделим биты в на сегментов и возьмем некоторые из них. Мы так же поделим биты для каждого числа из и оставим только один в каждом числе. По существу для каждого мы возьмем все сегментов. Если соответствующие сегменты и совпадают, то нам понадобится только один. Сегменты, которые мы берем для числа в , — сегмент, который "вылетает" из . Таким образом мы преобразуем начальную задачу о разделении в несколько задач на разделение с числами в бит.