<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="ru">
		<id>http://neerc.ifmo.ru/wiki/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=77.234.203.142&amp;*</id>
		<title>Викиконспекты - Вклад участника [ru]</title>
		<link rel="self" type="application/atom+xml" href="http://neerc.ifmo.ru/wiki/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=77.234.203.142&amp;*"/>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%A1%D0%BB%D1%83%D0%B6%D0%B5%D0%B1%D0%BD%D0%B0%D1%8F:%D0%92%D0%BA%D0%BB%D0%B0%D0%B4/77.234.203.142"/>
		<updated>2026-05-19T18:04:16Z</updated>
		<subtitle>Вклад участника</subtitle>
		<generator>MediaWiki 1.30.0</generator>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D0%B0%D1%80%D0%B0%D0%BB%D0%BB%D0%B5%D0%BB%D1%8C%D0%BD%D1%8B%D0%B9_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%BD%D0%B0%D1%85%D0%BE%D0%B6%D0%B4%D0%B5%D0%BD%D0%B8%D1%8F_%D0%B2%D1%8B%D0%BF%D1%83%D0%BA%D0%BB%D0%BE%D0%B9_%D0%BE%D0%B1%D0%BE%D0%BB%D0%BE%D1%87%D0%BA%D0%B8&amp;diff=81271</id>
		<title>Параллельный алгоритм нахождения выпуклой оболочки</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D0%B0%D1%80%D0%B0%D0%BB%D0%BB%D0%B5%D0%BB%D1%8C%D0%BD%D1%8B%D0%B9_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%BD%D0%B0%D1%85%D0%BE%D0%B6%D0%B4%D0%B5%D0%BD%D0%B8%D1%8F_%D0%B2%D1%8B%D0%BF%D1%83%D0%BA%D0%BB%D0%BE%D0%B9_%D0%BE%D0%B1%D0%BE%D0%BB%D0%BE%D1%87%D0%BA%D0%B8&amp;diff=81271"/>
				<updated>2021-11-29T13:03:47Z</updated>
		
		<summary type="html">&lt;p&gt;77.234.203.142: /* Параллельный алгоритм */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Задача&lt;br /&gt;
|definition = Пусть нам даны точки на плоскости. Нужно найти выпуклую оболочку на этих точках.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{Определение|definition='''Выпуклая оболочка''' {{---}} минимальная последовательность точек такая, что последовательное соединение этих точек дает выпуклый многоугольник, и в этом многоугольнике содержатся все точки.}}&lt;br /&gt;
&lt;br /&gt;
==Последовательный алгоритм==&lt;br /&gt;
&lt;br /&gt;
[[Файл:Parallel-convexhull-not-parallel-1.png|300px|thumb|right|Как мы будем двигаться]]&lt;br /&gt;
&lt;br /&gt;
Давайте первым делом найдем самую левую A (при нескольких таких выбрать нижнюю) и самую правую B (при нескольких таких выбрать самую верхнюю) точку на плоскости.&lt;br /&gt;
&lt;br /&gt;
Мы определенно знаем, что через них будет идти выпуклая оболочка, так как если она не будет идти через них, то это значит, что есть какой то отрезок между точками, который ниже и правее A, но это не так. Аналогично для правой верхней точки. Теперь будем строить от A до B верхнюю выпуклую оболочку для всех точек выше AB, и от B до A нижнюю выпуклую оболочку для всех точек ниже AB.&lt;br /&gt;
&lt;br /&gt;
Теперь рассмотрим верхнюю половину. Нижняя будет выполняться аналогично. Берем середину из самое левой точки и правой точки по координате x и делим наше множество точек на две части каким то образом. Теперь нам нужно сделать объединение двух выпуклых оболочек за &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;, для того, что бы итоговая асимптотика была &amp;lt;tex&amp;gt;O(n\log(n))&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Прежде чем решать эту задачу, давайте решим такую задачу:&lt;br /&gt;
&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Нам дан выпуклый многоугольник, который был разрезан отрезком X, и взята верхняя часть. Так же дана точка, находящаяся выше X. Нужно провести касательную из точки к многоугольнику.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
[[Файл:Parallel-convex-hull-tangent.png|300px|thumb|right|Касательные]]&lt;br /&gt;
&lt;br /&gt;
Мысленно представим соединение всех точек многоугольника с данной нам точкой. Заметим, что если мы воспользуемся [[Предикат_&amp;quot;левый_поворот&amp;quot;|предикатом &amp;quot;левый поворот&amp;quot;]], то сможем понять, находится ли точка, являющаяся касательной правее или левее, просто взяв предикат по точке куда ведем пересечение, данной точкой и следующей за точкой пересечения точкой. Проще говоря, нам нужно понять, &amp;quot;выше&amp;quot; ли находится прямая со следующей точкой или нет. Если оказалось, что она выше, то касательная находится выше. Если ниже, то касательная находится ниже.&lt;br /&gt;
&lt;br /&gt;
Таким образом, мы можем за &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt; понимать для точки, дальше ли ее точка касательной или она была раньше. То есть у нас есть унимодальная функция, и нам нужно найти переход от &amp;lt;tex&amp;gt;0&amp;lt;/tex&amp;gt; до &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt;. Похоже на задачу бинарного поиска. Давайте опишем шаги:&lt;br /&gt;
&lt;br /&gt;
 '''int''' l = 0, r = n - 2&lt;br /&gt;
 '''while''' 1 &amp;lt; r - l:&lt;br /&gt;
     m = (l + r) / 2&lt;br /&gt;
     '''if''' left_turn(a[m], K, a[m + 1]) &amp;gt; 0:&lt;br /&gt;
         l = m&lt;br /&gt;
     else:&lt;br /&gt;
         r = m&lt;br /&gt;
&lt;br /&gt;
Нахождение касательной из точки на многоугольник работает за &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Нам дано два выпуклых многоугольника A и B, которые были разрезаны отрезком X, и взята верхняя часть. Нужно провести такую касательную через 2 точки многоугольника, что все точки находятся либо на касательной либо ниже.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
[[Файл:Parallel-convex-hull-tangent-tangent.png|300px|thumb|right|Касательные из каждой точки]]&lt;br /&gt;
&lt;br /&gt;
Давайте мысленно проведем касательные из каждой точки A на многоугольник B. Только одна из них является истинной. Здесь будет работать примерно такая же стратегия, что и в прошлом. Если мы понимаем с помощью предиката левого поворота, что касательная выше, то правильная точка находится до нашей, иначе дальше нашей. Снова сделаем бинпоиск. Только на сей раз проверка на то, что точка находится левее/правее нужной будет требовать от нас &amp;lt;tex&amp;gt;O(log(n))&amp;lt;/tex&amp;gt;. Таким образом, алгоритм нахождения общей касательной будет работать за &amp;lt;tex&amp;gt;O(log^2(n))&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Теперь мы можем соединять две выпуклые оболочки в одну просто проводя касательную между ними и выпиливая у первого многоугольника все, что дальше точки касания, а у второго многоугольника все, что находится до точки касания за &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;. Задача решена.&lt;br /&gt;
&lt;br /&gt;
==Параллельный алгоритм==&lt;br /&gt;
&lt;br /&gt;
[[Файл:Parallel-convex-hull-sqrt.png|300px|thumb|right|Касательные]]&lt;br /&gt;
&lt;br /&gt;
Теперь нам нужно это как то ускорить. Давайте посмотрим, что же мы можем делать параллельно, и при этом не ухудшим асимптотику. Конечно же, у нас был поиск касательной за &amp;lt;tex&amp;gt;O(log^2(n))&amp;lt;/tex&amp;gt;, а можно сделать его за &amp;lt;tex&amp;gt;work = O(n)&amp;lt;/tex&amp;gt;, при этом уменьшив span&lt;br /&gt;
&lt;br /&gt;
Посмотрим на каждую фазу бинпоиска. Заметим, что мы можем просто не брать бинпоиск, а разделить массив рассмотрения на &amp;lt;tex&amp;gt;\sqrt{n}&amp;lt;/tex&amp;gt; частей.&lt;br /&gt;
&lt;br /&gt;
Для каждого элемента посмотрим его результат (поймем, выше ли реальная касательная), и, если оказалось, что у нас и у точки, которая находится на &amp;lt;tex&amp;gt;\sqrt{n}&amp;lt;/tex&amp;gt; различные результаты, то мы поймем, что правильный результат находится между нами. То есть мы возьмем все точки, которые находятся между нами, если у нас с соседом разные результаты и снова у каждой точки посмотрим, где находится верный результат. В этот раз мы уже точно узнаем результат, ведь теперь недостающих точек нет. То есть алгоритм будет такой:&lt;br /&gt;
&lt;br /&gt;
1) Берем &amp;lt;tex&amp;gt;\sqrt{n}&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;2\sqrt{n}&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;3\sqrt{n}&amp;lt;/tex&amp;gt;, ..., &amp;lt;tex&amp;gt;\sqrt{n}\sqrt{n}&amp;lt;/tex&amp;gt; и параллельно проверяем, подходят ли они под условие. Каждая из них возвращает &amp;lt;tex&amp;gt;0&amp;lt;/tex&amp;gt; или &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt; - левее или правее они результата. Каждый результат считает за себя &amp;lt;tex&amp;gt;k\sqrt{n}&amp;lt;/tex&amp;gt; и за соседа &amp;lt;tex&amp;gt;(k + 1)\sqrt{n}&amp;lt;/tex&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
2) Если оказалось, что результаты разные, то мы берем &amp;lt;tex&amp;gt;k\sqrt{n}&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;k\sqrt{n} + 1&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;k\sqrt{n} + 2&amp;lt;/tex&amp;gt;, ..., &amp;lt;tex&amp;gt;(k + 1)\sqrt{n}&amp;lt;/tex&amp;gt;, и смотрим параллельно подходят ли они под условия. Каждое их них возвращает 0 или 1 - левее или правее находится результат. В точке, где меняет 0 и 1 и есть верный ответ.&lt;br /&gt;
&lt;br /&gt;
Таким образом, у нас выходит примерно такой алгоритм - &lt;br /&gt;
&lt;br /&gt;
1) Создаем функцию, которая находит касательную для точки из первого многоугольника на второй многоугольник.&lt;br /&gt;
&lt;br /&gt;
 fun getFromPoint(K):&lt;br /&gt;
     '''int''' startAfter&lt;br /&gt;
     '''pfor''' i = 0...sqrt(n):&lt;br /&gt;
         '''int''' startCurrent = sqrt(n) * i&lt;br /&gt;
         '''int''' startNext = sqrt(n) * i + 1&lt;br /&gt;
         '''int''' finishCurrent = sqrt(n) * i&lt;br /&gt;
         '''int''' finishNext = sqrt(n) * i + 1&lt;br /&gt;
         '''boolean''' isStartBeforeAnswer = left_turn(startCurrent, K, startNext) &amp;gt; 0&lt;br /&gt;
         '''boolean''' isFinishBeforeAnswer = left_turn(finishCurrent, K, finishNext) &amp;gt; 0&lt;br /&gt;
         if (isStartBeforeAnswer != isFinishBeforeAnswer) startAfter = startCurrent&lt;br /&gt;
     '''pfor''' i = 0..sqrt(n):&lt;br /&gt;
         '''int''' start = i + startAfter&lt;br /&gt;
         '''int''' middle = i + startAfter + 1&lt;br /&gt;
         '''int''' finish = i + startAfter + 2&lt;br /&gt;
         if (left_turn(start, K, middle) != left_turn(middle, K, finish)) return middle&lt;br /&gt;
&lt;br /&gt;
Его &amp;lt;tex&amp;gt;work = \sqrt{n}&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;span = \log{n}&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
2) Создадим функцию, которая находит касательную $AB$ между двумя многоугольниками (она будет сильно похожа на предыдущую функцию)&lt;br /&gt;
&lt;br /&gt;
{{Теорема&lt;br /&gt;
|about=&lt;br /&gt;
|statement=Если предыдущая точка $prev$ и следующая точка $next$ лежат по одну сторону (в нашем случае ниже) от касательной на второй многоугольник, то эта точка $x$ является точкой искомой касательной. Иначе - если $prev$ находится выше касательной, то мы дальше $A$, иначе - до $A$.&lt;br /&gt;
|proof=&lt;br /&gt;
&lt;br /&gt;
Многоугольник выпуклый, значит, что если одна точка лежит на прямой, а две соседние по одну сторону от нее, значит, что эта прямая является касательной.&lt;br /&gt;
&lt;br /&gt;
Если $prev$ находится выше касательной, то это значит, что его касательная находится выше, значит, что AB тоже находится выше, а значит, что A находится раньше. Аналогично для обратного случая.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Здесь у нас совершенно тот же код, просто мы вместо предиката левый поворот смотрим, где будет находиться предыдущая точка и следующая.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;work = \sqrt{n}&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;span = \log{n}&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Итоговое время работы: &amp;lt;tex&amp;gt;work = n&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;span = \log{n}&amp;lt;/tex&amp;gt;&lt;/div&gt;</summary>
		<author><name>77.234.203.142</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D0%B0%D1%80%D0%B0%D0%BB%D0%BB%D0%B5%D0%BB%D1%8C%D0%BD%D1%8B%D0%B9_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%BD%D0%B0%D1%85%D0%BE%D0%B6%D0%B4%D0%B5%D0%BD%D0%B8%D1%8F_%D0%B2%D1%8B%D0%BF%D1%83%D0%BA%D0%BB%D0%BE%D0%B9_%D0%BE%D0%B1%D0%BE%D0%BB%D0%BE%D1%87%D0%BA%D0%B8&amp;diff=81270</id>
		<title>Параллельный алгоритм нахождения выпуклой оболочки</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D0%B0%D1%80%D0%B0%D0%BB%D0%BB%D0%B5%D0%BB%D1%8C%D0%BD%D1%8B%D0%B9_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%BD%D0%B0%D1%85%D0%BE%D0%B6%D0%B4%D0%B5%D0%BD%D0%B8%D1%8F_%D0%B2%D1%8B%D0%BF%D1%83%D0%BA%D0%BB%D0%BE%D0%B9_%D0%BE%D0%B1%D0%BE%D0%BB%D0%BE%D1%87%D0%BA%D0%B8&amp;diff=81270"/>
				<updated>2021-11-29T13:01:35Z</updated>
		
		<summary type="html">&lt;p&gt;77.234.203.142: /* Параллельный алгоритм */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Задача&lt;br /&gt;
|definition = Пусть нам даны точки на плоскости. Нужно найти выпуклую оболочку на этих точках.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{Определение|definition='''Выпуклая оболочка''' {{---}} минимальная последовательность точек такая, что последовательное соединение этих точек дает выпуклый многоугольник, и в этом многоугольнике содержатся все точки.}}&lt;br /&gt;
&lt;br /&gt;
==Последовательный алгоритм==&lt;br /&gt;
&lt;br /&gt;
[[Файл:Parallel-convexhull-not-parallel-1.png|300px|thumb|right|Как мы будем двигаться]]&lt;br /&gt;
&lt;br /&gt;
Давайте первым делом найдем самую левую A (при нескольких таких выбрать нижнюю) и самую правую B (при нескольких таких выбрать самую верхнюю) точку на плоскости.&lt;br /&gt;
&lt;br /&gt;
Мы определенно знаем, что через них будет идти выпуклая оболочка, так как если она не будет идти через них, то это значит, что есть какой то отрезок между точками, который ниже и правее A, но это не так. Аналогично для правой верхней точки. Теперь будем строить от A до B верхнюю выпуклую оболочку для всех точек выше AB, и от B до A нижнюю выпуклую оболочку для всех точек ниже AB.&lt;br /&gt;
&lt;br /&gt;
Теперь рассмотрим верхнюю половину. Нижняя будет выполняться аналогично. Берем середину из самое левой точки и правой точки по координате x и делим наше множество точек на две части каким то образом. Теперь нам нужно сделать объединение двух выпуклых оболочек за &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;, для того, что бы итоговая асимптотика была &amp;lt;tex&amp;gt;O(n\log(n))&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Прежде чем решать эту задачу, давайте решим такую задачу:&lt;br /&gt;
&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Нам дан выпуклый многоугольник, который был разрезан отрезком X, и взята верхняя часть. Так же дана точка, находящаяся выше X. Нужно провести касательную из точки к многоугольнику.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
[[Файл:Parallel-convex-hull-tangent.png|300px|thumb|right|Касательные]]&lt;br /&gt;
&lt;br /&gt;
Мысленно представим соединение всех точек многоугольника с данной нам точкой. Заметим, что если мы воспользуемся [[Предикат_&amp;quot;левый_поворот&amp;quot;|предикатом &amp;quot;левый поворот&amp;quot;]], то сможем понять, находится ли точка, являющаяся касательной правее или левее, просто взяв предикат по точке куда ведем пересечение, данной точкой и следующей за точкой пересечения точкой. Проще говоря, нам нужно понять, &amp;quot;выше&amp;quot; ли находится прямая со следующей точкой или нет. Если оказалось, что она выше, то касательная находится выше. Если ниже, то касательная находится ниже.&lt;br /&gt;
&lt;br /&gt;
Таким образом, мы можем за &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt; понимать для точки, дальше ли ее точка касательной или она была раньше. То есть у нас есть унимодальная функция, и нам нужно найти переход от &amp;lt;tex&amp;gt;0&amp;lt;/tex&amp;gt; до &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt;. Похоже на задачу бинарного поиска. Давайте опишем шаги:&lt;br /&gt;
&lt;br /&gt;
 '''int''' l = 0, r = n - 2&lt;br /&gt;
 '''while''' 1 &amp;lt; r - l:&lt;br /&gt;
     m = (l + r) / 2&lt;br /&gt;
     '''if''' left_turn(a[m], K, a[m + 1]) &amp;gt; 0:&lt;br /&gt;
         l = m&lt;br /&gt;
     else:&lt;br /&gt;
         r = m&lt;br /&gt;
&lt;br /&gt;
Нахождение касательной из точки на многоугольник работает за &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Нам дано два выпуклых многоугольника A и B, которые были разрезаны отрезком X, и взята верхняя часть. Нужно провести такую касательную через 2 точки многоугольника, что все точки находятся либо на касательной либо ниже.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
[[Файл:Parallel-convex-hull-tangent-tangent.png|300px|thumb|right|Касательные из каждой точки]]&lt;br /&gt;
&lt;br /&gt;
Давайте мысленно проведем касательные из каждой точки A на многоугольник B. Только одна из них является истинной. Здесь будет работать примерно такая же стратегия, что и в прошлом. Если мы понимаем с помощью предиката левого поворота, что касательная выше, то правильная точка находится до нашей, иначе дальше нашей. Снова сделаем бинпоиск. Только на сей раз проверка на то, что точка находится левее/правее нужной будет требовать от нас &amp;lt;tex&amp;gt;O(log(n))&amp;lt;/tex&amp;gt;. Таким образом, алгоритм нахождения общей касательной будет работать за &amp;lt;tex&amp;gt;O(log^2(n))&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Теперь мы можем соединять две выпуклые оболочки в одну просто проводя касательную между ними и выпиливая у первого многоугольника все, что дальше точки касания, а у второго многоугольника все, что находится до точки касания за &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;. Задача решена.&lt;br /&gt;
&lt;br /&gt;
==Параллельный алгоритм==&lt;br /&gt;
&lt;br /&gt;
[[Файл:Parallel-convex-hull-sqrt.png|300px|thumb|right|Касательные]]&lt;br /&gt;
&lt;br /&gt;
Теперь нам нужно это как то ускорить. Давайте посмотрим, что же мы можем делать параллельно, и при этом не ухудшим асимптотику. Конечно же, у нас был поиск касательной за &amp;lt;tex&amp;gt;O(log^2(n))&amp;lt;/tex&amp;gt;, а можно сделать его за &amp;lt;tex&amp;gt;work = O(n)&amp;lt;/tex&amp;gt;, при этом уменьшив span&lt;br /&gt;
&lt;br /&gt;
Посмотрим на каждую фазу бинпоиска. Заметим, что мы можем просто не брать бинпоиск, а разделить массив рассмотрения на &amp;lt;tex&amp;gt;\sqrt{n}&amp;lt;/tex&amp;gt; частей.&lt;br /&gt;
&lt;br /&gt;
Для каждого элемента посмотрим его результат (поймем, выше ли реальная касательная), и, если оказалось, что у нас и у точки, которая находится на &amp;lt;tex&amp;gt;\sqrt{n}&amp;lt;/tex&amp;gt; различные результаты, то мы поймем, что правильный результат находится между нами. То есть мы возьмем все точки, которые находятся между нами, если у нас с соседом разные результаты и снова у каждой точки посмотрим, где находится верный результат. В этот раз мы уже точно узнаем результат, ведь теперь недостающих точек нет. То есть алгоритм будет такой:&lt;br /&gt;
&lt;br /&gt;
1) Берем &amp;lt;tex&amp;gt;\sqrt{n}&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;2\sqrt{n}&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;3\sqrt{n}&amp;lt;/tex&amp;gt;, ..., &amp;lt;tex&amp;gt;\sqrt{n}\sqrt{n}&amp;lt;/tex&amp;gt; и параллельно проверяем, подходят ли они под условие. Каждая из них возвращает &amp;lt;tex&amp;gt;0&amp;lt;/tex&amp;gt; или &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt; - левее или правее они результата. Каждый результат считает за себя &amp;lt;tex&amp;gt;k\sqrt{n}&amp;lt;/tex&amp;gt; и за соседа &amp;lt;tex&amp;gt;(k + 1)\sqrt{n}&amp;lt;/tex&amp;gt;. &lt;br /&gt;
2) Если оказалось, что результаты разные, то мы берем &amp;lt;tex&amp;gt;k\sqrt{n}&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;k\sqrt{n} + 1&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;k\sqrt{n} + 2&amp;lt;/tex&amp;gt;, ..., &amp;lt;tex&amp;gt;(k + 1)\sqrt{n}&amp;lt;/tex&amp;gt;, и смотрим параллельно подходят ли они под условия. Каждое их них возвращает 0 или 1 - левее или правее находится результат. В точке, где меняет 0 и 1 и есть верный ответ.&lt;br /&gt;
&lt;br /&gt;
Таким образом, у нас выходит примерно такой алгоритм - &lt;br /&gt;
&lt;br /&gt;
1) Создаем функцию, которая находит касательную для точки из первого многоугольника на второй многоугольник.&lt;br /&gt;
&lt;br /&gt;
 fun getFromPoint(K):&lt;br /&gt;
     '''int''' startAfter&lt;br /&gt;
     '''pfor''' i = 0...sqrt(n):&lt;br /&gt;
         '''int''' startCurrent = sqrt(n) * i&lt;br /&gt;
         '''int''' startNext = sqrt(n) * i + 1&lt;br /&gt;
         '''int''' finishCurrent = sqrt(n) * i&lt;br /&gt;
         '''int''' finishNext = sqrt(n) * i + 1&lt;br /&gt;
         '''boolean''' isStartBeforeAnswer = left_turn(startCurrent, K, startNext) &amp;gt; 0&lt;br /&gt;
         '''boolean''' isFinishBeforeAnswer = left_turn(finishCurrent, K, finishNext) &amp;gt; 0&lt;br /&gt;
         if (isStartBeforeAnswer != isFinishBeforeAnswer) startAfter = startCurrent&lt;br /&gt;
     '''pfor''' i = 0..sqrt(n):&lt;br /&gt;
         '''int''' start = i + startAfter&lt;br /&gt;
         '''int''' middle = i + startAfter + 1&lt;br /&gt;
         '''int''' finish = i + startAfter + 2&lt;br /&gt;
         if (left_turn(start, K, middle) != left_turn(middle, K, finish)) return middle&lt;br /&gt;
&lt;br /&gt;
Его &amp;lt;tex&amp;gt;work = \sqrt{n}&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;span = \log{n}&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
2) Создадим функцию, которая находит касательную $AB$ между двумя многоугольниками (она будет сильно похожа на предыдущую функцию)&lt;br /&gt;
&lt;br /&gt;
{{Теорема&lt;br /&gt;
|about=&lt;br /&gt;
|statement=Если предыдущая точка $prev$ и следующая точка $next$ лежат по одну сторону (в нашем случае ниже) от касательной на второй многоугольник, то эта точка $x$ является точкой искомой касательной. Иначе - если $prev$ находится выше касательной, то мы дальше $A$, иначе - до $A$.&lt;br /&gt;
|proof=&lt;br /&gt;
&lt;br /&gt;
Многоугольник выпуклый, значит, что если одна точка лежит на прямой, а две соседние по одну сторону от нее, значит, что эта прямая является касательной.&lt;br /&gt;
&lt;br /&gt;
Если $prev$ находится выше касательной, то это значит, что его касательная находится выше, значит, что AB тоже находится выше, а значит, что A находится раньше. Аналогично для обратного случая.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Здесь у нас совершенно тот же код, просто мы вместо предиката левый поворот смотрим, где будет находиться предыдущая точка и следующая.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;work = \sqrt{n}&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;span = \log{n}&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Итоговое время работы: &amp;lt;tex&amp;gt;work = n&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;span = \log{n}&amp;lt;/tex&amp;gt;&lt;/div&gt;</summary>
		<author><name>77.234.203.142</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D0%B0%D1%80%D0%B0%D0%BB%D0%BB%D0%B5%D0%BB%D1%8C%D0%BD%D1%8B%D0%B9_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%BD%D0%B0%D1%85%D0%BE%D0%B6%D0%B4%D0%B5%D0%BD%D0%B8%D1%8F_%D0%B2%D1%8B%D0%BF%D1%83%D0%BA%D0%BB%D0%BE%D0%B9_%D0%BE%D0%B1%D0%BE%D0%BB%D0%BE%D1%87%D0%BA%D0%B8&amp;diff=81269</id>
		<title>Параллельный алгоритм нахождения выпуклой оболочки</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D0%B0%D1%80%D0%B0%D0%BB%D0%BB%D0%B5%D0%BB%D1%8C%D0%BD%D1%8B%D0%B9_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%BD%D0%B0%D1%85%D0%BE%D0%B6%D0%B4%D0%B5%D0%BD%D0%B8%D1%8F_%D0%B2%D1%8B%D0%BF%D1%83%D0%BA%D0%BB%D0%BE%D0%B9_%D0%BE%D0%B1%D0%BE%D0%BB%D0%BE%D1%87%D0%BA%D0%B8&amp;diff=81269"/>
				<updated>2021-11-29T12:32:01Z</updated>
		
		<summary type="html">&lt;p&gt;77.234.203.142: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Задача&lt;br /&gt;
|definition = Пусть нам даны точки на плоскости. Нужно найти выпуклую оболочку на этих точках.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{Определение|definition='''Выпуклая оболочка''' {{---}} минимальная последовательность точек такая, что последовательное соединение этих точек дает выпуклый многоугольник, и в этом многоугольнике содержатся все точки.}}&lt;br /&gt;
&lt;br /&gt;
==Последовательный алгоритм==&lt;br /&gt;
&lt;br /&gt;
[[Файл:Parallel-convexhull-not-parallel-1.png|300px|thumb|right|Как мы будем двигаться]]&lt;br /&gt;
&lt;br /&gt;
Давайте первым делом найдем самую левую A (при нескольких таких выбрать нижнюю) и самую правую B (при нескольких таких выбрать самую верхнюю) точку на плоскости.&lt;br /&gt;
&lt;br /&gt;
Мы определенно знаем, что через них будет идти выпуклая оболочка, так как если она не будет идти через них, то это значит, что есть какой то отрезок между точками, который ниже и правее A, но это не так. Аналогично для правой верхней точки. Теперь будем строить от A до B верхнюю выпуклую оболочку для всех точек выше AB, и от B до A нижнюю выпуклую оболочку для всех точек ниже AB.&lt;br /&gt;
&lt;br /&gt;
Теперь рассмотрим верхнюю половину. Нижняя будет выполняться аналогично. Берем середину из самое левой точки и правой точки по координате x и делим наше множество точек на две части каким то образом. Теперь нам нужно сделать объединение двух выпуклых оболочек за &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;, для того, что бы итоговая асимптотика была &amp;lt;tex&amp;gt;O(n\log(n))&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Прежде чем решать эту задачу, давайте решим такую задачу:&lt;br /&gt;
&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Нам дан выпуклый многоугольник, который был разрезан отрезком X, и взята верхняя часть. Так же дана точка, находящаяся выше X. Нужно провести касательную из точки к многоугольнику.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
[[Файл:Parallel-convex-hull-tangent.png|300px|thumb|right|Касательные]]&lt;br /&gt;
&lt;br /&gt;
Мысленно представим соединение всех точек многоугольника с данной нам точкой. Заметим, что если мы воспользуемся [[Предикат_&amp;quot;левый_поворот&amp;quot;|предикатом &amp;quot;левый поворот&amp;quot;]], то сможем понять, находится ли точка, являющаяся касательной правее или левее, просто взяв предикат по точке куда ведем пересечение, данной точкой и следующей за точкой пересечения точкой. Проще говоря, нам нужно понять, &amp;quot;выше&amp;quot; ли находится прямая со следующей точкой или нет. Если оказалось, что она выше, то касательная находится выше. Если ниже, то касательная находится ниже.&lt;br /&gt;
&lt;br /&gt;
Таким образом, мы можем за &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt; понимать для точки, дальше ли ее точка касательной или она была раньше. То есть у нас есть унимодальная функция, и нам нужно найти переход от &amp;lt;tex&amp;gt;0&amp;lt;/tex&amp;gt; до &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt;. Похоже на задачу бинарного поиска. Давайте опишем шаги:&lt;br /&gt;
&lt;br /&gt;
 '''int''' l = 0, r = n - 2&lt;br /&gt;
 '''while''' 1 &amp;lt; r - l:&lt;br /&gt;
     m = (l + r) / 2&lt;br /&gt;
     '''if''' left_turn(a[m], K, a[m + 1]) &amp;gt; 0:&lt;br /&gt;
         l = m&lt;br /&gt;
     else:&lt;br /&gt;
         r = m&lt;br /&gt;
&lt;br /&gt;
Нахождение касательной из точки на многоугольник работает за &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Нам дано два выпуклых многоугольника A и B, которые были разрезаны отрезком X, и взята верхняя часть. Нужно провести такую касательную через 2 точки многоугольника, что все точки находятся либо на касательной либо ниже.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
[[Файл:Parallel-convex-hull-tangent-tangent.png|300px|thumb|right|Касательные из каждой точки]]&lt;br /&gt;
&lt;br /&gt;
Давайте мысленно проведем касательные из каждой точки A на многоугольник B. Только одна из них является истинной. Здесь будет работать примерно такая же стратегия, что и в прошлом. Если мы понимаем с помощью предиката левого поворота, что касательная выше, то правильная точка находится до нашей, иначе дальше нашей. Снова сделаем бинпоиск. Только на сей раз проверка на то, что точка находится левее/правее нужной будет требовать от нас &amp;lt;tex&amp;gt;O(log(n))&amp;lt;/tex&amp;gt;. Таким образом, алгоритм нахождения общей касательной будет работать за &amp;lt;tex&amp;gt;O(log^2(n))&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Теперь мы можем соединять две выпуклые оболочки в одну просто проводя касательную между ними и выпиливая у первого многоугольника все, что дальше точки касания, а у второго многоугольника все, что находится до точки касания за &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;. Задача решена.&lt;br /&gt;
&lt;br /&gt;
==Параллельный алгоритм==&lt;br /&gt;
&lt;br /&gt;
[[Файл:Parallel-convex-hull-sqrt.png|300px|thumb|right|Касательные]]&lt;br /&gt;
&lt;br /&gt;
Теперь нам нужно это как то ускорить. Давайте посмотрим, что же мы можем делать параллельно, и при этом не ухудшим асимптотику. Конечно же, у нас был поиск касательной за &amp;lt;tex&amp;gt;O(log^2(n))&amp;lt;/tex&amp;gt;, а можно сделать его за &amp;lt;tex&amp;gt;work = O(n)&amp;lt;/tex&amp;gt;, при этом уменьшив span&lt;br /&gt;
&lt;br /&gt;
Посмотрим на каждую фазу бинпоиска. Заметим, что мы можем просто не брать бинпоиск, а разделить массив рассмотрения на &amp;lt;tex&amp;gt;\sqrt(n)&amp;lt;/tex&amp;gt; частей.&lt;br /&gt;
&lt;br /&gt;
Для каждого элемента посмотрим его результат (поймем, выше ли реальная касательная), и, если оказалось, что у нас и у точки, которая находится на &amp;lt;tex&amp;gt;\sqrt(n)&amp;lt;/tex&amp;gt; различные результаты, то мы поймем, что правильный результат находится между нами. То есть мы возьмем все точки, которые находятся между нами, если у нас с соседом разные результаты и снова у каждой точки посмотрим, где находится верный результат. В этот раз мы уже точно узнаем результат, ведь теперь недостающих точек нет. То есть алгоритм будет такой:&lt;br /&gt;
&lt;br /&gt;
1) Берем &amp;lt;tex&amp;gt;\sqrt{n}&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;2\sqrt{n}&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;3\sqrt{n}&amp;lt;/tex&amp;gt;, ..., &amp;lt;tex&amp;gt;\sqrt{n}\sqrt{n}&amp;lt;/tex&amp;gt; и параллельно проверяем, подходят ли они под условие. Каждая из них возвращает &amp;lt;tex&amp;gt;0&amp;lt;/tex&amp;gt; или &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt; - левее или правее они результата. Каждый результат считает за себя &amp;lt;tex&amp;gt;k\sqrt{n}&amp;lt;/tex&amp;gt; и за соседа &amp;lt;tex&amp;gt;(k + 1)\sqrt{n}&amp;lt;/tex&amp;gt;. &lt;br /&gt;
2) Если оказалось, что результаты разные, то мы берем &amp;lt;tex&amp;gt;k\sqrt{n}&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;k\sqrt{n} + 1&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;k\sqrt{n} + 2&amp;lt;/tex&amp;gt;, ..., &amp;lt;tex&amp;gt;(k + 1)\sqrt{n}&amp;lt;/tex&amp;gt;, и смотрим параллельно подходят ли они под условия. Каждое их них возвращает 0 или 1 - левее или правее находится результат. В точке, где меняет 0 и 1 и есть верный ответ.&lt;br /&gt;
&lt;br /&gt;
Таким образом, у нас выходит примерно такой алгоритм - &lt;br /&gt;
&lt;br /&gt;
1) Создаем функцию, которая находит касательную для точки из первого многоугольника на второй многоугольник.&lt;br /&gt;
&lt;br /&gt;
 fun get(K):&lt;br /&gt;
     '''int''' startAfter&lt;br /&gt;
     '''pfor''' i = 0...sqrt(n):&lt;br /&gt;
         '''int''' startCurrent = sqrt(n) * i&lt;br /&gt;
         '''int''' startNext = sqrt(n) * i + 1&lt;br /&gt;
         '''int''' finishCurrent = sqrt(n) * i&lt;br /&gt;
         '''int''' finishNext = sqrt(n) * i + 1&lt;br /&gt;
         '''boolean''' isStartBeforeAnswer = left_turn(startCurrent, K, startNext) &amp;gt; 0&lt;br /&gt;
         '''boolean''' isFinishBeforeAnswer = left_turn(finishCurrent, K, finishNext) &amp;gt; 0&lt;br /&gt;
         if (isStartBeforeAnswer != isFinishBeforeAnswer) startAfter = startCurrent&lt;br /&gt;
     '''pfor''' i = 0..sqrt(n):&lt;br /&gt;
         '''int''' start = i + startAfter&lt;br /&gt;
         '''int''' middle = i + startAfter + 1&lt;br /&gt;
         '''int''' finish = i + startAfter + 2&lt;br /&gt;
         if (left_turn(start, K, middle) != left_turn(middle, K, finish)) return middle&lt;br /&gt;
&lt;br /&gt;
мы запускаем pfor по &amp;lt;tex&amp;gt;\sqrt{n}&amp;lt;/tex&amp;gt; точек равных &amp;lt;tex&amp;gt;i \cdot \sqrt{n}&amp;lt;/tex&amp;gt;&lt;/div&gt;</summary>
		<author><name>77.234.203.142</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%AD%D0%BD%D1%82%D1%80%D0%BE%D0%BF%D0%B8%D1%8F_%D1%81%D0%BB%D1%83%D1%87%D0%B0%D0%B9%D0%BD%D0%BE%D0%B3%D0%BE_%D0%B8%D1%81%D1%82%D0%BE%D1%87%D0%BD%D0%B8%D0%BA%D0%B0&amp;diff=75111</id>
		<title>Энтропия случайного источника</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%AD%D0%BD%D1%82%D1%80%D0%BE%D0%BF%D0%B8%D1%8F_%D1%81%D0%BB%D1%83%D1%87%D0%B0%D0%B9%D0%BD%D0%BE%D0%B3%D0%BE_%D0%B8%D1%81%D1%82%D0%BE%D1%87%D0%BD%D0%B8%D0%BA%D0%B0&amp;diff=75111"/>
				<updated>2020-10-31T11:18:50Z</updated>
		
		<summary type="html">&lt;p&gt;77.234.203.142: /* Свойства */ Сделал скобочки побольше&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;__TOC__&lt;br /&gt;
== Определение ==&lt;br /&gt;
&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition = '''Энтропия случайного источника''' (англ. ''Shannon entropy'') {{---}} функция от вероятностей исходов: &amp;lt;tex&amp;gt;H: \bigcup\limits_{i=1}^{\infty} \mathbb{R}^i \rightarrow \mathbb{R} &amp;lt;/tex&amp;gt;, характеризующая количество информации, приходящейся на одно сообщение источника.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Свойства ==&lt;br /&gt;
&lt;br /&gt;
'''Энтропия должна удовлетворять следующим требованиям:'''&lt;br /&gt;
&lt;br /&gt;
* Функция &amp;lt;tex&amp;gt;H(p_1, p_2, \dots, p_n)&amp;lt;/tex&amp;gt; определена и непрерывна для всех таких наборов &amp;lt;tex&amp;gt;p_i\in[0,\;1]&amp;lt;/tex&amp;gt;, что &amp;lt;tex&amp;gt; \sum\limits_{i = 1}^{n} p_i  = 1&amp;lt;/tex&amp;gt; &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;tex dpi =&amp;quot;130&amp;quot;&amp;gt;H \underbrace{ \left( \dfrac{1}{n}, \dfrac{1}{n}, \dots, \dfrac{1}{n} \right)}_\text{n}  &amp;lt; H \underbrace{ \left( \dfrac{1}{n+1}, \dfrac{1}{n+1}, \dots, \dfrac{1}{n+1} \right) }_\text{n+1}&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;tex dpi =&amp;quot;130&amp;quot;&amp;gt; H(p_{1}q_{11}, p_{1}q_{12}, \dots, p_{n}q_{nk_n}) = H(p_1, p_2, \dots, p_n) + \sum\limits_{i=1}^{n} p_iH(q_{i1}, \dots, q_{ik_i})&amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt;\rhd&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Рассмотрим схему &amp;lt;tex&amp;gt;\mathcal{P}_m&amp;lt;/tex&amp;gt; c &amp;lt;tex&amp;gt;m&amp;lt;/tex&amp;gt; исходами и вероятностями &amp;lt;tex&amp;gt;\{p_1, p_2, \dots, p_m\}&amp;lt;/tex&amp;gt; и схему &amp;lt;tex&amp;gt;\mathcal{R}_k&amp;lt;/tex&amp;gt; с &amp;lt;tex&amp;gt;k&amp;lt;/tex&amp;gt; исходами и вероятностями &amp;lt;tex&amp;gt;\{q_1, q_2, \dots, q_k\}&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Образуем комбинированную схему c &amp;lt;tex&amp;gt;m + k - 1&amp;lt;/tex&amp;gt; исходами следующим образом:&lt;br /&gt;
&lt;br /&gt;
Выбирается случайным образом один из исходов схемы &amp;lt;tex&amp;gt;\mathcal{P}_m&amp;lt;/tex&amp;gt;, и если произошел &amp;lt;tex&amp;gt;m&amp;lt;/tex&amp;gt;-й исход, выбирается случайно один из исходов схемы &amp;lt;tex&amp;gt;\mathcal{R}_k&amp;lt;/tex&amp;gt;, а остальные &amp;lt;tex&amp;gt;m - 1&amp;lt;/tex&amp;gt; исходов схемы &amp;lt;tex&amp;gt;\mathcal{P}_m&amp;lt;/tex&amp;gt; считаются окончательными.&lt;br /&gt;
&lt;br /&gt;
В этой комбинированной схеме &amp;lt;tex&amp;gt;\mathcal{PR}&amp;lt;/tex&amp;gt; мы получаем исходы &amp;lt;tex&amp;gt;1, 2, \dots, m - 1, (m, 1), (m, 2), \dots, (m, k)&amp;lt;/tex&amp;gt; с вероятностями &amp;lt;tex&amp;gt;p_1, p_2, \dots, p_{m-1}, p_mq_1, p_mq_2, \dots, p_mq_k&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Легко видеть, что &amp;lt;tex&amp;gt;H(\mathcal{PR}) = H(\mathcal{P}_m) + p_mH(\mathcal{R}_k)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
 &lt;br /&gt;
