Участник:Artem.ustinov/НВП — различия между версиями
(→Оценка времени работы) |
(→Пример: Пояснил за нахождение ключей) |
||
| Строка 195: | Строка 195: | ||
|- | |- | ||
|<tex>\pi</tex>||style="background:#FFC9C9"|<tex>9</tex>||style="background:#FFC9C9"|<tex>3</tex>||style="background:#FFC9C9"|<tex>10</tex>||style="background:#FFC9C9"|<tex>4</tex>||style="background:#FFC9C9"|<tex>8</tex>||style="background:#B9FFB9"|<tex>1</tex>||style="background:#B9FFB9"|<tex>2</tex>||style="background:#B9FFB9"|<tex>12</tex>||style="background:#B9FFB9"|<tex>6</tex>||style="background:#B9FFB9"|<tex>5</tex>||style="background:#CFCFFF"|<tex>7</tex>||style="background:#CFCFFF"|<tex>11</tex> | |<tex>\pi</tex>||style="background:#FFC9C9"|<tex>9</tex>||style="background:#FFC9C9"|<tex>3</tex>||style="background:#FFC9C9"|<tex>10</tex>||style="background:#FFC9C9"|<tex>4</tex>||style="background:#FFC9C9"|<tex>8</tex>||style="background:#B9FFB9"|<tex>1</tex>||style="background:#B9FFB9"|<tex>2</tex>||style="background:#B9FFB9"|<tex>12</tex>||style="background:#B9FFB9"|<tex>6</tex>||style="background:#B9FFB9"|<tex>5</tex>||style="background:#CFCFFF"|<tex>7</tex>||style="background:#CFCFFF"|<tex>11</tex> | ||
| + | |- | ||
| + | |Смещение ||style="background:#FFC9C9"|<tex>1</tex>||style="background:#FFC9C9"|<tex>2</tex>||style="background:#FFC9C9"|<tex>3</tex>||style="background:#FFC9C9"|<tex>4</tex>||style="background:#FFC9C9"|<tex>5</tex>||style="background:#B9FFB9"|<tex>1</tex>||style="background:#B9FFB9"|<tex>2</tex>||style="background:#B9FFB9"|<tex>3</tex>||style="background:#B9FFB9"|<tex>4</tex>||style="background:#B9FFB9"|<tex>5</tex>||style="background:#CFCFFF"|<tex>1</tex>||style="background:#CFCFFF"|<tex>2</tex> | ||
|} | |} | ||
| Строка 202: | Строка 204: | ||
|- | |- | ||
|<tex>\pi</tex> ||style="background:#FFC9C9"|<tex>3</tex>||style="background:#FFC9C9"|<tex>4</tex>||style="background:#FFC9C9"|<tex>8</tex>||style="background:#FFC9C9"|<tex>9</tex>||style="background:#FFC9C9"|<tex>10</tex>||style="background:#B9FFB9"|<tex>1</tex>||style="background:#B9FFB9"|<tex>2</tex>||style="background:#B9FFB9"|<tex>5</tex>||style="background:#B9FFB9"|<tex>6</tex>||style="background:#B9FFB9"|<tex>12</tex>||style="background:#CFCFFF"|<tex>7</tex>||style="background:#CFCFFF"|<tex>11</tex> | |<tex>\pi</tex> ||style="background:#FFC9C9"|<tex>3</tex>||style="background:#FFC9C9"|<tex>4</tex>||style="background:#FFC9C9"|<tex>8</tex>||style="background:#FFC9C9"|<tex>9</tex>||style="background:#FFC9C9"|<tex>10</tex>||style="background:#B9FFB9"|<tex>1</tex>||style="background:#B9FFB9"|<tex>2</tex>||style="background:#B9FFB9"|<tex>5</tex>||style="background:#B9FFB9"|<tex>6</tex>||style="background:#B9FFB9"|<tex>12</tex>||style="background:#CFCFFF"|<tex>7</tex>||style="background:#CFCFFF"|<tex>11</tex> | ||
| + | |- | ||
| + | |Смещение ||style="background:#FFC9C9"|<tex>2</tex>||style="background:#FFC9C9"|<tex>4</tex>||style="background:#FFC9C9"|<tex>5</tex>||style="background:#FFC9C9"|<tex>1</tex>||style="background:#FFC9C9"|<tex>3</tex>||style="background:#B9FFB9"|<tex>1</tex>||style="background:#B9FFB9"|<tex>2</tex>||style="background:#B9FFB9"|<tex>5</tex>||style="background:#B9FFB9"|<tex>4</tex>||style="background:#B9FFB9"|<tex>3</tex>||style="background:#CFCFFF"|<tex>1</tex>||style="background:#CFCFFF"|<tex>2</tex> | ||
|} | |} | ||
| + | |||
| + | Обратные перестановки (<tex>\xi</tex>): | ||
| + | {| class="wikitable" style="center" style="background:#FFCC80" | ||
| + | ! colspan="5"|<tex>1</tex> || colspan="5"|<tex>2</tex> || colspan="3"|<tex>3</tex> | ||
| + | |-align="center" | ||
| + | | style="background:#FFD0D0"|<tex>4</tex>||style="background:#FFD0D0"|<tex>1</tex>||style="background:#FFD0D0"|<tex>5</tex>||style="background:#FFD0D0"|<tex>2</tex>||style="background:#FFD0D0"|<tex>3</tex> | ||
| + | | style="background:#D0FFD0"|<tex>1</tex>||style="background:#D0FFD0"|<tex>2</tex>||style="background:#D0FFD0"|<tex>5</tex>||style="background:#D0FFD0"|<tex>4</tex>||style="background:#D0FFD0"|<tex>3</tex> | ||
| + | | style="background:#D0D0FF"|<tex>1</tex>||style="background:#D0D0FF"|<tex>2</tex> | ||
| + | |} | ||
| + | |||
''' Первый блок ''' | ''' Первый блок ''' | ||
| − | Так как очередь <tex>B</tex> в начале пуста, то <tex>\mathtt{merged}=C_1^s</tex>. | + | Так как очередь <tex>B</tex> в начале пуста, то <tex>\mathtt{merged}=C_1^s</tex>. Присвоим ключи элементов в списке <tex>\mathtt{merged}</tex> как их индексы в этом списке. Восстанавливаем последовательность ключей элементов в порядке исходной последовательности, действуя обратной перестановкой смещений <tex>\xi_1</tex> на последовательность ключей в отсортированном блоке. |
| − | {| | + | {|11 |
| || | | || | ||
{| class="wikitable" style="text-align:center" | {| class="wikitable" style="text-align:center" | ||
| − | ! colspan="6"| | + | ! colspan="6"|Сортированный |
|- | |- | ||
| − | | <tex>\pi</tex> ||<tex> | + | | <tex>\pi</tex> ||<tex>3</tex>||<tex>4</tex>||<tex>8</tex>||<tex>9</tex>||<tex>10</tex> |
|- | |- | ||
| − | | <tex>key</tex> ||<tex> | + | | <tex>key</tex> ||<tex>1</tex>||<tex>2</tex>||<tex>3</tex>||<tex>4</tex>||<tex>5</tex> |
|} | |} | ||
| || | | || | ||
{| class="wikitable" style="text-align:center" | {| class="wikitable" style="text-align:center" | ||
| − | ! colspan="6"| | + | ! colspan="6"|Первый блок |
| + | |- | ||
| + | | <tex>\pi</tex> ||<tex>9</tex>||<tex>3</tex>||<tex>10</tex>||<tex>4</tex>||<tex>8</tex> | ||
|- | |- | ||
| − | | <tex> | + | | <tex>key</tex> ||<tex>4</tex>||<tex>1</tex>||<tex>5</tex>||<tex>2</tex>||<tex>3</tex> |
|- | |- | ||
| − | | <tex> | + | | <tex>\xi_1</tex> ||<tex>4</tex>||<tex>1</tex>||<tex>5</tex>||<tex>2</tex>||<tex>3</tex> |
|} | |} | ||
|} | |} | ||
| Строка 280: | Строка 296: | ||
|} | |} | ||
|} | |} | ||
| + | |||
| + | Получаем ключи элементов в <tex>C_2^s</tex> и находим перестановку ключей в порядке исходной последовательности, действуя перестановкой <tex>\xi_2</tex>: | ||
{| | {| | ||
| || | | || | ||
{| class="wikitable" style="text-align:center" | {| class="wikitable" style="text-align:center" | ||
| − | ! colspan="6"| | + | ! colspan="6"|Cортированный |
|- | |- | ||
| − | | <tex>\pi</tex> ||<tex>1</tex>||<tex>2</tex>||<tex> | + | | <tex>\pi</tex> ||<tex>1</tex>||<tex>2</tex>||<tex>5</tex>||<tex>6</tex>||<tex>12</tex> |
|- | |- | ||
| − | | <tex>key</tex> ||<tex>1</tex>||<tex>2</tex>||<tex> | + | | <tex>key</tex> ||<tex>1</tex>||<tex>2</tex>||<tex>5</tex>||<tex>6</tex>||<tex>8</tex> |
|} | |} | ||
| || | | || | ||
{| class="wikitable" style="text-align:center" | {| class="wikitable" style="text-align:center" | ||
| − | ! colspan="6"| | + | ! colspan="6"|Второй блок |
| + | |- | ||
| + | | <tex>\pi</tex> ||<tex>1</tex>||<tex>2</tex>||<tex>12</tex>||<tex>6</tex>||<tex>5</tex> | ||
|- | |- | ||
| − | | <tex> | + | | <tex>key</tex> ||<tex>1</tex>||<tex>2</tex>||<tex>8</tex>||<tex>6</tex>||<tex>5</tex> |
|- | |- | ||
| − | | <tex> | + | | <tex>\xi_2</tex> ||<tex>1</tex>||<tex>2</tex>||<tex>5</tex>||<tex>4</tex>||<tex>3</tex> |
|} | |} | ||
|} | |} | ||
| Строка 363: | Строка 383: | ||
|} | |} | ||
|} | |} | ||
| + | |||
| + | Получаем ключи элементов в <tex>C_3^s</tex> и находим перестановку ключей в порядке исходной последовательности, действуя перестановкой <tex>\xi_3</tex>: | ||
{| | {| | ||
| Строка 382: | Строка 404: | ||
|- | |- | ||
| <tex>key</tex> ||<tex>4</tex>||<tex>5</tex> | | <tex>key</tex> ||<tex>4</tex>||<tex>5</tex> | ||
| + | |- | ||
| + | | <tex>\xi_3</tex> ||<tex>1</tex>||<tex>2</tex> | ||
|} | |} | ||
|} | |} | ||
Версия 16:05, 17 января 2018
| Задача: |
| Дана перестановка множества . Требуется найти НВП за , где — длина НВП. |
Содержание
Алгоритм за O(n log log n)
Нахождение длины НВП
Основная идея
Пусть — входная перестановка.
Будем последовательно обрабатывать элементы в порядке
Для каждой длины предполагаемой НВП находим наименьший элемент, который может быть последним в возрастающей подпоследовательности длины и запишем его в массив . Будем называть его наилучшим элементом для длины .
- Если больше каждого элемента , вычисленного для подпоследовательности , тогда с ним можно сделать возрастающую подпоследовательность максимальной длины из уже рассмотренных, в которой он будет последним элементом. Значит, записываем его в конец .
- Иначе будет наилучшим элементом для уже существующей длины и сможет улучшить только один элемент в , тогда мы находим наименьшее и заменяем элементом .
Следует отметить, что полученный массив также образует возрастающую последовательность, на котором мы должны выполнять операции , соответственно целесообразно использовать приоритетную очередь, реализованную через Дерево ван Эмде Боаса. Так как данная структура данных производит описанные операции за , где k — количество бит чисел, которые позволяет хранить дерево, то полученный алгоритм работает за , потому что все элементы последовательности не превосходят n.
Пример
Типы операций
- Добавление элемента, который больше всех предыдущих:
- Замещение элемента более подходящим, т.е. добавление немаксимального элемента:
Пример последовательности
Состояние очереди при каждом добавлении
Псевдокод
int LIS([n]) PriorityQueue B // рабочая приоритетная очередь int k = 0 // длина НВП for i = 1 to n x = [i] // в любом случае добавляем в очередь очередной элемент // устаревшие будем удалять B.insert(x) if B.next(x) // добавленный элемент — не максимальный // удаляем следующее за x значение B.delete(B.next(x)) else // добавленный элемент — максимальный // предыдущие значения не трогаем, очередь увеличилась k = k + 1 return k
Расширение алгоритма до нахождения НВП
Основная идея
Будем запоминать пары: для каждого элемента записываем его "предшественника".
Тогда, пройдя по предшественникам, начиная с последнего элемента очереди , мы можем восстановить НВП.
Общий вид алгоритма
| predecessor | |||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|
Псевдокод
int[] LIS([n]) PriorityQueue B int k = 0 int predecessor[n] // резервируем позиций for i = 1 to n x = [i] B.insert(x) predecessor[x] = B.prev(x) if B.next(x) B.delete(B.next(x)) else k = k + 1 // по цепочке от последнего элемента // восстанавливаем НВП int result[k] int cur = B.max for i = k - 1 downto 0 result[i] = cur cur = predecessor[cur] return result
Оптимизация до O(n log log k)
Основная идея
Чтобы Дерево ван Эмде Боаса выполняло операции за , необходимо алфавит обрабатываемых значений уменьшить до .
Предположим, мы знаем такое приближение числа числом . Мы обсудим, как найти такое позже.
Во время обработки ключей элементов описанный выше алгоритм работает только с очередью и не зависит от предыдущих элементов последовательности, которые не находятся в очереди. Поэтому, если мы разобьем всю последовательность на блоки из элементов (последний блок может быть меньше), и нам удастся обрабатывать каждый как перестановку из элементов, сохраняя очередь для вычисленных ранее блоков, то мы получим асимптотическое время , а так как , то . (Мы будем обрабатывать блоки последовательно, т.е. с предыдущего блока у нас может остаться значений в очереди, которые дополняются значениями очередного блока — получаем верхнее ограничение в обрабатываемых возможных значений.)
Деление на блоки
Последовательность делится на блоки :
Обозначим за отсортированный блок . Отсортированные и неотсортированные блоки будем хранить в памяти.
Цифровая сортировка каждого блока отдельно будет давать нам время работы . Чтобы отсортировать их за линейное время, дополним каждый элемент номером его блока и получим пары . Цифровая сортировка этих пар, если принимать за старший разряд номер блока, а за младший значение элемента, будет работать за , потому что значения элементов и номера блоков не превосходят .
Обработка блока
Каждому элементу взаимно однозначно сопоставим ключ . Очередь будет работать непосредственно с ключами элементов.
Определим ключи элементов так, чтобы их значения были в промежутке . Работая с блоком , будем сливать элементы, ключи которых находятся в очереди , с в список . Поскольку мы предположили, что , то количество ключей в не больше , тогда длина не больше , что позволяет однозначно определить ключи на множестве . Как было замечено ранее, элементы, чьи ключи находятся в , располагаются в возрастающем порядке, поэтому возможно производить тривиальную операцию слияния за .
В итоге, получим отсортированный список . Сопоставим ключ каждому элементу как его позицию в этом списке, тогда справедливо утверждение, что , где , поэтому любая возрастающая последовательность ключей элементов будет соответствовать возрастающей последовательности элементов. Таким образом, приоритетная очередь сможет корректно работать с ключами элементов.
После того, как мы определили новые ключи для элементов, обновляем ключи в очереди .
Затем запускаем алгоритм , для ключей элементов в порядке исходной последовательности.
В итоге, обработка блока делится на следующие этапы:
- Достаем из очереди ключи , конвертируем их в элементы и кладём в список .
- Сливаем элементы в со следующим отсортированным блоком в список .
- Присваиваем новые ключи элементам в порядке списка .
- Вставляем в новые ключи элементов списка .
- Обрабатываем ключи элементов блока в порядке исходной последовательности с помощью алгоритма . Для восстановления НВП также используем массив "предшественников", который будет работать с соответствующими ключам элементами .
Пример
Предположим, что . Исходно получаем:
| Блок | ||||||||||||
| Смещение |
После сортировки:
| Блок | ||||||||||||
| Смещение |
Обратные перестановки ():
Первый блок
Так как очередь в начале пуста, то . Присвоим ключи элементов в списке как их индексы в этом списке. Восстанавливаем последовательность ключей элементов в порядке исходной последовательности, действуя обратной перестановкой смещений на последовательность ключей в отсортированном блоке.
|
| ||||||||||||||||||||||||||||||||||||||||||||
Обработка блока с помощью алгоритма .
В результате получаем
Второй блок
Восстанавливаем элементы из : .
Сливаем и восстановленные элементы из в и присваиваем элементам ключи как индексы элементов в полученном списке:
|
|
|
| ||||||||||||||||||||||||||||||||||||||||||||||
Получаем ключи элементов в и находим перестановку ключей в порядке исходной последовательности, действуя перестановкой :
|
| ||||||||||||||||||||||||||||||||||||||||||||
Обновляем ключи в очереди:
запускаем для блока:
В результате получаем:
Третий блок
Восстанавливаем элементы из : .
Сливаем и восстановленные элементы из и присваиваем ключи элементам:
|
|
|
| ||||||||||||||||||||||||||||||||||||
Получаем ключи элементов в и находим перестановку ключей в порядке исходной последовательности, действуя перестановкой :
Обновление старых ключей: запускаем для блока: Результат завершения алгоритма:
Получаем, что длина НВП — , и НВП оканчивается на . Восстановление НВП Начинаем восстановление с :
Нахождение размера блоковРассмотрим последовательность , где , — некоторое значение, меньшее . Будем последовательно для элементов этой последовательности запускать алгоритм, представленный выше. Если размер очереди становится больше , то условие перестает выполняться, тогда останавливаем алгоритм и переходим к следующему значению . Для каждого размер списка не больше , а количество блоков всего . То общее количество присваиваний новых ключей элементам последовательности, также как и количество операций слияния списков, не больше , где c — некоторая константа. Каждая операция с приоритетной очередью требует времени, так как элементы в не больше . Таким образом, время работы запущенного алгоритма для каждого — . Когда найдётся первое , то алгоритм успешно завершится. .
Общее время работы алгоритма для всех обработанных значений — . Заметим, что , так как в противном случае , что противоречит тому, что — первый из тех, которые больше . Следовательно, . Получаем время работы . См. также
Источники информации |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||



