Сортировка Хана — различия между версиями
Da1s60 (обсуждение | вклад) |
Da1s60 (обсуждение | вклад) |
||
| Строка 1: | Строка 1: | ||
| − | '''Сортировка Хана (Yijie Han)''' {{---}} сложный алгоритм сортировки целых чисел со сложностью <tex>O( | + | '''Сортировка Хана (Yijie Han)''' {{---}} сложный алгоритм сортировки целых чисел со сложностью <tex>O(nloglog n)</tex>, где <tex>n</tex> {{---}} количество элементов для сортировки. |
Данная статья писалась на основе брошюры Хана, посвященной этой сортировке. Изложение материала в данной статье идет примерно в том же порядке, в каком она предоставлена в работе Хана. | Данная статья писалась на основе брошюры Хана, посвященной этой сортировке. Изложение материала в данной статье идет примерно в том же порядке, в каком она предоставлена в работе Хана. | ||
| Строка 18: | Строка 18: | ||
|id=def2. | |id=def2. | ||
|definition= | |definition= | ||
| − | Алгоритм сортирующий <tex>n</tex> целых чисел из множества <tex>\{0, 1, \ldots, m - 1\}</tex> называется консервативным, если длина контейнера (число бит в контейнере), является <tex>O(\log(m + n))</tex>. Если длина больше, то алгоритм | + | Алгоритм сортирующий <tex>n</tex> целых чисел из множества <tex>\{0, 1, \ldots, m - 1\}</tex> называется консервативным, если длина контейнера (число бит в контейнере), является <tex>O(\log(m + n))</tex>. Если длина больше, то алгоритм неконсервативный. |
}} | }} | ||
{{Определение | {{Определение | ||
|id=def3. | |id=def3. | ||
|definition= | |definition= | ||
| − | Если мы сортируем целые числа из множества {0, 1, ..., <tex>m</tex> - 1} с длиной контейнера <tex>klog(m + n)</tex> с <tex>k</tex> >= 1, тогда мы сортируем с | + | Если мы сортируем целые числа из множества {0, 1, ..., <tex>m</tex> - 1} с длиной контейнера <tex>klog(m + n)</tex> с <tex>k</tex> >= 1, тогда мы сортируем с неконсервативным преимуществом <tex>k</tex>. |
}} | }} | ||
{{Определение | {{Определение | ||
| Строка 70: | Строка 70: | ||
|id=lemma2. | |id=lemma2. | ||
|statement= | |statement= | ||
| − | <tex>n</tex> целых чисел можно отсортировать в <tex>\sqrt{n}</tex> наборов <tex>S_{1}</tex>, <tex>S_{2}</tex>, ..., <tex>S_{\sqrt{n}}</tex> таким образом, что в каждом наборе <tex>\sqrt{n}</tex> чисел и <tex>S_{i}</tex> < <tex>S_{j}</tex> при <tex>i</tex> < <tex>j</tex>, за время <tex>O( | + | <tex>n</tex> целых чисел можно отсортировать в <tex>\sqrt{n}</tex> наборов <tex>S_{1}</tex>, <tex>S_{2}</tex>, ..., <tex>S_{\sqrt{n}}</tex> таким образом, что в каждом наборе <tex>\sqrt{n}</tex> чисел и <tex>S_{i}</tex> < <tex>S_{j}</tex> при <tex>i</tex> < <tex>j</tex>, за время <tex>O(nloglogn/logk)</tex> и место <tex>O(n)</tex> с не консервативным преимуществом <tex>kloglogn</tex> |
|proof= | |proof= | ||
| + | Доказательство данной леммы будет приведено далее в тексте статьи. | ||
}} | }} | ||
{{Лемма | {{Лемма | ||
| Строка 90: | Строка 91: | ||
}} | }} | ||
| − | Заметим, что когда <tex>g = O(logn)</tex> мы сортируем <tex>O(n)</tex> чисел в <tex>n/g</tex> контейнеров за время <tex>O((n/g) | + | Заметим, что когда <tex>g = O(logn)</tex> мы сортируем <tex>O(n)</tex> чисел в <tex>n/g</tex> контейнеров за время <tex>O((n/g)loglogn)</tex>, с использованием O(n/g) места. Выгода очевидна. |
{{Лемма | {{Лемма | ||
|id=lemma5. | |id=lemma5. | ||
|statement= | |statement= | ||
| − | Если принять, что каждый контейнер содержит <tex>logm > logn</tex> бит, и <tex>g</tex> чисел, в каждом из которых <tex>(logm)/g</tex> бит, упакованы в один контейнер. Если каждое число имеет маркер, содержащий <tex>(logn)/(2g)</tex> бит, и <tex>g</tex> маркеров упакованы в один контейнер таким же образом<tex>^*</tex>, что и числа, тогда <tex>n</tex> чисел в <tex>n/g</tex> контейнерах могут быть отсортированы по их маркерам за время <tex>O(( | + | Если принять, что каждый контейнер содержит <tex>logm > logn</tex> бит, и <tex>g</tex> чисел, в каждом из которых <tex>(logm)/g</tex> бит, упакованы в один контейнер. Если каждое число имеет маркер, содержащий <tex>(logn)/(2g)</tex> бит, и <tex>g</tex> маркеров упакованы в один контейнер таким же образом<tex>^*</tex>, что и числа, тогда <tex>n</tex> чисел в <tex>n/g</tex> контейнерах могут быть отсортированы по их маркерам за время <tex>O((nloglogn)/g)</tex> с использованием <tex>O(n/g)</tex> места. |
(*): если число <tex>a</tex> упаковано как <tex>s</tex>-ое число в <tex>t</tex>-ом контейнере для чисел, тогда маркер для <tex>a</tex> упакован как <tex>s</tex>-ый маркер в <tex>t</tex>-ом контейнере для маркеров. | (*): если число <tex>a</tex> упаковано как <tex>s</tex>-ое число в <tex>t</tex>-ом контейнере для чисел, тогда маркер для <tex>a</tex> упакован как <tex>s</tex>-ый маркер в <tex>t</tex>-ом контейнере для маркеров. | ||
|proof= | |proof= | ||
| Строка 112: | Строка 113: | ||
Заметим, что если длина контейнера <tex>logmloglogn</tex> и только <tex>logm</tex> бит используется для упаковки <tex>g <= logn</tex> чисел в один контейнер, тогда выбор в лемме три может быть сделан за время и место <tex>O(n/g)</tex>, потому, что упаковка в доказатльстве леммы три теперь может быть сделана за время <tex>O(n/g)</tex>. | Заметим, что если длина контейнера <tex>logmloglogn</tex> и только <tex>logm</tex> бит используется для упаковки <tex>g <= logn</tex> чисел в один контейнер, тогда выбор в лемме три может быть сделан за время и место <tex>O(n/g)</tex>, потому, что упаковка в доказатльстве леммы три теперь может быть сделана за время <tex>O(n/g)</tex>. | ||
| + | |||
| + | ==Сортировка <tex>n</tex> целых чисел в <tex>\sqrt{n}</tex> наборов== | ||
| + | Постановка задачи и решение некоторых проблем: | ||
| + | |||
| + | Рассмотрим проблему сортировки <tex>n</tex> целых чисел из множества {0, 1, ..., <tex>m</tex> - 1} в <tex>\sqrt{n}</tex> наборов как во второй лемме. Мы предполагаем, что в каждом контейнере <tex>kloglognlogm</tex> бит и хранит число в <tex>logm</tex> бит. Поэтому неконсервативное преимущество <tex>kloglogn</tex>. Мы так же предполагаем, что <tex>logm >= lognloglogn</tex>. Иначе мы можем использовать radix sort для сортировки за время <tex>O(nloglogn)</tex> и линейную память. Мы делим <tex>logm</tex> бит, используемых для представления каждого числа, в <tex>logn</tex> блоков. Таким образом каждый блок содержит как минимум <tex>loglogn</tex> бит. <tex>i</tex>-ый блок содержит с <tex>ilogm/logn</tex>-ого по <tex>((i + 1)logm/logn - 1)</tex>-ый биты. Биты считаются с наименьшего бита начиная с нуля. Теперь у нас имеется <tex>2logn</tex>-уровневый алгоритм, который работает следующим образом: | ||
Версия 01:36, 12 июня 2012
Сортировка Хана (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 для сортировки за время и линейную память. Мы делим бит, используемых для представления каждого числа, в блоков. Таким образом каждый блок содержит как минимум бит. -ый блок содержит с -ого по -ый биты. Биты считаются с наименьшего бита начиная с нуля. Теперь у нас имеется -уровневый алгоритм, который работает следующим образом: