Быстрый поиск наибольшей возрастающей подпоследовательности — различия между версиями

Материал из Викиконспекты
Перейти к: навигация, поиск
м (Псевдокод)
м
Строка 18: Строка 18:
  
 
* Если <tex>\pi(i)</tex> больше {{Acronym | <tex>\pi(1), \pi(2),~\dots~,\pi(i-1)</tex> | всех уже полученных значений B }}, значит с ним можно сделать максимальную, из уже рассмотренных, возрастающую подпоследовательность. Записываем его в конец <tex>B</tex>
 
* Если <tex>\pi(i)</tex> больше {{Acronym | <tex>\pi(1), \pi(2),~\dots~,\pi(i-1)</tex> | всех уже полученных значений B }}, значит с ним можно сделать максимальную, из уже рассмотренных, возрастающую подпоследовательность. Записываем его в конец <tex>B</tex>
* Иначе <tex>\pi(i)</tex> становится лучшим элементом для такой длины <tex>l</tex>, что: {{Acronym | <tex>B[l]</tex> | предыдущее значение}}<tex> = \min \{ B[1..j] > \pi(i) \}</tex>  
+
* Иначе <tex>\pi(i)</tex> заменяет наименьший лучший элемент, из тех, что больше <tex>\pi(i)</tex>.
  
 
Следует отметить, что полученный массив также образует возрастающую последовательность, где мы должны выполнять операции <tex>insert, next, delete</tex>, соответственно целесообразно использовать [[ Приоритетные очереди | приоритетную очередь]], реализованную через [[Дерево ван Эмде Боаса]]. Таким образом получаем <tex>O(\operatorname{log}\operatorname{log} n)</tex> амортизированного времени на одну операцию.
 
Следует отметить, что полученный массив также образует возрастающую последовательность, где мы должны выполнять операции <tex>insert, next, delete</tex>, соответственно целесообразно использовать [[ Приоритетные очереди | приоритетную очередь]], реализованную через [[Дерево ван Эмде Боаса]]. Таким образом получаем <tex>O(\operatorname{log}\operatorname{log} n)</tex> амортизированного времени на одну операцию.
Строка 67: Строка 67:
 
==== Псевдокод ====
 
==== Псевдокод ====
 
<code>
 
<code>
     '''function''' LIS(<tex>\pi</tex>[]): int
+
     '''int''' LIS('''vector<int>''' <tex>\pi</tex>)
         B = PriorityQueue() <font color=darkgreen>// рабочая приоритетная очередь</font>
+
         '''PriorityQueue''' B <font color=darkgreen>// рабочая приоритетная очередь</font>
         k = 0 <font color=darkgreen>// длина НВП</font>
+
         '''int''' k = 0       <font color=darkgreen>// длина НВП</font>
         n = <tex>\pi</tex>.size
+
         '''int''' n = <tex>\pi</tex>.size
 
         '''for''' i = 1 to n
 
         '''for''' i = 1 to n
 
             x = <tex>\pi</tex>[i]
 
             x = <tex>\pi</tex>[i]
Строка 76: Строка 76:
 
             <font color=darkgreen>// устаревшие будем удалять</font>
 
             <font color=darkgreen>// устаревшие будем удалять</font>
 
             B.insert(x)
 
             B.insert(x)
             '''if''' B.next(x) '''exists'''
+
             '''if''' <tex>\exists</tex> B.next(x)
 
                 <font color=darkgreen>// добавленный элемент — не максимальный</font>
 
                 <font color=darkgreen>// добавленный элемент — не максимальный</font>
 
                 <font color=darkgreen>// удаляем предыдущее значение — заменяем {{Acronym |следующий|минимальный из бóльших}}</font>
 
                 <font color=darkgreen>// удаляем предыдущее значение — заменяем {{Acronym |следующий|минимальный из бóльших}}</font>
Строка 132: Строка 132:
 
==== Псевдокод ====
 
==== Псевдокод ====
 
<code>
 
<code>
     '''function''' LIS(<tex>\pi</tex>[])[]
+
     '''vector<int>''' LIS('''vector<int>''' <tex>\pi</tex>)
         B = PriorityQueue()
+
         '''PriorityQueue''' B
         k = 0
+
         '''int''' k = 0
         n = <tex>\pi</tex>.size
+
         '''int''' n = <tex>\pi</tex>.size
         <font color=blue>predecessor = [n]</font> <font color=darkgreen>// резервируем <tex>n</tex> позиций</font>
+
         <font color=blue>'''vector<int>''' predecessor(n)</font> <font color=darkgreen>// резервируем <tex>n</tex> позиций</font>
 
         '''for''' i = 1 to n
 
         '''for''' i = 1 to n
 
             x = <tex>\pi</tex>[i]
 
             x = <tex>\pi</tex>[i]
 
             B.insert(x)
 
             B.insert(x)
 
             <font color=blue>predecessor[x] = B.prev(x)</font>
 
             <font color=blue>predecessor[x] = B.prev(x)</font>
             '''if''' B.next(x) '''exists'''
+
             '''if''' <tex>\exists</tex> B.next(x)
 
                 B.delete(B.next(x))
 
                 B.delete(B.next(x))
 
             '''else'''
 
             '''else'''
 
                 k = k + 1
 
                 k = k + 1
         <font color=darkgreen>//по цепочке от последнего элемента  
+
         <font color=darkgreen>// по цепочке от последнего элемента  
         //восстанавливаем НВП</font>
+
         // восстанавливаем НВП</font>
         <font color=blue>result = []
+
         <font color=blue>'''vector<int>''' result
         cur = B.max()
+
         '''int''' cur = B.max()
 
         result += [cur]
 
         result += [cur]
         '''while''' predecessor[cur] '''exists'''
+
         '''while''' <tex>\exists</tex> predecessor[cur]  
 
             result += [predecessor[cur]]
 
             result += [predecessor[cur]]
 
             cur = predecessor[cur]
 
             cur = predecessor[cur]
 
         '''return''' result</font>
 
         '''return''' result</font>
 
</code>
 
</code>
 
+
== Оптимизация до <tex>O(n\operatorname{log}\operatorname{log}k)</tex> ==
== Переименование до <tex>O(n\operatorname{log}\operatorname{log}k)</tex> ==
+
=== Деление на блоки ===

Версия 03:04, 6 января 2017

Эта статья находится в разработке!


Задача:
Дана перестановка [math]\pi[/math] [math]\{1, 2,~\dots,~n\}[/math]. Требуется найти НВП [math]\pi[/math] за [math]O(n\operatorname{log}\operatorname{log}k)[/math], где [math]k[/math] — длина НВП.


Task.jpg

Алгоритм [math]O(n\operatorname{log}\operatorname{log}n)[/math]

Нахождение длины НВП

Основная идея

Пусть [math]\pi(n)[/math] — входная перестановка.

Для каждой длины [math]l = 1, 2, \dots[/math] предполагаемой НВП находим наименьший элемент, что может быть последним в возрастающей подпоследовательности длины [math]l[/math], запишем их в массив [math]B[l][/math].

Если обрабатываемый элемент [math]\pi(i)[/math] больше последнего элемента какой-нибудь возрастающей последовательности, он может ее увеличить.

Будем последовательно обрабатывать элементы [math]\pi(1), \pi(2),~\dots,~\pi(n)[/math]:

  • Если [math]\pi(i)[/math] больше [math]\pi(1), \pi(2),~\dots~,\pi(i-1)[/math] , значит с ним можно сделать максимальную, из уже рассмотренных, возрастающую подпоследовательность. Записываем его в конец [math]B[/math]
  • Иначе [math]\pi(i)[/math] заменяет наименьший лучший элемент, из тех, что больше [math]\pi(i)[/math].

Следует отметить, что полученный массив также образует возрастающую последовательность, где мы должны выполнять операции [math]insert, next, delete[/math], соответственно целесообразно использовать приоритетную очередь, реализованную через Дерево ван Эмде Боаса. Таким образом получаем [math]O(\operatorname{log}\operatorname{log} n)[/math] амортизированного времени на одну операцию.

Пример

Типы операций:

Operation1.jpg

Operation2 1.jpg [math]\longrightarrow[/math] Operation2 2.jpg

Последовательность:

[math]\pi_1[/math] [math]\pi_2[/math] [math]\pi_3[/math] [math]\pi_4[/math] [math]\pi_5[/math] [math]\pi_6[/math] [math]\pi_7[/math] [math]\pi_8[/math] [math]\pi_9[/math] [math]\pi_{10}[/math] [math]\pi_{11}[/math] [math]\pi_{12}[/math]
9 3 10 4 8 1 2 12 6 5 7 11

Состояние очереди при каждом добавлении:

[math]B_1[/math] [math]B_2[/math] [math]B_3[/math] [math]B_4[/math] [math]B_5[/math] [math]~\pi_i~[/math]
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> [math]\pi[/math])
       PriorityQueue B // рабочая приоритетная очередь
       int k = 0       // длина НВП
       int n = [math]\pi[/math].size
       for i = 1 to n
           x = [math]\pi[/math][i]
           // в любом случае добавляем в очередь очередной элемент
           // устаревшие будем удалять
           B.insert(x)
           if [math]\exists[/math] B.next(x)
               // добавленный элемент — не максимальный
               // удаляем предыдущее значение — заменяем следующий
               B.delete(B.next(x))
           else
               // добавленный элемент - максимальный
               // предыдущие значения не трогаем, очередь увеличилась
               k = k + 1           
       return k

Расширение алгоритма до нахождения НВП

Основная идея

Будем запоминать пары: для каждого элемента записываем его "предшественника".

Тогда, выбрав какой-нибудь лучший элемент для максимальной длины, мы можем легко восстановить НВП .

Общий вид алгоритма

[math]B_1[/math] [math]B_2[/math] [math]B_3[/math] [math]B_4[/math] [math]B_5[/math] [math]~\pi_i~[/math]
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> [math]\pi[/math])
       PriorityQueue B
       int k = 0
       int n = [math]\pi[/math].size
       vector<int> predecessor(n) // резервируем [math]n[/math] позиций
       for i = 1 to n
           x = [math]\pi[/math][i]
           B.insert(x)
           predecessor[x] = B.prev(x)
           if [math]\exists[/math] B.next(x)
               B.delete(B.next(x))
           else
               k = k + 1
       // по цепочке от последнего элемента 
       // восстанавливаем НВП
       vector<int> result
       int cur = B.max()
       result += [cur]
       while [math]\exists[/math] predecessor[cur] 
           result += [predecessor[cur]]
           cur = predecessor[cur]
       return result

Оптимизация до [math]O(n\operatorname{log}\operatorname{log}k)[/math]

Деление на блоки