Потребуем выполнения этого свойства для любой меры неопределенности.&lt;br /&gt;
&amp;lt;tex&amp;gt;\lhd&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Вычисление энтропии==&lt;br /&gt;
&lt;br /&gt;
Для доказательства формулы вычисления энтропии сначала докажем лемму.&lt;br /&gt;
{{Лемма&lt;br /&gt;
|statement = &amp;lt;tex dpi=&amp;quot;140&amp;quot;&amp;gt;g(n) = H(\dfrac{1}{n}, \dfrac{1}{n}, \dots, \dfrac{1}{n}) = -k \log_2 \dfrac{1}{n} = k \log_2n&amp;lt;/tex&amp;gt;&lt;br /&gt;
|proof =&lt;br /&gt;
Будем рассматривать для &amp;lt;tex&amp;gt;k=1&amp;lt;/tex&amp;gt; (бит).&lt;br /&gt;
&lt;br /&gt;
Рассмотрим функцию &amp;lt;tex&amp;gt;g(mn)&amp;lt;/tex&amp;gt;:&lt;br /&gt;
: &amp;lt;tex&amp;gt;g(mn)=g(m)+ \sum\limits_{i=1}^{m} \dfrac{1}{m} g(n) = g(m)+g(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Пусть: &amp;lt;tex&amp;gt;g(2)=1 \quad&amp;lt;/tex&amp;gt;, тогда &amp;lt;tex&amp;gt;g(2^t)=t&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; \quad g(n^t)=t \cdot g(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Рассмотрим такое &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt;, что &amp;lt;tex&amp;gt;2^i \leqslant  n^t &amp;lt; 2^{i+1}&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Можно заметить, что если &amp;lt;tex&amp;gt; i=[ \log_2 n^t ] &amp;lt;/tex&amp;gt;, то неравенство останется верным.&lt;br /&gt;
&lt;br /&gt;
По предыдущим рассуждениям получаем, что:&lt;br /&gt;
:&amp;lt;tex&amp;gt;g(2^i) \leqslant g(n^t) &amp;lt; g(2^{i+1})&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
: &amp;lt;tex&amp;gt; i \leqslant t \cdot g(n) &amp;lt;i+1 \quad \quad &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Делим неравенство на &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt;:&lt;br /&gt;
: &amp;lt;tex dpi=&amp;quot;140&amp;quot;&amp;gt;\dfrac{i}{t} \leqslant g(n) &amp;lt; \dfrac{i+1}{t}&amp;lt;/tex&amp;gt;, то есть &amp;lt;tex dpi=&amp;quot;140&amp;quot;&amp;gt;\dfrac{[ \log_2 n^t ]}{t} \leqslant g(n) &amp;lt; \dfrac{[ \log_2 n^t ]+1}{t}&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Отсюда ясно, что если &amp;lt;tex&amp;gt; t\rightarrow \infty&amp;lt;/tex&amp;gt;, то получаем &amp;lt;tex&amp;gt;g(n) = \log_2n&amp;lt;/tex&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{Теорема&lt;br /&gt;
|statement= &amp;lt;tex dpi=&amp;quot;140&amp;quot;&amp;gt;H(p_1, p_2, \dots, p_n) = -k \sum\limits_{i=1}^{n} p_i\log_2p_i = k \sum\limits_{i=1}^{n} p_i\log_2\dfrac{1}{p_i}&amp;lt;/tex&amp;gt;&lt;br /&gt;
|proof =&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Теперь рассмотрим функцию &amp;lt;tex dpi=&amp;quot;140&amp;quot;&amp;gt;H(\dfrac{a_1}{b_1}, \dfrac{a_2}{b_2}, \dots, \dfrac{a_n}{b_n})&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Приведем дроби внутри функции к одному знаменателю, получаем: &amp;lt;tex dpi=&amp;quot;140&amp;quot;&amp;gt; H(\dfrac{a_1}{b_1}, \dfrac{a_2}{b_2}, \dots, \dfrac{a_n}{b_n}) = H(\dfrac{x_1}{b}, \dfrac{x_2}{b}, \dots, \dfrac{x_n}{b})&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Далее по свойству энтропии и доказанной лемме:&lt;br /&gt;
: &amp;lt;tex dpi=&amp;quot;140&amp;quot;&amp;gt;g(b)= H(\dfrac{x_1}{b}, \dfrac{x_2}{b}, \dots, \dfrac{x_n}{b}) + \sum\limits_{i=1}^{n} \dfrac{x_i}{b} g(x_i)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
: &amp;lt;tex dpi=&amp;quot;140&amp;quot;&amp;gt;H(\dfrac{x_1}{b}, \dfrac{x_2}{b}, \dots, \dfrac{x_n}{b}) = \log_2b - \sum\limits_{i=1}^{n} \dfrac{x_i}{b} \log_2x_i = -\sum\limits_{i=1}^{n} \dfrac{x_i}{b} \log_2 \dfrac{x_i}{b}&amp;lt;/tex&amp;gt;&lt;br /&gt;
При &amp;lt;tex dpi=&amp;quot;140&amp;quot;&amp;gt; p_i = \dfrac{x_i}{b} &amp;lt;/tex&amp;gt; получаем, что &amp;lt;tex dpi=&amp;quot;140&amp;quot;&amp;gt;H(p_1, p_2, \dots, p_n) = -\sum\limits_{i=1}^{n} p_i \log_2p_i = \sum\limits_{i=1}^{n} p_i \log_2 \dfrac{1}{p_i}&amp;lt;/tex&amp;gt; &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Примеры ==&lt;br /&gt;
=== Энтропия честной монеты ===&lt;br /&gt;
Рассмотрим [[Вероятностное пространство, элементарный исход, событие|вероятностное пространство]] {{---}} честная монета. &lt;br /&gt;
Найдем для нее энтропию:&lt;br /&gt;
:&amp;lt;tex dpi=&amp;quot;140&amp;quot;&amp;gt;H(X) = -\sum\limits_{i=1}^{n} p_i \log_2p_i = -\sum\limits_{i=1}^{2} {\dfrac{1}{2} \cdot \log_2 \dfrac{1}{2}} = -\sum\limits_{i=1}^{2} {\dfrac{1}{2} \cdot (-1)} = 1&amp;lt;/tex&amp;gt;&lt;br /&gt;
Это означает что после броска честной монеты мы получим информацию в размере &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt; бит, уменьшив степень неопределенности вдвое.&lt;br /&gt;
&lt;br /&gt;
=== Энтропия нечестной монеты ===&lt;br /&gt;
Найдем энтропию для [[Вероятностное пространство, элементарный исход, событие|вероятностного пространства]] нечестная монета с [[Схема Бернулли| распределением Бернулли]] &amp;lt;tex&amp;gt;\{0,2; 0,8\}&amp;lt;/tex&amp;gt;:&lt;br /&gt;
:&amp;lt;tex dpi=&amp;quot;140&amp;quot;&amp;gt;H(X) = -\sum\limits_{i=1}^{n} p_i \log_2p_i = -0.2\log_2(0.2)-0.8\log_2(0.8) \approx 0.722 &amp;lt; 1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Ограниченность энтропии ==&lt;br /&gt;
{{Теорема&lt;br /&gt;
|statement= &amp;lt;tex&amp;gt;0 \leqslant  H(p_1, p_2, \dots, p_n) \leqslant  \log_2n &amp;lt;/tex&amp;gt;&lt;br /&gt;
|proof =&lt;br /&gt;
1) Докажем первую часть неравенства:&lt;br /&gt;
&lt;br /&gt;
Так как &amp;lt;tex&amp;gt; p_i\in[0,\;1]&amp;lt;/tex&amp;gt;, тогда &amp;lt;tex dpi=&amp;quot;140&amp;quot;&amp;gt;\log_2\dfrac{1}{p_i} \geqslant 0 &amp;lt;/tex&amp;gt;. Таким образом &amp;lt;tex dpi=&amp;quot;140&amp;quot;&amp;gt; H(p_1, p_2, \dots, p_n) = \sum\limits_{i=1}^{n} p_i\log_2 \dfrac{1}{p_i} \geqslant 0 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
2) Докажем вторую часть неравенства:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex dpi=&amp;quot;140&amp;quot;&amp;gt; f(x)=\log_2x &amp;lt;/tex&amp;gt; {{---}} выпуклая вверх функция, &amp;lt;tex&amp;gt; p_1,p_2,\ldots,p_n&amp;gt;0&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; \sum \limits_{i=1}^{n} p_i = 1 &amp;lt;/tex&amp;gt;, тогда для нее выполняется неравенство Йенсена:&lt;br /&gt;
&amp;lt;tex dpi=&amp;quot;140&amp;quot;&amp;gt; \sum\limits_{i=1}^{n} p_i f(\dfrac{1}{p_i}) \leqslant  f(\sum\limits_{i=1}^{n} (p_i \cdot\dfrac{1}{p_i})) &amp;lt;/tex&amp;gt;&lt;br /&gt;
Таким образом получаем, что &amp;lt;tex&amp;gt; H(p_1, p_2, \dots, p_n) \leqslant  \log_2n &amp;lt;/tex&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
Тогда из теоремы и доказанной выше леммы следует, что для n исходов энтропия максимальна, если они все равновероятны.&lt;br /&gt;
== Условная и взаимная энтропия ==&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition = '''Условная энтропия''' (англ. ''conditional entropy'') {{---}} определяет количество остающейся энтропии (то есть, остающейся неопределенности) события &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; после того, как становится известным результат события &amp;lt;tex&amp;gt;B&amp;lt;/tex&amp;gt;. Она называется ''энтропия &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; при условии &amp;lt;tex&amp;gt;B&amp;lt;/tex&amp;gt;'', и обозначается &amp;lt;tex&amp;gt;H(A|B)&amp;lt;/tex&amp;gt;&lt;br /&gt;
}} &lt;br /&gt;
&amp;lt;tex&amp;gt;H(A|B)= - \sum\limits_{i=1}^{m}p(b_i)\sum\limits_{j=1}^{n} p(a_j|b_i)\log_2p(a_j|b_i) &amp;lt;/tex&amp;gt;&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition = '''Взаимная энтропия''' (англ. ''joint entropy'') {{---}} энтропия объединения двух событий &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;B&amp;lt;/tex&amp;gt;.  &lt;br /&gt;
}}&lt;br /&gt;
&amp;lt;tex&amp;gt; H(A \cap B) = -\sum\limits_{i=1}^{m} \sum\limits_{j=1}^{n} p(a_j \cap b_i)\log_2p(a_j \cap b_i) &amp;lt;/tex&amp;gt;&lt;br /&gt;
{{Утверждение&lt;br /&gt;
|statement= &amp;lt;tex&amp;gt; H(A \cap B) = H(A|B)+H(B)=H(B|A)+H(A) &amp;lt;/tex&amp;gt;&lt;br /&gt;
|proof= По формуле условной вероятности &amp;lt;tex dpi=&amp;quot;130&amp;quot;&amp;gt; p(a_j|b_i)=\dfrac{p(a_j \cap b_i)}{p(b_i)} &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex dpi=&amp;quot;140&amp;quot;&amp;gt; H(A|B)=-\sum\limits_{i=1}^{m}p(b_i)\sum\limits_{j=1}^{n} p(a_j|b_i)\log_2p(a_j|b_i) &amp;lt;/tex&amp;gt; &amp;lt;tex dpi=&amp;quot;140&amp;quot;&amp;gt;= - \sum\limits_{i=1}^{m}p(b_i) \sum\limits_{j=1}^{n} \dfrac{p(a_j \cap b_i)}{p(b_i)}\log_2 \dfrac {p(a_j \cap b_i)}{p(b_i)} = -\sum\limits_{i=1}^{m} \sum\limits_{j=1}^{n} p(a_j \cap b_i)\log_2 \dfrac {p(a_j \cap b_i)}{p(b_i)} = &amp;lt;/tex&amp;gt; &lt;br /&gt;
&amp;lt;tex dpi=&amp;quot;140&amp;quot;&amp;gt; = -\sum\limits_{i=1}^{m} \sum\limits_{j=1}^{n} p(a_j \cap b_i)\log_2p(a_j \cap b_i) + \sum\limits_{i=1}^{m} \sum\limits_{j=1}^{n} p(a_j \cap b_i)\log_2p(b_i) &amp;lt;/tex&amp;gt;&amp;lt;tex dpi=&amp;quot;140&amp;quot;&amp;gt;= H(A \cap B) +\sum\limits_{i=1}^{m} \sum\limits_{j=1}^{n} p(a_j \cap b_i)\log_2p(b_i) = &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex dpi=&amp;quot;140&amp;quot;&amp;gt; = H(A \cap B) +\sum\limits_{i=1}^{m} \log_2p(b_i)\sum\limits_{j=1}^{n} p(a_j \cap b_i) = H(A \cap B) +\sum\limits_{i=1}^{m} \log_2p(b_i)p(b_i) = &amp;lt;/tex&amp;gt;&amp;lt;tex dpi=&amp;quot;140&amp;quot;&amp;gt;H(A \cap B) - H(B) &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Таким образом получаем, что: &amp;lt;tex&amp;gt; H(A \cap B)= H(A|B)+H(B) &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Аналогично: &amp;lt;tex&amp;gt;H(B \cap A)= H(B|A)+H(A) &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Из двух полученных равенств следует, что &amp;lt;tex&amp;gt; H(A|B)+H(B)=H(B|A)+H(A) &amp;lt;/tex&amp;gt; &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== См. также ==&lt;br /&gt;
*[[Вероятностное пространство, элементарный исход, событие|Вероятностное пространство, элементарный исход, событие]]&lt;br /&gt;
*[[Условная вероятность|Условная вероятность]]&lt;br /&gt;
&lt;br /&gt;
== Источники информации ==&lt;br /&gt;
* И.В. Романовский &amp;quot;Дискретный анализ&amp;quot;&lt;br /&gt;
* [http://ru.wikipedia.org/wiki/Информационная_энтропия Википедия {{---}} Информационная энтропия]&lt;br /&gt;
* [https://en.wikipedia.org/wiki/Entropy_(information_theory) Wkipedia {{---}} Entropy(information_theory)] &lt;br /&gt;
[[Категория:Дискретная математика и алгоритмы]]&lt;br /&gt;
&lt;br /&gt;
[[Категория: Теория вероятности ]]&lt;/div&gt;</summary>
		<author><name>77.234.203.142</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Gaporf&amp;diff=75041</id>
		<title>Участник:Gaporf</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Gaporf&amp;diff=75041"/>
				<updated>2020-09-20T19:09:42Z</updated>
		
		<summary type="html">&lt;p&gt;77.234.203.142: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Николай Акимов, группа M33381&lt;/div&gt;</summary>
		<author><name>77.234.203.142</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%9A%D0%B0%D1%80%D0%BA%D0%BA%D0%B0%D0%B9%D0%BD%D0%B5%D0%BD%D0%B0-%D0%A1%D0%B0%D0%BD%D0%B4%D0%B5%D1%80%D1%81%D0%B0&amp;diff=73024</id>
		<title>Алгоритм Карккайнена-Сандерса</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%9A%D0%B0%D1%80%D0%BA%D0%BA%D0%B0%D0%B9%D0%BD%D0%B5%D0%BD%D0%B0-%D0%A1%D0%B0%D0%BD%D0%B4%D0%B5%D1%80%D1%81%D0%B0&amp;diff=73024"/>
				<updated>2020-03-21T11:04:36Z</updated>
		
		<summary type="html">&lt;p&gt;77.234.203.142: Лишняя буква c в примере&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Алгоритм Карккайнена-Сандерса''' (Kärkkäinen, Sanders) — алгоритм построения [[суффиксный массив | суффиксного массива]] за линейное время.&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
== Используемые обозначения ==&lt;br /&gt;
* В данном конспекте используется 0-индексация.&lt;br /&gt;
* &amp;lt;tex&amp;gt;S[i..j] &amp;lt;/tex&amp;gt; — подстрока строки &amp;lt;tex&amp;gt; S &amp;lt;/tex&amp;gt; с &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-го по &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;-й символы включительно.&lt;br /&gt;
* Пусть длина строки &amp;lt;tex&amp;gt; S &amp;lt;/tex&amp;gt; равна &amp;lt;tex&amp;gt; n &amp;lt;/tex&amp;gt;. Обозначим &amp;lt;tex&amp;gt; S^*[i..j] &amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt; j \geqslant n &amp;lt;/tex&amp;gt; как строку &amp;lt;tex&amp;gt; S[i..n-1] &amp;lt;/tex&amp;gt;, дополненную защитными символами &amp;lt;tex&amp;gt; \$ &amp;lt;/tex&amp;gt; до длины &amp;lt;tex&amp;gt; n &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Алгоритм «разделяй и властвуй» == &lt;br /&gt;
&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
'''Четным суффиксом''' назовем суффикс, начинающийся в четной позиции. &amp;lt;br&amp;gt;&lt;br /&gt;
'''Нечетным суффиксом''' — суффикс, начинающийся в нечетной позиции.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
# Строим суффиксный массив для нечетных суффиксов, рекурсивно сведя задачу к построению суффиксного массива для строки половинной длины.&lt;br /&gt;
# Строим суффиксный массив для четных суффиксов за линейное время, используя результат первого шага.&lt;br /&gt;
# Сливаем суффиксные массивы за линейное время.&lt;br /&gt;
&lt;br /&gt;
Получили асимптотическое уравнение &amp;lt;tex&amp;gt; T(n) = T\left( \dfrac{n}{2}\right) + O(n) &amp;lt;/tex&amp;gt;, решением которого является &amp;lt;tex&amp;gt; T(n) = O(n) &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Для упрощения алгоритма вначале дополним нашу строку до четной длины (например, добавлением &amp;lt;tex&amp;gt;\$&amp;lt;/tex&amp;gt; в конец). На шаге слияния мы сможем избавиться от него.&lt;br /&gt;
&lt;br /&gt;
=====База рекурсии=====&lt;br /&gt;
Если длина текущей строки &amp;lt;tex&amp;gt; S &amp;lt;/tex&amp;gt; равна двум, надо выполнить обычное сравнение суффиксов.&lt;br /&gt;
&lt;br /&gt;
=====Суффиксный массив для нечетных суффиксов=====&lt;br /&gt;
&lt;br /&gt;
# Отобразим исходную строку &amp;lt;tex&amp;gt; S^* &amp;lt;/tex&amp;gt; длины &amp;lt;tex&amp;gt; n &amp;lt;/tex&amp;gt; в строку &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; длины  &amp;lt;tex&amp;gt; \dfrac{n}{2} &amp;lt;/tex&amp;gt;  следующим образом:&lt;br /&gt;
#* Сделаем список, состоящий из пар символов вида &amp;lt;tex&amp;gt; S^*[i..i + 1] &amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt; i \bmod 2 = 1 &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Отсортируем его цифровой сортировкой за линейное время и получим новый алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Перекодируем строку &amp;lt;tex&amp;gt; S^*[1..n] &amp;lt;/tex&amp;gt; в алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;, получив строку &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; половинной длины.&lt;br /&gt;
# Рекурсивно построим суффиксный массив &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Построим суффиксный массив &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;. Очевидно, &amp;lt;tex&amp;gt; A_{S_o}[i] = 2 A_{S'}[i] + 1 &amp;lt;/tex&amp;gt;, так отношение упорядоченности любых двух строк в старом алфавите &amp;lt;tex&amp;gt; \Sigma &amp;lt;/tex&amp;gt; эквивалентно отношению упорядоченности в новом алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; по его построению.&lt;br /&gt;
&lt;br /&gt;
=====Суффиксный массив для четных суффиксов=====&lt;br /&gt;
На этом шаге мы за линейное время получим суффиксный массив &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt; для четных суффиксов, используя уже построенный &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Заметим, что сортировка множества четных суффиксов &amp;lt;tex&amp;gt; \{ S^*[i..n] \mid i \bmod 2 = 0 \} &amp;lt;/tex&amp;gt; аналогична сортировке множества пар &amp;lt;tex&amp;gt; \{ (S^*[i], S^*[i+1..n]) \mid i \bmod 2 = 0 \} &amp;lt;/tex&amp;gt;. Однако &amp;lt;tex&amp;gt; S^*[i+1..n] &amp;lt;/tex&amp;gt; — нечетный суффикс, и его относительную позицию мы уже узнали на шаге 1.&lt;br /&gt;
&lt;br /&gt;
Таким образом, чтобы отсортировать эти пары за линейное время, сначала сразу выпишем их в порядке возрастания второго элемента пары (то есть в порядке вхождения в массив &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;), а потом отсортируем устойчивой сортировкой подсчетом по первым элементам. После этого легко можно восстановить массив &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt;:&lt;br /&gt;
 M = []&lt;br /&gt;
 '''for''' i = 0 '''to''' n / 2 - 1&lt;br /&gt;
     M.add(Pair(S[&amp;lt;tex&amp;gt; A_{S_o}&amp;lt;/tex&amp;gt;[i] - 1], &amp;lt;tex&amp;gt; A_{S_o}&amp;lt;/tex&amp;gt;[i]))&lt;br /&gt;
 quick_stable_sort(M)&lt;br /&gt;
 &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 '''for''' i = 0 '''to''' n/2 - 1:&lt;br /&gt;
    &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt;.add(M[i].second - 1)&lt;br /&gt;
&lt;br /&gt;
Заметим, что массив &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt; перед сортировкой подсчетом не был явно отсортирован по вторым элементам, и хранил не суффиксы, а их позиции в строке &amp;lt;tex&amp;gt; S &amp;lt;/tex&amp;gt;, но важно, что он был отсортирован по возрастанию соответствующих этим позициям нечетных суффиксов. &lt;br /&gt;
&lt;br /&gt;
Получили, что весь второй шаг требует &amp;lt;tex&amp;gt; O(n) &amp;lt;/tex&amp;gt; времени.&lt;br /&gt;
&lt;br /&gt;
=====Слияние суффиксных массивов=====&lt;br /&gt;
Для суффиксного дерева третий шаг алгоритма опирается на специфические особенности суффиксных деревьев, которые не присущи суффиксным массивам&amp;lt;ref&amp;gt;M. Farach. Optimal suffix tree construction with large alphabets. http://www.cs.rutgers.edu/~farach/pubs/FarFerrMuthu00.pdf &amp;lt;/ref&amp;gt; .  &lt;br /&gt;
В случае [[Суффиксный массив|суффиксного массива]] слияние становится очень сложным, но все же оно было реализовано в алгоритме Ким-Сим-Парк-Парка&amp;lt;ref&amp;gt; D. K. Kim, J. S. Sim, H. Park, and K. Park. Linear-time construction of suffix arrays. http://www.springerlink.com/content/568156021q45r320/&amp;lt;/ref&amp;gt;. Однако простой модификацией алгоритма можно значительно упростить его.&lt;br /&gt;
&lt;br /&gt;
=== Пример ===&lt;br /&gt;
Покажем первые два шага агоритма для строки '''ababbbaa'''.&lt;br /&gt;
&lt;br /&gt;
Во-первых, добавив защитный символ '''$''', получив строку '''ababbbaa$''' (для этого алгоритма он не требуется, но может понадобиться в применениях суффиксного массива). Во-вторых, дополним ее до четной длины, получив '''ababbbaa$$'''.&lt;br /&gt;
&lt;br /&gt;
=====Суффиксный массив для нечетных суффиксов=====&lt;br /&gt;
# В новом алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; будет четыре элемента — '''ba''', '''bb''', '''a$''', '''$$'''. После сортировки они получат номера 2, 3, 1 и 0 соответственно.&lt;br /&gt;
# Переводим строку &amp;lt;tex&amp;gt;S^*[1..n]&amp;lt;/tex&amp;gt; = '''babbbaa$$$''' в новый алфавит. Сжатой строкой &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; будет '''23210'''.&lt;br /&gt;
# После рекурсивного вызова получим, что &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt; = [4, 3, 2, 0, 1], и &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt; = [9, 7, 5, 1, 3].&lt;br /&gt;
&lt;br /&gt;
=====Суффиксный массив для четных суффиксов=====&lt;br /&gt;
# Обойдя массив &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;, получим &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt; = [(&amp;lt;tex&amp;gt;\$&amp;lt;/tex&amp;gt;, 9), (&amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;, 7), (&amp;lt;tex&amp;gt;b&amp;lt;/tex&amp;gt;, 5), (&amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;, 1), (&amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;, 3)].&lt;br /&gt;
# После сортировки подсчетом по первому элементу, получим &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt;= [(&amp;lt;tex&amp;gt;\$&amp;lt;/tex&amp;gt;, 9), (&amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;, 7), (&amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;, 1), (&amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;, 3), (&amp;lt;tex&amp;gt;b&amp;lt;/tex&amp;gt;, 5)].&lt;br /&gt;
# Восстановив массив &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt;, получаем [8, 6, 0, 2, 4], что действительно является суффиксным массивом для четных суффиксов.&lt;br /&gt;
&lt;br /&gt;
=====Слияние суффиксных массивов=====&lt;br /&gt;
Если бы мы умели сливать &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt; за линейное время, получили бы:&lt;br /&gt;
&lt;br /&gt;
{| style=&amp;quot;background-color:#CCC;margin:0.5px&amp;quot;&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| №&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| Подстрока&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 9 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; \$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 8 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; \$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 7 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; a\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 6 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; aa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 0 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; ababbbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 2 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; abbbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 5 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; baa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; babbbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 4 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; bbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 3 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; bbbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Как и было сказано вначале, избавиться от лишних '''$''' легко, так как суффиксы, им соответствующие, будут первыми в суффиксном массиве (в данном случае достаточно выбросить &amp;quot;9&amp;quot; из суффиксного массива).&lt;br /&gt;
&lt;br /&gt;
== Алгоритм Карккайнена-Сандерса == &lt;br /&gt;
Изменим изначальный алгоритм следующим образом:&lt;br /&gt;
# Построим суффиксный массив для суффиксов, соответствующих позициям, не кратным трем. Рекурсивно сведем это к построению суффиксного массива для строки длиной в две трети исходной.&lt;br /&gt;
# Построим суффиксный массив для суффиксов, соответствующих кратным трем позициям, используя результат первого шага, за линейное время.&lt;br /&gt;
# Сольем эти суффиксные массивы в один за линейное время.&lt;br /&gt;
&lt;br /&gt;
Получили асимптотическое уравнение &amp;lt;tex&amp;gt; T(n) = T\left(\dfrac23 n\right) + O(n) &amp;lt;/tex&amp;gt;, решением которого также является &amp;lt;tex&amp;gt; T(n) = O(n) &amp;lt;/tex&amp;gt; (это видно из того, что сумма геометрической прогрессии с основанием &amp;lt;tex&amp;gt; \dfrac23 &amp;lt;/tex&amp;gt; равна &amp;lt;tex&amp;gt; 3 &amp;lt;/tex&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Аналогично первой версии алгоритма, дополним строку &amp;lt;tex&amp;gt; S &amp;lt;/tex&amp;gt; до длины, кратной трем, защитными символами &amp;lt;tex&amp;gt; \$ &amp;lt;/tex&amp;gt; и получим &amp;lt;tex&amp;gt;S^*&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* '''База рекурсии'''&lt;br /&gt;
Для этого алгоритма минимальной базой рекурсии будет строка длиной 4, так как она дополняется до длины 6, после чего вновь следует рекурсивный вызов строки длиной 4, и, если бы база была меньше 4, алгоритм вошел бы в бесконечную рекурсию. На этом этапе также можно применить обычную сортировку суффиксов, так как это потребует &amp;lt;tex&amp;gt; O(1) &amp;lt;/tex&amp;gt; действий.&lt;br /&gt;
&lt;br /&gt;
* '''Суффиксный массив для позиций не кратных 3'''&lt;br /&gt;
На этом шаге строится суффиксный массив &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; для множества суффиксов &amp;lt;tex&amp;gt; \{ S^*[i..n-1] \mid i \bmod 3 \ne 0 \} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Получим строку &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; аналогично предыдущему алгоритму:&lt;br /&gt;
#* Сделаем список, состоящий из троек &amp;lt;tex&amp;gt; S^*[i..i+2]&amp;lt;/tex&amp;gt; , где &amp;lt;tex&amp;gt; i \bmod 3 \ne 0 &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Отсортируем его за линейное время цифровой сортировкой и получим новый алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Перекодируем строку &amp;lt;tex&amp;gt; S^*[1..n]S^*[2..n+1] &amp;lt;/tex&amp;gt; в строку &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; длиной &amp;lt;tex&amp;gt; \dfrac23 n &amp;lt;/tex&amp;gt; в алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;. Тогда суффиксу &amp;lt;tex&amp;gt; S^*[i..n-1] &amp;lt;/tex&amp;gt; в старом алфавите, где &amp;lt;tex&amp;gt; i \bmod 3 = 1 &amp;lt;/tex&amp;gt;, в новом алфавите будет соответствовать строка &amp;lt;tex&amp;gt; S'\left[\dfrac{i-1}{3}..\dfrac{n}{3} - 1\right] &amp;lt;/tex&amp;gt;, а если &amp;lt;tex&amp;gt; i\bmod 3 = 2 &amp;lt;/tex&amp;gt;, то строка &amp;lt;tex&amp;gt; S'\left[\dfrac{n}{3} + \dfrac{i-2}{3}..\dfrac{2n}{3} - 1\right] &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Вызовем алгоритм рекурсивно для строки &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt;, получив суффиксный массив &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Пройдем по массиву &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt;. Если &amp;lt;tex&amp;gt; A_{S'}[i] &amp;lt; \dfrac{n}{3} &amp;lt;/tex&amp;gt;, то этот суффикс соответствует позиции &amp;lt;tex&amp;gt; j = 3A_{S'}[i] + 1 &amp;lt;/tex&amp;gt; в строке &amp;lt;tex&amp;gt; S &amp;lt;/tex&amp;gt;, если же &amp;lt;tex&amp;gt; A_{S'}[i] \geqslant \dfrac{n}{3} &amp;lt;/tex&amp;gt;, то этот суффикс соответствует позиции &amp;lt;tex&amp;gt; j =  3\left(A_{S'}[i] - \dfrac{n}{3}\right) + 2 &amp;lt;/tex&amp;gt; в строке &amp;lt;tex&amp;gt; S^* &amp;lt;/tex&amp;gt;. Псевдокод получения &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;:&lt;br /&gt;
 &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 '''for''' i = 0 '''to''' &amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;.length - 1:&lt;br /&gt;
    '''if''' &amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;[i] &amp;lt; n/3:&lt;br /&gt;
        &amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;.add(3 * &amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;[i] + 1)&lt;br /&gt;
    '''else''':&lt;br /&gt;
        &amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;.add(3 * (&amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;[i] - n/3) + 2)&lt;br /&gt;
&lt;br /&gt;
* '''Суффиксный массив для позиций кратных 3'''&lt;br /&gt;
Этот шаг также аналогичен первой версии алгоритма. Сортировка множества &amp;lt;tex&amp;gt; \{ S^*[i..n-1] \mid i \bmod 3 = 0 \} &amp;lt;/tex&amp;gt; аналогична сортировке пар &amp;lt;tex&amp;gt; \{ (S^*[i], S^*[i+1..n-1]) \mid i \bmod 3 = 0 \} &amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt; S^*[i+1..n-1] &amp;lt;/tex&amp;gt; — суффиксы в позициях, равных 1 по модулю 3, относительный порядок которых уже известен. Выпишем эти пары в порядке вхождения их в &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; и отсортируем по первому элементу устойчивой сортировкой подсчетом, получив суффиксный массив &amp;lt;tex&amp;gt; A_{S_0} &amp;lt;/tex&amp;gt;. Псевдокод этого шага:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;tex&amp;gt;A_{S_0}&amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 M = []&lt;br /&gt;
 '''for''' i = 0 '''to''' 2n/3 - 1:&lt;br /&gt;
     '''if''' &amp;lt;tex&amp;gt; A_{S_{12}}&amp;lt;/tex&amp;gt;[i] % 3 == 1:&lt;br /&gt;
         M.add(Pair(S*[&amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;[i] - 1], &amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;[i]))&lt;br /&gt;
 stable_sort(M)&lt;br /&gt;
 '''for''' i = 0 '''to''' n/3 - 1:&lt;br /&gt;
     &amp;lt;tex&amp;gt;A_{S_0}&amp;lt;/tex&amp;gt;.add(M[i].second - 1)&lt;br /&gt;
Аналогично, второй шаг требует &amp;lt;tex&amp;gt; O(n) &amp;lt;/tex&amp;gt; времени.&lt;br /&gt;
&lt;br /&gt;
* '''Слияние суффиксных массивов'''&lt;br /&gt;
На этом шаге мы должны слить суффиксные массивы &amp;lt;tex&amp;gt; A_{S_0} &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;, чтобы получить суффиксный массив &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; для всей строки &amp;lt;tex&amp;gt; S &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Применим стандартный алгоритм слияния двух отсортированных массивов. Заметим, что явно массивы не отсортированы, но соответствующие элементам массива суффиксы — отсортированы. &lt;br /&gt;
&lt;br /&gt;
Пусть на какой-то итерации слияния мы сравниваем суффиксы, соответствующие позициям &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt;, равной 1 по модулю 3, и &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; (она всегда будет равна 0 по модулю 3). Это аналогично сравнению пар &amp;lt;tex&amp;gt; (S^*[i], S^*[i+1..n-1]) &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; (S^*[j], S^*[j+1..n-1]) &amp;lt;/tex&amp;gt;. Сравнить первые элементы пар мы можем за &amp;lt;tex&amp;gt; O(1) &amp;lt;/tex&amp;gt;, а относительный порядок вторых элементов пар нам уже известен, так как они соответствуют позициям, равным 2 и 1 по модулю 3 соответственно.&lt;br /&gt;
&lt;br /&gt;
Аналогично, пусть на какой-то итерации слияния мы сравниваем суффиксы, соответствующие позициям &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt;, равной 2 по модулю 3, и &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; (она всегда будет равна 0 по модулю 3). Тогда это аналогично сравнению троек &amp;lt;tex&amp;gt; (S^*[i], S^*[i+1], S^*[i+2..n-1]) &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; (S^*[j], S^*[j+1], S^*[j+2..n-1]) &amp;lt;/tex&amp;gt;, что также можно делать за &amp;lt;tex&amp;gt; O(1) &amp;lt;/tex&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Псевдокод этой фазы:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 &amp;lt;font color=green&amp;gt;// Вначале предподсчитаем за O(n) обратную перестановку для суффиксного массива &amp;lt;tex&amp;gt; A_{S_{12}}&amp;lt;/tex&amp;gt;, то есть массив rank такой, что &amp;lt;tex&amp;gt; A_{S_{12}}&amp;lt;/tex&amp;gt;[rank[i]] = i.&lt;br /&gt;
 // Тогда мы сможем за O(1) сравнивать суффиксы по их позиции. &amp;lt;/font&amp;gt;&lt;br /&gt;
 rank = inverse(&amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;) &lt;br /&gt;
 '''while''' i &amp;lt; 2 * n/3 '''and''' j &amp;lt; n/3:&lt;br /&gt;
     pos12 = &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;[i]&lt;br /&gt;
     pos0  = &amp;lt;tex&amp;gt; A_{0} &amp;lt;/tex&amp;gt;[j]&lt;br /&gt;
     '''if''' pos12 % 3 == 1:&lt;br /&gt;
        ''' if''' Pair(S*[pos12], rank[pos12 + 1]) &amp;lt; Pair(S*[pos0], rank[pos0 + 1]):&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos12)&lt;br /&gt;
             i++&lt;br /&gt;
         '''else''':&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos0)&lt;br /&gt;
             j++  &lt;br /&gt;
     '''else''':&lt;br /&gt;
         '''if''' Triple(S*[pos12], S*[pos12 + 1], rank[pos12 + 2]) &amp;lt; Triple(S*[pos0], S*[pos0 + 1], rank[pos0 + 2]):&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos12)&lt;br /&gt;
             i++&lt;br /&gt;
         '''else''':&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos0)&lt;br /&gt;
             j++ &lt;br /&gt;
 '''while''' i &amp;lt; 2 * n/3:&lt;br /&gt;
     &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(&amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;[i])&lt;br /&gt;
     i++&lt;br /&gt;
 '''while''' j &amp;lt; n/3:&lt;br /&gt;
     &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(&amp;lt;tex&amp;gt; A_{S_{0}} &amp;lt;/tex&amp;gt;[j])&lt;br /&gt;
     j++&lt;br /&gt;
&lt;br /&gt;
Таким образом, получили простой метод слияния за &amp;lt;tex&amp;gt; O(n) &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Пример ===&lt;br /&gt;
Построим суффиксный массив для строки '''abbacab'''. После добавления защитного символа и дополнения до кратной трем длины, получим '''abbacab$$'''.&lt;br /&gt;
* '''Суффиксный массив для позиций не кратных 3'''&lt;br /&gt;
# Тройками, соответствующими равными 1 по модулю 3 позициям, будут: '''bba''', '''cab''', '''$$$''', соответствующими равным 2 по модулю 3 — '''bac''', '''ab$''', '''$$$'''. Новый алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; будет содержать элементы '''bba''', '''cab''', '''$$$''', '''bac''', '''ab$''', которые после сортировки получат номера 3, 4, 0, 2, 1 соответственно.&lt;br /&gt;
# Строкой '''bbacab$$$bacab$$$$''' в новом алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; будет &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; = 340210. &lt;br /&gt;
# После рекурсивного вызова получим &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt; = [5, 2, 4, 3, 0, 1]. Пересчитав &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;, получим [(5 - 3)*3 + 2, 2 * 3 + 1, (4 - 3) * 3 + 2, (3 - 3) * 3 + 2, 0 * 3 + 1, 1 * 3 + 1] = [8, 7, 5, 2, 1, 4].&lt;br /&gt;
&lt;br /&gt;
* '''Суффиксный массив для позиций кратных 3'''&lt;br /&gt;
# Обойдя массив &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; и выбрав в нем элементы, равные 1 по модулю 3, получим массив пар &amp;lt;tex&amp;gt;M&amp;lt;/tex&amp;gt; = [(&amp;lt;tex&amp;gt;b&amp;lt;/tex&amp;gt;, 7), (&amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;, 1), (&amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;, 4)]&lt;br /&gt;
# После устойчивой сортировки подсчетом по первому элементу, получим &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt; = [('''a''', 1), ('''a''', 4), ('''b''', 7)]&lt;br /&gt;
# Восстановив &amp;lt;tex&amp;gt; A_{S_0} &amp;lt;/tex&amp;gt;, получаем [0, 3, 6].&lt;br /&gt;
* '''Слияние суффиксных массивов'''&lt;br /&gt;
Рассмотрим, к примеру, третью итерацию слияния, к этой итерации массив &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [8, 7], &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt; = 2, &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; = 0, на ней мы сливаем суффиксы, соответствующие позициям 5 и 0. &lt;br /&gt;
# Образуем тройки &amp;lt;tex&amp;gt;(S^*[5], S^*[6], S^*[7..8])&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;(S^*[0], S^*[1], S^*[2..8])&amp;lt;/tex&amp;gt;.&lt;br /&gt;
# После получения относительного порядка суффиксов, получим тройки ('''a''', '''b''', 1) и ('''a''', '''b''', 3). Первая тройка меньше второй, поэтому добавляем суффикс, соответствующий позиции 5 в массив &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# В конце итерации получаем &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [8, 7, 5], &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt; = 3, &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; = 0.&lt;br /&gt;
К концу слияния получим &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [8, 7, 5, 0, 3, 6, 2, 1, 4]. Так как мы добавляли один символ &amp;lt;tex&amp;gt;\$&amp;lt;/tex&amp;gt; в начале алгоритма для дополнения строки до длины, кратной трем, выбросим последний суффикс из &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt;, получим в итоге, что &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [7, 5, 0, 3, 6, 2, 1, 4].&lt;br /&gt;
&lt;br /&gt;
{| style=&amp;quot;background-color:#CCC;margin:0.5px&amp;quot;&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| №&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| Подстрока&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 8 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; \$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 7 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; \$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 5 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; ab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 0 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; abbacab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 3 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; acab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 6 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; b\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 2 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; bacab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; bbacab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 4 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; cab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
Заметим, что № 9 будет выброшен, так как в начале алгоритма был добавлен один &amp;lt;tex&amp;gt; \$ &amp;lt;/tex&amp;gt; к строке&lt;br /&gt;
 &lt;br /&gt;
{|&lt;br /&gt;
| [[Файл:Kark_sanders_stage1.png|325px|thumb| Фаза 1]]&lt;br /&gt;
| [[Файл:Kark_sanders_stage2.png|325px|thumb| Фаза 2]]&lt;br /&gt;
| [[Файл:Kark_sanders_stage3.png|325px|thumb| Фаза 3]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Обобщение алгоритма ==&lt;br /&gt;
Массив LCP можно получить за линейное время [[Алгоритм_Касаи_и_др. | алгоритмом Касаи]].&lt;br /&gt;
&lt;br /&gt;
На самом деле, алгоритм можно обобщить&amp;lt;ref name=&amp;quot;generalisation&amp;quot;&amp;gt; Juha Kärkkäinen, Peter Sanders and Stefan Burkhardt. Linear work suffix array construction. http://www.cs.helsinki.fi/juha.karkkainen/publications/jacm05-revised.pdf &amp;lt;/ref&amp;gt;, взяв на первом шаге, к примеру, суффиксы, позиции которых по модулю 7 дают 3, 5 и 6. Для этого потребуются некоторое усложнение алгоритма, например, сортировка оставшихся суффиксов в нескольких группах на шаге 2 и слияние нескольких групп на шаге 3, но основная идея алгоритма остается той же. Множества, которые можно выбрать, на первом шаге определяются '''разностным покрытием''' (''difference cover'').&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
'''Разностное покрытие''' (англ. ''difference cover'') &amp;lt;tex&amp;gt; D &amp;lt;/tex&amp;gt; по модулю &amp;lt;tex&amp;gt;m &amp;lt;/tex&amp;gt; — множество чисел от &amp;lt;tex&amp;gt;0&amp;lt;/tex&amp;gt; до &amp;lt;tex&amp;gt;m - 1 &amp;lt;/tex&amp;gt; таких, что &amp;lt;tex&amp;gt; \forall i \in [0, m-1]: \exists j, k \in D: i \equiv k - j \pmod m &amp;lt;/tex&amp;gt;.&lt;br /&gt;
}} &lt;br /&gt;
Например, &amp;lt;tex&amp;gt; \{1, 2\} &amp;lt;/tex&amp;gt; является разностным покрытием по модулю &amp;lt;tex&amp;gt; 3 &amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt; \{3, 5, 6\} &amp;lt;/tex&amp;gt; является разностным покрытием по модулю &amp;lt;tex&amp;gt; 7 &amp;lt;/tex&amp;gt;, а &amp;lt;tex&amp;gt; \{1\} &amp;lt;/tex&amp;gt; — не является разностным покрытием по модулю &amp;lt;tex&amp;gt; 2 &amp;lt;/tex&amp;gt;, поэтому этот алгоритм не применим к нему. Подробнее узнать, как вычислять разностное покрытие для заданного модуля можно также здесь&amp;lt;ref name=&amp;quot;generalisation&amp;quot;/&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== См. также ==&lt;br /&gt;
* [[Суффиксный массив]]&lt;br /&gt;
&lt;br /&gt;
== Примечания ==&lt;br /&gt;
&amp;lt;references/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Источники информации==&lt;br /&gt;
* [[Суффиксный массив]]&lt;br /&gt;
* [http://www.cs.helsinki.fi/juha.karkkainen/publications/icalp03.pdf Juha Kärkkäinen and Peter Sanders. Simple linear work suffix array construction]&lt;br /&gt;
&lt;br /&gt;
[[Категория: Алгоритмы и структуры данных]]&lt;br /&gt;
[[Категория: Суффиксный массив]]&lt;/div&gt;</summary>
		<author><name>77.234.203.142</name></author>	</entry>

	</feed>