Быстрый поиск наибольшей возрастающей подпоследовательности — различия между версиями
|  (→Деление на блоки:  Исправлена логическая ошибка) |  (→Пример:  Подподраздел написан (предыдущий был о Визуализации)) | ||
| Строка 243: | Строка 243: | ||
| |style="background:#FFCC80"|2||style="background:#FF9980"|4||style="background:#FF8080"|5||style="background:#FFE680"|1||style="background:#FFB380"|3 | |style="background:#FFCC80"|2||style="background:#FF9980"|4||style="background:#FF8080"|5||style="background:#FFE680"|1||style="background:#FFB380"|3 | ||
| |} | |} | ||
| − | |||
| |} | |} | ||
| + | <tex>merged</tex> аналогичен сортированному, т.к. предыдущих ключей нет. | ||
| {| class="wikitable" style="center" | {| class="wikitable" style="center" | ||
| ! colspan="5"|Ключи сортированного блока | ! colspan="5"|Ключи сортированного блока | ||
| |-align="center" | |-align="center" | ||
| | 2||4||5||1||3 | | 2||4||5||1||3 | ||
| + | |} | ||
| + | {| class="wikitable" style="center" | ||
| + | ! colspan="5"| <tex>\xi</tex> | ||
| + | |-align="center" | ||
| + | | 4||1||5||2||3 | ||
| |} | |} | ||
| Пропускаем их через <tex>LIS</tex>: | Пропускаем их через <tex>LIS</tex>: | ||
| {| class="wikitable" style="center" | {| class="wikitable" style="center" | ||
| |-align="center"   | |-align="center"   | ||
| − | | style="background:#FFCC00"|  | + | | style="background:#FFCC00"| 4 ||   ||   || style="background: #77A9F4"| 4 | 
| |-align="center"   | |-align="center"   | ||
| − | + | | style="background:#FFCC00"| 1 ||   ||   || style="background: #77A9F4"| 1 | |
| |-align="center"   | |-align="center"   | ||
| − | |  | + | | 1 || style="background:#FFCC00"| 5 ||   || style="background: #77A9F4"| 5 | 
| |-align="center"   | |-align="center"   | ||
| − | | style="background:#FFCC00"|  | + | | 1 || style="background:#FFCC00"| 2 ||   || style="background: #77A9F4"| 2 | 
| |-align="center"   | |-align="center"   | ||
| − | | 1 || style="background:#FFCC00"| 3  | + | | 1 || 2 || style="background:#FFCC00"| 3 || style="background: #77A9F4"| 3 | 
| |} | |} | ||
| ''' Результат работы ''' | ''' Результат работы ''' | ||
| + | |||
| + | <tex>B: \{1, 2, 3\}</tex> | ||
| + | |||
| + | <tex>merged: \{3, 4, 8, 9, 10\}</tex> | ||
| ===== Второй блок ===== | ===== Второй блок ===== | ||
| + | Восстанавливаем элементы <tex>B: \{1, 2, 3\}</tex> из <tex>merged: \{3, 4, 8, 9, 10\}</tex>: <tex>\{3, 4, 8\}</tex> | ||
| {| | {| | ||
| | || | | || | ||
| Строка 281: | Строка 291: | ||
| |-align="center" | |-align="center" | ||
| |style="background:#FFE680"|1||style="background:#FFCC80"|2||style="background:#FF8080"|5||style="background:#FF9980"|4||style="background:#FFB380"|3 | |style="background:#FFE680"|1||style="background:#FFCC80"|2||style="background:#FF8080"|5||style="background:#FF9980"|4||style="background:#FFB380"|3 | ||
| + | |} | ||
| + | |} | ||
| + | |||
| + | {| | ||
| + | | || | ||
| + | {| class="wikitable" style="center" | ||
| + | |-align="center" | ||
| + | | colspan="8"|<tex>merged</tex> | ||
| + | |-align="center" | ||
| + | | 1||2||3||4||5||6||8||12 | ||
| + | |} | ||
| + | | || | ||
| + | {| class="wikitable" style="center" | ||
| + | |-align="center" | ||
| + | | colspan="3"|<tex>ind's\#0</tex> - индексы текущих | ||
| + | |-align="center" | ||
| + | | 3||4||7 | ||
| + | |} | ||
| + | | || | ||
| + | {| class="wikitable" style="center" | ||
| + | |-align="center" | ||
| + | | colspan="5"|<tex>ind's\#1</tex> - индексы новых | ||
| + | |-align="center" | ||
| + | | 1||2||5||6||8 | ||
| + | |} | ||
| + | |} | ||
| + | |||
| + | {| class="wikitable" style="center" | ||
| + | ! colspan="5"|Ключи сортированного блока | ||
| + | |-align="center" | ||
| + | | 1||2||5||4||3 | ||
| + | |} | ||
| + | {| class="wikitable" style="center" | ||
| + | ! colspan="5"| <tex>\xi</tex> | ||
| + | |-align="center" | ||
| + | | 1||2||5||4||3 | ||
| + | |} | ||
| + | Восстанавливаем порядок новых из <tex>ind's\#1</tex> и <tex>\xi</tex>: | ||
| + | {| class="wikitable" style="center" | ||
| + | ! colspan="5"| новые ключи | ||
| + | |-align="center" | ||
| + | | 1||2||8||6||5 | ||
| + | |} | ||
| + | Обновление старых ключей: | ||
| + | {| class="wikitable" style="center" | ||
| + | |-align="center"  | ||
| + | | style="background:#FFCC00"| 3 ||   ||   || style="background: #77A9F4"| 3 | ||
| + | |-align="center"  | ||
| + | | 3 || style="background:#FFCC00"| 4 ||   || style="background: #77A9F4"| 4 | ||
| + | |-align="center"  | ||
| + | | 3 || 4 || style="background:#FFCC00"| 7 || style="background: #77A9F4"| 7 | ||
| + | |} | ||
| + | <tex>LIS</tex> новых: | ||
| + | {| class="wikitable" style="center" | ||
| + | |-align="center"  | ||
| + | | style="background:#FFCC00"| 1 || 4 || 7 ||   || style="background: #77A9F4"| 1 | ||
| + | |-align="center"  | ||
| + | | 1 || style="background:#FFCC00"| 2 || 7 ||   || style="background: #77A9F4"| 2 | ||
| + | |-align="center"  | ||
| + | | 1 || 2 || 7 || style="background:#FFCC00"| 8 || style="background: #77A9F4"| 8 | ||
| + | |-align="center"  | ||
| + | | 1 || 2 || style="background:#FFCC00"| 6 || 8 || style="background: #77A9F4"| 6 | ||
| + | |-align="center"  | ||
| + | | 1 || 2 || style="background:#FFCC00"| 5 || 8 || style="background: #77A9F4"| 5 | ||
| + | |} | ||
| + | ''' Результат работы ''' | ||
| + | |||
| + | <tex>B: \{1, 2, 5, 8\}</tex> | ||
| + | |||
| + | <tex>merged: \{1,2,3,4,5,6,8,12\}</tex> | ||
| + | |||
| + | ===== Третий блок ===== | ||
| + | Восстанавливаем элементы <tex>B: \{1, 2, 5, 8\}</tex> из <tex>merged: \{1,2,3,4,5,6,8,12\}</tex>: <tex>\{1, 2, 5, 12\}</tex> | ||
| + | {| | ||
| + | | || | ||
| + | {| style="center" | ||
| + | ! colspan="5"|Первый блок | ||
| + | |-align="center" | ||
| + | |style="background:#FFB580"|7||style="background:#FF8B80"|11 | ||
| + | |-align="center" | ||
| + | |style="background:#FFC080"|1||style="background:#FF8080"|2 | ||
| + | |} | ||
| + | | || | ||
| + | {| style="center" | ||
| + | ! colspan="5"|Cортированный | ||
| + | |-align="center" | ||
| + | |style="background:#FFB580"|7||style="background:#FF8B80"|11 | ||
| + | |-align="center" | ||
| + | |style="background:#FFC080"|1||style="background:#FF8080"|2 | ||
| + | |} | ||
| + | |} | ||
| + | |||
| + | {| | ||
| + | | || | ||
| + | {| class="wikitable" style="center" | ||
| + | |-align="center" | ||
| + | | colspan="8"|<tex>merged</tex> | ||
| + | |-align="center" | ||
| + | | 1||2||5||7||11||12 | ||
| + | |} | ||
| + | | || | ||
| + | {| class="wikitable" style="center" | ||
| + | |-align="center" | ||
| + | | colspan="4"|<tex>ind's\#0</tex> - индексы текущих | ||
| + | |-align="center" | ||
| + | | 1||2||3||6 | ||
| + | |} | ||
| + | | || | ||
| + | {| class="wikitable" style="center" | ||
| + | |-align="center" | ||
| + | | colspan="2"|<tex>ind's\#1</tex> - индексы новых | ||
| + | |-align="center" | ||
| + | | 4||5 | ||
| + | |} | ||
| + | |} | ||
| + | {| class="wikitable" style="center" | ||
| + | ! colspan="5"|Ключи сортированного блока | ||
| + | |-align="center" | ||
| + | | 1||2 | ||
| + | |} | ||
| + | {| class="wikitable" style="center" | ||
| + | ! colspan="5"| <tex>\xi</tex> | ||
| + | |-align="center" | ||
| + | | 1||2 | ||
| + | |} | ||
| + | Восстанавливаем порядок новых из <tex>ind's\#1</tex> и <tex>\xi</tex>: | ||
| + | {| class="wikitable" style="center" | ||
| + | ! colspan="2"| новые ключи | ||
| + | |-align="center" | ||
| + | | 4||5 | ||
| + | |} | ||
| + | Обновление старых ключей: | ||
| + | {| class="wikitable" style="center" | ||
| + | |-align="center"  | ||
| + | | style="background:#FFCC00"| 1 || 4 || 7 ||   || style="background: #77A9F4"| 1 | ||
| + | |-align="center"  | ||
| + | | 1 || style="background:#FFCC00"| 2 || 7 ||   || style="background: #77A9F4"| 2 | ||
| + | |-align="center"  | ||
| + | | 1 || 2 || style="background:#FFCC00"| 3 ||   || style="background: #77A9F4"| 3 | ||
| + | |-align="center"  | ||
| + | | 1 || 2 || 3 || style="background:#FFCC00"| 6 || style="background: #77A9F4"| 6 | ||
| + | |} | ||
| + | <tex>LIS</tex> новых: | ||
| + | {| class="wikitable" style="center" | ||
| + | |-align="center"  | ||
| + | | 1 || 2 || 3 || style="background:#FFCC00"| 4 ||   || style="background: #77A9F4"| 4 | ||
| + | |-align="center"  | ||
| + | | 1 || 2 || 3 || 4 || style="background:#FFCC00"| 5 || style="background: #77A9F4"| 5 | ||
| + | |} | ||
| + | ''' Результат работы ''' | ||
| + | |||
| + | <tex>B: \{1, 2, 3, 4, 5\}</tex> | ||
| + | |||
| + | <tex>merged: \{1,2,5,7,11,12\}</tex> | ||
| + | |||
| + | ===== Восстановление НВП ===== | ||
| + | {| class="wikitable" style="center" | ||
| + | ! colspan="12"| <tex>predecessor</tex> | ||
| + | |-align="center" | ||
| + | | 1||2||3||4||5||6||7||8||9||10||11||12 | ||
| + | |-align="center" | ||
| + | |  ||1|| ||3||2||2||5||4|| ||3||7||8  | ||
| + | |} | ||
| + | Начинаем восстановление с <tex>merged[5] = 11</tex>: | ||
| + | {| class="wikitable" style="center" | ||
| + | |+ обратный порядок | ||
| + | | 11||7||5||2||1 | ||
| + | |} | ||
| + | {| class="wikitable" style="center" | ||
| + | |+ НВП | ||
| + | | 1||2||5||7||11 | ||
| |} | |} | ||
| ==== Псевдокод ==== | ==== Псевдокод ==== | ||
Версия 19:41, 7 января 2017
| Задача: | 
| Дана перестановка . Требуется найти НВП за , где — длина НВП. | 
Содержание
Алгоритм
Нахождение длины НВП
Основная идея
Пусть — входная перестановка.
Для каждой длины предполагаемой НВП находим наименьший элемент, что может быть последним в возрастающей подпоследовательности длины , запишем их в массив .
Если обрабатываемый элемент больше последнего элемента какой-нибудь возрастающей последовательности, он может ее увеличить.
Будем последовательно обрабатывать элементы :
- Если больше , значит с ним можно сделать максимальную, из уже рассмотренных, возрастающую подпоследовательность. Записываем его в конец
- Иначе заменяет наименьший лучший элемент, из тех, что больше .
Следует отметить, что полученный массив также образует возрастающую последовательность, где мы должны выполнять операции , соответственно целесообразно использовать приоритетную очередь, реализованную через Дерево ван Эмде Боаса. Таким образом получаем амортизированного времени на одну операцию.
Пример
Типы операций:
Последовательность:
| 9 | 3 | 10 | 4 | 8 | 1 | 2 | 12 | 6 | 5 | 7 | 11 | 
Состояние очереди при каждом добавлении:
| 9 | 9 | ||||
| 3 | 3 | ||||
| 3 | 10 | 10 | |||
| 3 | 4 | 4 | |||
| 3 | 4 | 8 | 8 | ||
| 1 | 4 | 8 | 1 | ||
| 1 | 2 | 8 | 2 | ||
| 1 | 2 | 8 | 12 | 12 | |
| 1 | 2 | 6 | 12 | 6 | |
| 1 | 2 | 5 | 12 | 5 | |
| 1 | 2 | 5 | 7 | 7 | |
| 1 | 2 | 5 | 7 | 11 | 11 | 
Псевдокод
int LIS(vector<int> ) PriorityQueue B // рабочая приоритетная очередь int k = 0 // длина НВП int n = .size for i = 1 to n x = [i] // в любом случае добавляем в очередь очередной элемент // устаревшие будем удалять B.insert(x) if B.next(x) // добавленный элемент — не максимальный // удаляем предыдущее значение — заменяем следующий B.delete(B.next(x)) else // добавленный элемент - максимальный // предыдущие значения не трогаем, очередь увеличилась k = k + 1 return k
Расширение алгоритма до нахождения НВП
Основная идея
Будем запоминать пары: для каждого элемента записываем его "предшественника".
Тогда, выбрав какой-нибудь лучший элемент для максимальной длины, мы можем легко восстановить НВП .
Общий вид алгоритма
| 9 | 9 | ||||
| 3 | 3 | ||||
| 3 | 10 | 10 | |||
| 3 | 4 | 4 | |||
| 3 | 4 | 8 | 8 | ||
| 1 | 4 | 8 | 1 | ||
| 1 | 2 | 8 | 2 | ||
| 1 | 2 | 8 | 12 | 12 | |
| 1 | 2 | 6 | 12 | 6 | |
| 1 | 2 | 5 | 12 | 5 | |
| 1 | 2 | 5 | 7 | 7 | |
| 1 | 2 | 5 | 7 | 11 | 11 | 
| predecessor | |||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 
| 1 | 3 | 2 | 2 | 5 | 4 | 3 | 7 | 8 | |||
Псевдокод
vector<int> LIS(vector<int> ) PriorityQueue B int k = 0 int n = .size vector<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 // по цепочке от последнего элемента // восстанавливаем НВП vector<int> result int cur = B.max() result += [cur] while predecessor[cur] result += [predecessor[cur]] cur = predecessor[cur] return result
Оптимизация до
Основная идея
Чтобы Дерево ван Эмде Боаса выполняло операции за , необходимо алфавит обрабатываемых значений уменьшить до .
Предположим, мы знаем такое приближение . Если мы разобьем всю последовательность на блоки из элементов и нам удастся обрабатывать каждый как перестановку из элементов, то мы получим асимптотическое время , а т.к. , то . (Мы будем обрабатывать блоки последовательно, т.е. с предыдущего блока у нас может остаться значений в очереди, которые дополняются значениями очередного блока - получаем верхнее ограничение в обрабатываемых возможных значений.)
Описанный здесь алгоритм подбора и получение асимптотической оценки в других подразделах рассмотрено не будет, т.к. в основном это доказательство, сложного для понимания/реализации ничего нет
Рассмотрим последовательность , где , - некоторое значение, меньшее .
Будем последовательно для элементов этой последовательности запускать алгоритм. Если условие перестает выполняться, прерываем выполнение. Таким образом, время работы для каждого будет . Найдется такой , который окажется больше , и алгоритм успешно завершится.
Общее время работы - . Заметим, что , т.к. в противном случае , что противоречит тому, что - первый из тех, что больше . Следовательно, .
Получаем время работы
Деление на блоки
Основная идея
Разделим исходную перестановку на блоки .
Получим сортированные варианты этих блоков . При лобовой цифровой сортировке мы получим . Дополним каждый элемент номером блока, в котором он находится и смещением в этом блоке. Теперь, рассматривая номер блока как старший разряд, элемент как младший разряд, можно сортировать цифровой сортировкой за линейное время .
Перестановка смещений, образованная в сортированном блоке есть не что иное, как обратная перестановка перестановки, элементы которой соотносятся между собой как элементы исходного блока. Находим обратную перестановку к найденной, назовем ее
Пример
Пусть . Исходно:
| Блок | 1 | 1 | 1 | 1 | 1 | 2 | 2 | 2 | 2 | 2 | 3 | 3 | 
| 9 | 3 | 10 | 4 | 8 | 1 | 2 | 12 | 6 | 5 | 7 | 11 | |
| Смещение | 1 | 2 | 3 | 4 | 5 | 1 | 2 | 3 | 4 | 5 | 1 | 2 | 
После сортировки:
| Блок | 1 | 1 | 1 | 1 | 1 | 2 | 2 | 2 | 2 | 2 | 3 | 3 | 
| 3 | 4 | 8 | 9 | 10 | 1 | 2 | 5 | 6 | 12 | 7 | 11 | |
| Смещение | 2 | 4 | 5 | 1 | 3 | 1 | 2 | 5 | 4 | 3 | 1 | 2 | 
Обратные перестановки ():
| 1 | 2 | 3 | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 4 | 1 | 5 | 2 | 3 | 1 | 2 | 5 | 4 | 3 | 1 | 2 | |
Обработка блока
Основная идея
- Достаем из очереди ключи и конвертируем их в элементы .
- Классический merge только что полученных элементов с элементами нового блока, но с модификацией: генерируются 2 массива - индексы элементов исходных массивов в новом.
- На массив индексов, относящиеся к новому блоку, действует перестановка смещений сортированного варианта этого блока. Таким образом мы добиваемся эквиваленции отношений ключей к отношениям элементов в очереди и элементов в блоке, а ключи находятся в диапазоне .
- Первый массив индексов, который соответствует элементам, ранее находящимся в очереди, вновь кладутся в очередь (обычными -ами). Второй массив обрабатывается описанным выше алгоритмом .
Визуализация
Помеченные зеленым - условные данные. Остальное - условные операции. Например значит взять элементы из массива c индексами из . - здесь обозначает результат операции merge.
Для первого блока содержательным является лишь ветка, начинающаяся с , что не противоречит представленной схеме.
Пример
Первый блок
| 
 | 
 | ||||||||||||||||||||||||||||||||
