Сортировка Хана — различия между версиями
м (rollbackEdits.php mass rollback) |
|||
(не показаны 4 промежуточные версии 2 участников) | |||
Строка 141: | Строка 141: | ||
===Алгоритм сортировки=== | ===Алгоритм сортировки=== | ||
− | Algorithm | + | Algorithm <tex>Sort(advantage</tex>, <tex>level</tex>, <tex>a_{0}</tex>, <tex>a_{1}</tex>, <tex>\ldots</tex>, <tex>a_{t}</tex>) |
− | <tex | + | <tex>advantage</tex> {{---}} это неконсервативное преимущество равное <tex>k\log\log n</tex>, <tex>a_{i}</tex>-ые это входящие целые числа в наборе, которые надо отсортировать, <tex>level</tex> это уровень рекурсии. |
− | # Если <tex | + | # Если <tex>level</tex> равен <tex>1</tex> тогда изучаем размер набора. Если размер меньше или равен <tex>\sqrt{n}</tex>, то <tex>return</tex>. Иначе делим этот набор в <tex>\leqslant</tex> 3 набора, используя [[#lemma2|лемму №2]], чтобы найти медиану, а затем используем [[#lemma5|лемму №5]] для сортировки. Для набора, где все элементы равны медиане, не рассматриваем текущий блок и текущим блоком делаем следующий. Создаем маркер, являющийся номером набора для каждого из чисел (0, 1 или 2). Затем направляем маркер для каждого числа назад к месту, где число находилось в начале. Также направляем двубитное число для каждого входного числа, указывающее на текущий блок. |
# От <tex dpi="130">u = 1</tex> до <tex dpi="130">k</tex> | # От <tex dpi="130">u = 1</tex> до <tex dpi="130">k</tex> | ||
## Упаковываем <tex dpi="130">a^{(u)}_{i}</tex>-ый в часть из <tex dpi="130">1/k</tex>-ых номеров контейнеров. Где <tex dpi="130">a^{(u)}_{i}</tex> содержит несколько непрерывных блоков, которые состоят из <tex dpi="150">\frac{1}{k}</tex>-ых битов <tex dpi="130">a_{i}</tex>. При этом у <tex dpi="130">a^{(u)}_{i}</tex> текущий блок это самый крупный блок. | ## Упаковываем <tex dpi="130">a^{(u)}_{i}</tex>-ый в часть из <tex dpi="130">1/k</tex>-ых номеров контейнеров. Где <tex dpi="130">a^{(u)}_{i}</tex> содержит несколько непрерывных блоков, которые состоят из <tex dpi="150">\frac{1}{k}</tex>-ых битов <tex dpi="130">a_{i}</tex>. При этом у <tex dpi="130">a^{(u)}_{i}</tex> текущий блок это самый крупный блок. | ||
− | ## Вызываем | + | ## Вызываем <tex>Sort(advantage</tex>, <tex>level - 1</tex>, <tex dpi="130">a^{(u)}_{0}</tex>, <tex dpi="130">a^{(u)}_{1}</tex>, <tex>\ldots</tex>, <tex dpi="130">a^{(u)}_{t}</tex>). Когда алгоритм возвращается из этой рекурсии, маркер, показывающий для каждого числа, к какому набору это число относится, уже направлен назад к месту, где число находится во входных данных. Число, имеющее наибольшее число бит в <tex dpi="130">a_{i}</tex>, показывающее на текущий блок в нем, так же направлено назад к <tex dpi="130">a_{i}</tex>. |
## Отправляем <tex dpi="130">a_{i}</tex>-ые к их наборам, используя [[#lemma5|лемму №5]]. | ## Отправляем <tex dpi="130">a_{i}</tex>-ые к их наборам, используя [[#lemma5|лемму №5]]. | ||
Algorithm IterateSort | Algorithm IterateSort | ||
− | Call | + | Call <tex>Sort(advantage</tex>, <tex dpi="130">\log_{k}((\log n)/4)</tex>, <tex dpi="130">a_{0}</tex>, <tex dpi="130">a_{1}</tex>, <tex dpi="130">\ldots</tex>, <tex dpi="130">a_{n - 1}</tex>); |
от 1 до 5 | от 1 до 5 | ||
− | # Помещаем <tex dpi="130">a_{i}</tex> в соответствующий набор с помощью bucket sort, потому что наборов около <tex dpi="130">\sqrt{n}</tex>. | + | # Помещаем <tex dpi="130">a_{i}</tex> в соответствующий набор с помощью блочной сортировки (англ. ''bucket sort''), потому что наборов около <tex dpi="130">\sqrt{n}</tex>. |
− | # Для каждого набора <tex dpi="130">S = </tex>{<tex dpi="130">a_{i_{0}}, a_{i_{1}}, \ldots, a_{i_{t}}</tex>}, если <tex dpi="130">t > \sqrt{n}</tex>, вызываем | + | # Для каждого набора <tex dpi="130">S = </tex>{<tex dpi="130">a_{i_{0}}, a_{i_{1}}, \ldots, a_{i_{t}}</tex>}, если <tex dpi="130">t > \sqrt{n}</tex>, вызываем <tex>Sort(advantage</tex>, <tex dpi="130">\log_{k}(\frac{\log n}{4})</tex>, <tex dpi="130">a_{i_{0}}, a_{i_{1}}, \ldots, a_{i_{t}}</tex>). |
Время работы алгоритма <tex dpi="150">O(\frac{n \log\log n}{\log k})</tex>, что доказывает лемму. | Время работы алгоритма <tex dpi="150">O(\frac{n \log\log n}{\log k})</tex>, что доказывает лемму. | ||
Строка 179: | Строка 179: | ||
Следует отметить, что, несмотря на размер таблицы <tex dpi="130">O(n^2)</tex>, потребность в памяти не превышает <tex dpi="130">O(n)</tex>, потому что хеширование используется только для уменьшения количества бит в числе. | Следует отметить, что, несмотря на размер таблицы <tex dpi="130">O(n^2)</tex>, потребность в памяти не превышает <tex dpi="130">O(n)</tex>, потому что хеширование используется только для уменьшения количества бит в числе. | ||
− | == | + | ==Сортировка по ключу== |
− | Предположим, что <tex dpi="130">n</tex> чисел должны быть отсортированы, и в каждом <tex dpi="130">\log m</tex> бит. Будем считать, что в каждом числе есть <tex dpi="130">h</tex> сегментов, в каждом из которых <tex dpi="130">\log m | + | Предположим, что <tex dpi="130">n</tex> чисел должны быть отсортированы, и в каждом <tex dpi="130">\log m</tex> бит. Будем считать, что в каждом числе есть <tex dpi="130">h</tex> сегментов, в каждом из которых <tex dpi="130">\log</tex> <tex dpi="150">\frac{m}{h}</tex> бит. Теперь применяем хеширование ко всем сегментам и получаем <tex dpi="130">2h \log n</tex> бит хешированных значений для каждого числа. После сортировки на хешированных значениях для всех начальных чисел начальная задача по сортировке <tex dpi="130">n</tex> чисел по <tex dpi="130">\log m</tex> бит в каждом стала задачей по сортировке <tex dpi="130">n</tex> чисел по <tex dpi="130">\log</tex> <tex dpi="150">\frac{m}{h}</tex> бит в каждом. |
− | Также рассмотрим проблему последующего разделения. Пусть <tex dpi="130">a_{1}</tex>, <tex dpi="130">a_{2}</tex>, <tex dpi="130">\ldots</tex>, <tex dpi="130">a_{p}</tex> {{---}} <tex dpi="130">p</tex> чисел и <tex dpi="130">S</tex> {{---}} множество чисeл. Необходимо разделить <tex dpi="130">S</tex> в <tex dpi="130">p + 1</tex> наборов, таких, что: <tex dpi="130">S_{0} < a_{1} < S_{1} < a_{2} < \ldots < a_{p} < S_{p}</tex>. Так как используется '''signature sorting'' | + | Также рассмотрим проблему последующего разделения. Пусть <tex dpi="130">a_{1}</tex>, <tex dpi="130">a_{2}</tex>, <tex dpi="130">\ldots</tex>, <tex dpi="130">a_{p}</tex> {{---}} <tex dpi="130">p</tex> чисел и <tex dpi="130">S</tex> {{---}} множество чисeл. Необходимо разделить <tex dpi="130">S</tex> в <tex dpi="130">p + 1</tex> наборов, таких, что: <tex dpi="130">S_{0} < a_{1} < S_{1} < a_{2} < \ldots < a_{p} < S_{p}</tex>. Так как используется '''сортировка по ключу''' (англ. ''signature sorting'') то перед тем, как делать вышеописанное разделение, необходимо поделить биты в <tex dpi="130">a_{i}</tex> на <tex dpi="130">h</tex> сегментов и взять некоторые из них. Так же делим биты для каждого числа из <tex dpi="130">S</tex> и оставляем только один в каждом числе. По существу, для каждого <tex dpi="130">a_{i}</tex> берутся все <tex dpi="130">h</tex> сегментов. Если соответствующие сегменты <tex dpi="130">a_{i}</tex> и <tex dpi="130">a_{j}</tex> совпадают, то нам понадобится только один. Сегмент, который берется для числа в <tex dpi="130">S</tex> это сегмент, который выделяется из <tex dpi="130">a_{i}</tex>. Таким образом, начальная задача о разделении <tex dpi="130">n</tex> чисел по <tex dpi="130">\log m</tex> бит преобразуется в несколько задач на разделение с числами по <tex dpi="150">\frac{\log m}{h}</tex> бит. |
'''Пример''': | '''Пример''': | ||
+ | [[Файл:Han-example.png|500px|thumb]] | ||
<tex dpi="130">a_{1} = 3, a_{2} = 5, a_{3} = 7, a_{4} = 10, S = \{1, 4, 6, 8, 9, 13, 14\}</tex>. | <tex dpi="130">a_{1} = 3, a_{2} = 5, a_{3} = 7, a_{4} = 10, S = \{1, 4, 6, 8, 9, 13, 14\}</tex>. | ||
Строка 193: | Строка 194: | ||
− | |||
− | Есть набор <tex dpi="130">T</tex> из <tex dpi="130">p</tex> чисел, которые отсортированы как <tex dpi="130">a_{1}, a_{2}, \ldots, a_{p}</tex>. Используем числа в <tex dpi="130">T</tex> для разделения набора <tex dpi="130">S</tex> из <tex dpi="130">q</tex> чисел <tex dpi="130">b_{1}, b_{2}, \ldots, b_{q}</tex> в <tex dpi="130">p + 1</tex> наборов <tex dpi="130">S_{0}, S_{1}, \ldots, S_{p}</tex>. Пусть <tex dpi="150">h = \frac{\log n}{c \log p}</tex> для константы <tex dpi="130">c > 1</tex>. (<tex dpi="150">\frac{h}{\log\log n \log p}</tex>)-битные числа могут храниться в одном контейнере, содержащим <tex dpi="150">\frac{\log n}{c \log\log n}</tex> бит. Сначала рассматриваем биты в каждом <tex dpi="130">a_{i}</tex> и каждом <tex dpi="130">b_{i}</tex> как сегменты одинаковой длины <tex dpi="150">\frac{h} {\log\log n}</tex>. Рассматриваем сегменты как числа. Чтобы получить неконсервативное преимущество для сортировки, числа в этих контейнерах (<tex dpi="130">a_{i}</tex>-ом и <tex dpi="130">b_{i}</tex>-ом) хешируются, и получается <tex dpi="150">\frac{h}{\log\log n}</tex> хешированных значений в одном контейнере. При вычислении хеш-значений сегменты не влияют друг на друга, можно даже отделить четные и нечетные сегменты в два контейнера. Не умаляя общности считаем, что хеш-значения считаются за константное время. Затем, посчитав значения, два контейнера объединяем в один. Пусть <tex dpi="130">a'_{i}</tex> {{---}} хеш-контейнер для <tex dpi="130">a_{i}</tex>, аналогично <tex dpi="130">b'_{i}</tex>. В сумме хеш-значения имеют <tex dpi="150">\frac{2 \log n}{c \log\log n}</tex> бит, хотя эти значения разделены на сегменты по <tex dpi="150">\frac{h}{ \log\log n}</tex> бит в каждом контейнере. Между сегментами получаются пустоты, которые забиваются нулями. Сначала упаковываются все сегменты в <tex dpi="150">\frac{2 \log n}{c \log\log n}</tex> бит. Потом рассматривается каждый хеш-контейнер как число, и эти хеш-контейнеры сортируются за линейное время (сортировка будет рассмотрена чуть позже). После этой сортировки биты в <tex dpi="130">a_{i}</tex> и <tex dpi="130">b_{i}</tex> разрезаны на <tex dpi="150">\frac{\log\log n}{h}</tex> сегментов. Таким образом, получилось дополнительное мультипликативное преимущество в <tex dpi="150">\frac{h} {\log\log n}</tex> | + | Использование '''сортировки по ключу''' в данном алгоритме: |
+ | |||
+ | Есть набор <tex dpi="130">T</tex> из <tex dpi="130">p</tex> чисел, которые отсортированы как <tex dpi="130">a_{1}, a_{2}, \ldots, a_{p}</tex>. Используем числа в <tex dpi="130">T</tex> для разделения набора <tex dpi="130">S</tex> из <tex dpi="130">q</tex> чисел <tex dpi="130">b_{1}, b_{2}, \ldots, b_{q}</tex> в <tex dpi="130">p + 1</tex> наборов <tex dpi="130">S_{0}, S_{1}, \ldots, S_{p}</tex>. Пусть <tex dpi="150">h = \frac{\log n}{c \log p}</tex> для константы <tex dpi="130">c > 1</tex>. (<tex dpi="150">\frac{h}{\log\log n \log p}</tex>)-битные числа могут храниться в одном контейнере, содержащим <tex dpi="150">\frac{\log n}{c \log\log n}</tex> бит. Сначала рассматриваем биты в каждом <tex dpi="130">a_{i}</tex> и каждом <tex dpi="130">b_{i}</tex> как сегменты одинаковой длины <tex dpi="150">\frac{h} {\log\log n}</tex>. Рассматриваем сегменты как числа. Чтобы получить неконсервативное преимущество для сортировки, числа в этих контейнерах (<tex dpi="130">a_{i}</tex>-ом и <tex dpi="130">b_{i}</tex>-ом) хешируются, и получается <tex dpi="150">\frac{h}{\log\log n}</tex> хешированных значений в одном контейнере. При вычислении хеш-значений сегменты не влияют друг на друга, можно даже отделить четные и нечетные сегменты в два контейнера. Не умаляя общности считаем, что хеш-значения считаются за константное время. Затем, посчитав значения, два контейнера объединяем в один. Пусть <tex dpi="130">a'_{i}</tex> {{---}} хеш-контейнер для <tex dpi="130">a_{i}</tex>, аналогично <tex dpi="130">b'_{i}</tex>. В сумме хеш-значения имеют <tex dpi="150">\frac{2 \log n}{c \log\log n}</tex> бит, хотя эти значения разделены на сегменты по <tex dpi="150">\frac{h}{ \log\log n}</tex> бит в каждом контейнере. Между сегментами получаются пустоты, которые забиваются нулями. Сначала упаковываются все сегменты в <tex dpi="150">\frac{2 \log n}{c \log\log n}</tex> бит. Потом рассматривается каждый хеш-контейнер как число, и эти хеш-контейнеры сортируются за линейное время (сортировка будет рассмотрена чуть позже). После этой сортировки биты в <tex dpi="130">a_{i}</tex> и <tex dpi="130">b_{i}</tex> разрезаны на <tex dpi="150">\frac{\log\log n}{h}</tex> сегментов. Таким образом, получилось дополнительное мультипликативное преимущество (англ. ''additional multiplicative advantage'') в <tex dpi="150">\frac{h} {\log\log n}</tex>. | ||
После того, как вышеописанный процесс повторится <tex dpi="130">g</tex> раз, получится неконсервативное преимущество в <tex dpi="150">(\frac{h} {\log\log n})^g</tex> раз, в то время как потрачено только <tex dpi="130">O(gqt)</tex> времени, так как каждое многократное деление происходит за линейное время <tex dpi="130">O(qt)</tex>. | После того, как вышеописанный процесс повторится <tex dpi="130">g</tex> раз, получится неконсервативное преимущество в <tex dpi="150">(\frac{h} {\log\log n})^g</tex> раз, в то время как потрачено только <tex dpi="130">O(gqt)</tex> времени, так как каждое многократное деление происходит за линейное время <tex dpi="130">O(qt)</tex>. | ||
Строка 206: | Строка 208: | ||
− | + | Операция '''сортировки за линейное время''' (англ. ''Linear-Time-Sort'') | |
− | Входные данные: <tex dpi="150">r | + | Входные данные: <tex dpi="150">r \geqslant n^{\frac{2}{5}}</tex> чисел <tex dpi="130">d_{i}</tex>, <tex dpi="130">d_{i}.value</tex> — значение числа <tex dpi="130">d_{i}</tex>, в котором <tex dpi="150">\frac{2 \log n}{c \log\log n}</tex> бит, <tex dpi="130">d_{i}.set</tex> — набор, в котором находится <tex dpi="130">d_{i}</tex>. Следует отметить, что всего есть <tex dpi="130">t</tex> наборов. |
# Сортируем все <tex dpi="130">d_{i}</tex> по <tex dpi="130">d_{i}.value</tex>, используя bucket sort. Пусть все отсортированные числа в <tex dpi="130">A[1..r]</tex>. Этот шаг занимает линейное время, так как сортируется не менее <tex dpi="150">n^{\frac{2}{5}}</tex> чисел. | # Сортируем все <tex dpi="130">d_{i}</tex> по <tex dpi="130">d_{i}.value</tex>, используя bucket sort. Пусть все отсортированные числа в <tex dpi="130">A[1..r]</tex>. Этот шаг занимает линейное время, так как сортируется не менее <tex dpi="150">n^{\frac{2}{5}}</tex> чисел. | ||
Строка 228: | Строка 230: | ||
− | Так как <tex dpi="130">q</tex> чисел не надо полностью сортировать и <tex dpi="130">q = p^2</tex>, то можно использовать [[#lemma6|лемму №6]] для сортировки. Для этого необходимо неконсервативное преимущество, которое получается с помощью [[Сортировка Хана#Signature sorting|signature sorting]]. Для этого используется линейная техника многократного деления (multi-dividing technique). | + | Так как <tex dpi="130">q</tex> чисел не надо полностью сортировать и <tex dpi="130">q = p^2</tex>, то можно использовать [[#lemma6|лемму №6]] для сортировки. Для этого необходимо неконсервативное преимущество, которое получается с помощью [[Сортировка Хана#Signature sorting|signature sorting]]. Для этого используется линейная техника многократного деления (англ. ''multi-dividing technique''). |
Строка 252: | Строка 254: | ||
==Источники информации== | ==Источники информации== | ||
− | * [http://www.sciencedirect.com/science/article/pii/S019667740300155X Deterministic Sorting in O(n | + | * [http://www.sciencedirect.com/science/article/pii/S019667740300155X Deterministic Sorting in O(n log log n) Time and Linear Space. Yijie Han.] |
* А. Андерссон. Fast deterministic sorting and searching in linear space. Proc. 1996 IEEE Symp. on Foundations of Computer Science. 135-141(1996) | * А. Андерссон. Fast deterministic sorting and searching in linear space. Proc. 1996 IEEE Symp. on Foundations of Computer Science. 135-141(1996) | ||
* [http://dl.acm.org/citation.cfm?id=1236460 A. Andersson, M. Thorup. Dynamic ordered sets with exponential search trees.] | * [http://dl.acm.org/citation.cfm?id=1236460 A. Andersson, M. Thorup. Dynamic ordered sets with exponential search trees.] | ||
Строка 259: | Строка 261: | ||
[[Категория: Дискретная математика и алгоритмы]] | [[Категория: Дискретная математика и алгоритмы]] | ||
− | [[Категория: | + | [[Категория: Сортировка]] |
Текущая версия на 19:13, 4 сентября 2022
Сортировка Хана (англ. Hansort) — сложный алгоритм сортировки целых чисел со сложностью
, где — количество элементов для сортировки.Данная статья писалась на основе брошюры Хана (англ. Yijie Han), посвященной этой сортировке.
Содержание
Описание
Алгоритм построен на основе экспоненциального поискового дерева Андерсона (англ. Andersson's exponential search tree). Сортировка происходит за счет вставки целых чисел в экспоненциальное поисковое дерево (далее — ЭП-дерево).
Экспоненциальное поисковое дерево Андерсона
Определение: |
ЭП-дерево — это дерево поиска, в котором все ключи хранятся в листьях этого дерева и количество детей у каждого узла уменьшается экспоненциально от глубины узла. |
Структура ЭП-дерева:
1) Корень имеет
сыновей . Все сыновья являются ЭП-деревьями.2) Каждое поддерево корня имеет
сыновей.В этом дереве
уровней. При нарушении баланса дерева необходимо балансирование, которое требует времени при вставленных целых числах. Такое время достигается за счет вставки чисел группами, а не поодиночке, как изначально предлагал Андерссон.Определения
Определение: |
Контейнер — объект, в которым хранятся наши данные. Например: 32-битные и 64-битные числа, массивы, вектора. |
Определение: |
Алгоритм, сортирующий | целых чисел из множества , называется консервативным, если длина контейнера (число бит в контейнере) равна . Если длина больше, то алгоритм неконсервативный.
Определение: |
Если сортируются целые числа из множества | с длиной контейнера с , тогда сортировка происходит с неконсервативным преимуществом .
Определение: |
Для множества
Набор если | определим
Определение: |
Предположим, есть набор | из чисел, которые уже отсортированы как и набор из чисел . Тогда разделением чисел числами называется набор , где .
Леммы
Лемма (№ 1): |
Даны целые числа , и является подмножеством множества , содержащим элементов, и . Функция , принадлежащая , может быть выбрана за время так, что количество коллизий . |
Лемма (№ 2): |
Выбор -ого наибольшего числа среди чисел, упакованных в контейнеров, может быть сделан за время и с использованием памяти. В том числе, так может быть найдена медиана. |
Доказательство: |
Так как возможно делать попарное сравнение | чисел в одном контейнере с числами в другом и извлекать большие числа из одного контейнера и меньшие из другого за константное время, возможно упаковать медианы из первого, второго, , -ого чисел из 5 контейнеров в один контейнер за константное время. Таким образом, набор из медиан теперь содержится в контейнерах. Рекурсивно находим медиану в . Используя , уберем хотя бы чисел среди . Затем упакуем оставшиеся из контейнеров в контейнеров и затем продолжим рекурсию.
Лемма (№ 3): |
Если целых чисел, в сумме использующих бит, упакованы в один контейнер, тогда чисел в контейнерах могут быть отсортированы за время с использованием памяти. |
Доказательство: |
Так как используется только бит в каждом контейнере для хранения чисел, используем bucket sort, чтобы отсортировать все контейнеры, представляя каждый как число, что занимает времени и памяти. Так как используется бит на контейнер, понадобится шаблонов для всех контейнеров. Затем поместим контейнеров с одинаковым шаблоном в одну группу. Для каждого шаблона останется не более контейнеров, которые не смогут образовать группу. Поэтому не более контейнеров не смогут сформировать группу. Для каждой группы помещаем -е число во всех контейнерах в один. Таким образом берутся -целых векторов и получаются -целых векторов, где -ый вектор содержит -ое число из входящего вектора. Эта транспозиция может быть сделана за время , с использованием памяти. Для всех групп это занимает время , с использованием памяти.Для контейнеров вне групп (которых штук) разбираем и собираем заново контейнеры. На это потребуется не более времени и памяти. После всего этого используем карманную сортировку вновь для сортировки контейнеров. Таким образом, все числа отсортированы.
|
Лемма (№ 4): |
Примем, что каждый контейнер содержит бит, и чисел, в каждом из которых бит, упакованы в один контейнер. Если каждое число имеет маркер, содержащий бит, и маркеров упакованы в один контейнер таким же образом , что и числа, тогда чисел в контейнерах могут быть отсортированы по их маркерам за время с использованием памяти.
(*): если число упаковано как -ое число в -ом контейнере для чисел, тогда маркер для упакован как -ый маркер в -ом контейнере для маркеров. |
Доказательство: |
Контейнеры для маркеров могут быть отсортированы с помощью bucket sort потому, что каждый контейнер использует лемме №3. Перемещаем каждую группу контейнеров для чисел. | бит. Сортировка сгруппирует контейнеры для чисел как в
Лемма (№ 5): |
Предположим, что каждый контейнер содержит бит, что чисел, в каждом из которых бит, упакованы в один контейнер, что каждое число имеет маркер, содержащий бит, и что маркеров упакованы в один контейнер тем же образом что и числа. Тогда чисел в контейнерах могут быть отсортированы по своим маркерам за время с использованием памяти. |
Доказательство: |
Заметим, что несмотря на то, что длина контейнера лемме №3 и лемме №4 сортируем контейнеры упакованных маркеров с помощью bucket sort. Для того, чтобы перемещать контейнеры чисел, помещаем вместо контейнеров чисел в одну группу. Для транспозиции чисел в группе, содержащей контейнеров, упаковываем контейнеров в , упаковывая контейнеров в один. Далее делаем транспозицию над контейнерами. Таким образом перемещение занимает всего времени для каждой группы и времени для всех чисел. После завершения транспозиции, распаковываем контейнеров в контейнеров. бит, всего бит используется для хранения упакованных чисел. Так же как в
|
Лемма (№ 6): |
целых чисел можно отсортировать в наборов , , , таким образом, что в каждом наборе чисел и при , за время и место с неконсервативным преимуществом . |
Доказательство: |
Алгоритм сортировки целых чисел в наборов, представленный ниже, является доказательством данной леммы.Постановка задачи и решение некоторых проблем:
Стоит отметить, что процесс помещения нестабилен, т.к. основан на алгоритме из леммы №5.
Рассмотрим число , которое является -ым в наборе . Рассмотрим блок (назовем его ), который является -ым м.ч. в . Когда используется вышеописанный метод перемещения нескольких следующих блоков (назовем это ) в , просто перемещен на позицию в наборе , но не обязательно на позицию (где расположен ). Если значение блока одинаково для всех чисел в , то это не создаст проблемы потому, что блок одинаков вне зависимости от того в какое место в помещен . Иначе у нас возникает проблема дальнейшей сортировки. Поэтому поступаем следующим образом: На каждой стадии числа в одном наборе работают на общем блоке, который назовем "текущий блок набора". Блоки, которые предшествуют текущему блоку содержат важные биты и идентичны для всех чисел в наборе. Когда помещаем больше бит в набор, последующие блоки помещаются в набор вместе с текущим блоком. Так вот, в вышеописанном процессе помещения предполагается, что самый значимый блок среди блоков — это текущий блок. Таким образом, после того, как эти блоков помещены в набор, изначальный текущий блок удаляется, потому что известно, что эти блоков перемещены в правильный набор, и нам не важно где находился начальный текущий блок. Тот текущий блок находится в перемещенных блоках.
Алгоритм сортировкиAlgorithm , , , , , )— это неконсервативное преимущество равное , -ые это входящие целые числа в наборе, которые надо отсортировать, это уровень рекурсии.
Algorithm IterateSort Call , , , , , );от 1 до 5
|
Уменьшение числа бит в числах
Один из способов ускорить сортировку — уменьшить число бит в числе. Один из способов уменьшить число бит в числе — использовать деление пополам (эту идею впервые подал van Emde Boas). Деление пополам заключается в том, что количество оставшихся бит в числе уменьшается в 2 раза. Это быстрый способ, требующий
памяти. Для своего дерева Андерссон использует хеширование, что позволяет сократить количество памяти до . Для того чтобы еще ускорить алгоритм, необходимо упаковать несколько чисел в один контейнер, чтобы затем за константное количество шагов произвести хеширование для всех чисел, хранимых в контейнере. Для этого используется хеш-функция для хеширования чисел в таблицу размера за константное время без коллизий. Для этого используется модифицированная хеш-функция авторства: Dierzfelbinger и Raman.
Алгоритм: Пусть целое число и пусть . Класс хеш-функций из в определен как и для всех из : .
Данный алгоритм базируется на лемме №1.
Взяв , получаем хеш-функцию , которая захеширует чисел из в таблицу размера без коллизий. Очевидно, что может быть посчитана для любого за константное время. Если упаковать несколько чисел в один контейнер так, что они разделены несколькими битами нулей, то можно применить ко всему контейнеру, и в результате все хеш-значения для всех чисел в контейнере будут посчитаны. Заметим, что это возможно только потому, что в вычисление хеш-значения вовлечены только ( ) и ( ).
Такая хеш-функция может быть найдена за .
Следует отметить, что, несмотря на размер таблицы
, потребность в памяти не превышает , потому что хеширование используется только для уменьшения количества бит в числе.Сортировка по ключу
Предположим, что
чисел должны быть отсортированы, и в каждом бит. Будем считать, что в каждом числе есть сегментов, в каждом из которых бит. Теперь применяем хеширование ко всем сегментам и получаем бит хешированных значений для каждого числа. После сортировки на хешированных значениях для всех начальных чисел начальная задача по сортировке чисел по бит в каждом стала задачей по сортировке чисел по бит в каждом.
Также рассмотрим проблему последующего разделения. Пусть , , , — чисел и — множество чисeл. Необходимо разделить в наборов, таких, что: . Так как используется сортировка по ключу (англ. signature sorting) то перед тем, как делать вышеописанное разделение, необходимо поделить биты в на сегментов и взять некоторые из них. Так же делим биты для каждого числа из и оставляем только один в каждом числе. По существу, для каждого берутся все сегментов. Если соответствующие сегменты и совпадают, то нам понадобится только один. Сегмент, который берется для числа в это сегмент, который выделяется из . Таким образом, начальная задача о разделении чисел по бит преобразуется в несколько задач на разделение с числами по бит.
Пример:
.
Делим числа на два сегмента. Для
получим верхний сегмент , нижний ; — верхний , нижний ; — верхний , нижний ; — верхний , нижний . Для элементов из S получим: для нижний , так как он выделяется из нижнего сегмента ; для нижний ; для нижний ; для нижний ; для верхний ; для верхний . Теперь все верхние сегменты, нижние сегменты и , нижние сегменты нижние сегменты формируют новые задачи на разделение.
Использование сортировки по ключу в данном алгоритме:
Есть набор
из чисел, которые отсортированы как . Используем числа в для разделения набора из чисел в наборов . Пусть для константы . ( )-битные числа могут храниться в одном контейнере, содержащим бит. Сначала рассматриваем биты в каждом и каждом как сегменты одинаковой длины . Рассматриваем сегменты как числа. Чтобы получить неконсервативное преимущество для сортировки, числа в этих контейнерах ( -ом и -ом) хешируются, и получается хешированных значений в одном контейнере. При вычислении хеш-значений сегменты не влияют друг на друга, можно даже отделить четные и нечетные сегменты в два контейнера. Не умаляя общности считаем, что хеш-значения считаются за константное время. Затем, посчитав значения, два контейнера объединяем в один. Пусть — хеш-контейнер для , аналогично . В сумме хеш-значения имеют бит, хотя эти значения разделены на сегменты по бит в каждом контейнере. Между сегментами получаются пустоты, которые забиваются нулями. Сначала упаковываются все сегменты в бит. Потом рассматривается каждый хеш-контейнер как число, и эти хеш-контейнеры сортируются за линейное время (сортировка будет рассмотрена чуть позже). После этой сортировки биты в и разрезаны на сегментов. Таким образом, получилось дополнительное мультипликативное преимущество (англ. additional multiplicative advantage) в .После того, как вышеописанный процесс повторится
раз, получится неконсервативное преимущество в раз, в то время как потрачено только времени, так как каждое многократное деление происходит за линейное время .
Хеш-функция, которая используется, находится следующим образом. Будут хешироватся сегменты, -ые, -ые, по счету в числе. Хеш-функцию для -ых по счету сегментов, получаем нарезанием всех чисел на сегментов. Рассматривая каждый сегмент как число, получаем чисел. Затем получаем одну хеш-функцию для этих чисел. Так как , то получится не более хеш-функций.
Рассмотрим сортировку за линейное время, о которой было упомянуто ранее. Предполагается, что хешированные значения для каждого контейнера упакованы в бит. Есть наборов, в каждом из которых хешированных контейнеров по бит в каждом. Эти контейнеры должны быть отсортированы в каждом наборе. Комбинируя все хеш-контейнеры в один pool, сортируем следующим образом.
Операция сортировки за линейное время (англ. Linear-Time-Sort)
Входные данные:
чисел , — значение числа , в котором бит, — набор, в котором находится . Следует отметить, что всего есть наборов.- Сортируем все по , используя bucket sort. Пусть все отсортированные числа в . Этот шаг занимает линейное время, так как сортируется не менее чисел.
- Помещаем все в .
Сортировка с использованием O(n log log n) времени и памяти
Для сортировки
целых чисел в диапазоне предполагается, что в нашем консервативном алгоритме используется контейнер длины . Далее везде считается, что все числа упакованы в контейнеры одинаковой длины.
Берем для ЭП-дерева Андерссона. Следовательно, у корня будет детей, и каждое ЭП-дерево в каждом ребенке будет иметь листьев. В отличие от оригинального дерева, за раз вставляется не один элемент, а , где — количество детей узла дерева, в котором числа должны спуститься вниз. Алгоритм полностью опускает все чисел на один уровень. В корне опускаются чисел на следующий уровень. После того, как все числа опустились на следующий уровень, они успешно разделились на наборов , в каждом из которых чисел и . Затем, берутся чисел из и опускаются на следующий уровень ЭП-дерева. Это повторяется, пока все числа не опустятся на следующий уровень. На этом шаге числа разделены на наборов , аналогичных наборам , в каждом из которых чисел. Теперь числа опускаются дальше в ЭП-дереве.
Нетрудно заметить, что перебалансирока занимает
времени с времени на уровень, аналогично стандартному ЭП-дереву Андерссона.
Нам следует нумеровать уровни ЭП-дерева с корня, начиная с нуля. Рассмотрим спуск вниз на уровне . Имеется наборов по чисел в каждом. Так как каждый узел на данном уровне имеет детей, то на уровень опускаются чисел для каждого набора, или всего чисел для всех наборов за один раз.
Спуск вниз можно рассматривать как сортировку чисел в каждом наборе вместе с числами из ЭП-дерева, так, что эти чисел разделены в наборов таких, что .
Так как чисел не надо полностью сортировать и , то можно использовать лемму №6 для сортировки. Для этого необходимо неконсервативное преимущество, которое получается с помощью signature sorting. Для этого используется линейная техника многократного деления (англ. multi-dividing technique).
После сокращений бит в signature sorting получаем неконсервативное преимущество в . Мы не волнуемся об этих сокращениях до конца потому, что после получения неконсервативного преимущества мы можем переключиться на лемму №6 для завершения разделения чисел с помощью чисел на наборы. Заметим, что по природе битового сокращения начальная задача разделения для каждого набора перешла в подзадач разделения на поднаборов для какого-то числа .
Теперь для каждого набора все его поднаборы в подзадачах собираются в один набор. Затем, используя лемму №6, делается разделение. Так как получено неконсервативное преимущество в и работа происходит на уровнях не ниже, чем , то алгоритм занимает времени.
В итоге разделились чисел числами в каждый набор. То есть получилось, что , где — сегмент , полученный с помощью битового сокращения. Такое разделение получилось комбинированием всех поднаборов в подзадачах. Предполагаем, что числа хранятся в массиве так, что числа в предшествуют числам в если и хранится после , но до .
Пусть находится в поднаборе . Чтобы позволить разделению выполниться, для каждого поднабора помещаем все в .
На это потребуется линейное время и место.
Теперь рассмотрим проблему упаковки, которая решается следующим образом. Считается, что число бит в контейнере , потому что в противном случае можно использовать radix sort для сортировки чисел. У контейнера есть хешированных значений (сегментов) в себе на уровне в ЭП-дереве. Полное число хешированных бит в контейнере равно бит. Хешированные биты в контейнере выглядят как , где -ые — хешированные биты, а нули — это просто нули. Сначала упаковываем контейнеров в один и получаем , где : элемент с номером из -ого контейнера. Используем шагов, чтобы упаковать в . Теперь упакованные хеш-биты занимают бит. Используем времени чтобы распаковать в контейнеров . Затем, используя времени, упаковываем эти контейнеров в один . Затем, используя шагов, упаковываем в . В итоге используется времени для упаковки контейнеров. Считаем, что время, потраченное на один контейнер — константа.
См. также
Источники информации
- Deterministic Sorting in O(n log log n) Time and Linear Space. Yijie Han.
- А. Андерссон. Fast deterministic sorting and searching in linear space. Proc. 1996 IEEE Symp. on Foundations of Computer Science. 135-141(1996)
- A. Andersson, M. Thorup. Dynamic ordered sets with exponential search trees.
- Wikipedia — Integer sorting