аналогичен сортированному, т.к. предыдущих ключей нет.
| Ключи сортированного блока | ||||
|---|---|---|---|---|
| 2 | 4 | 5 | 1 | 3 | 
| 4 | 1 | 5 | 2 | 3 | 
Пропускаем их через :
| 4 | 4 | ||
| 1 | 1 | ||
| 1 | 5 | 5 | |
| 1 | 2 | 2 | |
| 1 | 2 | 3 | 3 | 
Результат работы
Второй блок
Восстанавливаем элементы из :
| 
 | 
 | ||||||||||||||||||||||||||||||||
| 
 | 
 | 
 | |||||||||||||||||||||||||||||||||||
| Ключи сортированного блока | ||||
|---|---|---|---|---|
| 1 | 2 | 5 | 4 | 3 | 
| 1 | 2 | 5 | 4 | 3 | 
Восстанавливаем порядок новых из и :
| новые ключи | ||||
|---|---|---|---|---|
| 1 | 2 | 8 | 6 | 5 | 
Обновление старых ключей:
| 3 | 3 | ||
| 3 | 4 | 4 | |
| 3 | 4 | 7 | 7 | 
новых:
| 1 | 4 | 7 | 1 | |
| 1 | 2 | 7 | 2 | |
| 1 | 2 | 7 | 8 | 8 | 
| 1 | 2 | 6 | 8 | 6 | 
| 1 | 2 | 5 | 8 | 5 | 
Результат работы
Третий блок
Восстанавливаем элементы из :
| 
 | 
 | ||||||||||||||||||||
| 
 | 
 | 
 | |||||||||||||||||||||||||||||
| Ключи сортированного блока | ||||
|---|---|---|---|---|
| 1 | 2 | |||
| 1 | 2 | |||
Восстанавливаем порядок новых из и :
| новые ключи | |
|---|---|
| 4 | 5 | 
Обновление старых ключей:
| 1 | 4 | 7 | 1 | |
| 1 | 2 | 7 | 2 | |
| 1 | 2 | 3 | 3 | |
| 1 | 2 | 3 | 6 | 6 | 
новых:
| 1 | 2 | 3 | 4 | 4 | |
| 1 | 2 | 3 | 4 | 5 | 5 | 
Результат работы
Восстановление НВП
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 
| 1 | 3 | 2 | 2 | 5 | 4 | 3 | 7 | 8 | |||
Начинаем восстановление с :
| 11 | 7 | 5 | 2 | 1 | 
| 1 | 2 | 5 | 7 | 11 | 





