http://neerc.ifmo.ru/wiki/api.php?action=feedcontributions&user=Kabanov&feedformat=atomВикиконспекты - Вклад участника [ru]2024-03-28T21:32:19ZВклад участникаMediaWiki 1.30.0http://neerc.ifmo.ru/wiki/index.php?title=%D0%9C%D0%BE%D0%B4%D0%B5%D0%BB%D1%8C_%D1%81%D1%83%D1%89%D0%BD%D0%BE%D1%81%D1%82%D1%8C-%D1%81%D0%B2%D1%8F%D0%B7%D1%8C&diff=51150Модель сущность-связь2016-01-14T13:39:06Z<p>Kabanov: Новая страница: «== Сущности == == Связи == == Ассоциации == == Слабые сущности == == Альтернативные нотации ==»</p>
<hr />
<div>== Сущности ==<br />
<br />
== Связи ==<br />
<br />
== Ассоциации ==<br />
<br />
== Слабые сущности ==<br />
<br />
== Альтернативные нотации ==</div>Kabanovhttp://neerc.ifmo.ru/wiki/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Kabanov&diff=49084Участник:Kabanov2015-08-19T10:39:51Z<p>Kabanov: /* 4.5 Взаимосвязь алгоритмов Дейкстры и A* */</p>
<hr />
<div>Также как и алгоритм Эппштейна, K* выполняет поиск пути на графе <tex>G</tex> и использует граф путей <tex>P(G)</tex>. Граф путей ищется с помощью алгоритма Дейкстры для того, чтобы вычислить пути <tex>s-t</tex> в виде последовательности запасных путей. Общий принцип работы алгоритма K* следующий:<br />
<br />
1) K* применяет A* на графе <tex>G</tex> вместо обратного алгоритма Дейкстры, который используется алгоритмом Эппштейна.<br />
2) Мы запускаем A* на <tex>G</tex> и Дейкстру на <tex>P(G)</tex> в поочередном порядке, который позволяет Дейкстре вычислить требуемые пути до заверешения полного поиска алгоритма A* на графе <tex>G</tex> .<br />
<br />
== 4.1 Поиск A* на G == <br />
K* применяет A* к входному графу <tex>G</tex> для того, чтобы построить дерево поиска <tex>T</tex>. Заметим, что A*, также как и алгоритм Дейкстры, строит дерево поиска в процессе нахождения кратчайшего пути <tex>s-t</tex> . Эти деревья формируются с помощью ссылок на родительские узлы, которые хранятся в том время, как A* производит итерации для того, чтобы восстановить путь <tex>s-t</tex>, когда вершина <tex>t</tex> ещё не найдена. Запасные ребра, открытые в процессе поиска A* на графе G, немедленно добавляются в граф P(G), структура которого будет объясняться в разделе 4.3. <br />
<br />
В K* A* применяется к графу <tex>G</tex> в прямом направлении в отличие от алгоритма Эппштейна, из-за чего корнем дерева <tex>T</tex> является вершина начальная <tex>s</tex>. Это необходимо для того, чтобы была возможность работать c неявным описанием графа <tex>G</tex> через функцию successor (функция, возвращающая список исходящих ребер из данной вершины). На протяжение статьи будем считать граф <tex>G</tex> конечным, если не будет сказано иное. Заметим, что А* корректен на конечных графах. Будем следовать литературному соглашению, предполагая, что стоимость бесконечного пути неограниченна.<br />
<br />
== 4.2 Стоимость объезда ==<br />
[[Файл:kstar-figure-3.png|600px|thumb|center|'''Рисунок 3.''' Исходный граф, в котором сплошные линии представляют построенное A* дерево поиска <tex>T</tex>. Пунктирные линии являются запасными ребрами.]]<br />
Для ребра <tex>(u, v)</tex> стоимость '''объезда''' (англ. ''detour'') <tex>\delta(u, v)</tex> представляет стоимость '''ущерба''' (англ. ''disadvantage'') из-за взятия ребра объезда <tex>(u,v)</tex> в сравнении с кратчайшим путем <tex>s-t</tex> через <tex>v</tex>. Ни длина кратчайшего пути <tex>s-t</tex> через <tex>v</tex>, ни длина пути <tex>s-t</tex>, включающего запасные ребра <tex>(u, v)</tex> не известны, когда A* обнаруживает <tex>(u, v)</tex>. Обе длины могут быть оценены с помощью функции оценки <tex>f</tex>, которая использует эвристическую функцию <tex>h</tex>. Пусть <tex>f(v)</tex> будет <tex>f</tex>-значением с соответствии с деревом поиска <tex>T</tex> и <tex>f_u(v)</tex> будет <tex>f</tex>-значением в соответствии с родителем u, т.е. <tex>f_u(v) = g(u) + c(u, v) + h(v)</tex>. Тогда <tex>\delta(u, v)</tex> может быть определена так:<br />
<br />
<tex>\delta(u, v) = f_u(v) - f(v) = g(u) + c(u, v) + h(v) - g(v) - h(v) = g(u) + c(u, v) - g(v)</tex><br />
<br />
Заметим, что <tex>\delta(u, v)</tex> дает точную объездную метрику, поскольку оценочное <tex>h</tex>-значение не появляется в определении функции <tex>\delta(u, v)</tex>.<br />
<br />
== 4.3 Структура графа путей ==<br />
Структура графа путей <tex>P(G)</tex> довольно сложная. В принципе, <tex>P(G)</tex> будет ориентированным графом, вершины которого соответствуют ребрам в исходном графе <tex>G</tex>. Он будет организован как коллекция взаимосвязанных '''куч''' (англ. ''heap''). 2 бинарные кучи минимума присвоены к каждой вершине <tex>v</tex> в графе <tex>G</tex>, которые называются '''входящей кучей''' (англ. ''incoming heap'')<tex>H_{in}(v)</tex> и '''деревянной кучей''' (англ. ''tree heap'') <tex>H_{T}(v)</tex>. Эти кучи являются базисом <tex>P(G)</tex>. Как мы покажем далее, использование этих куч также играет главную роль в поддержании асимптотической сложности K*, также как в EA и LVEA.<br />
<br />
Входящая куча <tex>H_{in}(v)</tex> содержит узлы для каждого запасного ребра к вершине <tex>v</tex>, которые до сих пор были обнаружены A*. Узлы <tex>H_{in}(v)</tex> будут упорядочены в соответствии с <tex>\delta</tex>-значением соответствующих переходов. Узел владеющий ребром с минимальной стоимостью ущерба будет расположен на вершине кучи. Мы ограничим структуру кучи <tex>H_{in}(v)</tex> таким образом, что её корень в отличие от остальных узлов, будет иметь не более 1 ребенка. Обозначим его <tex>root_{in}(v)</tex>.<br />
<br />
'''Пример 4.''' Рисунок 4 иллюстрирует входящие кучи графа из рисунка 3. Цифры рядом с узлами кучи соответствуют <tex>\delta</tex>-значениям.<br />
<br />
[[Файл:kstar-figure-4.png|600px|thumb|center|'''Рисунок 4.''' Входящие кучи <tex>H_{in}(s_i)</tex>, полученные из графа, показанного на рисунке 3.]]<br />
<br />
Деревянная куча <tex>H_{T}(v)</tex> для произвольной вершины <tex>v</tex> строится следующим образом. Если <tex>v</tex> - стартовая вершина, т.е. <tex>v = s</tex>, то <tex>H_{T}(v)</tex> будет изначально пустой кучей. Затем в неё будет добавлен <tex>root_{in}(s)</tex>, если <tex>H_{in}(s)</tex> не пустая. Если <tex>v</tex> не стартовая вершина, то пусть вершина <tex>u</tex> будет родителем вершины <tex>v</tex> в дереве поиска <tex>T</tex>. Мы можем представить, что <tex>H_{T}(v)</tex> конструируется как копия <tex>H_{T}(u)</tex>, в которую добавлен <tex>root_{in}(v)</tex>. Если <tex>H_{in}(v)</tex> пустая, то <tex>H_{T}(v)</tex> идентична <tex>H_{T}(u)</tex>. Однако, для экономии памяти мы создаем только дешевую копию <tex>H_{T}(u)</tex>. Это осуществляется через создание копий только тех узлов кучи, которые лежат на обновленном пути в <tex>H_{T}(u)</tex>. Оставшаяся часть <tex>H_{T}(u)</tex> не копируется. Другими словами, <tex>root_{in}(v)</tex> вставляется в <tex>H_{T}(u)</tex> неразрушающим путем так, что структура <tex>H_{T}(u)</tex> сохраняется. В куче <tex>H_{T}(v)</tex> к <tex>root_{in}(v)</tex> могут быть присоединены 1 или 2 ребенка. К тому же, <tex>root_{in}(v)</tex> хранит только 1 собственного ребенка из <tex>H_{in}(v)</tex>. Мы обозначим корень <tex>H_{T}(v)</tex> как <tex>R(v)</tex>.<br />
<br />
[[Файл:kstar-figure-5.png|600px|thumb|center|'''Рисунок 5.''' Деревянные кучи <tex>H_{T}(s_i)</tex>, полученные из графа, показанного на рисунке 3.]]<br />
<br />
Назовем ребра, которые берут начало из входящих или деревянных куч, '''кучными ребрами''' (англ. ''heap edges''). Сформулируем следующую лемму.<br />
{{Лемма<br />
|about=1<br />
|statement=Все узлы, достижимые из <tex>R(v)</tex> по кучным ребрам, для каждой вершины <tex>v</tex> формируют тернарную кучу, упорядоченную в соответствии с <tex>\delta</tex>-значением. Мы назовем такую кучу '''графовой кучей''' (англ. ''graph heap'') вершины <tex>v</tex> и обозначим её как <tex>H_{G}(v)</tex>.<br />
|proof=Те узлы, которые находятся в <tex>H_{T}(v)</tex> или во входящей куче, на которую ссылается узел из <tex>H_{T}(v)</tex>, достижимы по кучным ребрам из <tex>R(v)</tex>. Деревянная куча <tex>H_{T}(v)</tex> формируется через добавление корней входящих куч всех вершин, лежащих на пути из стартовой вершины <tex>s</tex> до <tex>v</tex> в бинарной куче. Каждый из этих корней имеет максимум 3 детей: до 2 в <tex>H_{T}(v)</tex> и дополнительно единственного из входящей кучи. Любой другой узел, живущий во входящей куче имеет не больше 2 детей. Напомним, что каждая входящая куча - это бинарная куча с ограничением, что корень имеет единственного ребенка. Древовидная структура <tex>H_{G}(v)</tex> непосредственный результат древовидных структур <tex>H_{T}(v)</tex> и входящих куч. Более того, кучная характеристика деревянной кучи обеспечивает упорядочивание в соответствии с <tex>\delta</tex>-значением по ребрам из <tex>H_{T}(v)</tex>, а кучная характеристика входящих куч - по всем ребрам из <tex>H_{in}</tex>. Все это приводит к тому, что <tex>H_{G}(v)</tex> - тернарная куча, упорядоченная в соответствии с <tex>\delta</tex>-значением.<br />
}}<br />
<br />
Финальная структура <tex>P(G)</tex> получется из входящих и деревянных куч следующим образом. К каждому узлу <tex>n</tex> из <tex>P(G)</tex>, несущему ребро <tex>(u,v)</tex>, мы присоединим указатель, ссылающийся на <tex>R(u)</tex>, который является корневым узлом <tex>H_{T}(u)</tex>. Мы назовем такие указатели '''кросс-ребрами''' (англ. ''cross edges''), в то время как указатели, возникающие из куч названы кучными ребрами, как упоминалось раньше. Более того, мы добавим специальный узел <tex>\mathrm{R}</tex> в <tex>P(G)</tex> с одним выходящим кросс-ребром к <tex>R(t)</tex>.<br />
<br />
Более того, мы определим весовую функцию <tex>\Delta</tex> на ребрах из <tex>P(G)</tex>. Пусть <tex>(n,n')</tex> обозначает ребро в <tex>P(G)</tex>, и пусть <tex>e</tex> и <tex>e'</tex> обозначают ребра из <tex>G</tex>, соответствующие узлам <tex>n</tex> и <tex>n'</tex>. Тогда определим <tex>\Delta(n,n')</tex> следующим образом:<br />
<br />
<tex><br />
\Delta(n,n')=\begin{cases}<br />
\delta(e') - \delta(e),& \text{if}\ (n,n')\ \text{heap edge} \\<br />
\delta(e'),& \text{if}\ (n,n')\ \text{cross edge}.<br />
\end{cases}<br />
</tex><br />
<br />
Лемма 1 подразумевает, что куча упорядоченная в соответствии с <tex>\delta</tex>-значанием поддерживается для любого кучного ребра из <tex>P(G)</tex>. Эта упорядочивание кучи подразумевает, что <tex>\Delta(n,n')</tex> неотрицательна для любого кучного ребра <tex>(n,n')</tex>. Следовательно, <tex>\Delta</tex> также неотрицательна, т.е. <tex>\Delta(n,n') >= 0</tex> для любого ребра <tex>(n,n')</tex> в <tex>P(G)</tex>. Стоимость пути <tex>\sigma</tex>, т.е. <tex>C_{P(G)}(\sigma)</tex> равна <tex>\sum_{e \in \sigma}\Delta(e)</tex>. <br />
<br />
'''Пример 6.'''<br />
<br />
В оставшейся части этого раздела мы проиллюстрируем особенности структуры графа путей, которые актуальны для нахождения кратчайших путей <tex>s-t</tex>. <br />
<br />
Первое наблюдение в том, что <tex>P(G)</tex> ориентированный взвешенный граф. Каждый узел в <tex>P(G)</tex> несет запасное ребро из G. Использование бинарных куч в конструкции <tex>P(G)</tex> извлекает выгоду из следующих 2 свойств. Во-первых, произвольный узел в <tex>P(G)</tex> имеет не более 4 выходящих ребер. Одним из ребер будет точно кросс-ребро в то время, как оставшимися будут кучные ребра. Во-вторых, функция веса <tex>\Delta</tex> неотрицательна. Как станет ясно в разделе 5, эти свойства необходимы для доказательства правильности и определения сложности K*.<br />
<br />
Второе наблюдение заключается в существовании соответствия один-к-одному между путей <tex>s-t</tex> в <tex>G</tex> и путей в <tex>Р(G)</tex>, которые начинаются в <tex>\mathrm{R}</tex>.<br />
<br />
...<br />
<br />
{{Лемма<br />
|about=2<br />
|statement=Пусть <tex>n</tex> будет узлом графовой кучи <tex>H_{G}(w)</tex> для какой-нибудь вершины <tex>w</tex>. Пусть <tex>(u,v)</tex> будет ребром связанным с <tex>n</tex>. Тогда существует путь в дереве поиска <tex>T</tex> из <tex>v</tex> в <tex>w</tex>.<br />
|proof=...<br />
}}<br />
<br />
== 4.4 Алгоритмическая структура K* ==<br />
Алгоритмический принцип K* следующий. Будем запускать алгоритмы Дейкстры и A* на <tex>G</tex> с чередованием. Сначала, мы выполним A* на <tex>G</tex>, который будет работать до тех, пока вершина <tex>t</tex> не будет выбрана из очереди для раскрытия. Затем, вы запустим алгоритм Дейкстры на доступной части <tex>P(G)</tex>. Каждый узел раскрытый Дейкстрой представляет путь. Если точнее, то путь <tex>\sigma</tex> в <tex>P(G)</tex>, по которому Дейкстра достигла этого узла является решением. Путь <tex>s-t</tex> может быть построен из <tex>\sigma</tex> за линейное время путем вычисления последовательности запасных ребер <tex>seq(\sigma)</tex> и затем <tex>s-t</tex> пути из неё. Если Дейкстра находит <tex>k</tex> кратчайших путей, то K* завершается успешно. Иначе, A* возобновляется для исследования большей части <tex>G</tex>. Это приводит к росту <tex>P(G)</tex>, на котором алгоритм Дейкстры затем будет возобновлен. Мы будем повторять этот процесс до тех пор, пока алгоритм Дейкстры не найдет <tex>k</tex> кратчайших путей. <br />
<br />
Data: A graph given by its start vertex s ∈ V and its successor function succ and a<br />
natural number k<br />
Result: A list R containing k sidetrack edge sequences representing k solution paths<br />
<br />
1 <tex>open_D</tex> ← пустая приоритетная очередь<br />
2 <tex>closed_D</tex> ← пустая хеш-таблица<br />
3 <tex>R</tex> ← пустой список<br />
4 <tex>P(G)</tex> ← пустой граф путей<br />
5 Выполняем A* на графе <tex>G</tex> пока <tex>t</tex> не будет выбрана для раскрытия<br />
6 Если вершина <tex>t</tex> не была достигнута, то выходим без ответа<br />
7 Кладем <tex>\mathrm{R}</tex> в очередь <tex>open_D</tex><br />
8 '''while''' A queue or open D is not empty:<br />
9 '''if''' A queue is not empty:<br />
10 '''if''' очередь <tex>open_D</tex> не пуста:<br />
11 Let u be the head of the search queue of A ∗ and n the head of <tex>open_D</tex><br />
12 <tex>d = max\{ d(n) + \Delta(n, n') | n' \in succ(n) \}</tex><br />
13 '''if''' <tex>g(t) + d <= f</tex> (u) then переходим на строку 17.<br />
14 Возобновляем A* для того, чтобы исследовать более большую часть графа <tex>G</tex><br />
15 Обновляем <tex>P(G)</tex> and bring Dijkstra’s search into a consistent status<br />
16 Переходим на строку 8<br />
17 '''if''' очередь <tex>open_D</tex> пуста: переходим на строку 8.<br />
18 Remove from <tex>open_D</tex> and place on <tex>closed_D</tex> the node n with the minimal d-value.<br />
19 '''foreach''' <tex>n'</tex> referred by n in P(G):<br />
20 <tex>d(n') = d(n) + \Delta(n, n')</tex><br />
21 Attach to <tex>n'</tex> a parent link referring to <tex>n</tex>.<br />
22 Insert n 0 into <tex>open_D</tex><br />
23 Пусть <tex>\sigma</tex> будет путем в <tex>P(G)</tex>, через который узел n был достигнут.<br />
24 Добавим <tex>seq(\sigma)</tex> в конец списка <tex>R</tex>.<br />
25 '''if''' <tex>|R| = k</tex>: переходим на строку 26.<br />
26 Return R and exit.<br />
<br />
Алгоритм 1 содержит псевдокод K*. Код с 8 по 25 строчку образует главный цикл K*. Цикл завершается, когда очереди обоих алгоритмов А* и Дейкстры пусты. До 8 строчки выполняет некоторые подготовительные вещи. После инициализации, А* запускает на 5 строчке пока вершина <tex>t</tex> не будет выбрана им для рассмотрения, в этом случае кратчайший путь <tex>s-t</tex> будет найден. Если <tex>t</tex> не достигнута, то алгоритм завершается без ответа. Отметим, что он не завершится на бесконечных графах. Иначе, алгоритм добавляет специальную вершину <tex>R</tex>, которая назначена корнем <tex>P(G)</tex>, в поисковую очередь алгоритма Дейкстры. Затем, K* входит в главный цикл.<br />
<br />
K* поддерживает механизм планирования для контролирования, когда A* или Дейкстра будет возобновлены. Если очередь из A* не пуста, что означает, что А* ещё не завершил исследования всего графа G, то Дейкстра возобновляется тогда и только тогда, когда <tex>g(t) + d <= f(u)</tex>. Значение <tex>d</tex> является максимальным <tex>d</tex>-значением среди всех successor-ов головы поисковой очереди <tex>n</tex> алгоритма Дейкстры. Вершина <tex>u</tex> является головой поисковой очереди A*. Напомним, что <tex>d</tex> - функция расстояния, используемая в алгоритме Дейкстры. Если очередь поиска Дейкстры пуста или <tex>g(t) + d > f(u)</tex>, то А* возобновляется для того, чтобы исследовать более большую часть графа <tex>G</tex> (строка 14). То, как долго мы ему позволим работать, является компромиссом. Если мы запустим его только на маленьком количестве шагов, то мы дадим Дейкстре шанс найти необходимое количество путей скорее, чем они будут доступны в <tex>P(G)</tex>. С другой стороны, мы вызываем накладные расходы путем переключения A* и Дейкстры и поэтому должны ограничить количество переключений. Эти накладные расходы вызваны тем фактом, что после возобновления A* (строка 14), структура графа <tex>P(G)</tex> может измениться. Следовательно нам необходимо обновить <tex>P(G)</tex> (строка 15), как мы будет широко обсуждать в разделе 4.5. Это требует последующую проверку статуса Дейкстры. Мы должны быть уверены, что Дейкстра поддерживает согласованное состояние после изменений в <tex>P(G)</tex>. K* предусматривает условие, которые управляет решением, когда остановить A*, которое мы назовем ''условие расширения''. Для того, чтобы поддерживать аналогичную асимптотическую сложность как у EA и LVEA, мы должны определить условие расширения так, чтобы A* выполнялся пока количество рассмотренных вершин и количество внутренних ребер удваивается или <tex>G</tex> полностью исследован. Мы обсудим эту проблему несколько подробнее позже. В качестве полезного свойства, K* позволяет другое определения этого условия, которое может быть более эффективным на практике. В наших экспериментах в разделе 6, мы определили условие расширения так, что количество рассмотренных вершин или количество рассмотренных ребер ребер возрастает на 20% при каждом запуске A*. Этот механизм планирования включен до тех пор, пока A* не закончит исследовать весь граф <tex>G</tex>. Как только A* исследует весь граф <tex>G</tex> (строка 9), механизм планирования отключается и в дальнейшем работает только алгоритм Дейкстры.<br />
<br />
Строки 18-22 представляют обычный шаг рассмотрения узла алгоритмом Дейкстры. Отметим, что когда successor-узел <tex>n'</tex> сгенерирован, K* не проверяет был ли <tex>n'</tex> уже посещен до этого. Другими словами, каждый раз, когда узел генерируется, он рассматривает как новый. Эта стратегия обоснована на наблюдении, что путь s-t может содержать одно и то же ребро несколько раз. Строка 24 добавляет следующий путь <tex>s-t</tex> в результирующее множество R. Это делается путем конструирования последовательности запасных ребер <tex>seq(\sigma)</tex> из пути <tex>\sigma</tex>, через которые Дейкстра достигла узла <tex>n</tex>, который был только что рассмотрен. Алгоритм завершается, когда в результирующее множество добавлено <tex>k</tex> последовательностей запасных ребер (строка 25).<br />
<br />
== 4.5 Взаимосвязь алгоритмов Дейкстры и A* ==<br />
Тот факт, что оба алгоритма A* и Дейкстры делят между собой граф путей <tex>P(G)</tex>, вызывает обеспокоенность в отношении правильности работы Дейкстры на <tex>P(G)</tex>. Возобновление A* приводит к изменениям в структуре <tex>P(G)</tex>. Таким образом, после возобновления A*, мы обновляем <tex>P(G)</tex> и проверяет статус поиска Дейкстры (строка 15). В основном, A* может добавить новые узлы, менять <tex>\delta</tex>-значения существующих узлов или даже удалять узлы. A* может также существенно изменять дерево поиска <tex>T</tex>, которое будет в худшем случае разрушать структуру все деревянных куч <tex>H_{T}</tex>. Эти изменения могут приводить к глобальной реструктуризации или даже перестроению <tex>P(G)</tex> с нуля. В худшем случае это может сделать предыдущие поиски Дейкстры на <tex>P(G)</tex> бесполезными таким образом, что нам придется перезапускать алгоритм Дейкстры с нуля.<br />
<br />
Если использованная эвристическая оценка допустимая, то наше положение лучше. Нам по-прежнему может понадобится перестроение <tex>P(G)</tex>, но мы покажем, что это перестроение не мешает корректности поиска Дейкстры на <tex>P(G)</tex>. Другими словами, мы не теряем результаты, до сих пор полученные поиском Дейкстры.<br />
<br />
В случае монотонной эвристической оценки мы даже не нуждаемся в восстановлении или перестроении <tex>P(G)</tex>. Если <tex>h</tex> монотонная, то дерево поиска A* является деревом кратчайшего пути для всех раскрытых вершин. Следовательно, g-значения раскрытых вершин не меняются. Это означает, что <tex>\delta</tex>-значения для внутренних ребер никогда не изменятся. Ребра дерева раскрытых вершин не изменятся также. Следовательно, обновление <tex>\delta</tex>-значений, heaping-up, heaping-down (операции в кучах) или удаление узлов не влекут за собой каких-либо изменений в <tex>P(G)</tex>. Только добавление новых узлов приводит к изменениям в <tex>P(G)</tex>. Следовательно, восстановление или глобальное перестроение не требуется в данном случае.<br />
<br />
В оставшейся части этого раздела, мы сначала покажем, что корректность поиска Дейкстры на <tex>P(G)</tex> поддерживается в случае допустимой эвристической оценки. После этого мы покажем, что изменения в <tex>P(G)</tex> могут помешать завершенности поиска Дейкстры независимо от того, является ли эвристика допустимой или даже монотонной. Следовательно, мы предложим механизм для её поддержания.<br />
<br />
Мы фокусируемся дальше на корректности поиска Дейкстры на <tex>P(G)</tex> в случае допустимой эвристической оценки. Сначала, мы заявляем, что если <tex>h</tex> допустимая, то узлы исследованной части <tex>P(G)</tex> не поменяют свои <tex>\delta</tex>-значения.<br />
<br />
{{Лемма<br />
|about=6<br />
|statement=Пусть <tex>n</tex> будет произвольным узлов в <tex>P(G)</tex> и пусть <tex>(u,v)</tex> будет ребром, связанным с <tex>n</tex>. Если <tex>h</tex> допустимая функция, то значение <tex>\delta(u, v)</tex> никогда не изменится после того, как <tex>n</tex> будет рассмотрен алгоритмом Дейкстры.<br />
|proof=...<br />
}}<br />
<br />
Из леммы 6 мы может вывести следующее следствие.<br />
<br />
{{Лемма<br />
|about=следствие 3<br />
|statement=Пусть <tex>n</tex> будет произвольным узлом в <tex>P(G)</tex>. Если <tex>h</tex> допустимая функция, то <tex>n</tex> никогда не будет удален из <tex>P(G)</tex> после того, как <tex>n</tex> был рассмотрен алгоритмом Дейкстры.<br />
|proof=...<br />
}}<br />
<br />
Более того, мы докажем, что структура исследованной части <tex>P(G)</tex> не изменится.<br />
<br />
{{Лемма<br />
|about=7<br />
|statement=Пусть <tex>n</tex> будет произвольным узлов в <tex>P(G)</tex>. Если <tex>h</tex> допустимая функция, то <tex>n</tex> никогда не изменит свою позицию после того, как он был рассмотрен алгоритмом Дейкстры.<br />
|proof=...<br />
}}<br />
<br />
<br />
Леммы 6 и 7 обеспечивают, что изменения в <tex>P(G)</tex>, которые индуцируются A*, не влияют на часть <tex>P(G)</tex>, которую алгоритм Дейкстры уже исследовал. Это гарантирует корректность поиска Дейкстры на <tex>P(G)</tex>, если используемая эвристика допустимая. Таким образом, каждый путь, который предоставляет алгоритм Дейкстры корректен и его длина действительна. Однако, это не обеспечивает завершенность поиска Дейкстры на <tex>P(G)</tex>.<br />
<br />
Возможно, что узел <tex>n'</tex> присоединяется к другом узлу n как ребенок, после раскрытия узла <tex>n</tex>. В этом случае братья <tex>n'</tex> будут рассотрены до того, как <tex>n'</tex> станет ребенком n. Поэтому мы должны рассмотреть то, что было упущено во время поиска в связи с отсутствием <tex>n'</tex>. Мы добиваемся этого путем применения строк 20-22 к <tex>n'</tex> для каждого раскрытого направленного predecessor-а узла <tex>n'</tex>. Если <tex>n'</tex> ещё не выполняет условие планирования, A* будет неоднократно возобновляться пока механизм планирования не допустит алгоритму Дейкстры положить <tex>n'</tex> в поисковую очередь. Заметим, что таким образом не требуется каких-либо дополнительных усилий во время типичного поиска Дейкстры.<br />
<br />
Мы может быть уверены, что нерассмотренные узлы не будут принудительно опущены после применения операции heaping-up к <tex>n'</tex>. Иначе, мы могли бы иметь узел <tex>n''</tex>, который являлся бы ребенком n и впоследствии был бы заменен узлом <tex>n'</tex>. Заметим, что <tex>n''</tex> должен быть рассмотрен, поскольку <tex>n'</tex> был раскрыт. Однако, это противоречение к лемме 7, которая гарантирует, что этого не произойдет.<br />
<br />
Более того, следующее следствие гарантирует, что наилучшее <tex>d(n')</tex> не лучше, чем <tex>d</tex>-значение любой рассмотренной вершины, в частности, раскрытой вершины. Это означает, что мы не упустим возможность раскрыть <tex>n'</tex>.<br />
<br />
{{Лемма<br />
|about=следствие 3<br />
|statement=Пусть <tex>n</tex> будет узлом в <tex>P(G)</tex>, который был раскрыт Дейкстрой. Кроме того, пусть <tex>m</tex> будет узлом, который заново добавляется в <tex>P(G)</tex> или его позиция изменена, после того как <tex>n</tex> был раскрыт. Если <tex>h</tex> допустимая, тогда выполяется следующее:<br />
<tex>C_{P(G)}(R,m) >= d(n)</tex><br />
|proof=...<br />
}}<br />
<br />
== 4.6 Пример ==<br />
Мы проиллюстрируем работу алгоритма K* следующим примером. Мы будем рассматривать ориентированный взвешанный граф G на рисунке 7. Стартовой вершиной будет называться <tex>s_0</tex> и конечной вершиной - <tex>s_6</tex>. Нас интересует поиск 9 лучших путей из <tex>s_0</tex> в <tex>s_6</tex>. Для достижения этой цели мы применим алгоритм K* к <tex>G</tex>. Предположим, что эвристическая оценка существует. Значения эвристики даны в пометках c <tex>h(s_0)</tex> по <tex>h(s_6)</tex> на рисунке 7. Легко заметить, что эвристическая функция допустима.<br />
<br />
Первый раз A* делает итерации на графе <tex>G</tex> до тех пор, пока не будет найдена вершина <tex>s_6</tex>. Часть графа <tex>G</tex>, которая уже была рассмотрена иллиюстрируется на рисунке 8. Ребра, изображенные сплошными линиями, обозначают ребра дерева, в то время, как все остальные - запасные ребра. Они будет храниться в кучах <tex>H_{in}</tex>, показанных на рисунке 9. Номера, присвоенные узлах кучи, соответствуют <tex>\delta</tex>-значениям. На этом этапе поиска A* приостановлен и <tex>P(G)</tex> построен. Первоначально, только назначенный корень <tex>R</tex> явно доступен в <tex>P(G)</tex>. Инициализируется алгоритм Дейкстры. Это означает, что узел <tex>R</tex> добавляется в поискую очередь Дейкстры. Планировщику требуется доступ к successors К для того, чтобы решить следует ли возобновлять Дейкстру или A*. На данном этапе должна быть построена деревянная куча <tex>H_{T}(s_6)</tex>. Куча <tex>H_{T}(s_4)</tex> требуется для построения <tex>H_{T}(s_6)</tex>. Следовательно, строются деревянные кучи <tex>H_{T}(s_6)</tex>, <tex>H_{T}(s_4)</tex>, <tex>H_{T}(s_2)</tex> и <tex>H_{T}(s_0)</tex>. Результат показан на рисунке 10, где сплошные линии представляют кучные ребра и пунктирные линии показывают кросс-ребра. Во избежание путаницы на рисунке некоторые из ребер не полностью изображены. Мы указываем каждое из них, используя короткую стрелку с конретной целью.<br />
<br />
После построения, как показано 10, планировщик проверяет только ребенка <tex>(s_4, s_2)</tex> узла <tex>R</tex> на предмет того, что <tex>g(s_6) + d(s_4,s_2) <= f(s_1)</tex>. Отметим, что <tex>s_1</tex> является головой поисковой очереди A*. Значение <tex>d(s_4,s_2)</tex> равно 2, т.е. <tex>g(s_6) + d(s_4,s_2) = 7 + 2 = 9 = f(s_1)</tex>. Следовательно, планировщик позволяет Дейкстре раскрыть <tex>R</tex> и вставить <tex>(s_4,s_2)</tex> в поисковую очередь. При раскрытии <tex>R</tex> находится первый путь из ответа. Он строится из пути <tex>P(G)</tex>, содержащего единственный узел <tex>R</tex>. Этот путь приводит к пустой последовательности запасных ребер. Напомним, что пустая последовательность запасных ребер соответствует пути из <tex>s_0</tex> в <tex>s_6</tex> в дереве поиска, а именно <tex>s_0s_2s_4s_6</tex> длиной 7. Затем поиск Дейкстры приостанавливается, потому что для successor-ов узла <tex>(s_4,s_2)</tex> не выполняется условие <tex>g(s_6)+d(n)<=f(s_1)</tex>. Следовательно, возобновляется A*.<br />
<br />
Мы предполагаем, что условие раскрытия определено как раскрытие одной вершины для того, чтобы пример был простым и иллюстративным. Поэтому A* раскрывает <tex>s_1</tex> и останавливается. Исследованная часть <tex>G</tex> на текущем этапе показана на рисунке 11. Результат раскрытия приведет к обнаружению 2 новых запасных ребер <tex>(s_1,s_2)</tex> и <tex>(s_1,s_6)</tex>, которые будут добавлены в <tex>H_{in}(s_2)</tex> и <tex>H_{in}(s_6)</tex> соответственно. Обновленные кучи <tex>H_{in}(s_2)</tex> и <tex>H_{in}(s_6)</tex> представлены на рисунке 12. Другие кучи остаются неизменными, как на рисунке 9. Граф путей <tex>P(G)</tex> перестаивается, как показано на рисунке 13. Затем алгоритм Дейкстры возобновляется. Заметим, что поисковая очередь Дейкстры содержит только <tex>(s_4,s_2)</tex> с <tex>d = 2</tex> на этом моменте. Используя ручное выполнение мы можем легко увидеть, что Дейкстра будет выдавать в ответ пути, перечисленные в таблице 1.</div>Kabanovhttp://neerc.ifmo.ru/wiki/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Kabanov&diff=49056Участник:Kabanov2015-08-12T07:19:46Z<p>Kabanov: /* 4.3 Структура графа путей */</p>
<hr />
<div>Также как и алгоритм Эппштейна, K* выполняет поиск пути на графе <tex>G</tex> и использует граф путей <tex>P(G)</tex>. Граф путей ищется с помощью алгоритма Дейкстры для того, чтобы вычислить пути <tex>s-t</tex> в виде последовательности запасных путей. Общий принцип работы алгоритма K* следующий:<br />
<br />
1) K* применяет A* на графе <tex>G</tex> вместо обратного алгоритма Дейкстры, который используется алгоритмом Эппштейна.<br />
2) Мы запускаем A* на <tex>G</tex> и Дейкстру на <tex>P(G)</tex> в поочередном порядке, который позволяет Дейкстре вычислить требуемые пути до заверешения полного поиска алгоритма A* на графе <tex>G</tex> .<br />
<br />
== 4.1 Поиск A* на G == <br />
K* применяет A* к входному графу <tex>G</tex> для того, чтобы построить дерево поиска <tex>T</tex>. Заметим, что A*, также как и алгоритм Дейкстры, строит дерево поиска в процессе нахождения кратчайшего пути <tex>s-t</tex> . Эти деревья формируются с помощью ссылок на родительские узлы, которые хранятся в том время, как A* производит итерации для того, чтобы восстановить путь <tex>s-t</tex>, когда вершина <tex>t</tex> ещё не найдена. Запасные ребра, открытые в процессе поиска A* на графе G, немедленно добавляются в граф P(G), структура которого будет объясняться в разделе 4.3. <br />
<br />
В K* A* применяется к графу <tex>G</tex> в прямом направлении в отличие от алгоритма Эппштейна, из-за чего корнем дерева <tex>T</tex> является вершина начальная <tex>s</tex>. Это необходимо для того, чтобы была возможность работать c неявным описанием графа <tex>G</tex> через функцию successor (функция, возвращающая список исходящих ребер из данной вершины). На протяжение статьи будем считать граф <tex>G</tex> конечным, если не будет сказано иное. Заметим, что А* корректен на конечных графах. Будем следовать литературному соглашению, предполагая, что стоимость бесконечного пути неограниченна.<br />
<br />
== 4.2 Стоимость объезда ==<br />
[[Файл:kstar-figure-3.png|600px|thumb|center|'''Рисунок 3.''' Исходный граф, в котором сплошные линии представляют построенное A* дерево поиска <tex>T</tex>. Пунктирные линии являются запасными ребрами.]]<br />
Для ребра <tex>(u, v)</tex> стоимость '''объезда''' (англ. ''detour'') <tex>\delta(u, v)</tex> представляет стоимость '''ущерба''' (англ. ''disadvantage'') из-за взятия ребра объезда <tex>(u,v)</tex> в сравнении с кратчайшим путем <tex>s-t</tex> через <tex>v</tex>. Ни длина кратчайшего пути <tex>s-t</tex> через <tex>v</tex>, ни длина пути <tex>s-t</tex>, включающего запасные ребра <tex>(u, v)</tex> не известны, когда A* обнаруживает <tex>(u, v)</tex>. Обе длины могут быть оценены с помощью функции оценки <tex>f</tex>, которая использует эвристическую функцию <tex>h</tex>. Пусть <tex>f(v)</tex> будет <tex>f</tex>-значением с соответствии с деревом поиска <tex>T</tex> и <tex>f_u(v)</tex> будет <tex>f</tex>-значением в соответствии с родителем u, т.е. <tex>f_u(v) = g(u) + c(u, v) + h(v)</tex>. Тогда <tex>\delta(u, v)</tex> может быть определена так:<br />
<br />
<tex>\delta(u, v) = f_u(v) - f(v) = g(u) + c(u, v) + h(v) - g(v) - h(v) = g(u) + c(u, v) - g(v)</tex><br />
<br />
Заметим, что <tex>\delta(u, v)</tex> дает точную объездную метрику, поскольку оценочное <tex>h</tex>-значение не появляется в определении функции <tex>\delta(u, v)</tex>.<br />
<br />
== 4.3 Структура графа путей ==<br />
Структура графа путей <tex>P(G)</tex> довольно сложная. В принципе, <tex>P(G)</tex> будет ориентированным графом, вершины которого соответствуют ребрам в исходном графе <tex>G</tex>. Он будет организован как коллекция взаимосвязанных '''куч''' (англ. ''heap''). 2 бинарные кучи минимума присвоены к каждой вершине <tex>v</tex> в графе <tex>G</tex>, которые называются '''входящей кучей''' (англ. ''incoming heap'')<tex>H_{in}(v)</tex> и '''деревянной кучей''' (англ. ''tree heap'') <tex>H_{T}(v)</tex>. Эти кучи являются базисом <tex>P(G)</tex>. Как мы покажем далее, использование этих куч также играет главную роль в поддержании асимптотической сложности K*, также как в EA и LVEA.<br />
<br />
Входящая куча <tex>H_{in}(v)</tex> содержит узлы для каждого запасного ребра к вершине <tex>v</tex>, которые до сих пор были обнаружены A*. Узлы <tex>H_{in}(v)</tex> будут упорядочены в соответствии с <tex>\delta</tex>-значением соответствующих переходов. Узел владеющий ребром с минимальной стоимостью ущерба будет расположен на вершине кучи. Мы ограничим структуру кучи <tex>H_{in}(v)</tex> таким образом, что её корень в отличие от остальных узлов, будет иметь не более 1 ребенка. Обозначим его <tex>root_{in}(v)</tex>.<br />
<br />
'''Пример 4.''' Рисунок 4 иллюстрирует входящие кучи графа из рисунка 3. Цифры рядом с узлами кучи соответствуют <tex>\delta</tex>-значениям.<br />
<br />
[[Файл:kstar-figure-4.png|600px|thumb|center|'''Рисунок 4.''' Входящие кучи <tex>H_{in}(s_i)</tex>, полученные из графа, показанного на рисунке 3.]]<br />
<br />
Деревянная куча <tex>H_{T}(v)</tex> для произвольной вершины <tex>v</tex> строится следующим образом. Если <tex>v</tex> - стартовая вершина, т.е. <tex>v = s</tex>, то <tex>H_{T}(v)</tex> будет изначально пустой кучей. Затем в неё будет добавлен <tex>root_{in}(s)</tex>, если <tex>H_{in}(s)</tex> не пустая. Если <tex>v</tex> не стартовая вершина, то пусть вершина <tex>u</tex> будет родителем вершины <tex>v</tex> в дереве поиска <tex>T</tex>. Мы можем представить, что <tex>H_{T}(v)</tex> конструируется как копия <tex>H_{T}(u)</tex>, в которую добавлен <tex>root_{in}(v)</tex>. Если <tex>H_{in}(v)</tex> пустая, то <tex>H_{T}(v)</tex> идентична <tex>H_{T}(u)</tex>. Однако, для экономии памяти мы создаем только дешевую копию <tex>H_{T}(u)</tex>. Это осуществляется через создание копий только тех узлов кучи, которые лежат на обновленном пути в <tex>H_{T}(u)</tex>. Оставшаяся часть <tex>H_{T}(u)</tex> не копируется. Другими словами, <tex>root_{in}(v)</tex> вставляется в <tex>H_{T}(u)</tex> неразрушающим путем так, что структура <tex>H_{T}(u)</tex> сохраняется. В куче <tex>H_{T}(v)</tex> к <tex>root_{in}(v)</tex> могут быть присоединены 1 или 2 ребенка. К тому же, <tex>root_{in}(v)</tex> хранит только 1 собственного ребенка из <tex>H_{in}(v)</tex>. Мы обозначим корень <tex>H_{T}(v)</tex> как <tex>R(v)</tex>.<br />
<br />
[[Файл:kstar-figure-5.png|600px|thumb|center|'''Рисунок 5.''' Деревянные кучи <tex>H_{T}(s_i)</tex>, полученные из графа, показанного на рисунке 3.]]<br />
<br />
Назовем ребра, которые берут начало из входящих или деревянных куч, '''кучными ребрами''' (англ. ''heap edges''). Сформулируем следующую лемму.<br />
{{Лемма<br />
|about=1<br />
|statement=Все узлы, достижимые из <tex>R(v)</tex> по кучным ребрам, для каждой вершины <tex>v</tex> формируют тернарную кучу, упорядоченную в соответствии с <tex>\delta</tex>-значением. Мы назовем такую кучу '''графовой кучей''' (англ. ''graph heap'') вершины <tex>v</tex> и обозначим её как <tex>H_{G}(v)</tex>.<br />
|proof=Те узлы, которые находятся в <tex>H_{T}(v)</tex> или во входящей куче, на которую ссылается узел из <tex>H_{T}(v)</tex>, достижимы по кучным ребрам из <tex>R(v)</tex>. Деревянная куча <tex>H_{T}(v)</tex> формируется через добавление корней входящих куч всех вершин, лежащих на пути из стартовой вершины <tex>s</tex> до <tex>v</tex> в бинарной куче. Каждый из этих корней имеет максимум 3 детей: до 2 в <tex>H_{T}(v)</tex> и дополнительно единственного из входящей кучи. Любой другой узел, живущий во входящей куче имеет не больше 2 детей. Напомним, что каждая входящая куча - это бинарная куча с ограничением, что корень имеет единственного ребенка. Древовидная структура <tex>H_{G}(v)</tex> непосредственный результат древовидных структур <tex>H_{T}(v)</tex> и входящих куч. Более того, кучная характеристика деревянной кучи обеспечивает упорядочивание в соответствии с <tex>\delta</tex>-значением по ребрам из <tex>H_{T}(v)</tex>, а кучная характеристика входящих куч - по всем ребрам из <tex>H_{in}</tex>. Все это приводит к тому, что <tex>H_{G}(v)</tex> - тернарная куча, упорядоченная в соответствии с <tex>\delta</tex>-значением.<br />
}}<br />
<br />
Финальная структура <tex>P(G)</tex> получется из входящих и деревянных куч следующим образом. К каждому узлу <tex>n</tex> из <tex>P(G)</tex>, несущему ребро <tex>(u,v)</tex>, мы присоединим указатель, ссылающийся на <tex>R(u)</tex>, который является корневым узлом <tex>H_{T}(u)</tex>. Мы назовем такие указатели '''кросс-ребрами''' (англ. ''cross edges''), в то время как указатели, возникающие из куч названы кучными ребрами, как упоминалось раньше. Более того, мы добавим специальный узел <tex>\mathrm{R}</tex> в <tex>P(G)</tex> с одним выходящим кросс-ребром к <tex>R(t)</tex>.<br />
<br />
Более того, мы определим весовую функцию <tex>\Delta</tex> на ребрах из <tex>P(G)</tex>. Пусть <tex>(n,n')</tex> обозначает ребро в <tex>P(G)</tex>, и пусть <tex>e</tex> и <tex>e'</tex> обозначают ребра из <tex>G</tex>, соответствующие узлам <tex>n</tex> и <tex>n'</tex>. Тогда определим <tex>\Delta(n,n')</tex> следующим образом:<br />
<br />
<tex><br />
\Delta(n,n')=\begin{cases}<br />
\delta(e') - \delta(e),& \text{if}\ (n,n')\ \text{heap edge} \\<br />
\delta(e'),& \text{if}\ (n,n')\ \text{cross edge}.<br />
\end{cases}<br />
</tex><br />
<br />
Лемма 1 подразумевает, что куча упорядоченная в соответствии с <tex>\delta</tex>-значанием поддерживается для любого кучного ребра из <tex>P(G)</tex>. Эта упорядочивание кучи подразумевает, что <tex>\Delta(n,n')</tex> неотрицательна для любого кучного ребра <tex>(n,n')</tex>. Следовательно, <tex>\Delta</tex> также неотрицательна, т.е. <tex>\Delta(n,n') >= 0</tex> для любого ребра <tex>(n,n')</tex> в <tex>P(G)</tex>. Стоимость пути <tex>\sigma</tex>, т.е. <tex>C_{P(G)}(\sigma)</tex> равна <tex>\sum_{e \in \sigma}\Delta(e)</tex>. <br />
<br />
'''Пример 6.'''<br />
<br />
В оставшейся части этого раздела мы проиллюстрируем особенности структуры графа путей, которые актуальны для нахождения кратчайших путей <tex>s-t</tex>. <br />
<br />
Первое наблюдение в том, что <tex>P(G)</tex> ориентированный взвешенный граф. Каждый узел в <tex>P(G)</tex> несет запасное ребро из G. Использование бинарных куч в конструкции <tex>P(G)</tex> извлекает выгоду из следующих 2 свойств. Во-первых, произвольный узел в <tex>P(G)</tex> имеет не более 4 выходящих ребер. Одним из ребер будет точно кросс-ребро в то время, как оставшимися будут кучные ребра. Во-вторых, функция веса <tex>\Delta</tex> неотрицательна. Как станет ясно в разделе 5, эти свойства необходимы для доказательства правильности и определения сложности K*.<br />
<br />
Второе наблюдение заключается в существовании соответствия один-к-одному между путей <tex>s-t</tex> в <tex>G</tex> и путей в <tex>Р(G)</tex>, которые начинаются в <tex>\mathrm{R}</tex>.<br />
<br />
...<br />
<br />
{{Лемма<br />
|about=2<br />
|statement=Пусть <tex>n</tex> будет узлом графовой кучи <tex>H_{G}(w)</tex> для какой-нибудь вершины <tex>w</tex>. Пусть <tex>(u,v)</tex> будет ребром связанным с <tex>n</tex>. Тогда существует путь в дереве поиска <tex>T</tex> из <tex>v</tex> в <tex>w</tex>.<br />
|proof=...<br />
}}<br />
<br />
== 4.4 Алгоритмическая структура K* ==<br />
Алгоритмический принцип K* следующий. Будем запускать алгоритмы Дейкстры и A* на <tex>G</tex> с чередованием. Сначала, мы выполним A* на <tex>G</tex>, который будет работать до тех, пока вершина <tex>t</tex> не будет выбрана из очереди для раскрытия. Затем, вы запустим алгоритм Дейкстры на доступной части <tex>P(G)</tex>. Каждый узел раскрытый Дейкстрой представляет путь. Если точнее, то путь <tex>\sigma</tex> в <tex>P(G)</tex>, по которому Дейкстра достигла этого узла является решением. Путь <tex>s-t</tex> может быть построен из <tex>\sigma</tex> за линейное время путем вычисления последовательности запасных ребер <tex>seq(\sigma)</tex> и затем <tex>s-t</tex> пути из неё. Если Дейкстра находит <tex>k</tex> кратчайших путей, то K* завершается успешно. Иначе, A* возобновляется для исследования большей части <tex>G</tex>. Это приводит к росту <tex>P(G)</tex>, на котором алгоритм Дейкстры затем будет возобновлен. Мы будем повторять этот процесс до тех пор, пока алгоритм Дейкстры не найдет <tex>k</tex> кратчайших путей. <br />
<br />
Data: A graph given by its start vertex s ∈ V and its successor function succ and a<br />
natural number k<br />
Result: A list R containing k sidetrack edge sequences representing k solution paths<br />
<br />
1 <tex>open_D</tex> ← пустая приоритетная очередь<br />
2 <tex>closed_D</tex> ← пустая хеш-таблица<br />
3 <tex>R</tex> ← пустой список<br />
4 <tex>P(G)</tex> ← пустой граф путей<br />
5 Выполняем A* на графе <tex>G</tex> пока <tex>t</tex> не будет выбрана для раскрытия<br />
6 Если вершина <tex>t</tex> не была достигнута, то выходим без ответа<br />
7 Кладем <tex>\mathrm{R}</tex> в очередь <tex>open_D</tex><br />
8 '''while''' A queue or open D is not empty:<br />
9 '''if''' A queue is not empty:<br />
10 '''if''' очередь <tex>open_D</tex> не пуста:<br />
11 Let u be the head of the search queue of A ∗ and n the head of <tex>open_D</tex><br />
12 <tex>d = max\{ d(n) + \Delta(n, n') | n' \in succ(n) \}</tex><br />
13 '''if''' <tex>g(t) + d <= f</tex> (u) then переходим на строку 17.<br />
14 Возобновляем A* для того, чтобы исследовать более большую часть графа <tex>G</tex><br />
15 Обновляем <tex>P(G)</tex> and bring Dijkstra’s search into a consistent status<br />
16 Переходим на строку 8<br />
17 '''if''' очередь <tex>open_D</tex> пуста: переходим на строку 8.<br />
18 Remove from <tex>open_D</tex> and place on <tex>closed_D</tex> the node n with the minimal d-value.<br />
19 '''foreach''' <tex>n'</tex> referred by n in P(G):<br />
20 <tex>d(n') = d(n) + \Delta(n, n')</tex><br />
21 Attach to <tex>n'</tex> a parent link referring to <tex>n</tex>.<br />
22 Insert n 0 into <tex>open_D</tex><br />
23 Пусть <tex>\sigma</tex> будет путем в <tex>P(G)</tex>, через который узел n был достигнут.<br />
24 Добавим <tex>seq(\sigma)</tex> в конец списка <tex>R</tex>.<br />
25 '''if''' <tex>|R| = k</tex>: переходим на строку 26.<br />
26 Return R and exit.<br />
<br />
Алгоритм 1 содержит псевдокод K*. Код с 8 по 25 строчку образует главный цикл K*. Цикл завершается, когда очереди обоих алгоритмов А* и Дейкстры пусты. До 8 строчки выполняет некоторые подготовительные вещи. После инициализации, А* запускает на 5 строчке пока вершина <tex>t</tex> не будет выбрана им для рассмотрения, в этом случае кратчайший путь <tex>s-t</tex> будет найден. Если <tex>t</tex> не достигнута, то алгоритм завершается без ответа. Отметим, что он не завершится на бесконечных графах. Иначе, алгоритм добавляет специальную вершину <tex>R</tex>, которая назначена корнем <tex>P(G)</tex>, в поисковую очередь алгоритма Дейкстры. Затем, K* входит в главный цикл.<br />
<br />
K* поддерживает механизм планирования для контролирования, когда A* или Дейкстра будет возобновлены. Если очередь из A* не пуста, что означает, что А* ещё не завершил исследования всего графа G, то Дейкстра возобновляется тогда и только тогда, когда <tex>g(t) + d <= f(u)</tex>. Значение <tex>d</tex> является максимальным <tex>d</tex>-значением среди всех successor-ов головы поисковой очереди <tex>n</tex> алгоритма Дейкстры. Вершина <tex>u</tex> является головой поисковой очереди A*. Напомним, что <tex>d</tex> - функция расстояния, используемая в алгоритме Дейкстры. Если очередь поиска Дейкстры пуста или <tex>g(t) + d > f(u)</tex>, то А* возобновляется для того, чтобы исследовать более большую часть графа <tex>G</tex> (строка 14). То, как долго мы ему позволим работать, является компромиссом. Если мы запустим его только на маленьком количестве шагов, то мы дадим Дейкстре шанс найти необходимое количество путей скорее, чем они будут доступны в <tex>P(G)</tex>. С другой стороны, мы вызываем накладные расходы путем переключения A* и Дейкстры и поэтому должны ограничить количество переключений. Эти накладные расходы вызваны тем фактом, что после возобновления A* (строка 14), структура графа <tex>P(G)</tex> может измениться. Следовательно нам необходимо обновить <tex>P(G)</tex> (строка 15), как мы будет широко обсуждать в разделе 4.5. Это требует последующую проверку статуса Дейкстры. Мы должны быть уверены, что Дейкстра поддерживает согласованное состояние после изменений в <tex>P(G)</tex>. K* предусматривает условие, которые управляет решением, когда остановить A*, которое мы назовем ''условие расширения''. Для того, чтобы поддерживать аналогичную асимптотическую сложность как у EA и LVEA, мы должны определить условие расширения так, чтобы A* выполнялся пока количество рассмотренных вершин и количество внутренних ребер удваивается или <tex>G</tex> полностью исследован. Мы обсудим эту проблему несколько подробнее позже. В качестве полезного свойства, K* позволяет другое определения этого условия, которое может быть более эффективным на практике. В наших экспериментах в разделе 6, мы определили условие расширения так, что количество рассмотренных вершин или количество рассмотренных ребер ребер возрастает на 20% при каждом запуске A*. Этот механизм планирования включен до тех пор, пока A* не закончит исследовать весь граф <tex>G</tex>. Как только A* исследует весь граф <tex>G</tex> (строка 9), механизм планирования отключается и в дальнейшем работает только алгоритм Дейкстры.<br />
<br />
Строки 18-22 представляют обычный шаг рассмотрения узла алгоритмом Дейкстры. Отметим, что когда successor-узел <tex>n'</tex> сгенерирован, K* не проверяет был ли <tex>n'</tex> уже посещен до этого. Другими словами, каждый раз, когда узел генерируется, он рассматривает как новый. Эта стратегия обоснована на наблюдении, что путь s-t может содержать одно и то же ребро несколько раз. Строка 24 добавляет следующий путь <tex>s-t</tex> в результирующее множество R. Это делается путем конструирования последовательности запасных ребер <tex>seq(\sigma)</tex> из пути <tex>\sigma</tex>, через которые Дейкстра достигла узла <tex>n</tex>, который был только что рассмотрен. Алгоритм завершается, когда в результирующее множество добавлено <tex>k</tex> последовательностей запасных ребер (строка 25).<br />
<br />
== 4.5 Взаимосвязь алгоритмов Дейкстры и A* ==<br />
Тот факт, что оба алгоритма A* и Дейкстры делят между собой граф путей <tex>P(G)</tex>, вызывает обеспокоенность в отношении правильности работы Дейкстры на <tex>P(G)</tex>. Возобновление A* приводит к изменениям в структуре <tex>P(G)</tex>. Таким образом, после возобновления A*, мы обновляем <tex>P(G)</tex> и проверяет статус поиска Дейкстры (строка 15). В основном, A* может добавить новые узлы, менять <tex>\delta</tex>-значения существующих узлов или даже удалять узлы. A* может также существенно изменять дерево поиска <tex>T</tex>, которое будет в худшем случае разрушать структуру все деревянных куч <tex>H_{T}</tex>. Эти изменения могут приводить к глобальной реструктуризации или даже перестроению <tex>P(G)</tex> с нуля. В худшем случае это может сделать предыдущие поиски Дейкстры на <tex>P(G)</tex> бесполезными таким образом, что нам придется перезапускать алгоритм Дейкстры с нуля.<br />
<br />
Если использованная эвристическая оценка допустимая, то наше положение лучше. Нам по-прежнему может понадобится перестроение <tex>P(G)</tex>, но мы покажем, что это перестроение не мешает корректности поиска Дейкстры на <tex>P(G)</tex>. Другими словами, мы не теряем результаты, до сих пор полученные поиском Дейкстры. В случае монотонной эвристической оценки мы даже не нуждаемся в перестроении <tex>P(G)</tex>. Если <tex>h</tex> монотонная, то дерево поиска A* является деревом кратчайшего пути для всех раскрытых вершин. Следовательно, g-значения раскрытых вершин не изменится. Это означает, что <tex>\delta</tex>-значения для внутренних ребер никогда не изменятся. Ребра дерева раскрытых вершин не изменятся также. Следовательно, обновление <tex>\delta</tex>-значений, heaping-up, heaping-down (операции в кучах) или удаление узлов не влекут за собой каких-либо изменений в <tex>P(G)</tex>. Только добавление новых узлов приводит к изменениям в <tex>P(G)</tex>. Следовательно, перестроение или глобальная реструктуризация не требуется в данном случае.<br />
<br />
В оставшейся части этого раздела, мы сначала покажем, что корректность поиска Дейкстры на <tex>P(G)</tex> поддерживается в случае допустимой эвристической оценки. После этого мы покажем, что изменения в <tex>P(G)</tex> могут помешать завершенности поиска Дейкстры независимо от того, является ли эвристика допустимой или даже монотонной. Следовательно, мы предложим механизм для её поддержания.<br />
<br />
Мы фокусируемся дальше на корректности поиска Дейкстры на <tex>P(G)</tex> в случае допустимой эвристической оценки. Сначала, мы заявляем, что если <tex>h</tex> допустимая, то узлы исследованной части <tex>P(G)</tex> не поменяют свои <tex>\delta</tex>-значения.<br />
<br />
{{Лемма<br />
|about=6<br />
|statement=Пусть <tex>n</tex> будет произвольным узлов в <tex>P(G)</tex> и пусть <tex>(u,v)</tex> будет ребром, связанным с <tex>n</tex>. Если <tex>h</tex> допустимая функция, то значение <tex>\delta(u, v)</tex> никогда не изменится после того, как <tex>n</tex> будет рассмотрен алгоритмом Дейкстры.<br />
|proof=...<br />
}}<br />
<br />
Из леммы 6 мы может вывести следующее следствие.<br />
<br />
Следствие 2. Пусть <tex>n</tex> будет произвольным узлов в <tex>P(G)</tex>. Если <tex>h</tex> допустимая функция, то <tex>n</tex> никогда не будет удален из <tex>P(G)</tex> после того, как <tex>n</tex> был рассмотрен алгоритмом Дейкстры.<br />
<br />
...<br />
<br />
Более того, мы докажем, что структура исследованной части <tex>P(G)</tex> не изменится.<br />
<br />
{{Лемма<br />
|about=7<br />
|statement=Пусть <tex>n</tex> будет произвольным узлов в <tex>P(G)</tex>. Если <tex>h</tex> допустимая функция, то <tex>n</tex> никогда не изменит свою позицию после того, как он был рассмотрен алгоритмом Дейкстры.<br />
|proof=...<br />
}}<br />
<br />
<br />
Леммы 6 и 7 обеспечивают, что изменения в <tex>P(G)</tex>, которые индуцируются A*, не влияют на часть <tex>P(G)</tex>, которую алгоритм Дейкстры уже исследовал. Это гарантирует корректность поиска Дейкстры на <tex>P(G)</tex>, если используемая эвристика допустимая. Таким образом, каждый путь, который предоставляет алгоритм Дейкстры корректен и его длина действительна. Однако, это не обеспечивает завершенность поиска Дейкстры на <tex>P(G)</tex>.<br />
<br />
Возможно, что узел <tex>n'</tex> присоединяется к другом узлу n как ребенок, после раскрытия узла <tex>n</tex>. В этом случае братья <tex>n'</tex> будут рассотрены до того, как <tex>n'</tex> станет ребенком n. Поэтому мы должны рассмотреть то, что было упущено во время поиска в связи с отсутствием <tex>n'</tex>. Мы добиваемся этого путем применения строк 20-22 к <tex>n'</tex> для каждого раскрытого направленного predecessor-а узла <tex>n'</tex>. Если <tex>n'</tex> ещё не выполняет условие планирования, A* будет неоднократно возобновляться пока механизм планирования не допустит алгоритму Дейкстры положить <tex>n'</tex> в поисковую очередь. Заметим, что таким образом не требуется каких-либо дополнительных усилий во время типичного поиска Дейкстры.<br />
<br />
Мы может быть уверены, что нерассмотренные узлы не будут принудительно опущены после применения операции heaping-up к <tex>n'</tex>. Иначе, мы могли бы иметь узел <tex>n''</tex>, который являлся бы ребенком n и впоследствии был бы заменен узлом <tex>n'</tex>. Заметим, что <tex>n''</tex> должен быть рассмотрен, поскольку <tex>n'</tex> был раскрыт. Однако, это противоречение к лемме 7, которая гарантирует, что этого не произойдет.<br />
<br />
Более того, следующее следствие гарантирует, что наилучшее <tex>d(n')</tex> не лучше, чем <tex>d</tex>-значение любой рассмотренной вершины, в частности, раскрытой вершины. Это означает, что мы не упустим возможность раскрыть <tex>n'</tex>.<br />
<br />
{{Лемма<br />
|about=следствие 3<br />
|statement=Пусть <tex>n</tex> будет узлом в <tex>P(G)</tex>, который был раскрыт Дейкстрой. Кроме того, пусть <tex>m</tex> будет узлом, который заново добавляется в <tex>P(G)</tex> или его позиция изменена, после того как <tex>n</tex> был раскрыт. Если <tex>h</tex> допустимая, тогда выполяется следующее:<br />
<tex>C_{P(G)}(R,m) >= d(n)</tex><br />
|proof=...<br />
}}<br />
<br />
== 4.6 Пример ==<br />
Мы проиллюстрируем работу алгоритма K* следующим примером. Мы будем рассматривать ориентированный взвешанный граф G на рисунке 7. Стартовой вершиной будет называться <tex>s_0</tex> и конечной вершиной - <tex>s_6</tex>. Нас интересует поиск 9 лучших путей из <tex>s_0</tex> в <tex>s_6</tex>. Для достижения этой цели мы применим алгоритм K* к <tex>G</tex>. Предположим, что эвристическая оценка существует. Значения эвристики даны в пометках c <tex>h(s_0)</tex> по <tex>h(s_6)</tex> на рисунке 7. Легко заметить, что эвристическая функция допустима.<br />
<br />
Первый раз A* делает итерации на графе <tex>G</tex> до тех пор, пока не будет найдена вершина <tex>s_6</tex>. Часть графа <tex>G</tex>, которая уже была рассмотрена иллиюстрируется на рисунке 8. Ребра, изображенные сплошными линиями, обозначают ребра дерева, в то время, как все остальные - запасные ребра. Они будет храниться в кучах <tex>H_{in}</tex>, показанных на рисунке 9. Номера, присвоенные узлах кучи, соответствуют <tex>\delta</tex>-значениям. На этом этапе поиска A* приостановлен и <tex>P(G)</tex> построен. Первоначально, только назначенный корень <tex>R</tex> явно доступен в <tex>P(G)</tex>. Инициализируется алгоритм Дейкстры. Это означает, что узел <tex>R</tex> добавляется в поискую очередь Дейкстры. Планировщику требуется доступ к successors К для того, чтобы решить следует ли возобновлять Дейкстру или A*. На данном этапе должна быть построена деревянная куча <tex>H_{T}(s_6)</tex>. Куча <tex>H_{T}(s_4)</tex> требуется для построения <tex>H_{T}(s_6)</tex>. Следовательно, строются деревянные кучи <tex>H_{T}(s_6)</tex>, <tex>H_{T}(s_4)</tex>, <tex>H_{T}(s_2)</tex> и <tex>H_{T}(s_0)</tex>. Результат показан на рисунке 10, где сплошные линии представляют кучные ребра и пунктирные линии показывают кросс-ребра. Во избежание путаницы на рисунке некоторые из ребер не полностью изображены. Мы указываем каждое из них, используя короткую стрелку с конретной целью.<br />
<br />
После построения, как показано 10, планировщик проверяет только ребенка <tex>(s_4, s_2)</tex> узла <tex>R</tex> на предмет того, что <tex>g(s_6) + d(s_4,s_2) <= f(s_1)</tex>. Отметим, что <tex>s_1</tex> является головой поисковой очереди A*. Значение <tex>d(s_4,s_2)</tex> равно 2, т.е. <tex>g(s_6) + d(s_4,s_2) = 7 + 2 = 9 = f(s_1)</tex>. Следовательно, планировщик позволяет Дейкстре раскрыть <tex>R</tex> и вставить <tex>(s_4,s_2)</tex> в поисковую очередь. При раскрытии <tex>R</tex> находится первый путь из ответа. Он строится из пути <tex>P(G)</tex>, содержащего единственный узел <tex>R</tex>. Этот путь приводит к пустой последовательности запасных ребер. Напомним, что пустая последовательность запасных ребер соответствует пути из <tex>s_0</tex> в <tex>s_6</tex> в дереве поиска, а именно <tex>s_0s_2s_4s_6</tex> длиной 7. Затем поиск Дейкстры приостанавливается, потому что для successor-ов узла <tex>(s_4,s_2)</tex> не выполняется условие <tex>g(s_6)+d(n)<=f(s_1)</tex>. Следовательно, возобновляется A*.<br />
<br />
Мы предполагаем, что условие раскрытия определено как раскрытие одной вершины для того, чтобы пример был простым и иллюстративным. Поэтому A* раскрывает <tex>s_1</tex> и останавливается. Исследованная часть <tex>G</tex> на текущем этапе показана на рисунке 11. Результат раскрытия приведет к обнаружению 2 новых запасных ребер <tex>(s_1,s_2)</tex> и <tex>(s_1,s_6)</tex>, которые будут добавлены в <tex>H_{in}(s_2)</tex> и <tex>H_{in}(s_6)</tex> соответственно. Обновленные кучи <tex>H_{in}(s_2)</tex> и <tex>H_{in}(s_6)</tex> представлены на рисунке 12. Другие кучи остаются неизменными, как на рисунке 9. Граф путей <tex>P(G)</tex> перестаивается, как показано на рисунке 13. Затем алгоритм Дейкстры возобновляется. Заметим, что поисковая очередь Дейкстры содержит только <tex>(s_4,s_2)</tex> с <tex>d = 2</tex> на этом моменте. Используя ручное выполнение мы можем легко увидеть, что Дейкстра будет выдавать в ответ пути, перечисленные в таблице 1.</div>Kabanovhttp://neerc.ifmo.ru/wiki/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Kabanov&diff=48982Участник:Kabanov2015-08-06T09:58:30Z<p>Kabanov: </p>
<hr />
<div>Также как и алгоритм Эппштейна, K* выполняет поиск пути на графе <tex>G</tex> и использует граф путей <tex>P(G)</tex>. Граф путей ищется с помощью алгоритма Дейкстры для того, чтобы вычислить пути <tex>s-t</tex> в виде последовательности запасных путей. Общий принцип работы алгоритма K* следующий:<br />
<br />
1) K* применяет A* на графе <tex>G</tex> вместо обратного алгоритма Дейкстры, который используется алгоритмом Эппштейна.<br />
2) Мы запускаем A* на <tex>G</tex> и Дейкстру на <tex>P(G)</tex> в поочередном порядке, который позволяет Дейкстре вычислить требуемые пути до заверешения полного поиска алгоритма A* на графе <tex>G</tex> .<br />
<br />
== 4.1 Поиск A* на G == <br />
K* применяет A* к входному графу <tex>G</tex> для того, чтобы построить дерево поиска <tex>T</tex>. Заметим, что A*, также как и алгоритм Дейкстры, строит дерево поиска в процессе нахождения кратчайшего пути <tex>s-t</tex> . Эти деревья формируются с помощью ссылок на родительские узлы, которые хранятся в том время, как A* производит итерации для того, чтобы восстановить путь <tex>s-t</tex>, когда вершина <tex>t</tex> ещё не найдена. Запасные ребра, открытые в процессе поиска A* на графе G, немедленно добавляются в граф P(G), структура которого будет объясняться в разделе 4.3. <br />
<br />
В K* A* применяется к графу <tex>G</tex> в прямом направлении в отличие от алгоритма Эппштейна, из-за чего корнем дерева <tex>T</tex> является вершина начальная <tex>s</tex>. Это необходимо для того, чтобы была возможность работать c неявным описанием графа <tex>G</tex> через функцию successor (функция, возвращающая список исходящих ребер из данной вершины). На протяжение статьи будем считать граф <tex>G</tex> конечным, если не будет сказано иное. Заметим, что А* корректен на конечных графах. Будем следовать литературному соглашению, предполагая, что стоимость бесконечного пути неограниченна.<br />
<br />
== 4.2 Стоимость объезда ==<br />
[[Файл:kstar-figure-3.png|600px|thumb|center|'''Рисунок 3.''' Исходный граф, в котором сплошные линии представляют построенное A* дерево поиска <tex>T</tex>. Пунктирные линии являются запасными ребрами.]]<br />
Для ребра <tex>(u, v)</tex> стоимость '''объезда''' (англ. ''detour'') <tex>\delta(u, v)</tex> представляет стоимость '''ущерба''' (англ. ''disadvantage'') из-за взятия ребра объезда <tex>(u,v)</tex> в сравнении с кратчайшим путем <tex>s-t</tex> через <tex>v</tex>. Ни длина кратчайшего пути <tex>s-t</tex> через <tex>v</tex>, ни длина пути <tex>s-t</tex>, включающего запасные ребра <tex>(u, v)</tex> не известны, когда A* обнаруживает <tex>(u, v)</tex>. Обе длины могут быть оценены с помощью функции оценки <tex>f</tex>, которая использует эвристическую функцию <tex>h</tex>. Пусть <tex>f(v)</tex> будет <tex>f</tex>-значением с соответствии с деревом поиска <tex>T</tex> и <tex>f_u(v)</tex> будет <tex>f</tex>-значением в соответствии с родителем u, т.е. <tex>f_u(v) = g(u) + c(u, v) + h(v)</tex>. Тогда <tex>\delta(u, v)</tex> может быть определена так:<br />
<br />
<tex>\delta(u, v) = f_u(v) - f(v) = g(u) + c(u, v) + h(v) - g(v) - h(v) = g(u) + c(u, v) - g(v)</tex><br />
<br />
Заметим, что <tex>\delta(u, v)</tex> дает точную объездную метрику, поскольку оценочное <tex>h</tex>-значение не появляется в определении функции <tex>\delta(u, v)</tex>.<br />
<br />
== 4.3 Структура графа путей ==<br />
Структура графа путей <tex>P(G)</tex> довольно сложная. В принципе, <tex>P(G)</tex> будет ориентированным графом, вершины которого соответствуют ребрам в исходном графе <tex>G</tex>. Он будет организован как коллекция взаимосвязанных '''куч''' (англ. ''heap''). 2 бинарные кучи минимума присвоены к каждой вершине <tex>v</tex> в графе <tex>G</tex>, которые называются '''входящей кучей''' (англ. ''incoming heap'')<tex>H_{in}(v)</tex> и '''деревянной кучей''' (англ. ''tree heap'') <tex>H_{T}(v)</tex>. Эти кучи являются базисом <tex>P(G)</tex>. Как мы покажем далее, использование этих куч также играет главную роль в поддержании асимптотической сложности K*, также как в EA и LVEA.<br />
<br />
Входящая куча <tex>H_{in}(v)</tex> содержит узлы для каждого запасного ребра к вершине <tex>v</tex>, которые до сих пор были обнаружены A*. Узлы <tex>H_{in}(v)</tex> будут упорядочены в соответствии с <tex>\delta</tex>-значением соответствующих переходов. Узел владеющий ребром с минимальной стоимостью ущерба будет расположен на вершине кучи. Мы ограничим структуру кучи <tex>H_{in}(v)</tex> таким образом, что её корень в отличие от остальных узлов, будет иметь не более 1 ребенка. Обозначим его <tex>root_{in}(v)</tex>.<br />
<br />
'''Пример 4.''' Рисунок 4 иллюстрирует входящие кучи графа из рисунка 3. Цифры рядом с узлами кучи соответствуют <tex>\delta</tex>-значениям.<br />
<br />
[[Файл:kstar-figure-4.png|600px|thumb|center|'''Рисунок 4.''' Входящие кучи <tex>H_{in}(s_i)</tex>, полученные из графа, показанного на рисунке 3.]]<br />
<br />
Деревянная куча <tex>H_{T}(v)</tex> для произвольной вершины <tex>v</tex> строится следующим образом. Если <tex>v</tex> - стартовая вершина, т.е. <tex>v = s</tex>, то <tex>H_{T}(v)</tex> будет изначально пустой кучей. Затем в неё будет добавлен <tex>root_{in}(s)</tex>, если <tex>H_{in}(s)</tex> не пустая. Если <tex>v</tex> не стартовая вершина, то пусть вершина <tex>u</tex> будет родителем вершины <tex>v</tex> в дереве поиска <tex>T</tex>. Мы можем представить, что <tex>H_{T}(v)</tex> конструируется как копия <tex>H_{T}(u)</tex>, в которую добавлен <tex>root_{in}(v)</tex>. Если <tex>H_{in}(v)</tex> пустая, то <tex>H_{T}(v)</tex> идентична <tex>H_{T}(u)</tex>. Однако, для экономии памяти мы создаем только дешевую копию <tex>H_{T}(u)</tex>. Это осуществляется через создание копий только тех узлов кучи, которые лежат на обновленном пути в <tex>H_{T}(u)</tex>. Оставшаяся часть <tex>H_{T}(u)</tex> не копируется. Другими словами, <tex>root_{in}(v)</tex> вставляется в <tex>H_{T}(u)</tex> неразрушающим путем так, что структура <tex>H_{T}(u)</tex> сохраняется. В куче <tex>H_{T}(v)</tex> к <tex>root_{in}(v)</tex> могут быть присоединены 1 или 2 ребенка. К тому же, <tex>root_{in}(v)</tex> хранит только 1 собственного ребенка из <tex>H_{in}(v)</tex>. Мы обозначим корень <tex>H_{T}(v)</tex> как <tex>R(v)</tex>.<br />
<br />
[[Файл:kstar-figure-5.png|600px|thumb|center|'''Рисунок 5.''' Деревянные кучи <tex>H_{T}(s_i)</tex>, полученные из графа, показанного на рисунке 3.]]<br />
<br />
Назовем ребра, которые берут начало из входящих или деревянных куч, '''кучными ребрами''' (англ. ''heap edges''). Сформулируем следующую лемму.<br />
{{Лемма<br />
|about=1<br />
|statement=Все узлы, которые достижимы из <tex>R(v)</tex> через кучные ребра, для каждой вершины <tex>v</tex> формируют тернарную кучу, упорядоченную в соответствии с <tex>\delta</tex>-значением. Мы назовем такую кучу графовой кучей вершины <tex>v</tex> и обозначим её как <tex>H_{G}(v)</tex>.<br />
|proof=...<br />
}}<br />
<br />
Финальная структура <tex>P(G)</tex> получется из входящих и деревянных куч следующим образом. К каждому узлу <tex>n</tex> из <tex>P(G)</tex>, несущему ребро <tex>(u,v)</tex>, мы присоединим указатель, ссылающийся на <tex>R(u)</tex>, который является корневым узлом <tex>H_{T}(u)</tex>. Мы назовем такие указатели '''кросс-ребрами''' (англ. ''cross edges''), в то время как указатели, возникающие из куч названы кучными ребрами, как упоминалось раньше. Более того, мы добавим специальный узел <tex>\mathrm{R}</tex> в <tex>P(G)</tex> с одним выходящим кросс-ребром к <tex>R(t)</tex>.<br />
<br />
Более того, мы определим весовую функцию <tex>\Delta</tex> на ребрах из <tex>P(G)</tex>. Пусть <tex>(n,n')</tex> обозначает ребро в <tex>P(G)</tex>, и пусть <tex>e</tex> и <tex>e'</tex> обозначают ребра из <tex>G</tex>, соответствующие узлам <tex>n</tex> и <tex>n'</tex>. Тогда определим <tex>\Delta(n,n')</tex> следующим образом:<br />
<br />
<tex><br />
\Delta(n,n')=\begin{cases}<br />
\delta(e') - \delta(e),& \text{if}\ (n,n')\ \text{heap edge} \\<br />
\delta(e'),& \text{if}\ (n,n')\ \text{cross edge}.<br />
\end{cases}<br />
</tex><br />
<br />
Лемма 1 подразумевает, что куча упорядоченная в соответствии с <tex>\delta</tex>-значанием поддерживается для любого кучного ребра из <tex>P(G)</tex>. Эта упорядочивание кучи подразумевает, что <tex>\Delta(n,n')</tex> неотрицательна для любого кучного ребра <tex>(n,n')</tex>. Следовательно, <tex>\Delta</tex> также неотрицательна, т.е. <tex>\Delta(n,n') >= 0</tex> для любого ребра <tex>(n,n')</tex> в <tex>P(G)</tex>. Стоимость пути <tex>\sigma</tex>, т.е. <tex>C_{P(G)}(\sigma)</tex> равна <tex>\sum_{e \in \sigma}\Delta(e)</tex>. <br />
<br />
'''Пример 6.'''<br />
<br />
В оставшейся части этого раздела мы проиллюстрируем особенности структуры графа путей, которые актуальны для нахождения кратчайших путей <tex>s-t</tex>. <br />
<br />
Первое наблюдение в том, что <tex>P(G)</tex> ориентированный взвешенный граф. Каждый узел в <tex>P(G)</tex> несет запасное ребро из G. Использование бинарных куч в конструкции <tex>P(G)</tex> извлекает выгоду из следующих 2 свойств. Во-первых, произвольный узел в <tex>P(G)</tex> имеет не более 4 выходящих ребер. Одним из ребер будет точно кросс-ребро в то время, как оставшимися будут кучные ребра. Во-вторых, функция веса <tex>\Delta</tex> неотрицательна. Как станет ясно в разделе 5, эти свойства необходимы для доказательства правильности и определения сложности K*.<br />
<br />
Второе наблюдение заключается в существовании соответствия один-к-одному между путей <tex>s-t</tex> в <tex>G</tex> и путей в <tex>Р(G)</tex>, которые начинаются в <tex>\mathrm{R}</tex>.<br />
<br />
...<br />
<br />
{{Лемма<br />
|about=2<br />
|statement=Пусть <tex>n</tex> будет узлом графовой кучи <tex>H_{G}(w)</tex> для какой-нибудь вершины <tex>w</tex>. Пусть <tex>(u,v)</tex> будет ребром связанным с <tex>n</tex>. Тогда существует путь в дереве поиска <tex>T</tex> из <tex>v</tex> в <tex>w</tex>.<br />
|proof=...<br />
}}<br />
<br />
== 4.4 Алгоритмическая структура K* ==<br />
Алгоритмический принцип K* следующий. Будем запускать алгоритмы Дейкстры и A* на <tex>G</tex> с чередованием. Сначала, мы выполним A* на <tex>G</tex>, который будет работать до тех, пока вершина <tex>t</tex> не будет выбрана из очереди для раскрытия. Затем, вы запустим алгоритм Дейкстры на доступной части <tex>P(G)</tex>. Каждый узел раскрытый Дейкстрой представляет путь. Если точнее, то путь <tex>\sigma</tex> в <tex>P(G)</tex>, по которому Дейкстра достигла этого узла является решением. Путь <tex>s-t</tex> может быть построен из <tex>\sigma</tex> за линейное время путем вычисления последовательности запасных ребер <tex>seq(\sigma)</tex> и затем <tex>s-t</tex> пути из неё. Если Дейкстра находит <tex>k</tex> кратчайших путей, то K* завершается успешно. Иначе, A* возобновляется для исследования большей части <tex>G</tex>. Это приводит к росту <tex>P(G)</tex>, на котором алгоритм Дейкстры затем будет возобновлен. Мы будем повторять этот процесс до тех пор, пока алгоритм Дейкстры не найдет <tex>k</tex> кратчайших путей. <br />
<br />
Data: A graph given by its start vertex s ∈ V and its successor function succ and a<br />
natural number k<br />
Result: A list R containing k sidetrack edge sequences representing k solution paths<br />
<br />
1 <tex>open_D</tex> ← пустая приоритетная очередь<br />
2 <tex>closed_D</tex> ← пустая хеш-таблица<br />
3 <tex>R</tex> ← пустой список<br />
4 <tex>P(G)</tex> ← пустой граф путей<br />
5 Выполняем A* на графе <tex>G</tex> пока <tex>t</tex> не будет выбрана для раскрытия<br />
6 Если вершина <tex>t</tex> не была достигнута, то выходим без ответа<br />
7 Кладем <tex>\mathrm{R}</tex> в очередь <tex>open_D</tex><br />
8 '''while''' A queue or open D is not empty:<br />
9 '''if''' A queue is not empty:<br />
10 '''if''' очередь <tex>open_D</tex> не пуста:<br />
11 Let u be the head of the search queue of A ∗ and n the head of <tex>open_D</tex><br />
12 <tex>d = max\{ d(n) + \Delta(n, n') | n' \in succ(n) \}</tex><br />
13 '''if''' <tex>g(t) + d <= f</tex> (u) then переходим на строку 17.<br />
14 Возобновляем A* для того, чтобы исследовать более большую часть графа <tex>G</tex><br />
15 Обновляем <tex>P(G)</tex> and bring Dijkstra’s search into a consistent status<br />
16 Переходим на строку 8<br />
17 '''if''' очередь <tex>open_D</tex> пуста: переходим на строку 8.<br />
18 Remove from <tex>open_D</tex> and place on <tex>closed_D</tex> the node n with the minimal d-value.<br />
19 '''foreach''' <tex>n'</tex> referred by n in P(G):<br />
20 <tex>d(n') = d(n) + \Delta(n, n')</tex><br />
21 Attach to <tex>n'</tex> a parent link referring to <tex>n</tex>.<br />
22 Insert n 0 into <tex>open_D</tex><br />
23 Пусть <tex>\sigma</tex> будет путем в <tex>P(G)</tex>, через который узел n был достигнут.<br />
24 Добавим <tex>seq(\sigma)</tex> в конец списка <tex>R</tex>.<br />
25 '''if''' <tex>|R| = k</tex>: переходим на строку 26.<br />
26 Return R and exit.<br />
<br />
Алгоритм 1 содержит псевдокод K*. Код с 8 по 25 строчку образует главный цикл K*. Цикл завершается, когда очереди обоих алгоритмов А* и Дейкстры пусты. До 8 строчки выполняет некоторые подготовительные вещи. После инициализации, А* запускает на 5 строчке пока вершина <tex>t</tex> не будет выбрана им для рассмотрения, в этом случае кратчайший путь <tex>s-t</tex> будет найден. Если <tex>t</tex> не достигнута, то алгоритм завершается без ответа. Отметим, что он не завершится на бесконечных графах. Иначе, алгоритм добавляет специальную вершину <tex>R</tex>, которая назначена корнем <tex>P(G)</tex>, в поисковую очередь алгоритма Дейкстры. Затем, K* входит в главный цикл.<br />
<br />
K* поддерживает механизм планирования для контролирования, когда A* или Дейкстра будет возобновлены. Если очередь из A* не пуста, что означает, что А* ещё не завершил исследования всего графа G, то Дейкстра возобновляется тогда и только тогда, когда <tex>g(t) + d <= f(u)</tex>. Значение <tex>d</tex> является максимальным <tex>d</tex>-значением среди всех successor-ов головы поисковой очереди <tex>n</tex> алгоритма Дейкстры. Вершина <tex>u</tex> является головой поисковой очереди A*. Напомним, что <tex>d</tex> - функция расстояния, используемая в алгоритме Дейкстры. Если очередь поиска Дейкстры пуста или <tex>g(t) + d > f(u)</tex>, то А* возобновляется для того, чтобы исследовать более большую часть графа <tex>G</tex> (строка 14). То, как долго мы ему позволим работать, является компромиссом. Если мы запустим его только на маленьком количестве шагов, то мы дадим Дейкстре шанс найти необходимое количество путей скорее, чем они будут доступны в <tex>P(G)</tex>. С другой стороны, мы вызываем накладные расходы путем переключения A* и Дейкстры и поэтому должны ограничить количество переключений. Эти накладные расходы вызваны тем фактом, что после возобновления A* (строка 14), структура графа <tex>P(G)</tex> может измениться. Следовательно нам необходимо обновить <tex>P(G)</tex> (строка 15), как мы будет широко обсуждать в разделе 4.5. Это требует последующую проверку статуса Дейкстры. Мы должны быть уверены, что Дейкстра поддерживает согласованное состояние после изменений в <tex>P(G)</tex>. K* предусматривает условие, которые управляет решением, когда остановить A*, которое мы назовем ''условие расширения''. Для того, чтобы поддерживать аналогичную асимптотическую сложность как у EA и LVEA, мы должны определить условие расширения так, чтобы A* выполнялся пока количество рассмотренных вершин и количество внутренних ребер удваивается или <tex>G</tex> полностью исследован. Мы обсудим эту проблему несколько подробнее позже. В качестве полезного свойства, K* позволяет другое определения этого условия, которое может быть более эффективным на практике. В наших экспериментах в разделе 6, мы определили условие расширения так, что количество рассмотренных вершин или количество рассмотренных ребер ребер возрастает на 20% при каждом запуске A*. Этот механизм планирования включен до тех пор, пока A* не закончит исследовать весь граф <tex>G</tex>. Как только A* исследует весь граф <tex>G</tex> (строка 9), механизм планирования отключается и в дальнейшем работает только алгоритм Дейкстры.<br />
<br />
Строки 18-22 представляют обычный шаг рассмотрения узла алгоритмом Дейкстры. Отметим, что когда successor-узел <tex>n'</tex> сгенерирован, K* не проверяет был ли <tex>n'</tex> уже посещен до этого. Другими словами, каждый раз, когда узел генерируется, он рассматривает как новый. Эта стратегия обоснована на наблюдении, что путь s-t может содержать одно и то же ребро несколько раз. Строка 24 добавляет следующий путь <tex>s-t</tex> в результирующее множество R. Это делается путем конструирования последовательности запасных ребер <tex>seq(\sigma)</tex> из пути <tex>\sigma</tex>, через которые Дейкстра достигла узла <tex>n</tex>, который был только что рассмотрен. Алгоритм завершается, когда в результирующее множество добавлено <tex>k</tex> последовательностей запасных ребер (строка 25).<br />
<br />
== 4.5 Взаимосвязь алгоритмов Дейкстры и A* ==<br />
Тот факт, что оба алгоритма A* и Дейкстры делят между собой граф путей <tex>P(G)</tex>, вызывает обеспокоенность в отношении правильности работы Дейкстры на <tex>P(G)</tex>. Возобновление A* приводит к изменениям в структуре <tex>P(G)</tex>. Таким образом, после возобновления A*, мы обновляем <tex>P(G)</tex> и проверяет статус поиска Дейкстры (строка 15). В основном, A* может добавить новые узлы, менять <tex>\delta</tex>-значения существующих узлов или даже удалять узлы. A* может также существенно изменять дерево поиска <tex>T</tex>, которое будет в худшем случае разрушать структуру все деревянных куч <tex>H_{T}</tex>. Эти изменения могут приводить к глобальной реструктуризации или даже перестроению <tex>P(G)</tex> с нуля. В худшем случае это может сделать предыдущие поиски Дейкстры на <tex>P(G)</tex> бесполезными таким образом, что нам придется перезапускать алгоритм Дейкстры с нуля.<br />
<br />
Если использованная эвристическая оценка допустимая, то наше положение лучше. Нам по-прежнему может понадобится перестроение <tex>P(G)</tex>, но мы покажем, что это перестроение не мешает корректности поиска Дейкстры на <tex>P(G)</tex>. Другими словами, мы не теряем результаты, до сих пор полученные поиском Дейкстры. В случае монотонной эвристической оценки мы даже не нуждаемся в перестроении <tex>P(G)</tex>. Если <tex>h</tex> монотонная, то дерево поиска A* является деревом кратчайшего пути для всех раскрытых вершин. Следовательно, g-значения раскрытых вершин не изменится. Это означает, что <tex>\delta</tex>-значения для внутренних ребер никогда не изменятся. Ребра дерева раскрытых вершин не изменятся также. Следовательно, обновление <tex>\delta</tex>-значений, heaping-up, heaping-down (операции в кучах) или удаление узлов не влекут за собой каких-либо изменений в <tex>P(G)</tex>. Только добавление новых узлов приводит к изменениям в <tex>P(G)</tex>. Следовательно, перестроение или глобальная реструктуризация не требуется в данном случае.<br />
<br />
В оставшейся части этого раздела, мы сначала покажем, что корректность поиска Дейкстры на <tex>P(G)</tex> поддерживается в случае допустимой эвристической оценки. После этого мы покажем, что изменения в <tex>P(G)</tex> могут помешать завершенности поиска Дейкстры независимо от того, является ли эвристика допустимой или даже монотонной. Следовательно, мы предложим механизм для её поддержания.<br />
<br />
Мы фокусируемся дальше на корректности поиска Дейкстры на <tex>P(G)</tex> в случае допустимой эвристической оценки. Сначала, мы заявляем, что если <tex>h</tex> допустимая, то узлы исследованной части <tex>P(G)</tex> не поменяют свои <tex>\delta</tex>-значения.<br />
<br />
{{Лемма<br />
|about=6<br />
|statement=Пусть <tex>n</tex> будет произвольным узлов в <tex>P(G)</tex> и пусть <tex>(u,v)</tex> будет ребром, связанным с <tex>n</tex>. Если <tex>h</tex> допустимая функция, то значение <tex>\delta(u, v)</tex> никогда не изменится после того, как <tex>n</tex> будет рассмотрен алгоритмом Дейкстры.<br />
|proof=...<br />
}}<br />
<br />
Из леммы 6 мы может вывести следующее следствие.<br />
<br />
Следствие 2. Пусть <tex>n</tex> будет произвольным узлов в <tex>P(G)</tex>. Если <tex>h</tex> допустимая функция, то <tex>n</tex> никогда не будет удален из <tex>P(G)</tex> после того, как <tex>n</tex> был рассмотрен алгоритмом Дейкстры.<br />
<br />
...<br />
<br />
Более того, мы докажем, что структура исследованной части <tex>P(G)</tex> не изменится.<br />
<br />
{{Лемма<br />
|about=7<br />
|statement=Пусть <tex>n</tex> будет произвольным узлов в <tex>P(G)</tex>. Если <tex>h</tex> допустимая функция, то <tex>n</tex> никогда не изменит свою позицию после того, как он был рассмотрен алгоритмом Дейкстры.<br />
|proof=...<br />
}}<br />
<br />
<br />
Леммы 6 и 7 обеспечивают, что изменения в <tex>P(G)</tex>, которые индуцируются A*, не влияют на часть <tex>P(G)</tex>, которую алгоритм Дейкстры уже исследовал. Это гарантирует корректность поиска Дейкстры на <tex>P(G)</tex>, если используемая эвристика допустимая. Таким образом, каждый путь, который предоставляет алгоритм Дейкстры корректен и его длина действительна. Однако, это не обеспечивает завершенность поиска Дейкстры на <tex>P(G)</tex>.<br />
<br />
Возможно, что узел <tex>n'</tex> присоединяется к другом узлу n как ребенок, после раскрытия узла <tex>n</tex>. В этом случае братья <tex>n'</tex> будут рассотрены до того, как <tex>n'</tex> станет ребенком n. Поэтому мы должны рассмотреть то, что было упущено во время поиска в связи с отсутствием <tex>n'</tex>. Мы добиваемся этого путем применения строк 20-22 к <tex>n'</tex> для каждого раскрытого направленного predecessor-а узла <tex>n'</tex>. Если <tex>n'</tex> ещё не выполняет условие планирования, A* будет неоднократно возобновляться пока механизм планирования не допустит алгоритму Дейкстры положить <tex>n'</tex> в поисковую очередь. Заметим, что таким образом не требуется каких-либо дополнительных усилий во время типичного поиска Дейкстры.<br />
<br />
Мы может быть уверены, что нерассмотренные узлы не будут принудительно опущены после применения операции heaping-up к <tex>n'</tex>. Иначе, мы могли бы иметь узел <tex>n''</tex>, который являлся бы ребенком n и впоследствии был бы заменен узлом <tex>n'</tex>. Заметим, что <tex>n''</tex> должен быть рассмотрен, поскольку <tex>n'</tex> был раскрыт. Однако, это противоречение к лемме 7, которая гарантирует, что этого не произойдет.<br />
<br />
Более того, следующее следствие гарантирует, что наилучшее <tex>d(n')</tex> не лучше, чем <tex>d</tex>-значение любой рассмотренной вершины, в частности, раскрытой вершины. Это означает, что мы не упустим возможность раскрыть <tex>n'</tex>.<br />
<br />
{{Лемма<br />
|about=следствие 3<br />
|statement=Пусть <tex>n</tex> будет узлом в <tex>P(G)</tex>, который был раскрыт Дейкстрой. Кроме того, пусть <tex>m</tex> будет узлом, который заново добавляется в <tex>P(G)</tex> или его позиция изменена, после того как <tex>n</tex> был раскрыт. Если <tex>h</tex> допустимая, тогда выполяется следующее:<br />
<tex>C_{P(G)}(R,m) >= d(n)</tex><br />
|proof=...<br />
}}<br />
<br />
== 4.6 Пример ==<br />
Мы проиллюстрируем работу алгоритма K* следующим примером. Мы будем рассматривать ориентированный взвешанный граф G на рисунке 7. Стартовой вершиной будет называться <tex>s_0</tex> и конечной вершиной - <tex>s_6</tex>. Нас интересует поиск 9 лучших путей из <tex>s_0</tex> в <tex>s_6</tex>. Для достижения этой цели мы применим алгоритм K* к <tex>G</tex>. Предположим, что эвристическая оценка существует. Значения эвристики даны в пометках c <tex>h(s_0)</tex> по <tex>h(s_6)</tex> на рисунке 7. Легко заметить, что эвристическая функция допустима.<br />
<br />
Первый раз A* делает итерации на графе <tex>G</tex> до тех пор, пока не будет найдена вершина <tex>s_6</tex>. Часть графа <tex>G</tex>, которая уже была рассмотрена иллиюстрируется на рисунке 8. Ребра, изображенные сплошными линиями, обозначают ребра дерева, в то время, как все остальные - запасные ребра. Они будет храниться в кучах <tex>H_{in}</tex>, показанных на рисунке 9. Номера, присвоенные узлах кучи, соответствуют <tex>\delta</tex>-значениям. На этом этапе поиска A* приостановлен и <tex>P(G)</tex> построен. Первоначально, только назначенный корень <tex>R</tex> явно доступен в <tex>P(G)</tex>. Инициализируется алгоритм Дейкстры. Это означает, что узел <tex>R</tex> добавляется в поискую очередь Дейкстры. Планировщику требуется доступ к successors К для того, чтобы решить следует ли возобновлять Дейкстру или A*. На данном этапе должна быть построена деревянная куча <tex>H_{T}(s_6)</tex>. Куча <tex>H_{T}(s_4)</tex> требуется для построения <tex>H_{T}(s_6)</tex>. Следовательно, строются деревянные кучи <tex>H_{T}(s_6)</tex>, <tex>H_{T}(s_4)</tex>, <tex>H_{T}(s_2)</tex> и <tex>H_{T}(s_0)</tex>. Результат показан на рисунке 10, где сплошные линии представляют кучные ребра и пунктирные линии показывают кросс-ребра. Во избежание путаницы на рисунке некоторые из ребер не полностью изображены. Мы указываем каждое из них, используя короткую стрелку с конретной целью.<br />
<br />
После построения, как показано 10, планировщик проверяет только ребенка <tex>(s_4, s_2)</tex> узла <tex>R</tex> на предмет того, что <tex>g(s_6) + d(s_4,s_2) <= f(s_1)</tex>. Отметим, что <tex>s_1</tex> является головой поисковой очереди A*. Значение <tex>d(s_4,s_2)</tex> равно 2, т.е. <tex>g(s_6) + d(s_4,s_2) = 7 + 2 = 9 = f(s_1)</tex>. Следовательно, планировщик позволяет Дейкстре раскрыть <tex>R</tex> и вставить <tex>(s_4,s_2)</tex> в поисковую очередь. При раскрытии <tex>R</tex> находится первый путь из ответа. Он строится из пути <tex>P(G)</tex>, содержащего единственный узел <tex>R</tex>. Этот путь приводит к пустой последовательности запасных ребер. Напомним, что пустая последовательность запасных ребер соответствует пути из <tex>s_0</tex> в <tex>s_6</tex> в дереве поиска, а именно <tex>s_0s_2s_4s_6</tex> длиной 7. Затем поиск Дейкстры приостанавливается, потому что для successor-ов узла <tex>(s_4,s_2)</tex> не выполняется условие <tex>g(s_6)+d(n)<=f(s_1)</tex>. Следовательно, возобновляется A*.<br />
<br />
Мы предполагаем, что условие раскрытия определено как раскрытие одной вершины для того, чтобы пример был простым и иллюстративным. Поэтому A* раскрывает <tex>s_1</tex> и останавливается. Исследованная часть <tex>G</tex> на текущем этапе показана на рисунке 11. Результат раскрытия приведет к обнаружению 2 новых запасных ребер <tex>(s_1,s_2)</tex> и <tex>(s_1,s_6)</tex>, которые будут добавлены в <tex>H_{in}(s_2)</tex> и <tex>H_{in}(s_6)</tex> соответственно. Обновленные кучи <tex>H_{in}(s_2)</tex> и <tex>H_{in}(s_6)</tex> представлены на рисунке 12. Другие кучи остаются неизменными, как на рисунке 9. Граф путей <tex>P(G)</tex> перестаивается, как показано на рисунке 13. Затем алгоритм Дейкстры возобновляется. Заметим, что поисковая очередь Дейкстры содержит только <tex>(s_4,s_2)</tex> с <tex>d = 2</tex> на этом моменте. Используя ручное выполнение мы можем легко увидеть, что Дейкстра будет выдавать в ответ пути, перечисленные в таблице 1.</div>Kabanovhttp://neerc.ifmo.ru/wiki/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Kabanov&diff=48981Участник:Kabanov2015-08-06T09:27:55Z<p>Kabanov: </p>
<hr />
<div>Также как и алгоритм Эппштейна, K* выполняет поиск пути на графе <tex>G</tex> и использует граф путей <tex>P(G)</tex>. Граф путей ищется с помощью алгоритма Дейкстры для того, чтобы вычислить пути <tex>s-t</tex> в виде последовательности запасных путей. Общий принцип работы алгоритма K* следующий:<br />
<br />
1) K* применяет A* на графе <tex>G</tex> вместо обратного алгоритма Дейкстры, который используется алгоритмом Эппштейна.<br />
2) Мы запускаем A* на <tex>G</tex> и Дейкстру на <tex>P(G)</tex> в поочередном порядке, который позволяет Дейкстре вычислить требуемые пути до заверешения полного поиска алгоритма A* на графе <tex>G</tex> .<br />
<br />
== 4.1 Поиск A* на G == <br />
K* применяет A* к входному графу <tex>G</tex> для того, чтобы построить дерево поиска <tex>T</tex>. Заметим, что A*, также как и алгоритм Дейкстры, строит дерево поиска в процессе нахождения кратчайшего пути <tex>s-t</tex> . Эти деревья формируются с помощью ссылок на родительские узлы, которые хранятся в том время, как A* производит итерации для того, чтобы восстановить путь <tex>s-t</tex>, когда вершина <tex>t</tex> ещё не найдена. Запасные ребра, открытые в процессе поиска A* на графе G, немедленно добавляются в граф P(G), структура которого будет объясняться в разделе 4.3. <br />
<br />
В K* A* применяется к графу <tex>G</tex> в прямом направлении в отличие от алгоритма Эппштейна, из-за чего корнем дерева <tex>T</tex> является вершина начальная <tex>s</tex>. Это необходимо для того, чтобы была возможность работать c неявным описанием графа <tex>G</tex> через функцию successor (функция, возвращающая список исходящих ребер из данной вершины). На протяжение статьи будем считать граф <tex>G</tex> конечным, если не будет сказано иное. Заметим, что А* корректен на конечных графах. Будем следовать литературному соглашению, предполагая, что стоимость бесконечного пути неограниченна.<br />
<br />
== 4.2 Стоимость объезда ==<br />
[[Файл:kstar-figure-3.png|600px|thumb|center|'''Рисунок 3'''. Исходный граф, в котором сплошные линии представляют построенное A* дерево поиска <tex>T</tex>. Пунктирные линии являются запасными ребрами.]]<br />
Для ребра <tex>(u, v)</tex> стоимость '''объезда''' (англ. ''detour'') <tex>\delta(u, v)</tex> представляет стоимость '''ущерба''' (англ. ''disadvantage'') из-за взятия ребра объезда <tex>(u,v)</tex> в сравнении с кратчайшим путем <tex>s-t</tex> через <tex>v</tex>. Ни длина кратчайшего пути <tex>s-t</tex> через <tex>v</tex>, ни длина пути <tex>s-t</tex>, включающего запасные ребра <tex>(u, v)</tex> не известны, когда A* обнаруживает <tex>(u, v)</tex>. Обе длины могут быть оценены с помощью функции оценки <tex>f</tex>, которая использует эвристическую функцию <tex>h</tex>. Пусть <tex>f(v)</tex> будет <tex>f</tex>-значением с соответствии с деревом поиска <tex>T</tex> и <tex>f_u(v)</tex> будет <tex>f</tex>-значением в соответствии с родителем u, т.е. <tex>f_u(v) = g(u) + c(u, v) + h(v)</tex>. Тогда <tex>\delta(u, v)</tex> может быть определена так:<br />
<br />
<tex>\delta(u, v) = f_u(v) - f(v) = g(u) + c(u, v) + h(v) - g(v) - h(v) = g(u) + c(u, v) - g(v)</tex><br />
<br />
Заметим, что <tex>\delta(u, v)</tex> дает точную объездную метрику, поскольку оценочное <tex>h</tex>-значение не появляется в определении функции <tex>\delta(u, v)</tex>.<br />
<br />
== 4.3 Структура графа путей ==<br />
Структура графа путей <tex>P(G)</tex> довольно сложная. В принципе, <tex>P(G)</tex> будет ориентированным графом, вершины которого соответствуют ребрам в исходном графе <tex>G</tex>. Он будет организован как коллекция взаимосвязанных '''куч''' (англ. ''heap''). 2 бинарные кучи минимума присвоены к каждой вершине <tex>v</tex> в графе <tex>G</tex>, которые называются '''входящей кучей''' (англ. ''incoming heap'')<tex>H_{in}(v)</tex> и '''деревянной кучей''' (англ. ''tree heap'') <tex>H_{T}(v)</tex>. Эти кучи являются базисом <tex>P(G)</tex>. Как мы покажем далее, использование этих куч также играет главную роль в поддержании асимптотической сложности K*, также как в EA и LVEA.<br />
<br />
Входящая куча <tex>H_{in}(v)</tex> содержит узлы для каждого запасного ребра к вершине <tex>v</tex>, которые до сих пор были обнаружены A*. Узлы <tex>H_{in}(v)</tex> будут упорядочены в соответствии с <tex>\delta</tex>-значением соответствующих переходов. Узел владеющий ребром с минимальной стоимостью ущерба будет расположен на вершине кучи. Мы ограничим структуру кучи <tex>H_{in}(v)</tex> таким образом, что её корень в отличие от остальных узлов, будет иметь не более 1 ребенка. Обозначим его <tex>root_{in}(v)</tex>.<br />
<br />
[[Файл:kstar-figure-4.png|600px|thumb|center|'''Рисунок 4'''. Входящие кучи <tex>H_{in}(s_i)</tex>, полученные из графа, показанного на рисунке 3.]]<br />
<br />
Деревянная куча <tex>H_{T}(v)</tex> для произвольной вершины <tex>v</tex> строится следующим образом. Если <tex>v</tex> - стартовая вершина, т.е. <tex>v = s</tex>, то <tex>H_{T}(v)</tex> будет изначально пустой кучей. Затем в неё будет добавлен <tex>root_{in}(s)</tex>, если <tex>H_{in}(s)</tex> не пустая. Если <tex>v</tex> не стартовая вершина, то пусть вершина <tex>u</tex> будет родителем вершины <tex>v</tex> в дереве поиска <tex>T</tex>. Мы можем представить, что <tex>H_{T}(v)</tex> конструируется как копия <tex>H_{T}(u)</tex>, в которую добавлен <tex>root_{in}(v)</tex>. Если <tex>H_{in}(v)</tex> пустая, то <tex>H_{T}(v)</tex> идентична <tex>H_{T}(u)</tex>. Однако, для экономии памяти мы создаем только дешевую копию <tex>H_{T}(u)</tex>. Это осуществляется через создание копий только тех узлов кучи, которые лежат на обновленном пути в <tex>H_{T}(u)</tex>. Оставшаяся часть <tex>H_{T}(u)</tex> не копируется. Другими словами, <tex>root_{in}(v)</tex> вставляется в <tex>H_{T}(u)</tex> неразрушающим путем так, что структура <tex>H_{T}(u)</tex> сохраняется. В куче <tex>H_{T}(v)</tex> к <tex>root_{in}(v)</tex> могут быть присоединены 1 или 2 ребенка. К тому же, <tex>root_{in}(v)</tex> хранит только 1 собственного ребенка из <tex>H_{in}(v)</tex>. Мы обозначим корень <tex>H_{T}(v)</tex> как <tex>R(v)</tex>.<br />
<br />
[[Файл:kstar-figure-5.png|600px|thumb|center|'''Рисунок 5'''. Деревянные кучи <tex>H_{T}(s_i)</tex>, полученные из графа, показанного на рисунке 3.]]<br />
<br />
Назовем ребра, которые берут начало из входящих или деревянных куч, '''кучными ребрами''' (англ. ''heap edges''). Сформулируем следующую лемму.<br />
{{Лемма<br />
|about=1<br />
|statement=Все узлы, которые достижимы из <tex>R(v)</tex> через кучные ребра, для каждой вершины <tex>v</tex> формируют тернарную кучу, упорядоченную в соответствии с <tex>\delta</tex>-значением. Мы назовем такую кучу графовой кучей вершины <tex>v</tex> и обозначим её как <tex>H_{G}(v)</tex>.<br />
|proof=...<br />
}}<br />
<br />
Финальная структура <tex>P(G)</tex> получется из входящих и деревянных куч следующим образом. К каждому узлу <tex>n</tex> из <tex>P(G)</tex>, несущему ребро <tex>(u,v)</tex>, мы присоединим указатель, ссылающийся на <tex>R(u)</tex>, который является корневым узлом <tex>H_{T}(u)</tex>. Мы назовем такие указатели '''кросс-ребрами''' (англ. ''cross edges''), в то время как указатели, возникающие из куч названы кучными ребрами, как упоминалось раньше. Более того, мы добавим специальный узел <tex>\mathrm{R}</tex> в <tex>P(G)</tex> с одним выходящим кросс-ребром к <tex>R(t)</tex>.<br />
<br />
Более того, мы определим весовую функцию <tex>\Delta</tex> на ребрах из <tex>P(G)</tex>. Пусть <tex>(n,n')</tex> обозначает ребро в <tex>P(G)</tex>, и пусть <tex>e</tex> и <tex>e'</tex> обозначают ребра из <tex>G</tex>, соответствующие узлам <tex>n</tex> и <tex>n'</tex>. Тогда определим <tex>\Delta(n,n')</tex> следующим образом:<br />
<br />
<tex><br />
\Delta(n,n')=\begin{cases}<br />
\delta(e') - \delta(e),& \text{if}\ (n,n')\ \text{heap edge} \\<br />
\delta(e'),& \text{if}\ (n,n')\ \text{cross edge}.<br />
\end{cases}<br />
</tex><br />
<br />
Лемма 1 подразумевает, что куча упорядоченная в соответствии с <tex>\delta</tex>-значанием поддерживается для любого кучного ребра из <tex>P(G)</tex>. Эта упорядочивание кучи подразумевает, что <tex>\Delta(n,n')</tex> неотрицательна для любого кучного ребра <tex>(n,n')</tex>. Следовательно, <tex>\Delta</tex> также неотрицательна, т.е. <tex>\Delta(n,n') >= 0</tex> для любого ребра <tex>(n,n')</tex> в <tex>P(G)</tex>. Стоимость пути <tex>\sigma</tex>, т.е. <tex>C_{P(G)}(\sigma)</tex> равна <tex>\sum_{e \in \sigma}\Delta(e)</tex>. <br />
<br />
...<br />
<br />
{{Лемма<br />
|about=2<br />
|statement=Пусть <tex>n</tex> будет узлом графовой кучи <tex>H_{G}(w)</tex> для какой-нибудь вершины <tex>w</tex>. Пусть <tex>(u,v)</tex> будет ребром связанным с <tex>n</tex>. Тогда существует путь в дереве поиска <tex>T</tex> из <tex>v</tex> в <tex>w</tex>.<br />
|proof=...<br />
}}<br />
<br />
== 4.4 Алгоритмическая структура K* ==<br />
Алгоритмический принцип K* следующий. Будем запускать алгоритмы Дейкстры и A* на <tex>G</tex> с чередованием. Сначала, мы выполним A* на <tex>G</tex>, который будет работать до тех, пока вершина <tex>t</tex> не будет выбрана из очереди для раскрытия. Затем, вы запустим алгоритм Дейкстры на доступной части <tex>P(G)</tex>. Каждый узел раскрытый Дейкстрой представляет путь. Если точнее, то путь <tex>\sigma</tex> в <tex>P(G)</tex>, по которому Дейкстра достигла этого узла является решением. Путь <tex>s-t</tex> может быть построен из <tex>\sigma</tex> за линейное время путем вычисления последовательности запасных ребер <tex>seq(\sigma)</tex> и затем <tex>s-t</tex> пути из неё. Если Дейкстра находит <tex>k</tex> кратчайших путей, то K* завершается успешно. Иначе, A* возобновляется для исследования большей части <tex>G</tex>. Это приводит к росту <tex>P(G)</tex>, на котором алгоритм Дейкстры затем будет возобновлен. Мы будем повторять этот процесс до тех пор, пока алгоритм Дейкстры не найдет <tex>k</tex> кратчайших путей. <br />
<br />
Data: A graph given by its start vertex s ∈ V and its successor function succ and a<br />
natural number k<br />
Result: A list R containing k sidetrack edge sequences representing k solution paths<br />
<br />
1 <tex>open_D</tex> ← пустая приоритетная очередь<br />
2 <tex>closed_D</tex> ← пустая хеш-таблица<br />
3 <tex>R</tex> ← пустой список<br />
4 <tex>P(G)</tex> ← пустой граф путей<br />
5 Выполняем A* на графе <tex>G</tex> пока <tex>t</tex> не будет выбрана для раскрытия<br />
6 Если вершина <tex>t</tex> не была достигнута, то выходим без ответа<br />
7 Кладем <tex>\mathrm{R}</tex> в очередь <tex>open_D</tex><br />
8 '''while''' A queue or open D is not empty:<br />
9 '''if''' A queue is not empty:<br />
10 '''if''' очередь <tex>open_D</tex> не пуста:<br />
11 Let u be the head of the search queue of A ∗ and n the head of <tex>open_D</tex><br />
12 <tex>d = max\{ d(n) + \Delta(n, n') | n' \in succ(n) \}</tex><br />
13 '''if''' <tex>g(t) + d <= f</tex> (u) then переходим на строку 17.<br />
14 Возобновляем A* для того, чтобы исследовать более большую часть графа <tex>G</tex><br />
15 Обновляем <tex>P(G)</tex> and bring Dijkstra’s search into a consistent status<br />
16 Переходим на строку 8<br />
17 '''if''' очередь <tex>open_D</tex> пуста: переходим на строку 8.<br />
18 Remove from <tex>open_D</tex> and place on <tex>closed_D</tex> the node n with the minimal d-value.<br />
19 '''foreach''' <tex>n'</tex> referred by n in P(G):<br />
20 <tex>d(n') = d(n) + \Delta(n, n')</tex><br />
21 Attach to <tex>n'</tex> a parent link referring to <tex>n</tex>.<br />
22 Insert n 0 into <tex>open_D</tex><br />
23 Пусть <tex>\sigma</tex> будет путем в <tex>P(G)</tex>, через который узел n был достигнут.<br />
24 Добавим <tex>seq(\sigma)</tex> в конец списка <tex>R</tex>.<br />
25 '''if''' <tex>|R| = k</tex>: переходим на строку 26.<br />
26 Return R and exit.<br />
<br />
Алгоритм 1 содержит псевдокод K*. Код с 8 по 25 строчку образует главный цикл K*. Цикл завершается, когда очереди обоих алгоритмов А* и Дейкстры пусты. До 8 строчки выполняет некоторые подготовительные вещи. После инициализации, А* запускает на 5 строчке пока вершина <tex>t</tex> не будет выбрана им для рассмотрения, в этом случае кратчайший путь <tex>s-t</tex> будет найден. Если <tex>t</tex> не достигнута, то алгоритм завершается без ответа. Отметим, что он не завершится на бесконечных графах. Иначе, алгоритм добавляет специальную вершину <tex>R</tex>, которая назначена корнем <tex>P(G)</tex>, в поисковую очередь алгоритма Дейкстры. Затем, K* входит в главный цикл.<br />
<br />
K* поддерживает механизм планирования для контролирования, когда A* или Дейкстра будет возобновлены. Если очередь из A* не пуста, что означает, что А* ещё не завершил исследования всего графа G, то Дейкстра возобновляется тогда и только тогда, когда <tex>g(t) + d <= f(u)</tex>. Значение <tex>d</tex> является максимальным <tex>d</tex>-значением среди всех successor-ов головы поисковой очереди <tex>n</tex> алгоритма Дейкстры. Вершина <tex>u</tex> является головой поисковой очереди A*. Напомним, что <tex>d</tex> - функция расстояния, используемая в алгоритме Дейкстры. Если очередь поиска Дейкстры пуста или <tex>g(t) + d > f(u)</tex>, то А* возобновляется для того, чтобы исследовать более большую часть графа <tex>G</tex> (строка 14). То, как долго мы ему позволим работать, является компромиссом. Если мы запустим его только на маленьком количестве шагов, то мы дадим Дейкстре шанс найти необходимое количество путей скорее, чем они будут доступны в <tex>P(G)</tex>. С другой стороны, мы вызываем накладные расходы путем переключения A* и Дейкстры и поэтому должны ограничить количество переключений. Эти накладные расходы вызваны тем фактом, что после возобновления A* (строка 14), структура графа <tex>P(G)</tex> может измениться. Следовательно нам необходимо обновить <tex>P(G)</tex> (строка 15), как мы будет широко обсуждать в разделе 4.5. Это требует последующую проверку статуса Дейкстры. Мы должны быть уверены, что Дейкстра поддерживает согласованное состояние после изменений в <tex>P(G)</tex>. K* предусматривает условие, которые управляет решением, когда остановить A*, которое мы назовем ''условие расширения''. Для того, чтобы поддерживать аналогичную асимптотическую сложность как у EA и LVEA, мы должны определить условие расширения так, чтобы A* выполнялся пока количество рассмотренных вершин и количество внутренних ребер удваивается или <tex>G</tex> полностью исследован. Мы обсудим эту проблему несколько подробнее позже. В качестве полезного свойства, K* позволяет другое определения этого условия, которое может быть более эффективным на практике. В наших экспериментах в разделе 6, мы определили условие расширения так, что количество рассмотренных вершин или количество рассмотренных ребер ребер возрастает на 20% при каждом запуске A*. Этот механизм планирования включен до тех пор, пока A* не закончит исследовать весь граф <tex>G</tex>. Как только A* исследует весь граф <tex>G</tex> (строка 9), механизм планирования отключается и в дальнейшем работает только алгоритм Дейкстры.<br />
<br />
Строки 18-22 представляют обычный шаг рассмотрения узла алгоритмом Дейкстры. Отметим, что когда successor-узел <tex>n'</tex> сгенерирован, K* не проверяет был ли <tex>n'</tex> уже посещен до этого. Другими словами, каждый раз, когда узел генерируется, он рассматривает как новый. Эта стратегия обоснована на наблюдении, что путь s-t может содержать одно и то же ребро несколько раз. Строка 24 добавляет следующий путь <tex>s-t</tex> в результирующее множество R. Это делается путем конструирования последовательности запасных ребер <tex>seq(\sigma)</tex> из пути <tex>\sigma</tex>, через которые Дейкстра достигла узла <tex>n</tex>, который был только что рассмотрен. Алгоритм завершается, когда в результирующее множество добавлено <tex>k</tex> последовательностей запасных ребер (строка 25).<br />
<br />
== 4.5 Взаимосвязь алгоритмов Дейкстры и A* ==<br />
Тот факт, что оба алгоритма A* и Дейкстры делят между собой граф путей <tex>P(G)</tex>, вызывает обеспокоенность в отношении правильности работы Дейкстры на <tex>P(G)</tex>. Возобновление A* приводит к изменениям в структуре <tex>P(G)</tex>. Таким образом, после возобновления A*, мы обновляем <tex>P(G)</tex> и проверяет статус поиска Дейкстры (строка 15). В основном, A* может добавить новые узлы, менять <tex>\delta</tex>-значения существующих узлов или даже удалять узлы. A* может также существенно изменять дерево поиска <tex>T</tex>, которое будет в худшем случае разрушать структуру все деревянных куч <tex>H_{T}</tex>. Эти изменения могут приводить к глобальной реструктуризации или даже перестроению <tex>P(G)</tex> с нуля. В худшем случае это может сделать предыдущие поиски Дейкстры на <tex>P(G)</tex> бесполезными таким образом, что нам придется перезапускать алгоритм Дейкстры с нуля.<br />
<br />
Если использованная эвристическая оценка допустимая, то наше положение лучше. Нам по-прежнему может понадобится перестроение <tex>P(G)</tex>, но мы покажем, что это перестроение не мешает корректности поиска Дейкстры на <tex>P(G)</tex>. Другими словами, мы не теряем результаты, до сих пор полученные поиском Дейкстры. В случае монотонной эвристической оценки мы даже не нуждаемся в перестроении <tex>P(G)</tex>. Если <tex>h</tex> монотонная, то дерево поиска A* является деревом кратчайшего пути для всех раскрытых вершин. Следовательно, g-значения раскрытых вершин не изменится. Это означает, что <tex>\delta</tex>-значения для внутренних ребер никогда не изменятся. Ребра дерева раскрытых вершин не изменятся также. Следовательно, обновление <tex>\delta</tex>-значений, heaping-up, heaping-down (операции в кучах) или удаление узлов не влекут за собой каких-либо изменений в <tex>P(G)</tex>. Только добавление новых узлов приводит к изменениям в <tex>P(G)</tex>. Следовательно, перестроение или глобальная реструктуризация не требуется в данном случае.<br />
<br />
В оставшейся части этого раздела, мы сначала покажем, что корректность поиска Дейкстры на <tex>P(G)</tex> поддерживается в случае допустимой эвристической оценки. После этого мы покажем, что изменения в <tex>P(G)</tex> могут помешать завершенности поиска Дейкстры независимо от того, является ли эвристика допустимой или даже монотонной. Следовательно, мы предложим механизм для её поддержания.<br />
<br />
Мы фокусируемся дальше на корректности поиска Дейкстры на <tex>P(G)</tex> в случае допустимой эвристической оценки. Сначала, мы заявляем, что если <tex>h</tex> допустимая, то узлы исследованной части <tex>P(G)</tex> не поменяют свои <tex>\delta</tex>-значения.<br />
<br />
{{Лемма<br />
|about=6<br />
|statement=Пусть <tex>n</tex> будет произвольным узлов в <tex>P(G)</tex> и пусть <tex>(u,v)</tex> будет ребром, связанным с <tex>n</tex>. Если <tex>h</tex> допустимая функция, то значение <tex>\delta(u, v)</tex> никогда не изменится после того, как <tex>n</tex> будет рассмотрен алгоритмом Дейкстры.<br />
|proof=...<br />
}}<br />
<br />
Из леммы 6 мы может вывести следующее следствие.<br />
<br />
Следствие 2. Пусть <tex>n</tex> будет произвольным узлов в <tex>P(G)</tex>. Если <tex>h</tex> допустимая функция, то <tex>n</tex> никогда не будет удален из <tex>P(G)</tex> после того, как <tex>n</tex> был рассмотрен алгоритмом Дейкстры.<br />
<br />
...<br />
<br />
Более того, мы докажем, что структура исследованной части <tex>P(G)</tex> не изменится.<br />
<br />
{{Лемма<br />
|about=7<br />
|statement=Пусть <tex>n</tex> будет произвольным узлов в <tex>P(G)</tex>. Если <tex>h</tex> допустимая функция, то <tex>n</tex> никогда не изменит свою позицию после того, как он был рассмотрен алгоритмом Дейкстры.<br />
|proof=...<br />
}}<br />
<br />
<br />
Леммы 6 и 7 обеспечивают, что изменения в <tex>P(G)</tex>, которые индуцируются A*, не влияют на часть <tex>P(G)</tex>, которую алгоритм Дейкстры уже исследовал. Это гарантирует корректность поиска Дейкстры на <tex>P(G)</tex>, если используемая эвристика допустимая. Таким образом, каждый путь, который предоставляет алгоритм Дейкстры корректен и его длина действительна. Однако, это не обеспечивает завершенность поиска Дейкстры на <tex>P(G)</tex>.<br />
<br />
Возможно, что узел <tex>n'</tex> присоединяется к другом узлу n как ребенок, после раскрытия узла <tex>n</tex>. В этом случае братья <tex>n'</tex> будут рассотрены до того, как <tex>n'</tex> станет ребенком n. Поэтому мы должны рассмотреть то, что было упущено во время поиска в связи с отсутствием <tex>n'</tex>. Мы добиваемся этого путем применения строк 20-22 к <tex>n'</tex> для каждого раскрытого направленного predecessor-а узла <tex>n'</tex>. Если <tex>n'</tex> ещё не выполняет условие планирования, A* будет неоднократно возобновляться пока механизм планирования не допустит алгоритму Дейкстры положить <tex>n'</tex> в поисковую очередь. Заметим, что таким образом не требуется каких-либо дополнительных усилий во время типичного поиска Дейкстры.<br />
<br />
Мы может быть уверены, что нерассмотренные узлы не будут принудительно опущены после применения операции heaping-up к <tex>n'</tex>. Иначе, мы могли бы иметь узел <tex>n''</tex>, который являлся бы ребенком n и впоследствии был бы заменен узлом <tex>n'</tex>. Заметим, что <tex>n''</tex> должен быть рассмотрен, поскольку <tex>n'</tex> был раскрыт. Однако, это противоречение к лемме 7, которая гарантирует, что этого не произойдет.<br />
<br />
Более того, следующее следствие гарантирует, что наилучшее <tex>d(n')</tex> не лучше, чем <tex>d</tex>-значение любой рассмотренной вершины, в частности, раскрытой вершины. Это означает, что мы не упустим возможность раскрыть <tex>n'</tex>.<br />
<br />
{{Лемма<br />
|about=следствие 3<br />
|statement=Пусть <tex>n</tex> будет узлом в <tex>P(G)</tex>, который был раскрыт Дейкстрой. Кроме того, пусть <tex>m</tex> будет узлом, который заново добавляется в <tex>P(G)</tex> или его позиция изменена, после того как <tex>n</tex> был раскрыт. Если <tex>h</tex> допустимая, тогда выполяется следующее:<br />
<tex>C_{P(G)}(R,m) >= d(n)</tex><br />
|proof=...<br />
}}<br />
<br />
== 4.6 Пример ==<br />
Мы проиллюстрируем работу алгоритма K* следующим примером. Мы будем рассматривать ориентированный взвешанный граф G на рисунке 7. Стартовой вершиной будет называться <tex>s_0</tex> и конечной вершиной - <tex>s_6</tex>. Нас интересует поиск 9 лучших путей из <tex>s_0</tex> в <tex>s_6</tex>. Для достижения этой цели мы применим алгоритм K* к <tex>G</tex>. Предположим, что эвристическая оценка существует. Значения эвристики даны в пометках c <tex>h(s_0)</tex> по <tex>h(s_6)</tex> на рисунке 7. Легко заметить, что эвристическая функция допустима.<br />
<br />
Первый раз A* делает итерации на графе <tex>G</tex> до тех пор, пока не будет найдена вершина <tex>s_6</tex>. Часть графа <tex>G</tex>, которая уже была рассмотрена иллиюстрируется на рисунке 8. Ребра, изображенные сплошными линиями, обозначают ребра дерева, в то время, как все остальные - запасные ребра. Они будет храниться в кучах <tex>H_{in}</tex>, показанных на рисунке 9. Номера, присвоенные узлах кучи, соответствуют <tex>\delta</tex>-значениям. На этом этапе поиска A* приостановлен и <tex>P(G)</tex> построен. Первоначально, только назначенный корень <tex>R</tex> явно доступен в <tex>P(G)</tex>. Инициализируется алгоритм Дейкстры. Это означает, что узел <tex>R</tex> добавляется в поискую очередь Дейкстры. Планировщику требуется доступ к successors К для того, чтобы решить следует ли возобновлять Дейкстру или A*. На данном этапе должна быть построена деревянная куча <tex>H_{T}(s_6)</tex>. Куча <tex>H_{T}(s_4)</tex> требуется для построения <tex>H_{T}(s_6)</tex>. Следовательно, строются деревянные кучи <tex>H_{T}(s_6)</tex>, <tex>H_{T}(s_4)</tex>, <tex>H_{T}(s_2)</tex> и <tex>H_{T}(s_0)</tex>. Результат показан на рисунке 10, где сплошные линии представляют кучные ребра и пунктирные линии показывают кросс-ребра. Во избежание путаницы на рисунке некоторые из ребер не полностью изображены. Мы указываем каждое из них, используя короткую стрелку с конретной целью.<br />
<br />
После построения, как показано 10, планировщик проверяет только ребенка <tex>(s_4, s_2)</tex> узла <tex>R</tex> на предмет того, что <tex>g(s_6) + d(s_4,s_2) <= f(s_1)</tex>. Отметим, что <tex>s_1</tex> является головой поисковой очереди A*. Значение <tex>d(s_4,s_2)</tex> равно 2, т.е. <tex>g(s_6) + d(s_4,s_2) = 7 + 2 = 9 = f(s_1)</tex>. Следовательно, планировщик позволяет Дейкстре раскрыть <tex>R</tex> и вставить <tex>(s_4,s_2)</tex> в поисковую очередь. При раскрытии <tex>R</tex> находится первый путь из ответа. Он строится из пути <tex>P(G)</tex>, содержащего единственный узел <tex>R</tex>. Этот путь приводит к пустой последовательности запасных ребер. Напомним, что пустая последовательность запасных ребер соответствует пути из <tex>s_0</tex> в <tex>s_6</tex> в дереве поиска, а именно <tex>s_0s_2s_4s_6</tex> длиной 7. Затем поиск Дейкстры приостанавливается, потому что для successor-ов узла <tex>(s_4,s_2)</tex> не выполняется условие <tex>g(s_6)+d(n)<=f(s_1)</tex>. Следовательно, возобновляется A*.<br />
<br />
Мы предполагаем, что условие раскрытия определено как раскрытие одной вершины для того, чтобы пример был простым и иллюстративным. Поэтому A* раскрывает <tex>s_1</tex> и останавливается. Исследованная часть <tex>G</tex> на текущем этапе показана на рисунке 11. Результат раскрытия приведет к обнаружению 2 новых запасных ребер <tex>(s_1,s_2)</tex> и <tex>(s_1,s_6)</tex>, которые будут добавлены в <tex>H_{in}(s_2)</tex> и <tex>H_{in}(s_6)</tex> соответственно. Обновленные кучи <tex>H_{in}(s_2)</tex> и <tex>H_{in}(s_6)</tex> представлены на рисунке 12. Другие кучи остаются неизменными, как на рисунке 9. Граф путей <tex>P(G)</tex> перестаивается, как показано на рисунке 13. Затем алгоритм Дейкстры возобновляется. Заметим, что поисковая очередь Дейкстры содержит только <tex>(s_4,s_2)</tex> с <tex>d = 2</tex> на этом моменте. Используя ручное выполнение мы можем легко увидеть, что Дейкстра будет выдавать в ответ пути, перечисленные в таблице 1.</div>Kabanovhttp://neerc.ifmo.ru/wiki/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:Kstar-figure-5.png&diff=48980Файл:Kstar-figure-5.png2015-08-06T09:20:36Z<p>Kabanov: </p>
<hr />
<div></div>Kabanovhttp://neerc.ifmo.ru/wiki/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:Kstar-figure-4.png&diff=48979Файл:Kstar-figure-4.png2015-08-06T09:17:25Z<p>Kabanov: </p>
<hr />
<div></div>Kabanovhttp://neerc.ifmo.ru/wiki/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:Kstar-figure-3.png&diff=48978Файл:Kstar-figure-3.png2015-08-06T09:13:50Z<p>Kabanov: </p>
<hr />
<div></div>Kabanovhttp://neerc.ifmo.ru/wiki/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Kabanov&diff=48977Участник:Kabanov2015-08-05T13:29:23Z<p>Kabanov: </p>
<hr />
<div>Также как и алгоритм Эппштейна, K* выполняет поиск пути на графе <tex>G</tex> и использует граф путей <tex>P(G)</tex>. Граф путей ищется с помощью алгоритма Дейкстры для того, чтобы вычислить пути <tex>s-t</tex> в виде последовательности запасных путей. Общий принцип работы алгоритма K* следующий:<br />
<br />
1) K* применяет A* на графе <tex>G</tex> вместо обратного алгоритма Дейкстры, который используется алгоритмом Эппштейна.<br />
2) Мы запускаем A* на <tex>G</tex> и Дейкстру на <tex>P(G)</tex> в поочередном порядке, который позволяет Дейкстре вычислить требуемые пути до заверешения полного поиска алгоритма A* на графе <tex>G</tex> .<br />
<br />
== 4.1 Поиск A* на G == <br />
K* применяет A* к входному графу <tex>G</tex> для того, чтобы построить дерево поиска <tex>T</tex>. Заметим, что A*, также как и алгоритм Дейкстры, строит дерево поиска в процессе нахождения кратчайшего пути <tex>s-t</tex> . Эти деревья формируются с помощью ссылок на родительские узлы, которые хранятся в том время, как A* производит итерации для того, чтобы восстановить путь <tex>s-t</tex>, когда вершина <tex>t</tex> ещё не найдена. Запасные ребра, открытые в процессе поиска A* на графе G, немедленно добавляются в граф P(G), структура которого будет объясняться в разделе 4.3. <br />
<br />
В K* A* применяется к графу <tex>G</tex> в прямом направлении в отличие от алгоритма Эппштейна, из-за чего корнем дерева <tex>T</tex> является вершина начальная <tex>s</tex>. Это необходимо для того, чтобы была возможность работать c неявным описанием графа <tex>G</tex> через функцию successor (функция, возвращающая список исходящих ребер из данной вершины). На протяжение статьи будем считать граф <tex>G</tex> конечным, если не будет сказано иное. Заметим, что А* корректен на конечных графах. Будем следовать литературному соглашению, предполагая, что стоимость бесконечного пути неограниченна.<br />
<br />
== 4.2 Стоимость объезда ==<br />
Для ребра <tex>(u, v)</tex> стоимость '''объезда''' (англ. ''detour'') <tex>\delta(u, v)</tex> представляет стоимость '''ущерба''' (англ. ''disadvantage'') из-за взятия ребра объезда <tex>(u,v)</tex> в сравнении с кратчайшим путем <tex>s-t</tex> через <tex>v</tex>. Ни длина кратчайшего пути <tex>s-t</tex> через <tex>v</tex>, ни длина пути <tex>s-t</tex>, включающего запапасные ребра <tex>(u, v)</tex> не известны, когда A* обнаруживает <tex>(u, v)</tex>. Обе длины могут быть оценены с помощью функции оценки <tex>f</tex>, которая использует эвристическую функцию <tex>h</tex>. Пусть <tex>f(v)</tex> будет <tex>f</tex>-значением с соответствии с деревом поиска <tex>T</tex> и <tex>f_u(v)</tex> будет <tex>f</tex>-значением в соответствии с родителем u, т.е. <tex>f_u(v) = g(u) + c(u, v) + h(v)</tex>. Тогда <tex>\delta(u, v)</tex> может быть определена так:<br />
<br />
<tex>\delta(u, v) = f_u(v) - f(v) = g(u) + c(u, v) + h(v) - g(v) - h(v) = g(u) + c(u, v) - g(v)</tex><br />
<br />
Заметим, что <tex>\delta(u, v)</tex> дает точную объездную метрику, поскольку оценочное <tex>h</tex>-значение не появляется в определении функции <tex>\delta(u, v)</tex>.<br />
<br />
== 4.3 Структура графа путей ==<br />
Структура графа путей <tex>P(G)</tex> довольно сложная. В принципе, <tex>P(G)</tex> будет ориентированным графом, вершины которого соответствуют ребрам в исходном графе <tex>G</tex>. Он будет организован как коллекция взаимосвязанных '''куч''' (англ. ''heap''). 2 бинарные кучи минимума присвоены к каждой вершине <tex>v</tex> в графе <tex>G</tex>, которые называются '''входящей кучей''' (англ. ''incoming heap'')<tex>H_{in}(v)</tex> и '''деревянной кучей''' (англ. ''tree heap'') <tex>H_{T}(v)</tex>. Эти кучи являются базисом <tex>P(G)</tex>. Как мы покажем далее, использование этих куч также играет главную роль в поддержании асимптотической сложности K*, также как в EA и LVEA.<br />
<br />
Входящая куча <tex>H_{in}(v)</tex> содержит узлы для каждого запасного ребра к вершине <tex>v</tex>, которые до сих пор были обнаружены A*. Узлы <tex>H_{in}(v)</tex> будут упорядочены в соответствии с <tex>\delta</tex>-значением соответствующих переходов. Узел владеющий ребром с минимальной стоимостью ущерба будет расположен на вершине кучи. Мы ограничим структуру кучи <tex>H_{in}(v)</tex> таким образом, что её корень в отличие от остальных узлов, будет иметь не более 1 ребенка. Обозначим его <tex>root_{in}(v)</tex>.<br />
<br />
Деревянная куча <tex>H_{T}(v)</tex> для произвольной вершины <tex>v</tex> строится следующим образом. Если <tex>v</tex> - стартовая вершина, т.е. <tex>v = s</tex>, то <tex>H_{T}(v)</tex> будет изначально пустой кучей. Затем в неё будет добавлен <tex>root_{in}(s)</tex>, если <tex>H_{in}(s)</tex> не пустая. Если <tex>v</tex> не стартовая вершина, то пусть вершина <tex>u</tex> будет родителем вершины <tex>v</tex> в дереве поиска <tex>T</tex>. Мы можем представить, что <tex>H_{T}(v)</tex> конструируется как копия <tex>H_{T}(u)</tex>, в которую добавлен <tex>root_{in}(v)</tex>. Если <tex>H_{in}(v)</tex> пустая, то <tex>H_{T}(v)</tex> идентична <tex>H_{T}(u)</tex>. Однако, для экономии памяти мы создаем только дешевую копию <tex>H_{T}(u)</tex>. Это осуществляется через создание копий только тех узлов кучи, которые лежат на обновленном пути в <tex>H_{T}(u)</tex>. Оставшаяся часть <tex>H_{T}(u)</tex> не копируется. Другими словами, <tex>root_{in}(v)</tex> вставляется в <tex>H_{T}(u)</tex> неразрушающим путем так, что структура <tex>H_{T}(u)</tex> сохраняется. В куче <tex>H_{T}(v)</tex> к <tex>root_{in}(v)</tex> могут быть присоединены 1 или 2 ребенка. К тому же, <tex>root_{in}(v)</tex> хранит только 1 собственного ребенка из <tex>H_{in}(v)</tex>. Мы обозначим корень <tex>H_{T}(v)</tex> как <tex>R(v)</tex>.<br />
<br />
Назовем ребра, которые берут начало из входящих или деревянных куч, '''кучными ребрами''' (англ. ''heap edges''). Сформулируем следующую лемму.<br />
{{Лемма<br />
|about=1<br />
|statement=Все узлы, которые достижимы из <tex>R(v)</tex> через кучные ребра, для каждой вершины <tex>v</tex> формируют тернарную кучу, упорядоченную в соответствии с <tex>\delta</tex>-значением. Мы назовем такую кучу графовой кучей вершины <tex>v</tex> и обозначим её как <tex>H_{G}(v)</tex>.<br />
|proof=...<br />
}}<br />
<br />
Финальная структура <tex>P(G)</tex> получется из входящих и деревянных куч следующим образом. К каждому узлу <tex>n</tex> из <tex>P(G)</tex>, несущему ребро <tex>(u,v)</tex>, мы присоединим указатель, ссылающийся на <tex>R(u)</tex>, который является корневым узлом <tex>H_{T}(u)</tex>. Мы назовем такие указатели '''кросс-ребрами''' (англ. ''cross edges''), в то время как указатели, возникающие из куч названы кучными ребрами, как упоминалось раньше. Более того, мы добавим специальный узел <tex>\mathrm{R}</tex> в <tex>P(G)</tex> с одним выходящим кросс-ребром к <tex>R(t)</tex>.<br />
<br />
Более того, мы определим весовую функцию <tex>\Delta</tex> на ребрах из <tex>P(G)</tex>. Пусть <tex>(n,n')</tex> обозначает ребро в <tex>P(G)</tex>, и пусть <tex>e</tex> и <tex>e'</tex> обозначают ребра из <tex>G</tex>, соответствующие узлам <tex>n</tex> и <tex>n'</tex>. Тогда определим <tex>\Delta(n,n')</tex> следующим образом:<br />
<br />
<tex><br />
\Delta(n,n')=\begin{cases}<br />
\delta(e') - \delta(e),& \text{if}\ (n,n')\ \text{heap edge} \\<br />
\delta(e'),& \text{if}\ (n,n')\ \text{cross edge}.<br />
\end{cases}<br />
</tex><br />
<br />
Лемма 1 подразумевает, что куча упорядоченная в соответствии с <tex>\delta</tex>-значанием поддерживается для любого кучного ребра из <tex>P(G)</tex>. Эта упорядочивание кучи подразумевает, что <tex>\Delta(n,n')</tex> неотрицательна для любого кучного ребра <tex>(n,n')</tex>. Следовательно, <tex>\Delta</tex> также неотрицательна, т.е. <tex>\Delta(n,n') >= 0</tex> для любого ребра <tex>(n,n')</tex> в <tex>P(G)</tex>. Стоимость пути <tex>\sigma</tex>, т.е. <tex>C_{P(G)}(\sigma)</tex> равна <tex>\sum_{e \in \sigma}\Delta(e)</tex>. <br />
<br />
...<br />
<br />
{{Лемма<br />
|about=2<br />
|statement=Пусть <tex>n</tex> будет узлом графовой кучи <tex>H_{G}(w)</tex> для какой-нибудь вершины <tex>w</tex>. Пусть <tex>(u,v)</tex> будет ребром связанным с <tex>n</tex>. Тогда существует путь в дереве поиска <tex>T</tex> из <tex>v</tex> в <tex>w</tex>.<br />
|proof=...<br />
}}<br />
<br />
== 4.4 Алгоритмическая структура K* ==<br />
Алгоритмический принцип K* следующий. Будем запускать алгоритмы Дейкстры и A* на <tex>G</tex> с чередованием. Сначала, мы выполним A* на <tex>G</tex>, который будет работать до тех, пока вершина <tex>t</tex> не будет выбрана из очереди для раскрытия. Затем, вы запустим алгоритм Дейкстры на доступной части <tex>P(G)</tex>. Каждый узел раскрытый Дейкстрой представляет путь. Если точнее, то путь <tex>\sigma</tex> в <tex>P(G)</tex>, по которому Дейкстра достигла этого узла является решением. Путь <tex>s-t</tex> может быть построен из <tex>\sigma</tex> за линейное время путем вычисления последовательности запасных ребер <tex>seq(\sigma)</tex> и затем <tex>s-t</tex> пути из неё. Если Дейкстра находит <tex>k</tex> кратчайших путей, то K* завершается успешно. Иначе, A* возобновляется для исследования большей части <tex>G</tex>. Это приводит к росту <tex>P(G)</tex>, на котором алгоритм Дейкстры затем будет возобновлен. Мы будем повторять этот процесс до тех пор, пока алгоритм Дейкстры не найдет <tex>k</tex> кратчайших путей. <br />
<br />
Data: A graph given by its start vertex s ∈ V and its successor function succ and a<br />
natural number k<br />
Result: A list R containing k sidetrack edge sequences representing k solution paths<br />
<br />
1 <tex>open_D</tex> ← пустая приоритетная очередь<br />
2 <tex>closed_D</tex> ← пустая хеш-таблица<br />
3 <tex>R</tex> ← пустой список<br />
4 <tex>P(G)</tex> ← пустой граф путей<br />
5 Выполняем A* на графе <tex>G</tex> пока <tex>t</tex> не будет выбрана для раскрытия<br />
6 Если вершина <tex>t</tex> не была достигнута, то выходим без ответа<br />
7 Кладем <tex>\mathrm{R}</tex> в очередь <tex>open_D</tex><br />
8 '''while''' A queue or open D is not empty:<br />
9 '''if''' A queue is not empty:<br />
10 '''if''' очередь <tex>open_D</tex> не пуста:<br />
11 Let u be the head of the search queue of A ∗ and n the head of <tex>open_D</tex><br />
12 <tex>d = max\{ d(n) + \Delta(n, n') | n' \in succ(n) \}</tex><br />
13 '''if''' <tex>g(t) + d <= f</tex> (u) then переходим на строку 17.<br />
14 Возобновляем A* для того, чтобы исследовать более большую часть графа <tex>G</tex><br />
15 Обновляем <tex>P(G)</tex> and bring Dijkstra’s search into a consistent status<br />
16 Переходим на строку 8<br />
17 '''if''' очередь <tex>open_D</tex> пуста: переходим на строку 8.<br />
18 Remove from <tex>open_D</tex> and place on <tex>closed_D</tex> the node n with the minimal d-value.<br />
19 '''foreach''' <tex>n'</tex> referred by n in P(G):<br />
20 <tex>d(n') = d(n) + \Delta(n, n')</tex><br />
21 Attach to <tex>n'</tex> a parent link referring to <tex>n</tex>.<br />
22 Insert n 0 into <tex>open_D</tex><br />
23 Пусть <tex>\sigma</tex> будет путем в <tex>P(G)</tex>, через который узел n был достигнут.<br />
24 Добавим <tex>seq(\sigma)</tex> в конец списка <tex>R</tex>.<br />
25 '''if''' <tex>|R| = k</tex>: переходим на строку 26.<br />
26 Return R and exit.<br />
<br />
Алгоритм 1 содержит псевдокод K*. Код с 8 по 25 строчку образует главный цикл K*. Цикл завершается, когда очереди обоих алгоритмов А* и Дейкстры пусты. До 8 строчки выполняет некоторые подготовительные вещи. После инициализации, А* запускает на 5 строчке пока вершина <tex>t</tex> не будет выбрана им для рассмотрения, в этом случае кратчайший путь <tex>s-t</tex> будет найден. Если <tex>t</tex> не достигнута, то алгоритм завершается без ответа. Отметим, что он не завершится на бесконечных графах. Иначе, алгоритм добавляет специальную вершину <tex>R</tex>, которая назначена корнем <tex>P(G)</tex>, в поисковую очередь алгоритма Дейкстры. Затем, K* входит в главный цикл.<br />
<br />
K* поддерживает механизм планирования для контролирования, когда A* или Дейкстра будет возобновлены. Если очередь из A* не пуста, что означает, что А* ещё не завершил исследования всего графа G, то Дейкстра возобновляется тогда и только тогда, когда <tex>g(t) + d <= f(u)</tex>. Значение <tex>d</tex> является максимальным <tex>d</tex>-значением среди всех successor-ов головы поисковой очереди <tex>n</tex> алгоритма Дейкстры. Вершина <tex>u</tex> является головой поисковой очереди A*. Напомним, что <tex>d</tex> - функция расстояния, используемая в алгоритме Дейкстры. Если очередь поиска Дейкстры пуста или <tex>g(t) + d > f(u)</tex>, то А* возобновляется для того, чтобы исследовать более большую часть графа <tex>G</tex> (строка 14). То, как долго мы ему позволим работать, является компромиссом. Если мы запустим его только на маленьком количестве шагов, то мы дадим Дейкстре шанс найти необходимое количество путей скорее, чем они будут доступны в <tex>P(G)</tex>. С другой стороны, мы вызываем накладные расходы путем переключения A* и Дейкстры и поэтому должны ограничить количество переключений. Эти накладные расходы вызваны тем фактом, что после возобновления A* (строка 14), структура графа <tex>P(G)</tex> может измениться. Следовательно нам необходимо обновить <tex>P(G)</tex> (строка 15), как мы будет широко обсуждать в разделе 4.5. Это требует последующую проверку статуса Дейкстры. Мы должны быть уверены, что Дейкстра поддерживает согласованное состояние после изменений в <tex>P(G)</tex>. K* предусматривает условие, которые управляет решением, когда остановить A*, которое мы назовем ''условие расширения''. Для того, чтобы поддерживать аналогичную асимптотическую сложность как у EA и LVEA, мы должны определить условие расширения так, чтобы A* выполнялся пока количество рассмотренных вершин и количество внутренних ребер удваивается или <tex>G</tex> полностью исследован. Мы обсудим эту проблему несколько подробнее позже. В качестве полезного свойства, K* позволяет другое определения этого условия, которое может быть более эффективным на практике. В наших экспериментах в разделе 6, мы определили условие расширения так, что количество рассмотренных вершин или количество рассмотренных ребер ребер возрастает на 20% при каждом запуске A*. Этот механизм планирования включен до тех пор, пока A* не закончит исследовать весь граф <tex>G</tex>. Как только A* исследует весь граф <tex>G</tex> (строка 9), механизм планирования отключается и в дальнейшем работает только алгоритм Дейкстры.<br />
<br />
Строки 18-22 представляют обычный шаг рассмотрения узла алгоритмом Дейкстры. Отметим, что когда successor-узел <tex>n'</tex> сгенерирован, K* не проверяет был ли <tex>n'</tex> уже посещен до этого. Другими словами, каждый раз, когда узел генерируется, он рассматривает как новый. Эта стратегия обоснована на наблюдении, что путь s-t может содержать одно и то же ребро несколько раз. Строка 24 добавляет следующий путь <tex>s-t</tex> в результирующее множество R. Это делается путем конструирования последовательности запасных ребер <tex>seq(\sigma)</tex> из пути <tex>\sigma</tex>, через которые Дейкстра достигла узла <tex>n</tex>, который был только что рассмотрен. Алгоритм завершается, когда в результирующее множество добавлено <tex>k</tex> последовательностей запасных ребер (строка 25).<br />
<br />
== 4.5 Взаимосвязь алгоритмов Дейкстры и A* ==<br />
Тот факт, что оба алгоритма A* и Дейкстры делят между собой граф путей <tex>P(G)</tex>, вызывает обеспокоенность в отношении правильности работы Дейкстры на <tex>P(G)</tex>. Возобновление A* приводит к изменениям в структуре <tex>P(G)</tex>. Таким образом, после возобновления A*, мы обновляем <tex>P(G)</tex> и проверяет статус поиска Дейкстры (строка 15). В основном, A* может добавить новые узлы, менять <tex>\delta</tex>-значения существующих узлов или даже удалять узлы. A* может также существенно изменять дерево поиска <tex>T</tex>, которое будет в худшем случае разрушать структуру все деревянных куч <tex>H_{T}</tex>. Эти изменения могут приводить к глобальной реструктуризации или даже перестроению <tex>P(G)</tex> с нуля. В худшем случае это может сделать предыдущие поиски Дейкстры на <tex>P(G)</tex> бесполезными таким образом, что нам придется перезапускать алгоритм Дейкстры с нуля.<br />
<br />
Если использованная эвристическая оценка допустимая, то наше положение лучше. Нам по-прежнему может понадобится перестроение <tex>P(G)</tex>, но мы покажем, что это перестроение не мешает корректности поиска Дейкстры на <tex>P(G)</tex>. Другими словами, мы не теряем результаты, до сих пор полученные поиском Дейкстры. В случае монотонной эвристической оценки мы даже не нуждаемся в перестроении <tex>P(G)</tex>. Если <tex>h</tex> монотонная, то дерево поиска A* является деревом кратчайшего пути для всех раскрытых вершин. Следовательно, g-значения раскрытых вершин не изменится. Это означает, что <tex>\delta</tex>-значения для внутренних ребер никогда не изменятся. Ребра дерева раскрытых вершин не изменятся также. Следовательно, обновление <tex>\delta</tex>-значений, heaping-up, heaping-down (операции в кучах) или удаление узлов не влекут за собой каких-либо изменений в <tex>P(G)</tex>. Только добавление новых узлов приводит к изменениям в <tex>P(G)</tex>. Следовательно, перестроение или глобальная реструктуризация не требуется в данном случае.<br />
<br />
В оставшейся части этого раздела, мы сначала покажем, что корректность поиска Дейкстры на <tex>P(G)</tex> поддерживается в случае допустимой эвристической оценки. После этого мы покажем, что изменения в <tex>P(G)</tex> могут помешать завершенности поиска Дейкстры независимо от того, является ли эвристика допустимой или даже монотонной. Следовательно, мы предложим механизм для её поддержания.<br />
<br />
Мы фокусируемся дальше на корректности поиска Дейкстры на <tex>P(G)</tex> в случае допустимой эвристической оценки. Сначала, мы заявляем, что если <tex>h</tex> допустимая, то узлы исследованной части <tex>P(G)</tex> не поменяют свои <tex>\delta</tex>-значения.<br />
<br />
{{Лемма<br />
|about=6<br />
|statement=Пусть <tex>n</tex> будет произвольным узлов в <tex>P(G)</tex> и пусть <tex>(u,v)</tex> будет ребром, связанным с <tex>n</tex>. Если <tex>h</tex> допустимая функция, то значение <tex>\delta(u, v)</tex> никогда не изменится после того, как <tex>n</tex> будет рассмотрен алгоритмом Дейкстры.<br />
|proof=...<br />
}}<br />
<br />
Из леммы 6 мы может вывести следующее следствие.<br />
<br />
Следствие 2. Пусть <tex>n</tex> будет произвольным узлов в <tex>P(G)</tex>. Если <tex>h</tex> допустимая функция, то <tex>n</tex> никогда не будет удален из <tex>P(G)</tex> после того, как <tex>n</tex> был рассмотрен алгоритмом Дейкстры.<br />
<br />
...<br />
<br />
Более того, мы докажем, что структура исследованной части <tex>P(G)</tex> не изменится.<br />
<br />
{{Лемма<br />
|about=7<br />
|statement=Пусть <tex>n</tex> будет произвольным узлов в <tex>P(G)</tex>. Если <tex>h</tex> допустимая функция, то <tex>n</tex> никогда не изменит свою позицию после того, как он был рассмотрен алгоритмом Дейкстры.<br />
|proof=...<br />
}}<br />
<br />
<br />
Леммы 6 и 7 обеспечивают, что изменения в <tex>P(G)</tex>, которые индуцируются A*, не влияют на часть <tex>P(G)</tex>, которую алгоритм Дейкстры уже исследовал. Это гарантирует корректность поиска Дейкстры на <tex>P(G)</tex>, если используемая эвристика допустимая. Таким образом, каждый путь, который предоставляет алгоритм Дейкстры корректен и его длина действительна. Однако, это не обеспечивает завершенность поиска Дейкстры на <tex>P(G)</tex>.<br />
<br />
Возможно, что узел <tex>n'</tex> присоединяется к другом узлу n как ребенок, после раскрытия узла <tex>n</tex>. В этом случае братья <tex>n'</tex> будут рассотрены до того, как <tex>n'</tex> станет ребенком n. Поэтому мы должны рассмотреть то, что было упущено во время поиска в связи с отсутствием <tex>n'</tex>. Мы добиваемся этого путем применения строк 20-22 к <tex>n'</tex> для каждого раскрытого направленного predecessor-а узла <tex>n'</tex>. Если <tex>n'</tex> ещё не выполняет условие планирования, A* будет неоднократно возобновляться пока механизм планирования не допустит алгоритму Дейкстры положить <tex>n'</tex> в поисковую очередь. Заметим, что таким образом не требуется каких-либо дополнительных усилий во время типичного поиска Дейкстры.<br />
<br />
Мы может быть уверены, что нерассмотренные узлы не будут принудительно опущены после применения операции heaping-up к <tex>n'</tex>. Иначе, мы могли бы иметь узел <tex>n''</tex>, который являлся бы ребенком n и впоследствии был бы заменен узлом <tex>n'</tex>. Заметим, что <tex>n''</tex> должен быть рассмотрен, поскольку <tex>n'</tex> был раскрыт. Однако, это противоречение к лемме 7, которая гарантирует, что этого не произойдет.<br />
<br />
Более того, следующее следствие гарантирует, что наилучшее <tex>d(n')</tex> не лучше, чем <tex>d</tex>-значение любой рассмотренной вершины, в частности, раскрытой вершины. Это означает, что мы не упустим возможность раскрыть <tex>n'</tex>.<br />
<br />
{{Лемма<br />
|about=следствие 3<br />
|statement=Пусть <tex>n</tex> будет узлом в <tex>P(G)</tex>, который был раскрыт Дейкстрой. Кроме того, пусть <tex>m</tex> будет узлом, который заново добавляется в <tex>P(G)</tex> или его позиция изменена, после того как <tex>n</tex> был раскрыт. Если <tex>h</tex> допустимая, тогда выполяется следующее:<br />
<tex>C_{P(G)}(R,m) >= d(n)</tex><br />
|proof=...<br />
}}<br />
<br />
== 4.6 Пример ==<br />
Мы проиллюстрируем работу алгоритма K* следующим примером. Мы будем рассматривать ориентированный взвешанный граф G на рисунке 7. Стартовой вершиной будет называться <tex>s_0</tex> и конечной вершиной - <tex>s_6</tex>. Нас интересует поиск 9 лучших путей из <tex>s_0</tex> в <tex>s_6</tex>. Для достижения этой цели мы применим алгоритм K* к <tex>G</tex>. Предположим, что эвристическая оценка существует. Значения эвристики даны в пометках c <tex>h(s_0)</tex> по <tex>h(s_6)</tex> на рисунке 7. Легко заметить, что эвристическая функция допустима.<br />
<br />
Первый раз A* делает итерации на графе <tex>G</tex> до тех пор, пока не будет найдена вершина <tex>s_6</tex>. Часть графа <tex>G</tex>, которая уже была рассмотрена иллиюстрируется на рисунке 8. Ребра, изображенные сплошными линиями, обозначают ребра дерева, в то время, как все остальные - запасные ребра. Они будет храниться в кучах <tex>H_{in}</tex>, показанных на рисунке 9. Номера, присвоенные узлах кучи, соответствуют <tex>\delta</tex>-значениям. На этом этапе поиска A* приостановлен и <tex>P(G)</tex> построен. Первоначально, только назначенный корень <tex>R</tex> явно доступен в <tex>P(G)</tex>. Инициализируется алгоритм Дейкстры. Это означает, что узел <tex>R</tex> добавляется в поискую очередь Дейкстры. Планировщику требуется доступ к successors К для того, чтобы решить следует ли возобновлять Дейкстру или A*. На данном этапе должна быть построена деревянная куча <tex>H_{T}(s_6)</tex>. Куча <tex>H_{T}(s_4)</tex> требуется для построения <tex>H_{T}(s_6)</tex>. Следовательно, строются деревянные кучи <tex>H_{T}(s_6)</tex>, <tex>H_{T}(s_4)</tex>, <tex>H_{T}(s_2)</tex> и <tex>H_{T}(s_0)</tex>. Результат показан на рисунке 10, где сплошные линии представляют кучные ребра и пунктирные линии показывают кросс-ребра. Во избежание путаницы на рисунке некоторые из ребер не полностью изображены. Мы указываем каждое из них, используя короткую стрелку с конретной целью.<br />
<br />
После построения, как показано 10, планировщик проверяет только ребенка <tex>(s_4, s_2)</tex> узла <tex>R</tex> на предмет того, что <tex>g(s_6) + d(s_4,s_2) <= f(s_1)</tex>. Отметим, что <tex>s_1</tex> является головой поисковой очереди A*. Значение <tex>d(s_4,s_2)</tex> равно 2, т.е. <tex>g(s_6) + d(s_4,s_2) = 7 + 2 = 9 = f(s_1)</tex>. Следовательно, планировщик позволяет Дейкстре раскрыть <tex>R</tex> и вставить <tex>(s_4,s_2)</tex> в поисковую очередь. При раскрытии <tex>R</tex> находится первый путь из ответа. Он строится из пути <tex>P(G)</tex>, содержащего единственный узел <tex>R</tex>. Этот путь приводит к пустой последовательности запасных ребер. Напомним, что пустая последовательность запасных ребер соответствует пути из <tex>s_0</tex> в <tex>s_6</tex> в дереве поиска, а именно <tex>s_0s_2s_4s_6</tex> длиной 7. Затем поиск Дейкстры приостанавливается, потому что для successor-ов узла <tex>(s_4,s_2)</tex> не выполняется условие <tex>g(s_6)+d(n)<=f(s_1)</tex>. Следовательно, возобновляется A*.<br />
<br />
Мы предполагаем, что условие раскрытия определено как раскрытие одной вершины для того, чтобы пример был простым и иллюстративным. Поэтому A* раскрывает <tex>s_1</tex> и останавливается. Исследованная часть <tex>G</tex> на текущем этапе показана на рисунке 11. Результат раскрытия приведет к обнаружению 2 новых запасных ребер <tex>(s_1,s_2)</tex> и <tex>(s_1,s_6)</tex>, которые будут добавлены в <tex>H_{in}(s_2)</tex> и <tex>H_{in}(s_6)</tex> соответственно. Обновленные кучи <tex>H_{in}(s_2)</tex> и <tex>H_{in}(s_6)</tex> представлены на рисунке 12. Другие кучи остаются неизменными, как на рисунке 9. Граф путей <tex>P(G)</tex> перестаивается, как показано на рисунке 13. Затем алгоритм Дейкстры возобновляется. Заметим, что поисковая очередь Дейкстры содержит только <tex>(s_4,s_2)</tex> с <tex>d = 2</tex> на этом моменте. Используя ручное выполнение мы можем легко увидеть, что Дейкстра будет выдавать в ответ пути, перечисленные в таблице 1.</div>Kabanovhttp://neerc.ifmo.ru/wiki/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Kabanov&diff=48976Участник:Kabanov2015-08-05T13:15:25Z<p>Kabanov: </p>
<hr />
<div>Также как и алгоритм Эппштейна, K* выполняет поиск пути на графе <tex>G</tex> и использует граф путей <tex>P(G)</tex>. Граф путей ищется с помощью алгоритма Дейкстры для того, чтобы вычислить пути <tex>s-t</tex> в виде последовательности запасных путей. Общий принцип работы алгоритма K* следующий:<br />
<br />
1) K* применяет A* на графе <tex>G</tex> вместо обратного алгоритма Дейкстры, который используется алгоритмом Эппштейна.<br />
2) Мы запускаем A* на <tex>G</tex> и Дейкстру на <tex>P(G)</tex> в поочередном порядке, который позволяет Дейкстре вычислить требуемые пути до заверешения полного поиска алгоритма A* на графе <tex>G</tex> .<br />
<br />
== 4.1 Поиск A* на G == <br />
K* применяет A* к входному графу <tex>G</tex> для того, чтобы построить дерево поиска <tex>T</tex>. Заметим, что A*, также как и алгоритм Дейкстры, строит дерево поиска в процессе нахождения кратчайшего пути <tex>s-t</tex> . Эти деревья формируются с помощью ссылок на родительские узлы, которые хранятся в том время, как A* производит итерации для того, чтобы восстановить путь <tex>s-t</tex>, когда вершина <tex>t</tex> ещё не найдена. Запасные ребра, открытые в процессе поиска A* на графе G, немедленно добавляются в граф P(G), структура которого будет объясняться в разделе 4.3. <br />
<br />
В K* A* применяется к графу <tex>G</tex> в прямом направлении в отличие от алгоритма Эппштейна, из-за чего корнем дерева <tex>T</tex> является вершина начальная <tex>s</tex>. Это необходимо для того, чтобы была возможность работать c неявным описанием графа <tex>G</tex> через функцию successor (функция, возвращающая список исходящих ребер из данной вершины). На протяжение статьи будем считать граф <tex>G</tex> конечным, если не будет сказано иное. Заметим, что А* корректен на конечных графах. Будем следовать литературному соглашению, предполагая, что стоимость бесконечного пути неограниченна.<br />
<br />
== 4.2 Стоимость объезда ==<br />
Для ребра <tex>(u, v)</tex> стоимость '''объезда''' (англ. ''detour'') <tex>\delta(u, v)</tex> представляет стоимость '''ущерба''' (англ. ''disadvantage'') из-за взятия ребра объезда <tex>(u,v)</tex> в сравнении с кратчайшим путем <tex>s-t</tex> через <tex>v</tex>. Ни длина кратчайшего пути <tex>s-t</tex> через <tex>v</tex>, ни длина пути <tex>s-t</tex>, включающего запапасные ребра <tex>(u, v)</tex> не известны, когда A* обнаруживает <tex>(u, v)</tex>. Обе длины могут быть оценены с помощью функции оценки <tex>f</tex>, которая использует эвристическую функцию <tex>h</tex>. Пусть <tex>f(v)</tex> будет <tex>f</tex>-значением с соответствии с деревом поиска <tex>T</tex> и <tex>f_u(v)</tex> будет <tex>f</tex>-значением в соответствии с родителем u, т.е. <tex>f_u(v) = g(u) + c(u, v) + h(v)</tex>. Тогда <tex>\delta(u, v)</tex> может быть определена так:<br />
<br />
<tex>\delta(u, v) = f_u(v) - f(v) = g(u) + c(u, v) + h(v) - g(v) - h(v) = g(u) + c(u, v) - g(v)</tex><br />
<br />
Заметим, что <tex>\delta(u, v)</tex> дает точную объездную метрику, поскольку оценочное <tex>h</tex>-значение не появляется в определении функции <tex>\delta(u, v)</tex>.<br />
<br />
== 4.3 Структура графа путей ==<br />
Структура графа путей <tex>P(G)</tex> довольно сложная. В принципе, <tex>P(G)</tex> будет ориентированным графом, вершины которого соответствуют ребрам в исходном графе <tex>G</tex>. Он будет организован как коллекция взаимосвязанных '''куч''' (англ. ''heap''). 2 бинарные кучи минимума присвоены к каждой вершине <tex>v</tex> в графе <tex>G</tex>, которые называются '''входящей кучей''' (англ. ''incoming heap'')<tex>H_{in}(v)</tex> и '''деревянной кучей''' (англ. ''tree heap'') <tex>H_{T}(v)</tex>. Эти кучи являются базисом <tex>P(G)</tex>. Как мы покажем далее, использование этих куч также играет главную роль в поддержании асимптотической сложности K*, также как в EA и LVEA.<br />
<br />
Входящая куча <tex>H_{in}(v)</tex> содержит узлы для каждого запасного ребра к вершине <tex>v</tex>, которые до сих пор были обнаружены A*. Узлы <tex>H_{in}(v)</tex> будут упорядочены в соответствии с <tex>\delta</tex>-значением соответствующих переходов. Узел владеющий ребром с минимальной стоимостью ущерба будет расположен на вершине кучи. Мы ограничим структуру кучи <tex>H_{in}(v)</tex> таким образом, что её корень в отличие от остальных узлов, будет иметь не более 1 ребенка. Обозначим его <tex>root_{in}(v)</tex>.<br />
<br />
Деревянная куча <tex>H_{T}(v)</tex> для произвольной вершины <tex>v</tex> строится следующим образом. Если <tex>v</tex> - стартовая вершина, т.е. <tex>v = s</tex>, то <tex>H_{T}(v)</tex> будет изначально пустой кучей. Затем в неё будет добавлен <tex>root_{in}(s)</tex>, если <tex>H_{in}(s)</tex> не пустая. Если <tex>v</tex> не стартовая вершина, то пусть вершина <tex>u</tex> будет родителем вершины <tex>v</tex> в дереве поиска <tex>T</tex>. Мы можем представить, что <tex>H_{T}(v)</tex> конструируется как копия <tex>H_{T}(u)</tex>, в которую добавлен <tex>root_{in}(v)</tex>. Если <tex>H_{in}(v)</tex> пустая, то <tex>H_{T}(v)</tex> идентична <tex>H_{T}(u)</tex>. Однако, для экономии памяти мы создаем только дешевую копию <tex>H_{T}(u)</tex>. Это осуществляется через создание копий только тех узлов кучи, которые лежат на обновленном пути в <tex>H_{T}(u)</tex>. Оставшаяся часть <tex>H_{T}(u)</tex> не копируется. Другими словами, <tex>root_{in}(v)</tex> вставляется в <tex>H_{T}(u)</tex> неразрушающим путем так, что структура <tex>H_{T}(u)</tex> сохраняется. В куче <tex>H_{T}(v)</tex> к <tex>root_{in}(v)</tex> могут быть присоединены 1 или 2 ребенка. К тому же, <tex>root_{in}(v)</tex> хранит только 1 собственного ребенка из <tex>H_{in}(v)</tex>. Мы обозначим корень <tex>H_{T}(v)</tex> как <tex>R(v)</tex>.<br />
<br />
Назовем ребра, которые берут начало из входящих или деревянных куч, '''кучными ребрами''' (англ. ''heap edges''). Сформулируем следующую лемму.<br />
{{Лемма<br />
|about=1<br />
|statement=Все узлы, которые достижимы из <tex>R(v)</tex> через кучные ребра, для каждой вершины <tex>v</tex> формируют тернарную кучу, упорядоченную в соответствии с <tex>\delta</tex>-значением. Мы назовем такую кучу графовой кучей вершины <tex>v</tex> и обозначим её как <tex>H_{G}(v)</tex>.<br />
|proof=...<br />
}}<br />
<br />
Финальная структура <tex>P(G)</tex> получется из входящих и деревянных куч следующим образом. К каждому узлу <tex>n</tex> из <tex>P(G)</tex>, несущему ребро <tex>(u,v)</tex>, мы присоединим указатель, ссылающийся на <tex>R(u)</tex>, который является корневым узлом <tex>H_{T}(u)</tex>. Мы назовем такие указатели '''кросс-ребрами''' (англ. ''cross edges''), в то время как указатели, возникающие из куч названы кучными ребрами, как упоминалось раньше. Более того, мы добавим специальный узел <tex>\mathrm{R}</tex> в <tex>P(G)</tex> с одним выходящим кросс-ребром к <tex>R(t)</tex>.<br />
<br />
Более того, мы определим весовую функцию <tex>\Delta</tex> на ребрах из <tex>P(G)</tex>. Пусть <tex>(n,n')</tex> обозначает ребро в <tex>P(G)</tex>, и пусть <tex>e</tex> и <tex>e'</tex> обозначают ребра из <tex>G</tex>, соответствующие узлам <tex>n</tex> и <tex>n'</tex>. Тогда определим <tex>\Delta(n,n')</tex> следующим образом:<br />
<br />
<tex><br />
\Delta(n,n')=\begin{cases}<br />
\delta(e') - \delta(e),& \text{if}\ (n,n')\ \text{heap edge} \\<br />
\delta(e'),& \text{if}\ (n,n')\ \text{cross edge}.<br />
\end{cases}<br />
</tex><br />
<br />
Лемма 1 подразумевает, что куча упорядоченная в соответствии с <tex>\delta</tex>-значанием поддерживается для любого кучного ребра из <tex>P(G)</tex>. Эта упорядочивание кучи подразумевает, что <tex>\Delta(n,n')</tex> неотрицательна для любого кучного ребра <tex>(n,n')</tex>. Следовательно, <tex>\Delta</tex> также неотрицательна, т.е. <tex>\Delta(n,n') >= 0</tex> для любого ребра <tex>(n,n')</tex> в <tex>P(G)</tex>. Стоимость пути <tex>\sigma</tex>, т.е. <tex>C_{P(G)}(\sigma)</tex> равна <tex>\sum_{e \in \sigma}\Delta(e)</tex>. <br />
<br />
...<br />
<br />
{{Лемма<br />
|about=2<br />
|statement=Пусть <tex>n</tex> будет узлом графовой кучи <tex>H_{G}(w)</tex> для какой-нибудь вершины <tex>w</tex>. Пусть <tex>(u,v)</tex> будет ребром связанным с <tex>n</tex>. Тогда существует путь в дереве поиска <tex>T</tex> из <tex>v</tex> в <tex>w</tex>.<br />
|proof=...<br />
}}<br />
<br />
== 4.4 Алгоритмическая структура K* ==<br />
Алгоритмический принцип K* следующий. Будем запускать алгоритмы Дейкстры и A* на <tex>G</tex> с чередованием. Сначала, мы запустим A* на <tex>G</tex> пока вершина <tex>t</tex> не будет выбрана из очереди для рассмотрения. Затем, вы запустим алгоритмы Дейкстры на доступной части <tex>P(G)</tex>. Каждый узел рассмотрел Дейкстрой представляет путь решения. Если точнее, то путь <tex>\sigma</tex> в <tex>P(G)</tex>, по которому Дейкстра достигла этого узла является решением. Путь <tex>s-t</tex> может быть построен из <tex>\sigma</tex> за линейное время путем вычисления последовательности запасных ребер <tex>seq(\sigma)</tex> и затем <tex>s-t</tex> пути из неё. Если Дейкстра находит <tex>k</tex> кратчайших путей, то K* завершается успешно. Иначе, A* возобновляется для исследования большей части <tex>G</tex>. Это приводит к росту <tex>P(G)</tex>, на котором алгоритм Дейкстры затем будет возобновлен. Мы будем повторять этот процесс до тех пор, пока алгоритм Дейкстры не найдет <tex>k</tex> кратчайших путей. <br />
<br />
Data: A graph given by its start vertex s ∈ V and its successor function succ and a<br />
natural number k<br />
Result: A list R containing k sidetrack edge sequences representing k solution paths<br />
<br />
1 <tex>open_D</tex> ← пустая приоритетная очередь<br />
2 <tex>closed_D</tex> ← пустая хеш-таблица<br />
3 <tex>R</tex> ← пустой список<br />
4 <tex>P(G)</tex> ← пустой граф путей<br />
5 Выполняем A* на графе <tex>G</tex> пока <tex>t</tex> не будет выбрана для раскрытия<br />
6 Если вершина <tex>t</tex> не была достигнута, то выходим без ответа<br />
7 Кладем <tex>\mathrm{R}</tex> в очередь <tex>open_D</tex><br />
8 '''while''' A queue or open D is not empty:<br />
9 '''if''' A queue is not empty:<br />
10 '''if''' очередь <tex>open_D</tex> не пуста:<br />
11 Let u be the head of the search queue of A ∗ and n the head of <tex>open_D</tex><br />
12 <tex>d = max\{ d(n) + \Delta(n, n') | n' \in succ(n) \}</tex><br />
13 '''if''' <tex>g(t) + d <= f</tex> (u) then переходим на строку 17.<br />
14 Возобновляем A* для того, чтобы исследовать более большую часть графа <tex>G</tex><br />
15 Обновляем <tex>P(G)</tex> and bring Dijkstra’s search into a consistent status<br />
16 Переходим на строку 8<br />
17 '''if''' очередь <tex>open_D</tex> пуста: переходим на строку 8.<br />
18 Remove from <tex>open_D</tex> and place on <tex>closed_D</tex> the node n with the minimal d-value.<br />
19 '''foreach''' <tex>n'</tex> referred by n in P(G):<br />
20 <tex>d(n') = d(n) + \Delta(n, n')</tex><br />
21 Attach to <tex>n'</tex> a parent link referring to <tex>n</tex>.<br />
22 Insert n 0 into <tex>open_D</tex><br />
23 Пусть <tex>\sigma</tex> будет путем в <tex>P(G)</tex>, через который узел n был достигнут.<br />
24 Добавим <tex>seq(\sigma)</tex> в конец списка <tex>R</tex>.<br />
25 '''if''' <tex>|R| = k</tex>: переходим на строку 26.<br />
26 Return R and exit.<br />
<br />
Алгоритм 1 содержит псевдокод K*. Код с 8 по 25 строчку образует главный цикл K*. Цикл завершается, когда очереди обоих алгоритмов А* и Дейкстры пусты. До 8 строчки выполняет некоторые подготовительные вещи. После инициализации, А* запускает на 5 строчке пока вершина <tex>t</tex> не будет выбрана им для рассмотрения, в этом случае кратчайший путь <tex>s-t</tex> будет найден. Если <tex>t</tex> не достигнута, то алгоритм завершается без ответа. Отметим, что он не завершится на бесконечных графах. Иначе, алгоритм добавляет специальную вершину <tex>R</tex>, которая назначена корнем <tex>P(G)</tex>, в поисковую очередь алгоритма Дейкстры. Затем, K* входит в главный цикл.<br />
<br />
K* поддерживает механизм планирования для контролирования, когда A* или Дейкстра будет возобновлены. Если очередь из A* не пуста, что означает, что А* ещё не завершил исследования всего графа G, то Дейкстра возобновляется тогда и только тогда, когда <tex>g(t) + d <= f(u)</tex>. Значение <tex>d</tex> является максимальным <tex>d</tex>-значением среди всех successor-ов головы поисковой очереди <tex>n</tex> алгоритма Дейкстры. Вершина <tex>u</tex> является головой поисковой очереди A*. Напомним, что <tex>d</tex> - функция расстояния, используемая в алгоритме Дейкстры. Если очередь поиска Дейкстры пуста или <tex>g(t) + d > f(u)</tex>, то А* возобновляется для того, чтобы исследовать более большую часть графа <tex>G</tex> (строка 14). То, как долго мы ему позволим работать, является компромиссом. Если мы запустим его только на маленьком количестве шагов, то мы дадим Дейкстре шанс найти необходимое количество путей скорее, чем они будут доступны в <tex>P(G)</tex>. С другой стороны, мы вызываем накладные расходы путем переключения A* и Дейкстры и поэтому должны ограничить количество переключений. Эти накладные расходы вызваны тем фактом, что после возобновления A* (строка 14), структура графа <tex>P(G)</tex> может измениться. Следовательно нам необходимо обновить <tex>P(G)</tex> (строка 15), как мы будет широко обсуждать в разделе 4.5. Это требует последующую проверку статуса Дейкстры. Мы должны быть уверены, что Дейкстра поддерживает согласованное состояние после изменений в <tex>P(G)</tex>. K* предусматривает условие, которые управляет решением, когда остановить A*, которое мы назовем ''условие расширения''. Для того, чтобы поддерживать аналогичную асимптотическую сложность как у EA и LVEA, мы должны определить условие расширения так, чтобы A* выполнялся пока количество рассмотренных вершин и количество внутренних ребер удваивается или <tex>G</tex> полностью исследован. Мы обсудим эту проблему несколько подробнее позже. В качестве полезного свойства, K* позволяет другое определения этого условия, которое может быть более эффективным на практике. В наших экспериментах в разделе 6, мы определили условие расширения так, что количество рассмотренных вершин или количество рассмотренных ребер ребер возрастает на 20% при каждом запуске A*. Этот механизм планирования включен до тех пор, пока A* не закончит исследовать весь граф <tex>G</tex>. Как только A* исследует весь граф <tex>G</tex> (строка 9), механизм планирования отключается и в дальнейшем работает только алгоритм Дейкстры.<br />
<br />
Строки 18-22 представляют обычный шаг рассмотрения узла алгоритмом Дейкстры. Отметим, что когда successor-узел <tex>n'</tex> сгенерирован, K* не проверяет был ли <tex>n'</tex> уже посещен до этого. Другими словами, каждый раз, когда узел генерируется, он рассматривает как новый. Эта стратегия обоснована на наблюдении, что путь s-t может содержать одно и то же ребро несколько раз. Строка 24 добавляет следующий путь <tex>s-t</tex> в результирующее множество R. Это делается путем конструирования последовательности запасных ребер <tex>seq(\sigma)</tex> из пути <tex>\sigma</tex>, через которые Дейкстра достигла узла <tex>n</tex>, который был только что рассмотрен. Алгоритм завершается, когда в результирующее множество добавлено <tex>k</tex> последовательностей запасных ребер (строка 25).<br />
<br />
== 4.5 Взаимосвязь алгоритмов Дейкстры и A* ==<br />
Тот факт, что оба алгоритма A* и Дейкстры делят между собой граф путей <tex>P(G)</tex>, вызывает обеспокоенность в отношении правильности работы Дейкстры на <tex>P(G)</tex>. Возобновление A* приводит к изменениям в структуре <tex>P(G)</tex>. Таким образом, после возобновления A*, мы обновляем <tex>P(G)</tex> и проверяет статус поиска Дейкстры (строка 15). В основном, A* может добавить новые узлы, менять <tex>\delta</tex>-значения существующих узлов или даже удалять узлы. A* может также существенно изменять дерево поиска <tex>T</tex>, которое будет в худшем случае разрушать структуру все деревянных куч <tex>H_{T}</tex>. Эти изменения могут приводить к глобальной реструктуризации или даже перестроению <tex>P(G)</tex> с нуля. В худшем случае это может сделать предыдущие поиски Дейкстры на <tex>P(G)</tex> бесполезными таким образом, что нам придется перезапускать алгоритм Дейкстры с нуля.<br />
<br />
Если использованная эвристическая оценка допустимая, то наше положение лучше. Нам по-прежнему может понадобится перестроение <tex>P(G)</tex>, но мы покажем, что это перестроение не мешает корректности поиска Дейкстры на <tex>P(G)</tex>. Другими словами, мы не теряем результаты, до сих пор полученные поиском Дейкстры. В случае монотонной эвристической оценки мы даже не нуждаемся в перестроении <tex>P(G)</tex>. Если <tex>h</tex> монотонная, то дерево поиска A* является деревом кратчайшего пути для всех раскрытых вершин. Следовательно, g-значения раскрытых вершин не изменится. Это означает, что <tex>\delta</tex>-значения для внутренних ребер никогда не изменятся. Ребра дерева раскрытых вершин не изменятся также. Следовательно, обновление <tex>\delta</tex>-значений, heaping-up, heaping-down (операции в кучах) или удаление узлов не влекут за собой каких-либо изменений в <tex>P(G)</tex>. Только добавление новых узлов приводит к изменениям в <tex>P(G)</tex>. Следовательно, перестроение или глобальная реструктуризация не требуется в данном случае.<br />
<br />
В оставшейся части этого раздела, мы сначала покажем, что корректность поиска Дейкстры на <tex>P(G)</tex> поддерживается в случае допустимой эвристической оценки. После этого мы покажем, что изменения в <tex>P(G)</tex> могут помешать завершенности поиска Дейкстры независимо от того, является ли эвристика допустимой или даже монотонной. Следовательно, мы предложим механизм для её поддержания.<br />
<br />
Мы фокусируемся дальше на корректности поиска Дейкстры на <tex>P(G)</tex> в случае допустимой эвристической оценки. Сначала, мы заявляем, что если <tex>h</tex> допустимая, то узлы исследованной части <tex>P(G)</tex> не поменяют свои <tex>\delta</tex>-значения.<br />
<br />
{{Лемма<br />
|about=6<br />
|statement=Пусть <tex>n</tex> будет произвольным узлов в <tex>P(G)</tex> и пусть <tex>(u,v)</tex> будет ребром, связанным с <tex>n</tex>. Если <tex>h</tex> допустимая функция, то значение <tex>\delta(u, v)</tex> никогда не изменится после того, как <tex>n</tex> будет рассмотрен алгоритмом Дейкстры.<br />
|proof=...<br />
}}<br />
<br />
Из леммы 6 мы может вывести следующее следствие.<br />
<br />
Следствие 2. Пусть <tex>n</tex> будет произвольным узлов в <tex>P(G)</tex>. Если <tex>h</tex> допустимая функция, то <tex>n</tex> никогда не будет удален из <tex>P(G)</tex> после того, как <tex>n</tex> был рассмотрен алгоритмом Дейкстры.<br />
<br />
...<br />
<br />
Более того, мы докажем, что структура исследованной части <tex>P(G)</tex> не изменится.<br />
<br />
{{Лемма<br />
|about=7<br />
|statement=Пусть <tex>n</tex> будет произвольным узлов в <tex>P(G)</tex>. Если <tex>h</tex> допустимая функция, то <tex>n</tex> никогда не изменит свою позицию после того, как он был рассмотрен алгоритмом Дейкстры.<br />
|proof=...<br />
}}<br />
<br />
<br />
Леммы 6 и 7 обеспечивают, что изменения в <tex>P(G)</tex>, которые индуцируются A*, не влияют на часть <tex>P(G)</tex>, которую алгоритм Дейкстры уже исследовал. Это гарантирует корректность поиска Дейкстры на <tex>P(G)</tex>, если используемая эвристика допустимая. Таким образом, каждый путь, который предоставляет алгоритм Дейкстры корректен и его длина действительна. Однако, это не обеспечивает завершенность поиска Дейкстры на <tex>P(G)</tex>.<br />
<br />
Возможно, что узел <tex>n'</tex> присоединяется к другом узлу n как ребенок, после раскрытия узла <tex>n</tex>. В этом случае братья <tex>n'</tex> будут рассотрены до того, как <tex>n'</tex> станет ребенком n. Поэтому мы должны рассмотреть то, что было упущено во время поиска в связи с отсутствием <tex>n'</tex>. Мы добиваемся этого путем применения строк 20-22 к <tex>n'</tex> для каждого раскрытого направленного predecessor-а узла <tex>n'</tex>. Если <tex>n'</tex> ещё не выполняет условие планирования, A* будет неоднократно возобновляться пока механизм планирования не допустит алгоритму Дейкстры положить <tex>n'</tex> в поисковую очередь. Заметим, что таким образом не требуется каких-либо дополнительных усилий во время типичного поиска Дейкстры.<br />
<br />
Мы может быть уверены, что нерассмотренные узлы не будут принудительно опущены после применения операции heaping-up к <tex>n'</tex>. Иначе, мы могли бы иметь узел <tex>n''</tex>, который являлся бы ребенком n и впоследствии был бы заменен узлом <tex>n'</tex>. Заметим, что <tex>n''</tex> должен быть рассмотрен, поскольку <tex>n'</tex> был раскрыт. Однако, это противоречение к лемме 7, которая гарантирует, что этого не произойдет.<br />
<br />
Более того, следующее следствие гарантирует, что наилучшее <tex>d(n')</tex> не лучше, чем <tex>d</tex>-значение любой рассмотренной вершины, в частности, раскрытой вершины. Это означает, что мы не упустим возможность раскрыть <tex>n'</tex>.<br />
<br />
{{Лемма<br />
|about=следствие 3<br />
|statement=Пусть <tex>n</tex> будет узлом в <tex>P(G)</tex>, который был раскрыт Дейкстрой. Кроме того, пусть <tex>m</tex> будет узлом, который заново добавляется в <tex>P(G)</tex> или его позиция изменена, после того как <tex>n</tex> был раскрыт. Если <tex>h</tex> допустимая, тогда выполяется следующее:<br />
<tex>C_{P(G)}(R,m) >= d(n)</tex><br />
|proof=...<br />
}}<br />
<br />
== 4.6 Пример ==<br />
Мы проиллюстрируем работу алгоритма K* следующим примером. Мы будем рассматривать ориентированный взвешанный граф G на рисунке 7. Стартовой вершиной будет называться <tex>s_0</tex> и конечной вершиной - <tex>s_6</tex>. Нас интересует поиск 9 лучших путей из <tex>s_0</tex> в <tex>s_6</tex>. Для достижения этой цели мы применим алгоритм K* к <tex>G</tex>. Предположим, что эвристическая оценка существует. Значения эвристики даны в пометках c <tex>h(s_0)</tex> по <tex>h(s_6)</tex> на рисунке 7. Легко заметить, что эвристическая функция допустима.<br />
<br />
Первый раз A* делает итерации на графе <tex>G</tex> до тех пор, пока не будет найдена вершина <tex>s_6</tex>. Часть графа <tex>G</tex>, которая уже была рассмотрена иллиюстрируется на рисунке 8. Ребра, изображенные сплошными линиями, обозначают ребра дерева, в то время, как все остальные - запасные ребра. Они будет храниться в кучах <tex>H_{in}</tex>, показанных на рисунке 9. Номера, присвоенные узлах кучи, соответствуют <tex>\delta</tex>-значениям. На этом этапе поиска A* приостановлен и <tex>P(G)</tex> построен. Первоначально, только назначенный корень <tex>R</tex> явно доступен в <tex>P(G)</tex>. Инициализируется алгоритм Дейкстры. Это означает, что узел <tex>R</tex> добавляется в поискую очередь Дейкстры. Планировщику требуется доступ к successors К для того, чтобы решить следует ли возобновлять Дейкстру или A*. На данном этапе должна быть построена деревянная куча <tex>H_{T}(s_6)</tex>. Куча <tex>H_{T}(s_4)</tex> требуется для построения <tex>H_{T}(s_6)</tex>. Следовательно, строются деревянные кучи <tex>H_{T}(s_6)</tex>, <tex>H_{T}(s_4)</tex>, <tex>H_{T}(s_2)</tex> и <tex>H_{T}(s_0)</tex>. Результат показан на рисунке 10, где сплошные линии представляют кучные ребра и пунктирные линии показывают кросс-ребра. Во избежание путаницы на рисунке некоторые из ребер не полностью изображены. Мы указываем каждое из них, используя короткую стрелку с конретной целью.<br />
<br />
После построения, как показано 10, планировщик проверяет только ребенка <tex>(s_4, s_2)</tex> узла <tex>R</tex> на предмет того, что <tex>g(s_6) + d(s_4,s_2) <= f(s_1)</tex>. Отметим, что <tex>s_1</tex> является головой поисковой очереди A*. Значение <tex>d(s_4,s_2)</tex> равно 2, т.е. <tex>g(s_6) + d(s_4,s_2) = 7 + 2 = 9 = f(s_1)</tex>. Следовательно, планировщик позволяет Дейкстре раскрыть <tex>R</tex> и вставить <tex>(s_4,s_2)</tex> в поисковую очередь. При раскрытии <tex>R</tex> находится первый путь из ответа. Он строится из пути <tex>P(G)</tex>, содержащего единственный узел <tex>R</tex>. Этот путь приводит к пустой последовательности запасных ребер. Напомним, что пустая последовательность запасных ребер соответствует пути из <tex>s_0</tex> в <tex>s_6</tex> в дереве поиска, а именно <tex>s_0s_2s_4s_6</tex> длиной 7. Затем поиск Дейкстры приостанавливается, потому что для successor-ов узла <tex>(s_4,s_2)</tex> не выполняется условие <tex>g(s_6)+d(n)<=f(s_1)</tex>. Следовательно, возобновляется A*.<br />
<br />
Мы предполагаем, что условие раскрытия определено как раскрытие одной вершины для того, чтобы пример был простым и иллюстративным. Поэтому A* раскрывает <tex>s_1</tex> и останавливается. Исследованная часть <tex>G</tex> на текущем этапе показана на рисунке 11. Результат раскрытия приведет к обнаружению 2 новых запасных ребер <tex>(s_1,s_2)</tex> и <tex>(s_1,s_6)</tex>, которые будут добавлены в <tex>H_{in}(s_2)</tex> и <tex>H_{in}(s_6)</tex> соответственно. Обновленные кучи <tex>H_{in}(s_2)</tex> и <tex>H_{in}(s_6)</tex> представлены на рисунке 12. Другие кучи остаются неизменными, как на рисунке 9. Граф путей <tex>P(G)</tex> перестаивается, как показано на рисунке 13. Затем алгоритм Дейкстры возобновляется. Заметим, что поисковая очередь Дейкстры содержит только <tex>(s_4,s_2)</tex> с <tex>d = 2</tex> на этом моменте. Используя ручное выполнение мы можем легко увидеть, что Дейкстра будет выдавать в ответ пути, перечисленные в таблице 1.</div>Kabanovhttp://neerc.ifmo.ru/wiki/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Kabanov&diff=48975Участник:Kabanov2015-08-05T12:41:54Z<p>Kabanov: </p>
<hr />
<div>Также как и алгоритм Эппштейна, K* выполняет поиск пути на графе <tex>G</tex> и использует граф путей <tex>P(G)</tex>. Граф путей ищется с помощью алгоритма Дейкстры для того, чтобы вычислить пути <tex>s-t</tex> в виде последовательности запасных путей. Общий принцип работы алгоритма K* следующий:<br />
1) K* применяет A* на графе <tex>G</tex> вместо обратного алгоритма Дейкстры, который использует алгоритм Eppstein.<br />
2) Мы запускаем A* на <tex>G</tex> и Дейкстру на <tex>P(G)</tex> в поочередном порядке, который позволяет Дейкстре вычислить требуемые пути до заверешения полного поиска алгоритма A* на графе <tex>G</tex> .<br />
<br />
== 4.1 Поиск A* на G == <br />
K* применяет A* к входному графу <tex>G</tex> для того, чтобы построить дерево поиска <tex>T</tex>. Заметим, что A*, также как и алгоритм Дейкстры, строит дерево поиска в процессе нахождения кратчайшего пути <tex>s-t</tex> . Эти деревья формируются с помощью ссылок на родительские узлы, которые храняться в том время, как A* производит итерации для того, чтобы восстановить путь <tex>s-t</tex>, когда вершина <tex>t</tex> ещё не найдена. Запасные ребра, открытые в процессе поиска A* на графе G, немедленно добавляются в граф P(G), структура которого будет объясняться в разделе 4.3. <br />
<br />
В отличие от алгоритма Эппштейна в K* A* применяется к графу <tex>G</tex> в прямом направлении, из-за чего корнем дерева <tex>T</tex> является вершина <tex>s</tex>. Это необходимо для того, чтобы была возможность работать c неявным описанием графа <tex>G</tex> через функцию successor (функция, возвращающая список исходящих ребер из данной вершины). На протяжение статьи будем считать граф <tex>G</tex> конечным, если не будет сказано иное. Заметим, что А* корректен на конечных графах. Будем следовать литературному соглашению, предполагая, что стоимость бесконечного пути неограниченна.<br />
<br />
== 4.2 Стоимость объезда ==<br />
Для ребра <tex>(u, v)</tex> стоимость объезда <tex>\delta(u, v)</tex> представляет стоимость ущерба из-за взятия ребра объезда <tex>(u,v)</tex> в сравнении с кратчайшим путем <tex>s-t</tex> через <tex>v</tex>. Ни длина кратчайшего пути <tex>s-t</tex> через <tex>v</tex>, ни длина пути <tex>s-t</tex>, включающего запапасные ребра <tex>(u, v)</tex> не известны, когда A* обнаруживает <tex>(u, v)</tex>. Обе длины могут быть оценены с помощью функции оценки <tex>f</tex>, которая использует эвристическую функцию <tex>h</tex>. Путь <tex>f(v)</tex> будет <tex>f</tex>-значением с соответствии с деревом поиска <tex>T</tex> и <tex>f_u(v)</tex> будет <tex>f</tex>-значанием в соответствии с родителем u, т.е. <tex>f_u(v) = g(u) + c(u, v) + h(v)</tex>. <tex>\delta(u, v)</tex> может быть определена так:<br />
<br />
<tex>\delta(u, v) = f_u(v) - f(v) = g(u) + c(u, v) + h(v) - g(v) - h(v) = g(u) + c(u, v) - g(v)</tex><br />
<br />
Заметим, что <tex>\delta(u, v)</tex> дает точную объездную метрику, поскольку функция оценки <tex>h</tex>-значения не появляется в определении функции <tex>\delta(u, v)</tex>.<br />
<br />
== 4.3 Структура графа путей ==<br />
Структура графа путей <tex>P(G)</tex> довольно сложная. В принципе, <tex>P(G)</tex> будет ориентированным графом, вершины которого соответствуют ребрам в исходном графе <tex>G</tex>. Он будет организован как коллекция взаимосвязанных куч (англ. heap). 2 бинарные минимальные кучи присвоены к каждой вершине <tex>v</tex> в графе <tex>G</tex>, которые называются входящей кучей <tex>H_{in}(v)</tex> и деревянной кучей <tex>H_{T}(v)</tex>. Эти кучи являются базисом <tex>P(G)</tex>. Как мы покажем далее, испльзование этих куч также играет главную роль в поддержании асимптотической сложности K*, также как в EA и LVEA.<br />
<br />
Входящая куча <tex>H_{in}(v)</tex> содержит узлы для каждого запасного ребра к вершине <tex>v</tex>, которые до сих пор были обнаружены A*. Узлы <tex>H_{in}(v)</tex> будут упорядочены в соответствии с <tex>\delta</tex>-значением соответствующих переходов. Узел владеющий ребром с минимальной стоимостью ущерба будет расположен на вершине кучи. Мы ограничим структуру кучи <tex>H_{in}(v)</tex> таким образом, что её корень в отличие от остальных узлов, имеет не более 1 ребенка. Мы обозначим его <tex>root_{in}(v)</tex>.<br />
<br />
Деревянная куча <tex>H_{T}(v)</tex> для произвольной вершины <tex>v</tex> строится следующим образом. Если <tex>v</tex> - стартовая вершина, т.е. <tex>v = s</tex>, то <tex>H_{T}(v)</tex> будет изначально пустой кучей. Затем узел в неё будет добавлен <tex>root_{in}(s)</tex>, если <tex>H_{in}(s)</tex> не пустая. Если <tex>v</tex> не стартовая вершина, то пусть вершина <tex>u</tex> будет родителем вершины <tex>v</tex> в дереве поиска <tex>T</tex>. Мы можем представить, что <tex>H_{T}(v)</tex> конструируется как копия <tex>H_{T}(u)</tex>, в которую добавлен <tex>root_{in}(v)</tex>. Если <tex>H_{in}(v)</tex> пустая, то <tex>H_{T}(v)</tex> идентична <tex>H_{T}(u)</tex>. Однако, для экономии памяти мы создаем только дешевую копию <tex>H_{T}(u)</tex>. Это осуществляется через создание копий только узлов кучи, которые лежат на обновленном пути <tex>H_{T}(u)</tex>. Оставшаяся часть <tex>H_{T}(u)</tex> не копируется. Другими словами, <tex>root_{in}(v)</tex> вставляется в <tex>H_{T}(u)</tex> неразрушающим путем так, что структура <tex>H_{T}(u)</tex> сохраняется. В куче <tex>H_{T}(v)</tex> 1 или 2 ребенка могут быть присоединены к <tex>root_{in}(v)</tex>. К тому же, <tex>root_{in}(v)</tex> хранит только 1 собственного ребенка из <tex>H_{in}(v)</tex>. Мы обозначим корень <tex>H_{T}(v)</tex> как <tex>R(v)</tex>.<br />
<br />
Обратимся к ребрам, которые берут начало из входящих или деревянных куч, как к кучным ребрам. Сформулируем следующую лемму.<br />
{{Лемма<br />
|about=1<br />
|statement=Все узлы, которые достижимы из <tex>R(v)</tex> через кучные ребра для каждой вершины <tex>v</tex>, формируют тернарную кучу, упорядоченную в соответствии с <tex>\delta</tex>-значением. Мы назовем такую кучу графовой кучей вершины <tex>v</tex> и обозначим его как <tex>H_{G}(v)</tex>.<br />
|proof=...<br />
}}<br />
<br />
Финальная структура <tex>P(G)</tex> получется из входящих и деревянных куч следующим образом. К каждому узлу <tex>n</tex> из <tex>P(G)</tex>, несущему ребро <tex>(u,v)</tex>, мы присоединим указатель, ссылающийся на <tex>R(u)</tex>, который является корневым узлом <tex>H_{T}(u)</tex>. Мы назовем такие указатели кросс-ребрами, в то время как указатели, возникающие из куч названы кучными ребрами, как упоминалось раньше. Более того, мы добавим специальный узел <tex>R</tex> в <tex>P(G)</tex> с одним выходящим кросс-ребром к <tex>R(t)</tex>.<br />
<br />
Более того, мы определим весовую функцию <tex>\Delta</tex> на ребрах из <tex>P(G)</tex>. Пусть <tex>(n,n')</tex> обозначает ребро в <tex>P(G)</tex>, и пусть <tex>e</tex> и <tex>e'</tex> обозначают ребра из <tex>G</tex> соответствующие узлам <tex>n</tex> и <tex>n'</tex>. Тогда определим <tex>\Delta(n,n')</tex> следующим образом:<br />
<br />
<tex><br />
\Delta(n,n')=\begin{cases}<br />
\delta(e') - \delta(e),& \text{if}\ (n,n')\ \text{heap edge} \\<br />
\delta(e'),& \text{if}\ (n,n')\ \text{cross edge}.<br />
\end{cases}<br />
</tex><br />
<br />
Лемма 1 подразумевает, что куча упорядоченная в соответствии с <tex>\delta</tex>-значанием поддерживается по любому кучному ребру из <tex>P(G)</tex>. Эта упорядочивание кучи подразумевает, что <tex>\Delta(n,n')</tex> неотрицательна для любого кучного ребра <tex>(n,n')</tex>. Следовательно, <tex>\Delta</tex> также неотрицательна, т.е. <tex>\Delta(n,n') >= 0</tex> для любого ребра <tex>(n,n')</tex> в <tex>P(G)</tex>. Стоимость пути <tex>\sigma</tex>, т.е. <tex>C_{P(G)}(\sigma)</tex> равна <tex>\sum_{e \in \sigma}\Delta(e)</tex>. <br />
<br />
...<br />
<br />
{{Лемма<br />
|about=2<br />
|statement=Пусть <tex>n</tex> будет узлов графовой кучи <tex>H_{G}(w)</tex> для какой-нибудь вершины <tex>w</tex>. Пусть <tex>(u,v)</tex> будет ребром связанным с <tex>n</tex>. Тогда существует путь в дереве поиска <tex>T</tex> из <tex>v</tex> в <tex>w</tex>.<br />
|proof=...<br />
}}<br />
<br />
== 4.4 Алгоритмическая структура K* ==<br />
Алгоритмический принцип K* следующий. Будем запускать алгоритмы Дейкстры и A* на <tex>G</tex> с чередованием. Сначала, мы запустим A* на <tex>G</tex> пока вершина <tex>t</tex> не будет выбрана из очереди для рассмотрения. Затем, вы запустим алгоритмы Дейкстры на доступной части <tex>P(G)</tex>. Каждый узел рассмотрел Дейкстрой представляет путь решения. Если точнее, то путь <tex>\sigma</tex> в <tex>P(G)</tex>, по которому Дейкстра достигла этого узла является решением. Путь <tex>s-t</tex> может быть построен из <tex>\sigma</tex> за линейное время путем вычисления последовательности запасных ребер <tex>seq(\sigma)</tex> и затем <tex>s-t</tex> пути из неё. Если Дейкстра находит <tex>k</tex> кратчайших путей, то K* завершается успешно. Иначе, A* возобновляется для исследования большей части <tex>G</tex>. Это приводит к росту <tex>P(G)</tex>, на котором алгоритм Дейкстры затем будет возобновлен. Мы будем повторять этот процесс до тех пор, пока алгоритм Дейкстры не найдет <tex>k</tex> кратчайших путей. <br />
<br />
Data: A graph given by its start vertex s ∈ V and its successor function succ and a<br />
natural number k<br />
Result: A list R containing k sidetrack edge sequences representing k solution paths<br />
<br />
1 <tex>open_D</tex> ← пустая приоритетная очередь<br />
2 <tex>closed_D</tex> ← пустая хеш-таблица<br />
3 <tex>R</tex> ← пустой список<br />
4 <tex>P(G)</tex> ← пустой граф путей<br />
5 Выполняем A* на графе <tex>G</tex> пока <tex>t</tex> не будет выбрана для раскрытия<br />
6 Если вершина <tex>t</tex> не была достигнута, то выходим без ответа<br />
7 Кладем <tex>\mathrm{R}</tex> в очередь <tex>open_D</tex><br />
8 '''while''' A queue or open D is not empty:<br />
9 '''if''' A queue is not empty:<br />
10 '''if''' очередь <tex>open_D</tex> не пуста:<br />
11 Let u be the head of the search queue of A ∗ and n the head of <tex>open_D</tex><br />
12 <tex>d = max\{ d(n) + \Delta(n, n') | n' \in succ(n) \}</tex><br />
13 '''if''' <tex>g(t) + d <= f</tex> (u) then переходим на строку 17.<br />
14 Возобновляем A* для того, чтобы исследовать более большую часть графа <tex>G</tex><br />
15 Обновляем <tex>P(G)</tex> and bring Dijkstra’s search into a consistent status<br />
16 Переходим на строку 8<br />
17 '''if''' очередь <tex>open_D</tex> пуста: переходим на строку 8.<br />
18 Remove from <tex>open_D</tex> and place on <tex>closed_D</tex> the node n with the minimal d-value.<br />
19 '''foreach''' <tex>n'</tex> referred by n in P(G):<br />
20 <tex>d(n') = d(n) + \Delta(n, n')</tex><br />
21 Attach to <tex>n'</tex> a parent link referring to <tex>n</tex>.<br />
22 Insert n 0 into <tex>open_D</tex><br />
23 Пусть <tex>\sigma</tex> будет путем в <tex>P(G)</tex>, через который узел n был достигнут.<br />
24 Добавим <tex>seq(\sigma)</tex> в конец списка <tex>R</tex>.<br />
25 '''if''' <tex>|R| = k</tex>: переходим на строку 26.<br />
26 Return R and exit.<br />
<br />
Алгоритм 1 содержит псевдокод K*. Код с 8 по 25 строчку образует главный цикл K*. Цикл завершается, когда очереди обоих алгоритмов А* и Дейкстры пусты. До 8 строчки выполняет некоторые подготовительные вещи. После инициализации, А* запускает на 5 строчке пока вершина <tex>t</tex> не будет выбрана им для рассмотрения, в этом случае кратчайший путь <tex>s-t</tex> будет найден. Если <tex>t</tex> не достигнута, то алгоритм завершается без ответа. Отметим, что он не завершится на бесконечных графах. Иначе, алгоритм добавляет специальную вершину <tex>R</tex>, которая назначена корнем <tex>P(G)</tex>, в поисковую очередь алгоритма Дейкстры. Затем, K* входит в главный цикл.<br />
<br />
K* поддерживает механизм планирования для контролирования, когда A* или Дейкстра будет возобновлены. Если очередь из A* не пуста, что означает, что А* ещё не завершил исследования всего графа G, то Дейкстра возобновляется тогда и только тогда, когда <tex>g(t) + d <= f(u)</tex>. Значение <tex>d</tex> является максимальным <tex>d</tex>-значением среди всех successor-ов головы поисковой очереди <tex>n</tex> алгоритма Дейкстры. Вершина <tex>u</tex> является головой поисковой очереди A*. Напомним, что <tex>d</tex> - функция расстояния, используемая в алгоритме Дейкстры. Если очередь поиска Дейкстры пуста или <tex>g(t) + d > f(u)</tex>, то А* возобновляется для того, чтобы исследовать более большую часть графа <tex>G</tex> (строка 14). То, как долго мы ему позволим работать, является компромиссом. Если мы запустим его только на маленьком количестве шагов, то мы дадим Дейкстре шанс найти необходимое количество путей скорее, чем они будут доступны в <tex>P(G)</tex>. С другой стороны, мы вызываем накладные расходы путем переключения A* и Дейкстры и поэтому должны ограничить количество переключений. Эти накладные расходы вызваны тем фактом, что после возобновления A* (строка 14), структура графа <tex>P(G)</tex> может измениться. Следовательно нам необходимо обновить <tex>P(G)</tex> (строка 15), как мы будет широко обсуждать в разделе 4.5. Это требует последующую проверку статуса Дейкстры. Мы должны быть уверены, что Дейкстра поддерживает согласованное состояние после изменений в <tex>P(G)</tex>. K* предусматривает условие, которые управляет решением, когда остановить A*, которое мы назовем ''условие расширения''. Для того, чтобы поддерживать аналогичную асимптотическую сложность как у EA и LVEA, мы должны определить условие расширения так, чтобы A* выполнялся пока количество рассмотренных вершин и количество внутренних ребер удваивается или <tex>G</tex> полностью исследован. Мы обсудим эту проблему несколько подробнее позже. В качестве полезного свойства, K* позволяет другое определения этого условия, которое может быть более эффективным на практике. В наших экспериментах в разделе 6, мы определили условие расширения так, что количество рассмотренных вершин или количество рассмотренных ребер ребер возрастает на 20% при каждом запуске A*. Этот механизм планирования включен до тех пор, пока A* не закончит исследовать весь граф <tex>G</tex>. Как только A* исследует весь граф <tex>G</tex> (строка 9), механизм планирования отключается и в дальнейшем работает только алгоритм Дейкстры.<br />
<br />
Строки 18-22 представляют обычный шаг рассмотрения узла алгоритмом Дейкстры. Отметим, что когда successor-узел <tex>n'</tex> сгенерирован, K* не проверяет был ли <tex>n'</tex> уже посещен до этого. Другими словами, каждый раз, когда узел генерируется, он рассматривает как новый. Эта стратегия обоснована на наблюдении, что путь s-t может содержать одно и то же ребро несколько раз. Строка 24 добавляет следующий путь <tex>s-t</tex> в результирующее множество R. Это делается путем конструирования последовательности запасных ребер <tex>seq(\sigma)</tex> из пути <tex>\sigma</tex>, через которые Дейкстра достигла узла <tex>n</tex>, который был только что рассмотрен. Алгоритм завершается, когда в результирующее множество добавлено <tex>k</tex> последовательностей запасных ребер (строка 25).<br />
<br />
== 4.5 Взаимосвязь алгоритмов Дейкстры и A* ==<br />
Тот факт, что оба алгоритма A* и Дейкстры делят между собой граф путей <tex>P(G)</tex>, вызывает обеспокоенность в отношении правильности работы Дейкстры на <tex>P(G)</tex>. Возобновление A* приводит к изменениям в структуре <tex>P(G)</tex>. Таким образом, после возобновления A*, мы обновляем <tex>P(G)</tex> и проверяет статус поиска Дейкстры (строка 15). В основном, A* может добавить новые узлы, менять <tex>\delta</tex>-значения существующих узлов или даже удалять узлы. A* может также существенно изменять дерево поиска <tex>T</tex>, которое будет в худшем случае разрушать структуру все деревянных куч <tex>H_{T}</tex>. Эти изменения могут приводить к глобальной реструктуризации или даже перестроению <tex>P(G)</tex> с нуля. В худшем случае это может сделать предыдущие поиски Дейкстры на <tex>P(G)</tex> бесполезными таким образом, что нам придется перезапускать алгоритм Дейкстры с нуля.<br />
<br />
Если использованная эвристическая оценка допустимая, то наше положение лучше. Нам по-прежнему может понадобится перестроение <tex>P(G)</tex>, но мы покажем, что это перестроение не мешает корректности поиска Дейкстры на <tex>P(G)</tex>. Другими словами, мы не теряем результаты, до сих пор полученные поиском Дейкстры. В случае монотонной эвристической оценки мы даже не нуждаемся в перестроении <tex>P(G)</tex>. Если <tex>h</tex> монотонная, то дерево поиска A* является деревом кратчайшего пути для всех раскрытых вершин. Следовательно, g-значения раскрытых вершин не изменится. Это означает, что <tex>\delta</tex>-значения для внутренних ребер никогда не изменятся. Ребра дерева раскрытых вершин не изменятся также. Следовательно, обновление <tex>\delta</tex>-значений, heaping-up, heaping-down (операции в кучах) или удаление узлов не влекут за собой каких-либо изменений в <tex>P(G)</tex>. Только добавление новых узлов приводит к изменениям в <tex>P(G)</tex>. Следовательно, перестроение или глобальная реструктуризация не требуется в данном случае.<br />
<br />
В оставшейся части этого раздела, мы сначала покажем, что корректность поиска Дейкстры на <tex>P(G)</tex> поддерживается в случае допустимой эвристической оценки. После этого мы покажем, что изменения в <tex>P(G)</tex> могут помешать завершенности поиска Дейкстры независимо от того, является ли эвристика допустимой или даже монотонной. Следовательно, мы предложим механизм для её поддержания.<br />
<br />
Мы фокусируемся дальше на корректности поиска Дейкстры на <tex>P(G)</tex> в случае допустимой эвристической оценки. Сначала, мы заявляем, что если <tex>h</tex> допустимая, то узлы исследованной части <tex>P(G)</tex> не поменяют свои <tex>\delta</tex>-значения.<br />
<br />
{{Лемма<br />
|about=6<br />
|statement=Пусть <tex>n</tex> будет произвольным узлов в <tex>P(G)</tex> и пусть <tex>(u,v)</tex> будет ребром, связанным с <tex>n</tex>. Если <tex>h</tex> допустимая функция, то значение <tex>\delta(u, v)</tex> никогда не изменится после того, как <tex>n</tex> будет рассмотрен алгоритмом Дейкстры.<br />
|proof=...<br />
}}<br />
<br />
Из леммы 6 мы может вывести следующее следствие.<br />
<br />
Следствие 2. Пусть <tex>n</tex> будет произвольным узлов в <tex>P(G)</tex>. Если <tex>h</tex> допустимая функция, то <tex>n</tex> никогда не будет удален из <tex>P(G)</tex> после того, как <tex>n</tex> был рассмотрен алгоритмом Дейкстры.<br />
<br />
...<br />
<br />
Более того, мы докажем, что структура исследованной части <tex>P(G)</tex> не изменится.<br />
<br />
{{Лемма<br />
|about=7<br />
|statement=Пусть <tex>n</tex> будет произвольным узлов в <tex>P(G)</tex>. Если <tex>h</tex> допустимая функция, то <tex>n</tex> никогда не изменит свою позицию после того, как он был рассмотрен алгоритмом Дейкстры.<br />
|proof=...<br />
}}<br />
<br />
<br />
Леммы 6 и 7 обеспечивают, что изменения в <tex>P(G)</tex>, которые индуцируются A*, не влияют на часть <tex>P(G)</tex>, которую алгоритм Дейкстры уже исследовал. Это гарантирует корректность поиска Дейкстры на <tex>P(G)</tex>, если используемая эвристика допустимая. Таким образом, каждый путь, который предоставляет алгоритм Дейкстры корректен и его длина действительна. Однако, это не обеспечивает завершенность поиска Дейкстры на <tex>P(G)</tex>.<br />
<br />
Возможно, что узел <tex>n'</tex> присоединяется к другом узлу n как ребенок, после раскрытия узла <tex>n</tex>. В этом случае братья <tex>n'</tex> будут рассотрены до того, как <tex>n'</tex> станет ребенком n. Поэтому мы должны рассмотреть то, что было упущено во время поиска в связи с отсутствием <tex>n'</tex>. Мы добиваемся этого путем применения строк 20-22 к <tex>n'</tex> для каждого раскрытого направленного predecessor-а узла <tex>n'</tex>. Если <tex>n'</tex> ещё не выполняет условие планирования, A* будет неоднократно возобновляться пока механизм планирования не допустит алгоритму Дейкстры положить <tex>n'</tex> в поисковую очередь. Заметим, что таким образом не требуется каких-либо дополнительных усилий во время типичного поиска Дейкстры.<br />
<br />
Мы может быть уверены, что нерассмотренные узлы не будут принудительно опущены после применения операции heaping-up к <tex>n'</tex>. Иначе, мы могли бы иметь узел <tex>n''</tex>, который являлся бы ребенком n и впоследствии был бы заменен узлом <tex>n'</tex>. Заметим, что <tex>n''</tex> должен быть рассмотрен, поскольку <tex>n'</tex> был раскрыт. Однако, это противоречение к лемме 7, которая гарантирует, что этого не произойдет.<br />
<br />
Более того, следующее следствие гарантирует, что наилучшее <tex>d(n')</tex> не лучше, чем <tex>d</tex>-значение любой рассмотренной вершины, в частности, раскрытой вершины. Это означает, что мы не упустим возможность раскрыть <tex>n'</tex>.<br />
<br />
{{Лемма<br />
|about=следствие 3<br />
|statement=Пусть <tex>n</tex> будет узлом в <tex>P(G)</tex>, который был раскрыт Дейкстрой. Кроме того, пусть <tex>m</tex> будет узлом, который заново добавляется в <tex>P(G)</tex> или его позиция изменена, после того как <tex>n</tex> был раскрыт. Если <tex>h</tex> допустимая, тогда выполяется следующее:<br />
<tex>C_{P(G)}(R,m) >= d(n)</tex><br />
|proof=...<br />
}}<br />
<br />
== 4.6 Пример ==<br />
Мы проиллюстрируем работу алгоритма K* следующим примером. Мы будем рассматривать ориентированный взвешанный граф G на рисунке 7. Стартовой вершиной будет называться <tex>s_0</tex> и конечной вершиной - <tex>s_6</tex>. Нас интересует поиск 9 лучших путей из <tex>s_0</tex> в <tex>s_6</tex>. Для достижения этой цели мы применим алгоритм K* к <tex>G</tex>. Предположим, что эвристическая оценка существует. Значения эвристики даны в пометках c <tex>h(s_0)</tex> по <tex>h(s_6)</tex> на рисунке 7. Легко заметить, что эвристическая функция допустима.<br />
<br />
Первый раз A* делает итерации на графе <tex>G</tex> до тех пор, пока не будет найдена вершина <tex>s_6</tex>. Часть графа <tex>G</tex>, которая уже была рассмотрена иллиюстрируется на рисунке 8. Ребра, изображенные сплошными линиями, обозначают ребра дерева, в то время, как все остальные - запасные ребра. Они будет храниться в кучах <tex>H_{in}</tex>, показанных на рисунке 9. Номера, присвоенные узлах кучи, соответствуют <tex>\delta</tex>-значениям. На этом этапе поиска A* приостановлен и <tex>P(G)</tex> построен. Первоначально, только назначенный корень <tex>R</tex> явно доступен в <tex>P(G)</tex>. Инициализируется алгоритм Дейкстры. Это означает, что узел <tex>R</tex> добавляется в поискую очередь Дейкстры. Планировщику требуется доступ к successors К для того, чтобы решить следует ли возобновлять Дейкстру или A*. На данном этапе должна быть построена деревянная куча <tex>H_{T}(s_6)</tex>. Куча <tex>H_{T}(s_4)</tex> требуется для построения <tex>H_{T}(s_6)</tex>. Следовательно, строются деревянные кучи <tex>H_{T}(s_6)</tex>, <tex>H_{T}(s_4)</tex>, <tex>H_{T}(s_2)</tex> и <tex>H_{T}(s_0)</tex>. Результат показан на рисунке 10, где сплошные линии представляют кучные ребра и пунктирные линии показывают кросс-ребра. Во избежание путаницы на рисунке некоторые из ребер не полностью изображены. Мы указываем каждое из них, используя короткую стрелку с конретной целью.<br />
<br />
После построения, как показано 10, планировщик проверяет только ребенка <tex>(s_4, s_2)</tex> узла <tex>R</tex> на предмет того, что <tex>g(s_6) + d(s_4,s_2) <= f(s_1)</tex>. Отметим, что <tex>s_1</tex> является головой поисковой очереди A*. Значение <tex>d(s_4,s_2)</tex> равно 2, т.е. <tex>g(s_6) + d(s_4,s_2) = 7 + 2 = 9 = f(s_1)</tex>. Следовательно, планировщик позволяет Дейкстре раскрыть <tex>R</tex> и вставить <tex>(s_4,s_2)</tex> в поисковую очередь. При раскрытии <tex>R</tex> находится первый путь из ответа. Он строится из пути <tex>P(G)</tex>, содержащего единственный узел <tex>R</tex>. Этот путь приводит к пустой последовательности запасных ребер. Напомним, что пустая последовательность запасных ребер соответствует пути из <tex>s_0</tex> в <tex>s_6</tex> в дереве поиска, а именно <tex>s_0s_2s_4s_6</tex> длиной 7. Затем поиск Дейкстры приостанавливается, потому что для successor-ов узла <tex>(s_4,s_2)</tex> не выполняется условие <tex>g(s_6)+d(n)<=f(s_1)</tex>. Следовательно, возобновляется A*.<br />
<br />
Мы предполагаем, что условие раскрытия определено как раскрытие одной вершины для того, чтобы пример был простым и иллюстративным. Поэтому A* раскрывает <tex>s_1</tex> и останавливается. Исследованная часть <tex>G</tex> на текущем этапе показана на рисунке 11. Результат раскрытия приведет к обнаружению 2 новых запасных ребер <tex>(s_1,s_2)</tex> и <tex>(s_1,s_6)</tex>, которые будут добавлены в <tex>H_{in}(s_2)</tex> и <tex>H_{in}(s_6)</tex> соответственно. Обновленные кучи <tex>H_{in}(s_2)</tex> и <tex>H_{in}(s_6)</tex> представлены на рисунке 12. Другие кучи остаются неизменными, как на рисунке 9. Граф путей <tex>P(G)</tex> перестаивается, как показано на рисунке 13. Затем алгоритм Дейкстры возобновляется. Заметим, что поисковая очередь Дейкстры содержит только <tex>(s_4,s_2)</tex> с <tex>d = 2</tex> на этом моменте. Используя ручное выполнение мы можем легко увидеть, что Дейкстра будет выдавать в ответ пути, перечисленные в таблице 1.</div>Kabanovhttp://neerc.ifmo.ru/wiki/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Kabanov&diff=48974Участник:Kabanov2015-08-05T12:16:07Z<p>Kabanov: </p>
<hr />
<div>Также как и алгоритм Eppstein, K* выполняет поиск пути на графе <tex>G</tex> и использует граф путей <tex>P(G)</tex>. Граф путей ищется с помощью алгоритма Дейкстры для того, чтобы пути <tex>s-t</tex> в виде последовательности запасных путей. Общий принцип работы алгоритма K* следующий:<br />
1) K* применяет A* на графе <tex>G</tex> вместо обратного алгоритма Дейкстры, который использует алгоритм Eppstein.<br />
2) Мы запускаем A* на <tex>G</tex> и Дейкстру на <tex>P(G)</tex> поочередно порядке, который позволяет Дейкстре доставить пути решение до заверешения поиска на <tex>G</tex> алгоритма A*.<br />
<br />
== Поиск A* на G. == <br />
K* применяет A* к входному графу <tex>G</tex> для того, чтобы определить дерево поиска <tex>T</tex>. <br />
В отличие от алгоритма Eppstein в K* A* применяется к графу <tex>G</tex> в прямом порядке из-за чего коренем дерева <tex>T </tex> является вершина <tex>s</tex>. Это необходимо для того, чтобы была возможность работать c неявным описанием графа <tex>G</tex> через функцию successor. На протяжении статьи будем считать граф <tex>G</tex> конечным, если не будет сказано иначе. Заметим, что А* корректен на конечных графах. Будем следовать литературному соглашению, предполагая, что стоимость бесконечного пути неограниченна.<br />
<br />
== Стоимость объезда ==<br />
Для ребра <tex>(u, v)</tex> стоимость объезда <tex>\delta(u, v)</tex> является стоимостью ущерба из-за взятия ребра объезда <tex>(u,v)</tex> в сравнении с кратчайшим путем <tex>s-t</tex> через <tex>v</tex>. Ни длина кратчайшего пути <tex>s-t</tex> через <tex>v</tex>, ни длина пути <tex>s-t</tex>, включающего запапасные ребра <tex>(u, v)</tex> не известны, когда A* обнаруживает <tex>(u, v)</tex>. Обе длины могут быть оценены с помощью функции оценки <tex>f</tex>, которая использует эвристическую функцию <tex>h</tex>. Путь <tex>f(v)</tex> будет <tex>f</tex>-значением с соответствии с деревом поиска <tex>T</tex> и <tex>f_u(v)</tex> будет <tex>f</tex>-значанием в соответствии с родителем u, т.е. <tex>f_u(v) = g(u) + c(u, v) + h(v)</tex>. <tex>\delta(u, v)</tex> может быть определена так:<br />
<br />
<tex>\delta(u, v) = f_u(v) - f(v) = g(u) + c(u, v) + h(v) - g(v) - h(v) = g(u) + c(u, v) - g(v)</tex><br />
<br />
Заметим, что <tex>\delta(u, v)</tex> дает точную объездную метрику, поскольку функция оценки <tex>h</tex>-значения не появляется в определении функции <tex>\delta(u, v)</tex>.<br />
<br />
== Структура графе путей ==<br />
Структура графа путей <tex>P(G)</tex> довольно сложная. В принципе, <tex>P(G)</tex> будет ориентированным графом, вершины которого соответствуют ребрам в исходном графе <tex>G</tex>. Он будет организован как коллекция взаимосвязанных куч (англ. heap). 2 бинарные минимальные кучи присвоены к каждой вершине <tex>v</tex> в графе <tex>G</tex>, которые называются входящей кучей <tex>H_{in}(v)</tex> и деревянной кучей <tex>H_{T}(v)</tex>. Эти кучи являются базисом <tex>P(G)</tex>. Как мы покажем далее, испльзование этих куч также играет главную роль в поддержании асимптотической сложности K*, также как в EA и LVEA.<br />
<br />
Входящая куча <tex>H_{in}(v)</tex> содержит узлы для каждого запасного ребра к вершине <tex>v</tex>, которые до сих пор были обнаружены A*. Узлы <tex>H_{in}(v)</tex> будут упорядочены в соответствии с <tex>\delta</tex>-значением соответствующих переходов. Узел владеющий ребром с минимальной стоимостью ущерба будет расположен на вершине кучи. Мы ограничим структуру кучи <tex>H_{in}(v)</tex> таким образом, что её корень в отличие от остальных узлов, имеет не более 1 ребенка. Мы обозначим его <tex>root_{in}(v)</tex>.<br />
<br />
Деревянная куча <tex>H_{T}(v)</tex> для произвольной вершины <tex>v</tex> строится следующим образом. Если <tex>v</tex> - стартовая вершина, т.е. <tex>v = s</tex>, то <tex>H_{T}(v)</tex> будет изначально пустой кучей. Затем узел в неё будет добавлен <tex>root_{in}(s)</tex>, если <tex>H_{in}(s)</tex> не пустая. Если <tex>v</tex> не стартовая вершина, то пусть вершина <tex>u</tex> будет родителем вершины <tex>v</tex> в дереве поиска <tex>T</tex>. Мы можем представить, что <tex>H_{T}(v)</tex> конструируется как копия <tex>H_{T}(u)</tex>, в которую добавлен <tex>root_{in}(v)</tex>. Если <tex>H_{in}(v)</tex> пустая, то <tex>H_{T}(v)</tex> идентична <tex>H_{T}(u)</tex>. Однако, для экономии памяти мы создаем только дешевую копию <tex>H_{T}(u)</tex>. Это осуществляется через создание копий только узлов кучи, которые лежат на обновленном пути <tex>H_{T}(u)</tex>. Оставшаяся часть <tex>H_{T}(u)</tex> не копируется. Другими словами, <tex>root_{in}(v)</tex> вставляется в <tex>H_{T}(u)</tex> неразрушающим путем так, что структура <tex>H_{T}(u)</tex> сохраняется. В куче <tex>H_{T}(v)</tex> 1 или 2 ребенка могут быть присоединены к <tex>root_{in}(v)</tex>. К тому же, <tex>root_{in}(v)</tex> хранит только 1 собственного ребенка из <tex>H_{in}(v)</tex>. Мы обозначим корень <tex>H_{T}(v)</tex> как <tex>R(v)</tex>.<br />
<br />
Обратимся к ребрам, которые берут начало из входящих или деревянных куч, как к кучным ребрам. Сформулируем следующую лемму.<br />
{{Лемма<br />
|about=1<br />
|statement=Все узлы, которые достижимы из <tex>R(v)</tex> через кучные ребра для каждой вершины <tex>v</tex>, формируют тернарную кучу, упорядоченную в соответствии с <tex>\delta</tex>-значением. Мы назовем такую кучу графовой кучей вершины <tex>v</tex> и обозначим его как <tex>H_{G}(v)</tex>.<br />
|proof=...<br />
}}<br />
<br />
Финальная структура <tex>P(G)</tex> получется из входящих и деревянных куч следующим образом. К каждому узлу <tex>n</tex> из <tex>P(G)</tex>, несущему ребро <tex>(u,v)</tex>, мы присоединим указатель, ссылающийся на <tex>R(u)</tex>, который является корневым узлом <tex>H_{T}(u)</tex>. Мы назовем такие указатели кросс-ребрами, в то время как указатели, возникающие из куч названы кучными ребрами, как упоминалось раньше. Более того, мы добавим специальный узел <tex>R</tex> в <tex>P(G)</tex> с одним выходящим кросс-ребром к <tex>R(t)</tex>.<br />
<br />
Более того, мы определим весовую функцию <tex>\Delta</tex> на ребрах из <tex>P(G)</tex>. Пусть <tex>(n,n')</tex> обозначает ребро в <tex>P(G)</tex>, и пусть <tex>e</tex> и <tex>e'</tex> обозначают ребра из <tex>G</tex> соответствующие узлам <tex>n</tex> и <tex>n'</tex>. Тогда определим <tex>\Delta(n,n')</tex> следующим образом:<br />
<br />
<tex><br />
\Delta(n,n')=\begin{cases}<br />
\delta(e') - \delta(e),& \text{if}\ (n,n')\ \text{heap edge} \\<br />
\delta(e'),& \text{if}\ (n,n')\ \text{cross edge}.<br />
\end{cases}<br />
</tex><br />
<br />
Лемма 1 подразумевает, что куча упорядоченная в соответствии с <tex>\delta</tex>-значанием поддерживается по любому кучному ребру из <tex>P(G)</tex>. Эта упорядочивание кучи подразумевает, что <tex>\Delta(n,n')</tex> неотрицательна для любого кучного ребра <tex>(n,n')</tex>. Следовательно, <tex>\Delta</tex> также неотрицательна, т.е. <tex>\Delta(n,n') >= 0</tex> для любого ребра <tex>(n,n')</tex> в <tex>P(G)</tex>. Стоимость пути <tex>\sigma</tex>, т.е. <tex>C_{P(G)}(\sigma)</tex> равна <tex>\sum_{e \in \sigma}\Delta(e)</tex>. <br />
<br />
...<br />
<br />
{{Лемма<br />
|about=2<br />
|statement=Пусть <tex>n</tex> будет узлов графовой кучи <tex>H_{G}(w)</tex> для какой-нибудь вершины <tex>w</tex>. Пусть <tex>(u,v)</tex> будет ребром связанным с <tex>n</tex>. Тогда существует путь в дереве поиска <tex>T</tex> из <tex>v</tex> в <tex>w</tex>.<br />
|proof=...<br />
}}<br />
<br />
== Алгоритмическая структура K* ==<br />
Алгоритмический принцип K* следующий. Будем запускать алгоритмы Дейкстры и A* на <tex>G</tex> с чередованием. Сначала, мы запустим A* на <tex>G</tex> пока вершина <tex>t</tex> не будет выбрана из очереди для рассмотрения. Затем, вы запустим алгоритмы Дейкстры на доступной части <tex>P(G)</tex>. Каждый узел рассмотрел Дейкстрой представляет путь решения. Если точнее, то путь <tex>\sigma</tex> в <tex>P(G)</tex>, по которому Дейкстра достигла этого узла является решением. Путь <tex>s-t</tex> может быть построен из <tex>\sigma</tex> за линейное время путем вычисления последовательности запасных ребер <tex>seq(\sigma)</tex> и затем <tex>s-t</tex> пути из неё. Если Дейкстра находит <tex>k</tex> кратчайших путей, то K* завершается успешно. Иначе, A* возобновляется для исследования большей части <tex>G</tex>. Это приводит к росту <tex>P(G)</tex>, на котором алгоритм Дейкстры затем будет возобновлен. Мы будем повторять этот процесс до тех пор, пока алгоритм Дейкстры не найдет <tex>k</tex> кратчайших путей. <br />
<br />
Data: A graph given by its start vertex s ∈ V and its successor function succ and a<br />
natural number k<br />
Result: A list R containing k sidetrack edge sequences representing k solution paths<br />
<br />
1 <tex>open_D</tex> ← пустая приоритетная очередь<br />
2 <tex>closed_D</tex> ← пустая хеш-таблица<br />
3 <tex>R</tex> ← пустой список<br />
4 <tex>P(G)</tex> ← пустой граф путей<br />
5 Выполняем A* на графе <tex>G</tex> пока <tex>t</tex> не будет выбрана для раскрытия<br />
6 Если вершина <tex>t</tex> не была достигнута, то выходим без ответа<br />
7 Кладем <tex>\mathrm{R}</tex> в очередь <tex>open_D</tex><br />
8 '''while''' A queue or open D is not empty:<br />
9 '''if''' A queue is not empty:<br />
10 '''if''' очередь <tex>open_D</tex> не пуста:<br />
11 Let u be the head of the search queue of A ∗ and n the head of <tex>open_D</tex><br />
12 <tex>d = max\{ d(n) + \Delta(n, n') | n' \in succ(n) \}</tex><br />
13 '''if''' <tex>g(t) + d <= f</tex> (u) then переходим на строку 17.<br />
14 Возобновляем A* для того, чтобы исследовать более большую часть графа <tex>G</tex><br />
15 Обновляем <tex>P(G)</tex> and bring Dijkstra’s search into a consistent status<br />
16 Переходим на строку 8<br />
17 '''if''' очередь <tex>open_D</tex> пуста: переходим на строку 8.<br />
18 Remove from <tex>open_D</tex> and place on <tex>closed_D</tex> the node n with the minimal d-value.<br />
19 '''foreach''' <tex>n'</tex> referred by n in P(G):<br />
20 <tex>d(n') = d(n) + \Delta(n, n')</tex><br />
21 Attach to <tex>n'</tex> a parent link referring to <tex>n</tex>.<br />
22 Insert n 0 into <tex>open_D</tex><br />
23 Пусть <tex>\sigma</tex> будет путем в <tex>P(G)</tex>, через который узел n был достигнут.<br />
24 Добавим <tex>seq(\sigma)</tex> в конец списка <tex>R</tex>.<br />
25 '''if''' <tex>|R| = k</tex>: переходим на строку 26.<br />
26 Return R and exit.<br />
<br />
Алгоритм 1 содержит псевдокод K*. Код с 8 по 25 строчку образует главный цикл K*. Цикл завершается, когда очереди обоих алгоритмов А* и Дейкстры пусты. До 8 строчки выполняет некоторые подготовительные вещи. После инициализации, А* запускает на 5 строчке пока вершина <tex>t</tex> не будет выбрана им для рассмотрения, в этом случае кратчайший путь <tex>s-t</tex> будет найден. Если <tex>t</tex> не достигнута, то алгоритм завершается без ответа. Отметим, что он не завершится на бесконечных графах. Иначе, алгоритм добавляет специальную вершину <tex>R</tex>, которая назначена корнем <tex>P(G)</tex>, в поисковую очередь алгоритма Дейкстры. Затем, K* входит в главный цикл.<br />
K* поддерживает механизм планирования для контролирования, когда A* или Дейкстра будет возобновлены. Если очередь из A* не пуста, что означает, что А* ещё не завершил исследования всего графа G, то Дейкстра возобновляется тогда и только тогда, когда <tex>g(t) + d <= f(u)</tex>. Значение d является максимальным <tex>d</tex>-значением среди всех successor-ов головы поисковой очереди <tex>n</tex> алгоритма Дейкстры. Вершина <tex>u</tex> является головой поисковой очереди A*. Напомним, что <tex>d</tex> - функция расстояния, используемая в алгоритме Дейкстры. Если очередь поиска Дейкстры пуста или <tex>g(t) + d > f(u)</tex>, то А* возобновляется для того, чтобы исследовать более большую часть графа <tex>G</tex> (строка 14). То, как долго мы ему позволим работать, является компромиссом. Если мы запустим его только на маленьком количестве шагов, то мы дадим Дейкстре шанс найти необходимое количество путей скорее, чем они будут доступны в <tex>P(G)</tex>. С другой стороны, мы вызываем накладные расходы путем переключения A* и Дейкстры и поэтому должны ограничить количество переключений. Эти накладные расходы вызваны тем фактом, что после возобновления A* (строка 14), структура графа <tex>P(G)</tex> может измениться. Следовательно нам необходимо обновить <tex>P(G)</tex> (строка 15), как мы будет широко обсуждать в разделе 4.5. Это требует последующую проверку статуса Дейкстры. Мы должны быть уверены, что Дейкстра поддерживает согласованное состояние после изменений в <tex>P(G)</tex>. K* предусматривает условие, которые управляет решением, когда остановить A*, которое мы назовем ''условие расширения''. Для того, чтобы поддерживать аналогичную асимптотическую сложность как у EA и LVEA, мы должны определить условие расширения так, чтобы A* выполнялся пока количество рассмотренных вершин и количество внутренних ребер удваивается или G полностью исследован. Мы обсудим эту проблему несколько подробнее позже. В качестве полезного свойства, K* позволяет другое определения этого условия, которое может быть более эффективным на практике. В наших экспериментах в разделе 6, мы определили условие расширения так, что количество рассмотренных вершин или количество рассмотренных ребер ребер возрастает на 20% при каждом запуске A*. Этот механизм планирования включен до тех пор, пока A* не закончит исследовать весь граф <tex>G</tex>. Как только A* исследует весь граф G (строка 9), механизм планирования отключается и в дальнейшем работает только алгоритм Дейкстры.<br />
<br />
Строки 18-22 представляют обычный шаг рассмотрения узла алгоритмом Дейкстры. Отметим, что когда successor-узел <tex>n'</tex> сгенерирован, K* не проверяет был ли <tex>n'</tex> уже посещен до этого. Другими словами, каждый раз, когда узел генерируется, он рассматривает как новый. Эта стратегия обоснована на наблюдении, что путь s-t может содержать одно и то же ребро несколько раз. Строка 24 добавляет следующий путь <tex>s-t</tex> в результирующее множество R. Это делается путем конструирования последовательности запасных ребер <tex>seq(\sigma)</tex> из пути <tex>\sigma</tex>, через которые Дейкстра достигла узла <tex>n</tex>, который был только что рассмотрен. Алгоритм завершается, когда в результирующее множество добавлено <tex>k</tex> последовательностей запасных ребер (строка 25).<br />
<br />
== Взаимосвязь алгоритмов Дейкстры и A* ==<br />
Тот факт, что оба алгоритма A* и Дейкстры делят между собой граф путей <tex>P(G)</tex>, вызывает обеспокоенность в отношении правильности работы Дейкстры на <tex>P(G)</tex>. Возобновление A* приводит к изменениям в структуре <tex>P(G)</tex>. Таким образом, после возобновления A*, мы обновляем <tex>P(G)</tex> и проверяет статус поиска Дейкстры (строка 15). В основном, A* может добавить новые узлы, менять <tex>\delta</tex>-значения существующих узлов или даже удалять узлы. A* может также существенно изменять дерево поиска <tex>T</tex>, которое будет в худшем случае разрушать структуру все деревянных куч <tex>H_{T}</tex>. Эти изменения могут приводить к глобальной реструктуризации или даже перестроению <tex>P(G)</tex> с нуля. В худшем случае это может сделать предыдущие поиски Дейкстры на <tex>P(G)</tex> бесполезными таким образом, что нам придется перезапускать алгоритм Дейкстры с нуля.<br />
<br />
Если использованная эвристическая оценка допустимая, то наше положение лучше. Нам по-прежнему может понадобится перестроение <tex>P(G)</tex>, но мы покажем, что это перестроение не мешает корректности поиска Дейкстры на <tex>P(G)</tex>. Другими словами, мы не теряем результаты, до сих пор полученные поиском Дейкстры. В случае монотонной эвристической оценки мы даже не нуждаемся в перестроении <tex>P(G)</tex>. Если <tex>h</tex> монотонная, то дерево поиска A* является деревом кратчайшего пути для всех раскрытых вершин. Следовательно, g-значения раскрытых вершин не изменится. Это означает, что <tex>\delta</tex>-значения для внутренних ребер никогда не изменятся. Ребра дерева раскрытых вершин не изменятся также. Следовательно, обновление <tex>\delta</tex>-значений, heaping-up, heaping-down (операции в кучах) или удаление узлов не влекут за собой каких-либо изменений в <tex>P(G)</tex>. Только добавление новых узлов приводит к изменениям в <tex>P(G)</tex>. Следовательно, перестроение или глобальная реструктуризация не требуется в данном случае.<br />
<br />
В оставшейся части этого раздела, мы сначала покажем, что корректность поиска Дейкстры на <tex>P(G)</tex> поддерживается в случае допустимой эвристической оценки. После этого мы покажем, что изменения в <tex>P(G)</tex> могут помешать завершенности поиска Дейкстры независимо от того, является ли эвристика допустимой или даже монотонной. Следовательно, мы предложим механизм для её поддержания.<br />
<br />
Мы фокусируемся дальше на корректности поиска Дейкстры на <tex>P(G)</tex> в случае допустимой эвристической оценки. Сначала, мы заявляем, что если <tex>h</tex> допустимая, то узлы исследованной части <tex>P(G)</tex> не поменяют свои <tex>\delta</tex>-значения.<br />
<br />
{{Лемма<br />
|about=6<br />
|statement=Пусть <tex>n</tex> будет произвольным узлов в <tex>P(G)</tex> и пусть <tex>(u,v)</tex> будет ребром, связанным с <tex>n</tex>. Если <tex>h</tex> допустимая функция, то значение <tex>\delta(u, v)</tex> никогда не изменится после того, как <tex>n</tex> будет рассмотрен алгоритмом Дейкстры.<br />
|proof=...<br />
}}<br />
<br />
Из леммы 6 мы может вывести следующее следствие.<br />
<br />
Следствие 2. Пусть <tex>n</tex> будет произвольным узлов в <tex>P(G)</tex>. Если <tex>h</tex> допустимая функция, то <tex>n</tex> никогда не будет удален из <tex>P(G)</tex> после того, как <tex>n</tex> был рассмотрен алгоритмом Дейкстры.<br />
<br />
...<br />
<br />
Более того, мы докажем, что структура исследованной части <tex>P(G)</tex> не изменится.<br />
<br />
{{Лемма<br />
|about=7<br />
|statement=Пусть <tex>n</tex> будет произвольным узлов в <tex>P(G)</tex>. Если <tex>h</tex> допустимая функция, то <tex>n</tex> никогда не изменит свою позицию после того, как он был рассмотрен алгоритмом Дейкстры.<br />
|proof=...<br />
}}<br />
<br />
<br />
Леммы 6 и 7 обеспечивают, что изменения в <tex>P(G)</tex>, которые индуцируются A*, не влияют на часть <tex>P(G)</tex>, которую алгоритм Дейкстры уже исследовал. Это гарантирует корректность поиска Дейкстры на <tex>P(G)</tex>, если используемая эвристика допустимая. Таким образом, каждый путь, который предоставляет алгоритм Дейкстры корректен и его длина действительна. Однако, это не обеспечивает завершенность поиска Дейкстры на <tex>P(G)</tex>.<br />
<br />
Возможно, что узел <tex>n'</tex> присоединяется к другом узлу n как ребенок, после раскрытия узла <tex>n</tex>. В этом случае братья <tex>n'</tex> будут рассотрены до того, как <tex>n'</tex> станет ребенком n. Поэтому мы должны рассмотреть то, что было упущено во время поиска в связи с отсутствием <tex>n'</tex>. Мы добиваемся этого путем применения строк 20-22 к <tex>n'</tex> для каждого раскрытого направленного predecessor-а узла <tex>n'</tex>. Если <tex>n'</tex> ещё не выполняет условие планирования, A* будет неоднократно возобновляться пока механизм планирования не допустит алгоритму Дейкстры положить <tex>n'</tex> в поисковую очередь. Заметим, что таким образом не требуется каких-либо дополнительных усилий во время типичного поиска Дейкстры.<br />
<br />
Мы может быть уверены, что нерассмотренные узлы не будут принудительно опущены после применения операции heaping-up к <tex>n'</tex>. Иначе, мы могли бы иметь узел <tex>n''</tex>, который являлся бы ребенком n и впоследствии был бы заменен узлом <tex>n'</tex>. Заметим, что <tex>n''</tex> должен быть рассмотрен, поскольку <tex>n'</tex> был раскрыт. Однако, это противоречение к лемме 7, которая гарантирует, что этого не произойдет.<br />
<br />
Более того, следующее следствие гарантирует, что наилучшее <tex>d(n')</tex> не лучше, чем <tex>d</tex>-значение любой рассмотренной вершины, в частности, раскрытой вершины. Это означает, что мы не упустим возможность раскрыть <tex>n'</tex>.<br />
<br />
{{Лемма<br />
|about=следствие 3<br />
|statement=Пусть <tex>n</tex> будет узлом в <tex>P(G)</tex>, который был раскрыт Дейкстрой. Кроме того, пусть <tex>m</tex> будет узлом, который заново добавляется в <tex>P(G)</tex> или его позиция изменена, после того как <tex>n</tex> был раскрыт. Если <tex>h</tex> допустимая, тогда выполяется следующее:<br />
<tex>C_{P(G)}(R,m) >= d(n)</tex><br />
|proof=...<br />
}}<br />
<br />
== Пример ==<br />
Мы проиллюстрируем работу алгоритма K* следующим примером. Мы будем рассматривать ориентированный взвешанный граф G на рисунке 7. Стартовой вершиной будет называться <tex>s_0</tex> и конечной вершиной - <tex>s_6</tex>. Нас интересует поиск 9 лучших путей из <tex>s_0</tex> в <tex>s_6</tex>. Для достижения этой цели мы применим алгоритм K* к <tex>G</tex>. Предположим, что эвристическая оценка существует. Значения эвристики даны в пометках c <tex>h(s_0)</tex> по <tex>h(s_6)</tex> на рисунке 7. Легко заметить, что эвристическая функция допустима.<br />
<br />
Первый раз A* делает итерации на графе <tex>G</tex> до тех пор, пока не будет найдена вершина <tex>s_6</tex>. Часть графа <tex>G</tex>, которая уже была рассмотрена иллиюстрируется на рисунке 8. Ребра, изображенные сплошными линиями, обозначают ребра дерева, в то время, как все остальные - запасные ребра. Они будет храниться в кучах <tex>H_{in}</tex>, показанных на рисунке 9. Номера, присвоенные узлах кучи, соответствуют <tex>\delta</tex>-значениям. На этом этапе поиска A* приостановлен и <tex>P(G)</tex> построен. Первоначально, только назначенный корень <tex>R</tex> явно доступен в <tex>P(G)</tex>. Инициализируется алгоритм Дейкстры. Это означает, что узел <tex>R</tex> добавляется в поискую очередь Дейкстры. Планировщику требуется доступ к successors К для того, чтобы решить следует ли возобновлять Дейкстру или A*. На данном этапе должна быть построена деревянная куча <tex>H_{T}(s_6)</tex>. Куча <tex>H_{T}(s_4)</tex> требуется для построения <tex>H_{T}(s_6)</tex>. Следовательно, строются деревянные кучи <tex>H_{T}(s_6)</tex>, <tex>H_{T}(s_4)</tex>, <tex>H_{T}(s_2)</tex> и <tex>H_{T}(s_0)</tex>. Результат показан на рисунке 10, где сплошные линии представляют кучные ребра и пунктирные линии показывают кросс-ребра. Во избежание путаницы на рисунке некоторые из ребер не полностью изображены. Мы указываем каждое из них, используя короткую стрелку с конретной целью.<br />
<br />
После построения, как показано 10, планировщик проверяет только ребенка <tex>(s_4, s_2)</tex> узла <tex>R</tex> на предмет того, что <tex>g(s_6) + d(s_4,s_2) <= f(s_1)</tex>. Отметим, что <tex>s_1</tex> является головой поисковой очереди A*. Значение <tex>d(s_4,s_2)</tex> равно 2, т.е. <tex>g(s_6) + d(s_4,s_2) = 7 + 2 = 9 = f(s_1)</tex>. Следовательно, планировщик позволяет Дейкстре раскрыть <tex>R</tex> и вставить <tex>(s_4,s_2)</tex> в поисковую очередь. При раскрытии <tex>R</tex> находится первый путь из ответа. Он строится из пути <tex>P(G)</tex>, содержащего единственный узел <tex>R</tex>. Этот путь приводит к пустой последовательности запасных ребер. Напомним, что пустая последовательность запасных ребер соответствует пути из <tex>s_0</tex> в <tex>s_6</tex> в дереве поиска, а именно <tex>s_0s_2s_4s_6</tex> длиной 7. Затем поиск Дейкстры приостанавливается, потому что для successor-ов узла <tex>(s_4,s_2)</tex> не выполняется условие <tex>g(s_6)+d(n)<=f(s_1)</tex>. Следовательно, возобновляется A*.<br />
<br />
Мы предполагаем, что условие раскрытия определено как раскрытие одной вершины для того, чтобы пример был простым и иллюстративным. Поэтому A* раскрывает <tex>s_1</tex> и останавливается. Исследованная часть <tex>G</tex> на текущем этапе показана на рисунке 11. Результат раскрытия приведет к обнаружению 2 новых запасных ребер <tex>(s_1,s_2)</tex> и <tex>(s_1,s_6)</tex>, которые будут добавлены в <tex>H_{in}(s_2)</tex> и <tex>H_{in}(s_6)</tex> соответственно. Обновленные кучи <tex>H_{in}(s_2)</tex> и <tex>H_{in}(s_6)</tex> представлены на рисунке 12. Другие кучи остаются неизменными, как на рисунке 9. Граф путей <tex>P(G)</tex> перестаивается, как показано на рисунке 13. Затем алгоритм Дейкстры возобновляется. Заметим, что поисковая очередь Дейкстры содержит только <tex>(s_4,s_2)</tex> с <tex>d = 2</tex> на этом моменте. Используя ручное выполнение мы можем легко увидеть, что Дейкстра будет выдавать в ответ пути, перечисленные в таблице 1.</div>Kabanovhttp://neerc.ifmo.ru/wiki/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Kabanov&diff=48973Участник:Kabanov2015-08-05T10:51:48Z<p>Kabanov: </p>
<hr />
<div>Также как и алгоритм Eppstein, K* выполняет поиск пути на графе <tex>G</tex> и использует граф путей <tex>P(G)</tex>. Граф путей ищется с помощью алгоритма Дейкстры для того, чтобы пути <tex>s-t</tex> в виде последовательности запасных путей. Общий принцип работы алгоритма K* следующий:<br />
1) K* применяет A* на графе <tex>G</tex> вместо обратного алгоритма Дейкстры, который использует алгоритм Eppstein.<br />
2) Мы запускаем A* на <tex>G</tex> и Дейкстру на <tex>P(G)</tex> поочередно порядке, который позволяет Дейкстре доставить пути решение до заверешения поиска на <tex>G</tex> алгоритма A*.<br />
<br />
== Поиск A* на G. == <br />
K* применяет A* к входному графу <tex>G</tex> для того, чтобы определить дерево поиска <tex>T</tex>. <br />
В отличие от алгоритма Eppstein в K* A* применяется к графу <tex>G</tex> в прямом порядке из-за чего коренем дерева <tex>T </tex> является вершина <tex>s</tex>. Это необходимо для того, чтобы была возможность работать c неявным описанием графа <tex>G</tex> через функцию successor. На протяжении статьи будем считать граф <tex>G</tex> конечным, если не будет сказано иначе. Заметим, что А* корректен на конечных графах. Будем следовать литературному соглашению, предполагая, что стоимость бесконечного пути неограниченна.<br />
<br />
== Стоимость объезда ==<br />
Для ребра <tex>(u, v)</tex> стоимость объезда <tex>\delta(u, v)</tex> является стоимостью ущерба из-за взятия ребра объезда <tex>(u,v)</tex> в сравнении с кратчайшим путем <tex>s-t</tex> через <tex>v</tex>. Ни длина кратчайшего пути s-t через v, ни длина пути s-t, включающего запапасные ребра <tex>(u, v)</tex> не известны, когда A* обнаруживает <tex>(u, v)</tex>. Обе длины могут быть оценены с помощью функции оценки <tex>f</tex>, которая использует эвристическую функцию <tex>h</tex>. Путь <tex>f(v)</tex> будет <tex>f</tex>-значением с соответствии с деревом поиска <tex>T</tex> и <tex>f_u(v)</tex> будет <tex>f</tex>-значанием в соответствии с родителем u, т.е. <tex>f_u(v) = g(u) + c(u, v) + h(v)</tex>. <tex>\delta(u, v)</tex> может быть определена так:<br />
<br />
<tex>\delta(u, v) = f_u(v) - f(v) = g(u) + c(u, v) + h(v) - g(v) - h(v) = g(u) + c(u, v) - g(v)</tex><br />
<br />
Заметим, что <tex>\delta(u, v)</tex> дает точную объездную метрику, поскольку функция оценки <tex>h</tex>-значения не появляется в определении функции <tex>\delta(u, v)</tex>.<br />
<br />
== Структура графе путей ==<br />
Структура графа путей <tex>P(G)</tex> довольно сложная. В принципе, <tex>P(G)</tex> будет ориентированным графом, вершины которого соответствуют ребрам в исходном графе G. Он будет организован как коллекция взаимосвязанных куч (англ. heap). 2 бинарные минимальные кучи присвоены к каждой вершине v в графе G, которые называются входящей кучей <tex>H_{in}(v)</tex> и деревянной кучей <tex>H_{T}(v)</tex>. Эти кучи являются базисом <tex>P(G)</tex>. Как мы покажем далее, испльзование этих куч также играет главную роль в поддержании асимптотической сложности K*, также как в EA и LVEA.<br />
<br />
Входящая куча <tex>H_{in}(v)</tex> содержит узлы для каждого запасного ребра к вершине v, которые до сих пор были обнаружены A*. Узлы <tex>H_{in}(v)</tex> будут упорядочены в соответствии с <tex>\delta</tex>-значением соответствующих переходов. Узел владеющий ребром с минимальной стоимостью ущерба будет расположен на вершине кучи. Мы ограничим структуру кучи <tex>H_{in}(v)</tex> таким образом, что её корень в отличие от остальных узлов, имеет не более 1 ребенка. Мы обозначим его <tex>root_{in}(v)</tex>.<br />
<br />
Деревянная куча <tex>H_{T}(v)</tex> для произвольной вершины <tex>v</tex> строится следующим образом. Если <tex>v</tex> - стартовая вершина, т.е. <tex>v = s</tex>, то <tex>H_{T}(v)</tex> будет изначально пустой кучей. Затем узел в неё будет добавлен <tex>root_{in}(s)</tex>, если <tex>H_{in}(s)</tex> не пустая. Если <tex>v</tex> не стартовая вершина, то пусть вершина <tex>u</tex> будет родителем вершины <tex>v</tex> в дереве поиска <tex>T</tex>. Мы можем представить, что <tex>H_{T}(v)</tex> конструируется как копия <tex>H_{T}(u)</tex>, в которую добавлен <tex>root_{in}(v)</tex>. Если <tex>H_{in}(v)</tex> пустая, то <tex>H_{T}(v)</tex> идентична <tex>H_{T}(u)</tex>. Однако, для экономии памяти мы создаем только дешевую копию <tex>H_{T}(u)</tex>. Это осуществляется через создание копий только узлов кучи, которые лежат на обновленном пути <tex>H_{T}(u)</tex>. Оставшаяся часть <tex>H_{T}(u)</tex> не копируется. Другими словами, <tex>root_{in}(v)</tex> вставляется в <tex>H_{T}(u)</tex> неразрушающим путем так, что структура <tex>H_{T}(u)</tex> сохраняется. В куче <tex>H_{T}(v)</tex> 1 или 2 ребенка могут быть присоединены к <tex>root_{in}(v)</tex>. К тому же, root_{in}(v) хранит только 1 собственного ребенка из <tex>H_{in}(v)</tex>. Мы обозначим корень <tex>H_{T}(v)</tex> как <tex>R(v)</tex>.<br />
<br />
Обратимся к ребрам, которые берут начало из входящих или деревянных куч, как к кучным ребрам. Сформулируем следующую лемму.<br />
Лемма 1. Все узлы, которые достижимы из <tex>R(v)</tex> через кучные ребра для каждой вершины <tex>v</tex>, формируют тернарную кучу, упорядоченную в соответствии с <tex>\delta</tex>-значением. Мы назовем такую кучу графовой кучей вершины <tex>v</tex> и обозначим его как <tex>H_{G}(v)</tex>.<br />
<br />
...<br />
<br />
Финальная структура <tex>P(G)</tex> получется из входящих и деревянных куч следующим образом. К каждому узлу <tex>n</tex> из <tex>P(G)</tex>, несущему ребро <tex>(u,v)</tex>, мы присоединим указатель, ссылающийся на <tex>R(u)</tex>, который является корневым узлом <tex>H_{T}(u)</tex>. Мы назовем такие указатели кросс-ребрами, в то время как указатели, возникающие из куч названы кучными ребрами, как упоминалось раньше. Более того, мы добавим специальный узел <tex>R</tex> в <tex>P(G)</tex> с одним выходящим кросс-ребром к <tex>R(t)</tex>.<br />
<br />
Более того, мы определим весовую функцию <tex>\Delta</tex> на ребрах из <tex>P(G)</tex>. Пусть <tex>(n,n')</tex> обозначает ребро в <tex>P(G)</tex>, и пусть <tex>e</tex> и <tex>e'</tex> обозначают ребра из <tex>G</tex> соответствующие узлам <tex>n</tex> и <tex>n'</tex>. Тогда определим <tex>\Delta(n,n')</tex> следующим образом:<br />
<br />
\Delta(n,n')=\delta(e') - \delta(e) если (n,n') кучное ребро<br />
\Delta(n,n')=\delta(e') если (n,n') кросс-ребро.<br />
<br />
Лемма 1 подразумевает, что куча упорядоченная в соответствии с \delta-значанием поддерживается по любому кучному ребру из <tex>P(G)</tex>. Эта упорядочивание кучи подразумевает, что \Delta(n,n') неотрицательна для любого кучного ребра (n,n'). Следовательно, \Delta также неотрицательна, т.е. \Delta(n,n') >= 0 для любого ребра (n,n') в <tex>P(G)</tex>. Стоимость пути \sigma, т.е. C_{P(G)}(\sigma) равна \sum_{e \in \sigma}\Delta(e). <br />
<br />
...<br />
<br />
Лемма 2. Пусть <tex>n</tex> будет узлов графовой кучи <tex>H_{G}(w)</tex> для какой-нибудь вершины <tex>w</tex>. Пусть <tex>(u,v)</tex> будет ребром связанным с <tex>n</tex>. Тогда существует путь в дереве поиска <tex>T</tex> из <tex>v</tex> в <tex>w</tex>.<br />
<br />
...<br />
<br />
== Алгоритмическая структура K* ==<br />
Алгоритмический принцип K* следующий. Будем запускать алгоритмы Дейкстры и A* на G с чередованием. Сначала, мы запустим A* на G пока вершина t не будет выбрана из очереди для рассмотрения. Затем, вы запустим алгоритмы Дейкстры на доступной части <tex>P(G)</tex>. Каждый узел рассмотрел Дейкстрой представляет путь решения. Если точнее, то путь <tex>\sigma</tex> в <tex>P(G)</tex>, по которому Дейкстра достигла этого узла является решением. Путь s-t может быть построен из \sigma за линейное время путем вычисления последовательности запасных ребер seq(\sigma) и затем s-t пути из неё. Если Дейкстра находит k кратчайших путей, то K* завершается успешно. Иначе, A* возобновляется для исследования большей части G. Это приводит к росту <tex>P(G)</tex>, на котором алгоритм Дейкстры затем будет возобновлен. Мы будем повторять этот процесс до тех пор, пока алгоритм Дейкстры не найдет k кратчайших путей. <br />
<br />
Алгоритм 1 содержит псевдокод K*. Код с 8 по 25 строчку образует главный цикл K*. Цикл завершается, когда очереди обоих алгоритмов А* и Дейкстры пусты. До 8 строчки выполняет некоторые подготовительные вещи. После инициализации, А* запускает на 5 строчке пока вершина t не будет выбрана им для рассмотрения, в этом случае кратчайший путь s-t будет найден. Если t не достигнута, то алгоритм завершается без решения. Отметим, что он не завершится на бесконечных графах. Иначе, алгоритм добавляет специальную вершину R, которая назначена корнем <tex>P(G)</tex>, в поисковую очередь алгоритма Дейкстры. Затем, K* входит в главный цикл.<br />
K* поддерживает механизм планирования для контролирования, когда A* или Дейкстра будет возобновлены. Если очередь из A* не пуста, что означает, что А* ещё не завершил исследования всего графа G, то Дейкстра возобновляется тогда и только тогда, когда g(t) + d <= f(u). Значение d является максимальным d-значением среди всех successor-ов головы поисковой очереди n алгоритма Дейкстры. Вершина u является головой поисковой очереди A*. Напомним, что d - функция расстояния, используемая в алгоритме Дейкстры. Если очередь поиска Дейкстры пуста или g(t) + d > f(u), то А* возобновляется для того, чтобы исследовать более большую часть графа G (строка 14). То, как долго мы ему позволим работать, является компромиссом. Если мы запустим его только на маленьком количестве шагов, то мы дадим Дейкстре шанс найти необходимое количество путей скорее, чем они будут доступны в <tex>P(G)</tex>. С другой стороны, мы вызываем накладные расходы путем переключения A* и Дейкстры и поэтому должны ограничить количество переключений. Эти накладные расходы вызваны тем фактом, что после возобновления A* (строка 14), структура графа <tex>P(G)</tex> может измениться. Следовательно нам необходимо обновить <tex>P(G)</tex> (строка 15), как мы будет широко обсуждать в разделе 4.5. Это требует последующую проверку статуса Дейкстры. Мы должны быть уверены, что Дейкстра поддерживает согласованное состояние после изменений в <tex>P(G)</tex>. K* предусматривает условие, которые управляет решением, когда остановить A*, которое мы назовем ''условие расширения''. Для того, чтобы поддерживать аналогичную асимптотическую сложность как у EA и LVEA, мы должны определить условие расширения так, чтобы A* выполнялся пока количество рассмотренных вершин и количество внутренних ребер удваивается или G полностью исследован. Мы обсудим эту проблему несколько подробнее позже. В качестве полезного свойства, K* позволяет другое определения этого условия, которое может быть более эффективным на практике. В наших экспериментах в разделе 6, мы определили условие расширения так, что количество рассмотренных вершин или количество рассмотренных ребер ребер возрастает на 20% при каждом запуске A*. Этот механизм планирования включен до тех пор, пока A* не закончит исследовать весь граф G. Как только A* исследует весь граф G (строка 9), механизм планирования отключается и в дальнейшем работает только алгоритм Дейкстры.<br />
<br />
Строки 18-22 представляют обычный шаг рассмотрения узла алгоритмом Дейкстры. Отметим, что когда successor-узел n' сгенерирован, K* не проверяет был ли n' уже посещен до этого. Другими словами, каждый раз, когда узел генерируется, он рассматривает как новый. Эта стратегия обоснована на наблюдении, что путь s-t может содержать одно и то же ребро несколько раз. Строка 24 добавляет следующий путь s-t в результирующее множество R. Это делается путем конструирования последовательности запасных ребер seq(\sigma) из пути \sigma, через которые Дейкстра достигла узла n, который был только что рассмотрен. Алгоритм завершается, когда в результирующее множество добавлено k последовательностей запасных ребер (строка 25).<br />
<br />
== Взаимосвязь алгоритмов Дейкстры и A* ==<br />
Тот факт, что оба алгоритма A* и Дейкстры делят между собой граф путей <tex>P(G)</tex>, вызывает обеспокоенность в отношении правильности работы Дейкстры на <tex>P(G)</tex>. Возобновление A* приводит к изменениям в структуре <tex>P(G)</tex>. Таким образом, после возобновления A*, мы обновляем <tex>P(G)</tex> и проверяет статус поиска Дейкстры (строка 15). В основном, A* может добавить новые узлы, менять \delta-значения существующих узлов или даже удалять узлы. A* может также существенно изменять дерево поиска T, которое будет в худшем случае разрушать структуру все деревянных куч H_{T}. Эти изменения могут приводить к глобальной реструктуризации или даже перестроению <tex>P(G)</tex> с нуля. В худшем случае это может сделать предыдущие поиски Дейкстры на <tex>P(G)</tex> бесполезными таким образом, что нам придется перезапускать алгоритм Дейкстры с нуля.<br />
<br />
Если использованная эвристическая оценка допустимая, то наше положение лучше. Нам по-прежнему может понадобится перестроение <tex>P(G)</tex>, но мы покажем, что это перестроение не мешает корректности поиска Дейкстры на <tex>P(G)</tex>. Другими словами, мы не теряем результаты, до сих пор полученные поиском Дейкстры. В случае монотонной эвристической оценки мы даже не нуждаемся в перестроении <tex>P(G)</tex>. Если h монотонная, то дерево поиска A* является деревом кратчайшего пути для всех раскрытых вершин. Следовательно, g-значения раскрытых вершин не изменится. Это означает, что \delta-значения для внутренних ребер никогда не изменятся. Ребра дерева раскрытых вершин не изменятся также. Следовательно, обновление \delta-значений, heaping-up, heaping-down (операции в кучах) или удаление узлов не влекут за собой каких-либо изменений в <tex>P(G)</tex>. Только добавление новых узлов приводит к изменениям в <tex>P(G)</tex>. Следовательно, перестроение или глобальная реструктуризация не требуется в данном случае.<br />
<br />
В оставшейся части этого раздела, мы сначала покажем, что корректность поиска Дейкстры на <tex>P(G)</tex> поддерживается в случае допустимой эвристической оценки. После этого мы покажем, что изменения в <tex>P(G)</tex> могут помешать завершенности поиска Дейкстры независимо от того, является ли эвристика допустимой или даже монотонной. Следовательно, мы предложим механизм для её поддержания.<br />
<br />
Мы фокусируемся дальше на корректности поиска Дейкстры на <tex>P(G)</tex> в случае допустимой эвристической оценки. Сначала, мы заявляем, что если h допустимая, то узлы исследованной части <tex>P(G)</tex> не поменяют свои \delta-значения.<br />
<br />
Лемма 6. Пусть n будет произвольным узлов в <tex>P(G)</tex> и пусть (u,v) будет ребром, связанным с n. Если h допустимая функция, то значение \delta(u,v) никогда не изменится после того, как n будет рассмотрен алгоритмом Дейкстры.<br />
<br />
...<br />
<br />
Из леммы 6 мы может вывести следующее следствие.<br />
<br />
Следствие 2. Пусть n будет произвольным узлов в <tex>P(G)</tex>. Если h допустимая функция, то n никогда не будет удален из <tex>P(G)</tex> после того, как n был рассмотрен алгоритмом Дейкстры.<br />
<br />
...<br />
<br />
Более того, мы докажем, что структура исследованной части <tex>P(G)</tex> не изменится.<br />
<br />
Лемма 7. Пусть n будет произвольным узлов в <tex>P(G)</tex>. Если h допустимая функция, то n никогда не изменит свою позицию после того, как он был рассмотрен алгоритмом Дейкстры.<br />
<br />
...<br />
<br />
Леммы 6 и 7 обеспечивают, что изменения в <tex>P(G)</tex>, которые индуцируются A*, не влияют на часть <tex>P(G)</tex>, которую алгоритм Дейкстры уже исследовал. Это гарантирует корректность поиска Дейкстры на <tex>P(G)</tex>, если используемая эвристика допустимая. Таким образом, каждый путь, который предоставляет алгоритм Дейкстры корректен и его длина действительна. Однако, это не обеспечивает завершенность поиска Дейкстры на <tex>P(G)</tex>.<br />
<br />
Возможно, что узел n' присоединяется к другом узлу n как ребенок, после раскрытия узла n. В этом случае братья n' будут рассотрены до того, как n' станет ребенком n. Поэтому мы должны рассмотреть то, что было упущено во время поиска в связи с отсутствием n'. Мы добиваемся этого путем применения строк 20-22 к n' для каждого раскрытого направленного predecessor-а узла n'. Если n' ещё не выполняет условие планирования, A* будет неоднократно возобновляться пока механизм планирования не допустит алгоритму Дейкстры положить n' в поисковую очередь. Заметим, что таким образом не требуется каких-либо дополнительных усилий во время типичного поиска Дейкстры.<br />
<br />
Мы может быть уверены, что нерассмотренные узлы не будут принудительно опущены после применения операции heaping-up к n'. Иначе, мы могли бы иметь узел n'', который являлся бы ребенком n и впоследствии был бы заменен узлом n'. Заметим, что n'' должен быть рассмотрен, поскольку n' был раскрыт. Однако, это противоречение к лемме 7, которая гарантирует, что этого не произойдет.<br />
<br />
Более того, следующее следствие гарантирует, что наилучшее d(n') не лучше, чем d-значение любой рассмотренной вершины, в частности, раскрытой вершины. Это означает, что мы не упустим возможность раскрыть n'.<br />
<br />
Следствие 3. Пусть n будет узлом в <tex>P(G)</tex>, который был раскрыт Дейкстрой. Кроме того, пусть m будет узлом, который заново добавляется в <tex>P(G)</tex> или его позиция изменена, после того как n был раскрыт. Если h допустимая, тогда выполяется следующее:<br />
C_{P(G)}(R,m) >= d(n)<br />
<br />
...<br />
<br />
== Пример ==<br />
Мы проиллюстрируем работу алгоритма K* следующим примером. Мы будем рассматривать ориентированный взвешанный граф G на рисунке 7. Стартовой вершиной будет называться s_0 и конечной вершиной - s_6. Нас интересен поиск 9 лучших путей из s_0 в s_6. Для достижения этой цели мы применим алгоритм K* к G. Предположим, что эвристическая оценка существует. Значения эвристики даны в пометках c h(s_0) по h(s_6) на рисунке 7. Легко заметить, что эвристическая функция допустима.<br />
<br />
Первый раз A* делает итерации на графе G до тех пор, пока не будет найдена вершина s_6. Часть графа G, которая уже была рассмотрена иллиюстрируется на рисунке 8. Ребра, изображенные сплошными линиями, обозначают ребра дерева, в то время, как все остальные - запасные ребра. Они будет храниться в кучах H_{in}, показанных на рисунке 9. Номера, присвоенные узлах кучи, соответствуют \delta-значениям. На этом этапе поиска A* приостановлен и <tex>P(G)</tex> построен. Первоначально, только назначенный корень R явно доступен в <tex>P(G)</tex>. Инициализируется алгоритм Дейкстры. Это означает, что узел R добавляется в поискую очередь Дейкстры. Планировщику требуется доступ к successors К для того, чтобы решить следует ли возобновлять Дейкстру или A*. На данном этапе должна быть построена деревянная куча H_{T}(s_6). Куча H_{T}(s_4) требуется для построения H_{T}(s_6). Следовательно, строются деревянные кучи H_{T}(s_6), H_{T}(s_4), H_{T}(s_2) и H_{T}(s_0). Результат показан на рисунке 10, где сплошные линии представляют кучные ребра и пунктирные линии показывают кросс-ребра. Во избежание путаницы на рисунке некоторые из ребер не полностью изображены. Мы указываем каждое из них, используя короткую стрелку с конретной целью.<br />
<br />
После построения, как показано 10, планировщик проверяет только ребенка (s_4, s_2) узла R на предмет того, что g(s_6)+d(s_4,s_2) <= f(s_1). Отметим, что s_1 является головой поисковой очереди A*. Значение d(s_4,s_2) равно 2, т.е. g(s_6)+d(s_4,s_2) = 7 + 2 = 9 = f(s1). Следовательно, планировщик позволяет Дейкстре раскрыть R и вставить (s_4,s_2) в поисковую очередь. При раскрытии R находится первый путь из ответа. Он строится из пути <tex>P(G)</tex>, содержащего единственный узел R. Этот путь приводит к пустой последовательности запасных ребер. Напомним, что пустая последовательность запасных ребер соответствует пути из s_0 в s_6 в дереве поиска, а именно s_0s_2s_4s_6 длиной 7. Затем поиск Дейкстры приостанавливается, потому что для successor-ов узла (s_4,s_2) не выполняется условие g(s_6)+d(n)<=f(s_1). Следовательно, возобновляется A*.<br />
<br />
Мы предполагаем, что условие раскрытия определено как раскрытие одной вершины для того, чтобы пример был простым и иллюстративным. Поэтому A* раскрывает s_1 и останавливается. Исследованная часть G на текущем этапе показана на рисунке 11. Результат раскрытия приведет к обнаружению 2 новых запасных ребер (s_1,s_2) и (s_1,s_6), которые будут добавлены в H_{in}(s_2) и H_{in}(s_6) соответственно. Обновленные кучи H_{in}(s_2) и H_{in}(s_6) представлены на рисунке 12. Другие кучи остаются неизменными, как на рисунке 9. Граф путей <tex>P(G)</tex> перестаивается, как показано на рисунке 13. Затем алгоритм Дейкстры возобновляется. Заметим, что поисковая очередь Дейкстры содержит только (s_4,s_2) с d=2 на этом моменте. Используя ручное выполнение мы можем легко увидеть, что Дейкстра будет выдавать в ответ пути, перечисленные в таблице 1.</div>Kabanovhttp://neerc.ifmo.ru/wiki/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Kabanov&diff=48972Участник:Kabanov2015-08-05T10:35:39Z<p>Kabanov: </p>
<hr />
<div>Также как и алгоритм Eppstein, K* выполняет поиск пути на графе G и использует граф путей P(G). Граф путей ищется с помощью алгоритма Дейкстры для того, чтобы пути s-t в виде последовательности запасных путей. Общий принцип работы алгоритма K* следующий:<br />
1) K* применяет A* на графе G вместо обратного алгоритма Дейкстры, который использует алгоритм Eppstein.<br />
2) Мы запускаем A* на G и Дейкстру на P(G) поочередно порядке, который позволяет Дейкстре доставить пути решение до заверешения поиска на G алгоритма A*.<br />
<br />
== 4.1 Поиск A* на G. == <br />
K* применяет A* к входному графу G для того, чтобы определить дерево поиска T. <br />
В отличие от алгоритма Eppstein в K* A* применяется к графу G в прямом порядке из-за чего коренем дерева T является вершина s. Это необходимо для того, чтобы была возможность работать c неявным описанием графа G через функцию successor. На протяжении статьи будем считать граф G конечным, если не будет сказано иначе. Заметим, что А* корректен на конечных графах. Будем следовать литературному соглашению, предполагая, что стоимость бесконечного пути неограниченна.<br />
<br />
== Стоимость объезда ==<br />
Для ребра (u, v) стоимость объезда \delta(u, v) является стоимостью ущерба из-за взятия ребра объезда (u, v) в сравнении с кратчайшим путем s-t через v. Ни длина кратчайшего пути s-t через v, ни длина пути s-t, включающего запапасные ребра (u, v) не известны, когда A* обнаруживает (u, v). Обе длины могут быть оценены с помощью функции оценки f, которая использует эвристическую функцию h. Путь f(v) будет f-значением с соответствии с деревом поиска T и f_u(v) будет f-значанием в соответствии с родителем u, т.е. f_u(v) = g(u) + c(u, v) + h(v). \delta(u, v) может быть определена так:<br />
<br />
\delta(u, v) = f_u(v) - f(v) = g(u) + c(u, v) + h(v) - g(v) - h(v) = g(u) + c(u, v) - g(v)<br />
<br />
Заметим, что \delta(u, v) дает точную объездную метрику, поскольку функция оценки h-значения не появляется в определении функции \delta(u, v).<br />
<br />
== Структура графе путей ==<br />
Структура графа путей P(G) довольно сложная. В принципе, P(G) будет ориентированным графом, вершины которого соответствуют ребрам в исходном графе G. Он будет организован как коллекция взаимосвязанных куч (англ. heap). 2 бинарные минимальные кучи присвоены к каждой вершине v в графе G, которые называются входящей кучей H_{in}(v) и деревянной кучей H_{T}(v). Эти кучи являются базисом P(G). Как мы покажем далее, испльзование этих куч также играет главную роль в поддержании асимптотической сложности K*, также как в EA и LVEA.<br />
Входящая куча H_{in}(v) содержит узлы для каждого запасного ребра к вершине v, которые до сих пор были обнаружены A*. Узлы H_{in}(v) будут упорядочены в соответствии с \delta-значением соответствующих переходов. Узел владеющий ребром с минимальной стоимостью ущерба будет расположен на вершине кучи. Мы ограничим структуру кучи H_{in}(v) таким образом, что её корень в отличие от остальных узлов, имеет не более 1 ребенка. Мы обозначим его root_{in}(v).<br />
<br />
Деревянная куча H_{T}(v) для произвольной вершины v строится следующим образом. Если v - стартовая вершина, т.е. v=s, то H_{T}(v) будет изначально пустой кучей. Затем узел в неё будет добавлен root_{in}(s), если H_{in}(s) не пустая. Если v не стартовая вершина, то пусть вершина u будет родителем вершины v в дереве поиска T. Мы можем представить, что H_{T}(v) конструируется как копия H_{T}(u), в которую добавлен root_{in}(v). Если H_{in}(v) пустая, то H_{T}(v) идентична H_{T}(u). Однако, для экономии памяти мы создаем только дешевую копию H_{T}(u). Это осуществляется через создание копий только узлов кучи, которые лежат на обновленном пути H_{T}(u). Оставшаяся часть H_{T}(u) не копируется. Другими словами, root_{in}(v) вставляется в H_{T}(u) неразрушающим путем так, что структура H_{T}(u) сохраняется. В куче H_{T}(v) 1 или 2 ребенка могут быть присоединены к root_{in}(v). К тому же, root_{in}(v) хранит только 1 собственного ребенка из H_{in}(v). Мы обозначим корень H_{T}(v) как R(v).<br />
<br />
Обратимся к ребрам, которые берут начало из входящих или деревянных куч, как к кучным ребрам. Сформулируем следующую лемму.<br />
Лемма 1. Все узлы, которые достижимы из R(v) через кучные ребра для каждой вершины v, формируют тернарную кучу, упорядоченную в соответствии с \delta-значением. Мы назовем такую кучу графовой кучей вершины v и обозначим его как H_{G}(v).<br />
<br />
...<br />
<br />
Финальная структура P(G) получется из входящих и деревянных куч следующим образом. К каждому узлу n из P(G), несущему ребро (u,v), мы присоединим указатель, ссылающийся на R(u), который является корневым узлом H_{T}(u). Мы назовем такие указатели кросс-ребрами, в то время как указатели, возникающие из куч названы кучными ребрами, как упоминалось раньше. Более того, мы добавим специальный узел R в P(G) с одним выходящим кросс-ребром к R(t).<br />
<br />
Более того, мы определим весовую функцию \Delta на ребрах из P(G). Пусть (n, n') обозначает ребро в P(G), и пусть e и e' обозначают ребра из G соответствующие узлам n и n'. Тогда определим \Delta(n,n') следующим образом:<br />
<br />
\Delta(n,n')=\delta(e') - \delta(e) если (n,n') кучное ребро<br />
\Delta(n,n')=\delta(e') если (n,n') кросс-ребро.<br />
<br />
Лемма 1 подразумевает, что куча упорядоченная в соответствии с \delta-значанием поддерживается по любому кучному ребру из P(G). Эта упорядочивание кучи подразумевает, что \Delta(n,n') неотрицательна для любого кучного ребра (n,n'). Следовательно, \Delta также неотрицательна, т.е. \Delta(n,n') >= 0 для любого ребра (n,n') в P(G). Стоимость пути \sigma, т.е. C_{P(G)}(\sigma) равна \sum_{e \in \sigma}\Delta(e). <br />
<br />
...<br />
<br />
Лемма 2. Пусть n будет узлов графовой кучи H_{G}(w) для какой-нибудь вершины w. Пусть (u,v) будет ребром связанным с n. Тогда существует путь в дереве поиска T из v в w.<br />
<br />
...<br />
<br />
== Алгоритмическая структура K* ==<br />
Алгоритмический принцип K* следующий. Будем запускать алгоритмы Дейкстры и A* на G с чередованием. Сначала, мы запустим A* на G пока вершина t не будет выбрана из очереди для рассмотрения. Затем, вы запустим алгоритмы Дейкстры на доступной части P(G). Каждый узел рассмотрел Дейкстрой представляет путь решения. Если точнее, то путь \sigma в P(G), по которому Дейкстра достигла этого узла является решением. Путь s-t может быть построен из \sigma за линейное время путем вычисления последовательности запасных ребер seq(\sigma) и затем s-t пути из неё. Если Дейкстра находит k кратчайших путей, то K* завершается успешно. Иначе, A* возобновляется для исследования большей части G. Это приводит к росту P(G), на котором алгоритм Дейкстры затем будет возобновлен. Мы будем повторять этот процесс до тех пор, пока алгоритм Дейкстры не найдет k кратчайших путей. <br />
<br />
Алгоритм 1 содержит псевдокод K*. Код с 8 по 25 строчку образует главный цикл K*. Цикл завершается, когда очереди обоих алгоритмов А* и Дейкстры пусты. До 8 строчки выполняет некоторые подготовительные вещи. После инициализации, А* запускает на 5 строчке пока вершина t не будет выбрана им для рассмотрения, в этом случае кратчайший путь s-t будет найден. Если t не достигнута, то алгоритм завершается без решения. Отметим, что он не завершится на бесконечных графах. Иначе, алгоритм добавляет специальную вершину R, которая назначена корнем P(G), в поисковую очередь алгоритма Дейкстры. Затем, K* входит в главный цикл.<br />
K* поддерживает механизм планирования для контролирования, когда A* или Дейкстра будет возобновлены. Если очередь из A* не пуста, что означает, что А* ещё не завершил исследования всего графа G, то Дейкстра возобновляется тогда и только тогда, когда g(t) + d <= f(u). Значение d является максимальным d-значением среди всех successor-ов головы поисковой очереди n алгоритма Дейкстры. Вершина u является головой поисковой очереди A*. Напомним, что d - функция расстояния, используемая в алгоритме Дейкстры. Если очередь поиска Дейкстры пуста или g(t) + d > f(u), то А* возобновляется для того, чтобы исследовать более большую часть графа G (строка 14). То, как долго мы ему позволим работать, является компромиссом. Если мы запустим его только на маленьком количестве шагов, то мы дадим Дейкстре шанс найти необходимое количество путей скорее, чем они будут доступны в P(G). С другой стороны, мы вызываем накладные расходы путем переключения A* и Дейкстры и поэтому должны ограничить количество переключений. Эти накладные расходы вызваны тем фактом, что после возобновления A* (строка 14), структура графа P(G) может измениться. Следовательно нам необходимо обновить P(G) (строка 15), как мы будет широко обсуждать в разделе 4.5. Это требует последующую проверку статуса Дейкстры. Мы должны быть уверены, что Дейкстра поддерживает согласованное состояние после изменений в P(G). K* предусматривает условие, которые управляет решением, когда остановить A*, которое мы назовем ''условие расширения''. Для того, чтобы поддерживать аналогичную асимптотическую сложность как у EA и LVEA, мы должны определить условие расширения так, чтобы A* выполнялся пока количество рассмотренных вершин и количество внутренних ребер удваивается или G полностью исследован. Мы обсудим эту проблему несколько подробнее позже. В качестве полезного свойства, K* позволяет другое определения этого условия, которое может быть более эффективным на практике. В наших экспериментах в разделе 6, мы определили условие расширения так, что количество рассмотренных вершин или количество рассмотренных ребер ребер возрастает на 20% при каждом запуске A*. Этот механизм планирования включен до тех пор, пока A* не закончит исследовать весь граф G. Как только A* исследует весь граф G (строка 9), механизм планирования отключается и в дальнейшем работает только алгоритм Дейкстры.<br />
<br />
Строки 18-22 представляют обычный шаг рассмотрения узла алгоритмом Дейкстры. Отметим, что когда successor-узел n' сгенерирован, K* не проверяет был ли n' уже посещен до этого. Другими словами, каждый раз, когда узел генерируется, он рассматривает как новый. Эта стратегия обоснована на наблюдении, что путь s-t может содержать одно и то же ребро несколько раз. Строка 24 добавляет следующий путь s-t в результирующее множество R. Это делается путем конструирования последовательности запасных ребер seq(\sigma) из пути \sigma, через которые Дейкстра достигла узла n, который был только что рассмотрен. Алгоритм завершается, когда в результирующее множество добавлено k последовательностей запасных ребер (строка 25).<br />
<br />
== Взаимосвязь алгоритмов Дейкстры и A* ==<br />
Тот факт, что оба алгоритма A* и Дейкстры делят между собой граф путей P(G), вызывает обеспокоенность в отношении правильности работы Дейкстры на P(G). Возобновление A* приводит к изменениям в структуре P(G). Таким образом, после возобновления A*, мы обновляем P(G) и проверяет статус поиска Дейкстры (строка 15). В основном, A* может добавить новые узлы, менять \delta-значения существующих узлов или даже удалять узлы. A* может также существенно изменять дерево поиска T, которое будет в худшем случае разрушать структуру все деревянных куч H_{T}. Эти изменения могут приводить к глобальной реструктуризации или даже перестроению P(G) с нуля. В худшем случае это может сделать предыдущие поиски Дейкстры на P(G) бесполезными таким образом, что нам придется перезапускать алгоритм Дейкстры с нуля.<br />
<br />
Если использованная эвристическая оценка допустимая, то наше положение лучше. Нам по-прежнему может понадобится перестроение P(G), но мы покажем, что это перестроение не мешает корректности поиска Дейкстры на P(G). Другими словами, мы не теряем результаты, до сих пор полученные поиском Дейкстры. В случае монотонной эвристической оценки мы даже не нуждаемся в перестроении P(G). Если h монотонная, то дерево поиска A* является деревом кратчайшего пути для всех раскрытых вершин. Следовательно, g-значения раскрытых вершин не изменится. Это означает, что \delta-значения для внутренних ребер никогда не изменятся. Ребра дерева раскрытых вершин не изменятся также. Следовательно, обновление \delta-значений, heaping-up, heaping-down (операции в кучах) или удаление узлов не влекут за собой каких-либо изменений в P(G). Только добавление новых узлов приводит к изменениям в P(G). Следовательно, перестроение или глобальная реструктуризация не требуется в данном случае.<br />
<br />
В оставшейся части этого раздела, мы сначала покажем, что корректность поиска Дейкстры на P(G) поддерживается в случае допустимой эвристической оценки. После этого мы покажем, что изменения в P(G) могут помешать завершенности поиска Дейкстры независимо от того, является ли эвристика допустимой или даже монотонной. Следовательно, мы предложим механизм для её поддержания.<br />
<br />
Мы фокусируемся дальше на корректности поиска Дейкстры на P(G) в случае допустимой эвристической оценки. Сначала, мы заявляем, что если h допустимая, то узлы исследованной части P(G) не поменяют свои \delta-значения.<br />
<br />
Лемма 6. Пусть n будет произвольным узлов в P(G) и пусть (u,v) будет ребром, связанным с n. Если h допустимая функция, то значение \delta(u,v) никогда не изменится после того, как n будет рассмотрен алгоритмом Дейкстры.<br />
<br />
...<br />
<br />
Из леммы 6 мы может вывести следующее следствие.<br />
<br />
Следствие 2. Пусть n будет произвольным узлов в P(G). Если h допустимая функция, то n никогда не будет удален из P(G) после того, как n был рассмотрен алгоритмом Дейкстры.<br />
<br />
...<br />
<br />
Более того, мы докажем, что структура исследованной части P(G) не изменится.<br />
<br />
Лемма 7. Пусть n будет произвольным узлов в P(G). Если h допустимая функция, то n никогда не изменит свою позицию после того, как он был рассмотрен алгоритмом Дейкстры.<br />
<br />
...<br />
<br />
Леммы 6 и 7 обеспечивают, что изменения в P(G), которые индуцируются A*, не влияют на часть P(G), которую алгоритм Дейкстры уже исследовал. Это гарантирует корректность поиска Дейкстры на P(G), если используемая эвристика допустимая. Таким образом, каждый путь, который предоставляет алгоритм Дейкстры корректен и его длина действительна. Однако, это не обеспечивает завершенность поиска Дейкстры на P(G).<br />
<br />
Возможно, что узел n' присоединяется к другом узлу n как ребенок, после раскрытия узла n. В этом случае братья n' будут рассотрены до того, как n' станет ребенком n. Поэтому мы должны рассмотреть то, что было упущено во время поиска в связи с отсутствием n'. Мы добиваемся этого путем применения строк 20-22 к n' для каждого раскрытого направленного predecessor-а узла n'. Если n' ещё не выполняет условие планирования, A* будет неоднократно возобновляться пока механизм планирования не допустит алгоритму Дейкстры положить n' в поисковую очередь. Заметим, что таким образом не требуется каких-либо дополнительных усилий во время типичного поиска Дейкстры.<br />
<br />
Мы может быть уверены, что нерассмотренные узлы не будут принудительно опущены после применения операции heaping-up к n'. Иначе, мы могли бы иметь узел n'', который являлся бы ребенком n и впоследствии был бы заменен узлом n'. Заметим, что n'' должен быть рассмотрен, поскольку n' был раскрыт. Однако, это противоречение к лемме 7, которая гарантирует, что этого не произойдет.<br />
<br />
Более того, следующее следствие гарантирует, что наилучшее d(n') не лучше, чем d-значение любой рассмотренной вершины, в частности, раскрытой вершины. Это означает, что мы не упустим возможность раскрыть n'.<br />
<br />
Следствие 3. Пусть n будет узлом в P(G), который был раскрыт Дейкстрой. Кроме того, пусть m будет узлом, который заново добавляется в P(G) или его позиция изменена, после того как n был раскрыт. Если h допустимая, тогда выполяется следующее:<br />
C_{P(G)}(R,m) >= d(n)<br />
<br />
...<br />
<br />
== Пример ==<br />
Мы проиллюстрируем работу алгоритма K* следующим примером. Мы будем рассматривать ориентированный взвешанный граф G на рисунке 7. Стартовой вершиной будет называться s_0 и конечной вершиной - s_6. Нас интересен поиск 9 лучших путей из s_0 в s_6. Для достижения этой цели мы применим алгоритм K* к G. Предположим, что эвристическая оценка существует. Значения эвристики даны в пометках c h(s_0) по h(s_6) на рисунке 7. Легко заметить, что эвристическая функция допустима.<br />
<br />
Первый раз A* делает итерации на графе G до тех пор, пока не будет найдена вершина s_6. Часть графа G, которая уже была рассмотрена иллиюстрируется на рисунке 8. Ребра, изображенные сплошными линиями, обозначают ребра дерева, в то время, как все остальные - запасные ребра. Они будет храниться в кучах H_{in}, показанных на рисунке 9. Номера, присвоенные узлах кучи, соответствуют \delta-значениям. На этом этапе поиска A* приостановлен и P(G) построен. Первоначально, только назначенный корень R явно доступен в P(G). Инициализируется алгоритм Дейкстры. Это означает, что узел R добавляется в поискую очередь Дейкстры. Планировщику требуется доступ к successors К для того, чтобы решить следует ли возобновлять Дейкстру или A*. На данном этапе должна быть построена деревянная куча H_{T}(s_6). Куча H_{T}(s_4) требуется для построения H_{T}(s_6). Следовательно, строются деревянные кучи H_{T}(s_6), H_{T}(s_4), H_{T}(s_2) и H_{T}(s_0). Результат показан на рисунке 10, где сплошные линии представляют кучные ребра и пунктирные линии показывают кросс-ребра. Во избежание путаницы на рисунке некоторые из ребер не полностью изображены. Мы указываем каждое из них, используя короткую стрелку с конретной целью.<br />
<br />
После построения, как показано 10, планировщик проверяет только ребенка (s_4, s_2) узла R на предмет того, что g(s_6)+d(s_4,s_2) <= f(s_1). Отметим, что s_1 является головой поисковой очереди A*. Значение d(s_4,s_2) равно 2, т.е. g(s_6)+d(s_4,s_2) = 7 + 2 = 9 = f(s1). Следовательно, планировщик позволяет Дейкстре раскрыть R и вставить (s_4,s_2) в поисковую очередь. При раскрытии R находится первый путь из ответа. Он строится из пути P(G), содержащего единственный узел R. Этот путь приводит к пустой последовательности запасных ребер. Напомним, что пустая последовательность запасных ребер соответствует пути из s_0 в s_6 в дереве поиска, а именно s_0s_2s_4s_6 длиной 7. Затем поиск Дейкстры приостанавливается, потому что для successor-ов узла (s_4,s_2) не выполняется условие g(s_6)+d(n)<=f(s_1). Следовательно, возобновляется A*.<br />
<br />
Мы предполагаем, что условие раскрытия определено как раскрытие одной вершины для того, чтобы пример был простым и иллюстративным. Поэтому A* раскрывает s_1 и останавливается. Исследованная часть G на текущем этапе показана на рисунке 11. Результат раскрытия приведет к обнаружению 2 новых запасных ребер (s_1,s_2) и (s_1,s_6), которые будут добавлены в H_{in}(s_2) и H_{in}(s_6) соответственно. Обновленные кучи H_{in}(s_2) и H_{in}(s_6) представлены на рисунке 12. Другие кучи остаются неизменными, как на рисунке 9. Граф путей P(G) перестаивается, как показано на рисунке 13. Затем алгоритм Дейкстры возобновляется. Заметим, что поисковая очередь Дейкстры содержит только (s_4,s_2) с d=2 на этом моменте. Используя ручное выполнение мы можем легко увидеть, что Дейкстра будет выдавать в ответ пути, перечисленные в таблице 1.</div>Kabanovhttp://neerc.ifmo.ru/wiki/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Kabanov&diff=48970Участник:Kabanov2015-08-04T13:24:45Z<p>Kabanov: </p>
<hr />
<div>4 Алгоритм K*<br />
Также как и алгоритм Eppstein, K* выполняет поиск пути на графе G и использует граф путей P(G). Граф путей ищется с помощью алгоритма Дейкстры для того, чтобы пути s-t в виде последовательности запасных путей. Общий принцип работы алгоритма K* следующий:<br />
1) K* применяет A* на графе G вместо обратного алгоритма Дейкстры, который использует алгоритм Eppstein.<br />
2) Мы запускаем A* на G и Дейкстру на P(G) поочередно порядке, который позволяет Дейкстре доставить пути решение до заверешения поиска на G алгоритма A*.<br />
<br />
4.1 Поиск A* на G. <br />
K* применяет A* к входному графу G для того, чтобы определить дерево поиска T. <br />
В отличие от алгоритма Eppstein в K* A* применяется к графу G в прямом порядке из-за чего коренем дерева T является вершина s. Это необходимо для того, чтобы была возможность работать c неявным описанием графа G через функцию successor. На протяжении статьи будем считать граф G конечным, если не будет сказано иначе. Заметим, что А* корректен на конечных графах. Будем следовать литературному соглашению, предполагая, что стоимость бесконечного пути неограниченна.<br />
<br />
4.2 Стоимость объезда<br />
Для ребра (u, v) стоимость объезда \delta(u, v) является стоимостью ущерба из-за взятия ребра объезда (u, v) в сравнении с кратчайшим путем s-t через v. Ни длина кратчайшего пути s-t через v, ни длина пути s-t, включающего запапасные ребра (u, v) не известны, когда A* обнаруживает (u, v). Обе длины могут быть оценены с помощью функции оценки f, которая использует эвристическую функцию h. Путь f(v) будет f-значением с соответствии с деревом поиска T и f_u(v) будет f-значанием в соответствии с родителем u, т.е. f_u(v) = g(u) + c(u, v) + h(v). \delta(u, v) может быть определена так:<br />
<br />
\delta(u, v) = f_u(v) - f(v) = g(u) + c(u, v) + h(v) - g(v) - h(v) = g(u) + c(u, v) - g(v)<br />
<br />
Заметим, что \delta(u, v) дает точную объездную метрику, поскольку функция оценки h-значения не появляется в определении функции \delta(u, v).<br />
<br />
4.3 Структура графе путей<br />
Структура графа путей P(G) довольно сложная. В принципе, P(G) будет ориентированным графом, вершины которого соответствуют ребрам в исходном графе G. Он будет организован как коллекция взаимосвязанных куч (англ. heap). 2 бинарные минимальные кучи присвоены к каждой вершине v в графе G, которые называются входящей кучей H_{in}(v) и деревянной кучей H_{T}(v). Эти кучи являются базисом P(G). Как мы покажем далее, испльзование этих куч также играет главную роль в поддержании асимптотической сложности K*, также как в EA и LVEA.<br />
Входящая куча H_{in}(v) содержит узлы для каждого запасного ребра к вершине v, которые до сих пор были обнаружены A*. Узлы H_{in}(v) будут упорядочены в соответствии с \delta-значением соответствующих переходов. Узел владеющий ребром с минимальной стоимостью ущерба будет расположен на вершине кучи. Мы ограничим структуру кучи H_{in}(v) таким образом, что её корень в отличие от остальных узлов, имеет не более 1 ребенка. Мы обозначим его root_{in}(v).<br />
<br />
Деревянная куча H_{T}(v) для произвольной вершины v строится следующим образом. Если v - стартовая вершина, т.е. v=s, то H_{T}(v) будет изначально пустой кучей. Затем узел в неё будет добавлен root_{in}(s), если H_{in}(s) не пустая. Если v не стартовая вершина, то пусть вершина u будет родителем вершины v в дереве поиска T. Мы можем представить, что H_{T}(v) конструируется как копия H_{T}(u), в которую добавлен root_{in}(v). Если H_{in}(v) пустая, то H_{T}(v) идентична H_{T}(u). Однако, для экономии памяти мы создаем только дешевую копию H_{T}(u). Это осуществляется через создание копий только узлов кучи, которые лежат на обновленном пути H_{T}(u). Оставшаяся часть H_{T}(u) не копируется. Другими словами, root_{in}(v) вставляется в H_{T}(u) неразрушающим путем так, что структура H_{T}(u) сохраняется. В куче H_{T}(v) 1 или 2 ребенка могут быть присоединены к root_{in}(v). К тому же, root_{in}(v) хранит только 1 собственного ребенка из H_{in}(v). Мы обозначим корень H_{T}(v) как R(v).<br />
<br />
Обратимся к ребрам, которые берут начало из входящих или деревянных куч, как к кучным ребрам. Сформулируем следующую лемму.<br />
Лемма 1. Все узлы, которые достижимы из R(v) через кучные ребра для каждой вершины v, формируют тернарную кучу, упорядоченную в соответствии с \delta-значением. Мы назовем такую кучу графовой кучей вершины v и обозначим его как H_{G}(v).<br />
<br />
...<br />
<br />
Финальная структура P(G) получется из входящих и деревянных куч следующим образом. К каждому узлу n из P(G), несущему ребро (u,v), мы присоединим указатель, ссылающийся на R(u), который является корневым узлом H_{T}(u). Мы назовем такие указатели кросс-ребрами, в то время как указатели, возникающие из куч названы кучными ребрами, как упоминалось раньше. Более того, мы добавим специальный узел R в P(G) с одним выходящим кросс-ребром к R(t).<br />
<br />
Более того, мы определим весовую функцию \Delta на ребрах из P(G). Пусть (n, n') обозначает ребро в P(G), и пусть e и e' обозначают ребра из G соответствующие узлам n и n'. Тогда определим \Delta(n,n') следующим образом:<br />
<br />
\Delta(n,n')=\delta(e') - \delta(e) если (n,n') кучное ребро<br />
\Delta(n,n')=\delta(e') если (n,n') кросс-ребро.<br />
<br />
Лемма 1 подразумевает, что куча упорядоченная в соответствии с \delta-значанием поддерживается по любому кучному ребру из P(G). Эта упорядочивание кучи подразумевает, что \Delta(n,n') неотрицательна для любого кучного ребра (n,n'). Следовательно, \Delta также неотрицательна, т.е. \Delta(n,n') >= 0 для любого ребра (n,n') в P(G). Стоимость пути \sigma, т.е. C_{P(G)}(\sigma) равна \sum_{e \in \sigma}\Delta(e). <br />
<br />
...<br />
<br />
Лемма 2. Пусть n будет узлов графовой кучи H_{G}(w) для какой-нибудь вершины w. Пусть (u,v) будет ребром связанным с n. Тогда существует путь в дереве поиска T из v в w.<br />
<br />
...<br />
<br />
4.4 Алгоритмическая структура K*<br />
Алгоритмический принцип K* следующий. Будем запускать алгоритмы Дейкстры и A* на G с чередованием. Сначала, мы запустим A* на G пока вершина t не будет выбрана из очереди для рассмотрения. Затем, вы запустим алгоритмы Дейкстры на доступной части P(G). Каждый узел рассмотрел Дейкстрой представляет путь решения. Если точнее, то путь \sigma в P(G), по которому Дейкстра достигла этого узла является решением. Путь s-t может быть построен из \sigma за линейное время путем вычисления последовательности запасных ребер seq(\sigma) и затем s-t пути из неё. Если Дейкстра находит k кратчайших путей, то K* завершается успешно. Иначе, A* возобновляется для исследования большей части G. Это приводит к росту P(G), на котором алгоритм Дейкстры затем будет возобновлен. Мы будем повторять этот процесс до тех пор, пока алгоритм Дейкстры не найдет k кратчайших путей. <br />
Алгоритм 1 содержит псевдокод K*. Код с 8 по 25 строчку образует главный цикл K*. Цикл завершается, когда очереди обоих алгоритмов А* и Дейкстры пусты. До 8 строчки выполняет некоторые подготовительные вещи. После инициализации, А* запускает на 5 строчке пока вершина t не будет выбрана им для рассмотрения, в этом случае кратчайший путь s-t будет найден. Если t не достигнута, то алгоритм завершается без решения. Отметим, что он не завершится на бесконечных графах. Иначе, алгоритм добавляет специальную вершину R, которая назначена корнем P(G), в поисковую очередь алгоритма Дейкстры. Затем, K* входит в главный цикл.<br />
K* поддерживает механизм планирования для контролирования, когда A* или Дейкстра будет возобновлены. Если очередь из A* не пуста, что означает, что А* ещё не завершил исследования всего графа G, то Дейкстра возобновляется тогда и только тогда, когда g(t) + d <= f(u). Значение d является максимальным d-значением среди всех successor-ов головы поисковой очереди n алгоритма Дейкстры. Вершина u является головой поисковой очереди A*. Напомним, что d - функция расстояния, используемая в алгоритме Дейкстры.</div>Kabanovhttp://neerc.ifmo.ru/wiki/index.php?title=%D0%A2%D0%B5%D0%BE%D1%80%D0%B5%D1%82%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B8%D0%B9_%D0%BC%D0%B8%D0%BD%D0%B8%D0%BC%D1%83%D0%BC_%D0%BF%D0%BE_%D1%84%D1%83%D0%BD%D0%BA%D1%86%D0%B8%D0%BE%D0%BD%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D0%BC%D1%83_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7%D1%83_%D0%B7%D0%B0_5_%D1%81%D0%B5%D0%BC%D0%B5%D1%81%D1%82%D1%80&diff=44771Теоретический минимум по функциональному анализу за 5 семестр2015-01-24T14:17:47Z<p>Kabanov: </p>
<hr />
<div>= 1 Определение МП, замыкание в МП. =<br />
{{Определение<br />
|id=defms<br />
|definition=<br />
Для некоторого множества <tex>X</tex>, отображение <tex> \rho : X \times X \to \mathbb{R^+} </tex> {{---}} называется '''метрикой''' на <tex>X</tex>, если выполняются аксиомы<br />
# <tex> \rho (x, y) \ge 0 ;\ \rho (x, y) = 0 \iff x = y </tex><br />
# <tex> \rho (x, y) = \rho (y, x) </tex> <br />
# <tex> \rho (x, y) \le \rho (x, z) + \rho (z, y) </tex> {{---}} неравенство треугольника<br />
<br />
Пару <tex>(X, \rho)</tex> называют '''метрическим пространством'''.<br />
}}<br />
<br />
{{Определение<br />
|id=defint<br />
|definition=<br />
'''Замыкание (closure)''' множества <tex>A</tex> называется множество <tex>\mathrm{Cl} A = \bigcap\limits_{A \subset F } F</tex>, где <tex> F </tex> — замкнутые множества.<br />
}}<br />
<br />
= 2 Принцип вложенных шаров в полном МП. =<br />
{{Утверждение<br />
|about=<br />
принцип вложенных шаров<br />
|statement=<br />
Пусть <tex>(X, \rho)</tex> — полное. <tex>\overline V_n</tex> — замкнутые шары. <tex>\overline V_{n + 1} \subset \overline V_n</tex>, <tex>r_n \to 0</tex>. Тогда <tex>\bigcap\limits_{n=1}^{\infty} \overline V_n \ne \varnothing</tex>, и состоит из одной точки.<br />
}}<br />
<br />
= 3 Теорема Бэра о категориях. =<br />
{{Определение<br />
|definition=<br />
Подмножество <tex>A</tex> топологического пространства <tex>X</tex> имеет '''I категорию по Бэру в пространстве <tex>X</tex>''', если оно является не более чем счетным объединением нигде не плотных в <tex>X</tex> множеств. В противном случае оно имеет '''II категорию по Бэру'''.<br />
}}<br />
<br />
{{Теорема<br />
|author=Бэр<br />
|statement=<br />
Полное МП является множеством II категории в себе.<br />
}}<br />
<br />
= 4 Критерий компактности Хаусдорфа в МП. =<br />
{{Теорема<br />
|author=Хаусдорф<br />
|statement=<br />
Пусть <tex>X</tex> {{---}} полное метрическое пространство, <tex>K \subset X</tex>, <tex>K</tex> {{---}} замкнуто. <br />
Тогда <tex>K</tex> {{---}} компакт <tex>\iff</tex> <tex>K</tex> {{---}} вполне ограниченно.<br />
}}<br />
<br />
= 5 Пространство <tex>R^{\infty}</tex> : метрика, покоординатная сходимость. =<br />
* <tex>X = \mathbb{R}^{\infty}</tex>. Превращение в МП должно быть связано с желаемой операцией предельного перехода. В случае конечномерного пространства сходимость совпадает с покоординатной сходимостью, хотим того же самого для бесконечномерного. Введем метрику: <tex>\rho(\overline x, \overline y) = \sum\limits_{n = 1}^{\infty} {1 \over 2^n}{|x_n - y_n| \over 1 + |x_n - y_n|}</tex> (стандартный способ превратить в метрическое пространство счетное произведение метрических пространств, коим и является <tex>R^{\infty}</tex>). Проверим, что эта метрика удовлетворяет аксиомам:<br />
** этот ряд всегда сходящийся, так как мажорируется убывающей геометрической прогрессией <tex>\sum\limits_{n=1}^{\infty} {1 \over 2^n} = 1</tex>, соответственно, расстояние ограничено единицей.<br />
** первая аксиома: неотрицательность очевидна, равенство метрики нулю в обе стороны очевидно<br />
** вторая аксиома: еще очевиднее<br />
** третья аксиома легко вытекает из следующего утверждения:<br />
{{Утверждение<br />
|statement=<tex> {|x - z| \over 1 + |x - z|} \le {|x - y| \over 1 + |x - y|} + {|y - z| \over 1 + |y - z|}</tex><br />
}} <br />
<br />
{{Утверждение<br />
|statement=Сходимость в метрике <tex> \mathbb{R}^{\infty} </tex> эквивалентна покоординатной. <br />
}}<br />
<br />
= 6 Норма в линейном множестве, определение предела по норме, арифметика предела. =<br />
{{Определение<br />
|definition=<br />
Функция <tex>\| \cdot \|: L \to \mathbb{R}</tex> называется нормой в пространстве <tex>L</tex>, если для нее выполняется:<br />
# <tex>\forall x \in L: \| x \| \ge 0</tex>, <tex>\| x \| = 0 \iff x = \mathrm{0}</tex><br />
# <tex>\forall \alpha \in \mathbb{R}\ \forall x \in L: \| \alpha x \| = |\alpha |\| x \|</tex><br />
# <tex>\forall x, y \in L: \| x + y \| \le \| x \| + \| y \|</tex><br />
Пространство с введенной на нем нормой называют '''нормированным пространством'''.<br />
}}<br />
<br />
В нормированных пространствах определение предела записывается аналогично пределу вещественной последовательности, отличаясь лишь заменой знака модуля на знак нормы.<br />
<br />
Например, если <tex>E \subset X</tex>, <tex>a</tex> — предельная точка множества <tex>E</tex>, <tex>f \colon E \to Y</tex> (где <tex>X</tex> и <tex>Y</tex> — нормированные пространства), то <tex>A</tex> называется пределом функции <tex>f</tex> при <tex>x \to a</tex> и обозначается <tex>\lim\limits_{x \to a} f(x)</tex>, если для любого положительного <tex>\varepsilon</tex> найдётся <tex>\delta > 0</tex>, для которого выполняется следствие <tex>0 < \|x - a\| < \delta \implies \|f(x) - A\| < \varepsilon</tex>.<br />
<br />
Специфика нормированных пространств — структура линейного пространства на рассматриваемом множестве. То есть, точки пространства можно складывать и умножать на числа, и эти операции будут непрерывными по норме пространства.<br />
<br />
{{Утверждение<br />
|statement=<br />
Пусть <tex>x_n</tex>, <tex>y_n</tex> — последовательности точек нормированного пространства <tex>(X, \|\cdot\|)</tex>, а <tex>\alpha_n</tex> — вещественная последовательность. Известно, что <tex>x_n \to x</tex>, <tex>y_n \to y</tex>, <tex>\alpha_n \to \alpha</tex>.<br />
<br />
Тогда:<br />
# <tex>x_n + y_n \to x + y</tex><br />
# <tex>\alpha_n x_n \to \alpha x</tex><br />
# <tex>\|x_n\| \to \|x\|</tex><br />
}}<br />
<br />
= 7 Эквивалентность норм в конечномерном НП. =<br />
{{Определение<br />
|definition=<br />
Нормы <tex>\| \|_1</tex>, <tex>\| \|_2</tex> '''эквивалентны''', если существуют константы <tex>m, M > 0</tex> такие, что <tex>\forall x: m\|x\|_2 \le \|x\|_1 \le M \|x\|_2</tex>. Очевидно, что отношение эквивалентности норм является отношением эквивалентности (то есть выполняется рефлексивность, симметриченость и транзитивность).<br />
}}<br />
<br />
Это определение равносильно тому, что сходимость последовательностей в них равносильна: <tex>x_n \xrightarrow[]{\|\|_1} x \iff x_n \xrightarrow[]{\|\|_2} x</tex>.<br />
<br />
{{Определение<br />
|definition=<br />
Пространство <tex> X </tex> '''конечномерно''', если <tex> \exists n = dim X < \infty: \exists e_1, e_2, \ldots, e_n: X = \mathcal L(e_1, \ldots, e_n)</tex>.<br />
}}<br />
<br />
{{Теорема<br />
|author=Рисс<br />
|statement=<br />
В конечномерных пространствах любые две нормы эквивалентны.<br />
}}<br />
<br />
= 8 Замкнутость конечномерного линейного подмножества НП. =<br />
{{Определение<br />
|definition=Подпространство в алгебраическом смысле не обязательно замкнуто в исходном пространстве. Поэтому в функциональном анализе собственно '''подпространством''' называется именно ''замкнутое'' подпространство, а ''алгебраические'' подпространства называют '''линейными подмножествами'''.<br />
}}<br />
<br />
{{Теорема<br />
|statement=<br />
Пусть <tex>X</tex> — НП и <tex>Y</tex> — линейное конечномерное подмножество в <tex>X</tex>, тогда <tex>Y</tex> — замкнуто в <tex>X</tex>, т.е.<br />
<tex>\mathrm{Cl} Y = Y</tex>.}}<br />
<br />
= 9 Лемма Рисса о почти перпендикуляре, пример ее применения. =<br />
{{Лемма<br />
|author=Рисc<br />
|about=о почти перпендикуляре<br />
|statement=<br />
Пусть <tex>X</tex> — НП, а <tex>Y</tex> {{---}} собственное (то есть не совпадающее с <tex>X</tex>) подпространство <tex>X</tex>, тогда <tex>\forall \varepsilon \in (0, 1) \; \exists z_{\varepsilon} \in X : \|z_{\varepsilon}\| = 1,\; \rho(z_{\varepsilon}, Y) \geq 1 - \varepsilon</tex> (где <tex>\rho(z, Y) = \inf\limits_{y \in Y} \|z-y\|</tex>)<br />
}}<br />
<br />
{{Теорема<br />
|about=некомпактность шара в бесконечномерном пространстве<br />
|statement=<br />
Если <tex>X</tex> {{---}} бесконечномерное НП, то единичный шар <tex>S_1 = \{ x \in X \mid \|x \| = 1\}</tex> в нем не компактен.<br />
|proof=<br />
Возьмем <tex>x \in S_1</tex>, <tex>Y_1 = \mathcal{L}(x_1)</tex> — собственное подпространство <tex>X</tex>, применим лемму Рисса, возьмем <tex>\varepsilon = {1 \over 2}</tex>, существует <tex>x_2: \| x_2 \| = 1, \| x_2 - x_1 \| \ge {1 \over 2}</tex>, заметим, что <tex>x_2</tex> окажется в <tex>S_1</tex>.<br />
<br />
<tex>Y_2 = \mathcal{L}(x_1, x_2)</tex>, опять применим лемму Рисса, существует <tex>x_3 \in X: \| x_3 - x_j \| \ge {1 \over 2}, j = 1, 2</tex>, <tex>x_3</tex> будет в <tex>S_1</tex>.<br />
<br />
Продолжаем так же для <tex>Y_3 \dots Y_n \dots</tex>. Процесс никогда не завершится, так как <tex>X</tex> — бесконечномерное и не может быть линейной оболочкой конечного числа векторов. Таким образом построили бесконечную систему точек в <tex>S_1</tex>, но из которой нельзя выделить сходящуюся подпоследовательность, так как <tex>\| x_n - x_m \| \ge {1 \over 2}</tex>, следовательно, <tex>S_1</tex> не компактно.<br />
}}<br />
<br />
= 10 Банаховы пространства на примерах <tex>C [0,1]</tex> и <tex>L_p(E)</tex>. =<br />
Чо-то не нашёл, где это и что именно сюда надо пилить<br />
<br />
= 11 Определение скалярного произведения, равенство параллелограмма, неравенство Шварца. =<br />
Пусть <tex>H</tex> — линейное пространство. Величина <tex>(x, y) \in \mathbb R</tex> называется скалярным произведением точек множества <tex>H</tex>, если она удовлетворяет следующим трём аксиомам:<br />
# <tex>(x, x) \ge 0</tex>, <tex>(x, x) = 0 \iff x = 0</tex><br />
# <tex>(x, y) = (y, x)</tex><br />
# <tex>(\alpha x + \beta y, z) = \alpha(x, z) + \beta(y, z)</tex><br />
<br />
Основное значение для скалярного произведения имеет неравенство Шварца:<br />
{{Утверждение<br />
|statement=<br />
<tex>|(x, y)| \le \sqrt{(x, x)}\sqrt{(y, y)}</tex><br />
}}<br />
<br />
//не нашёл этого в конспектах, беру с википедии<br />
<br />
Характеристическим свойством, выделяющим гильбертовы пространства <tex>H</tex> среди прочих банаховых пространств, является равенство параллелограмма:<br />
<tex>\forall x,y\in H\ \quad \|x+y\|^2+\|x-y\|^2=2(\|x\|^2+\|y\|^2)</tex><br />
<br />
= 12 Наилучшее приближение в НП в случае конечномерного подпространства. =<br />
Пусть <tex>X</tex> {{---}} [[Нормированные_пространства#определение и примеры|нормированное пространство]], к примеру, <tex>L_p</tex>. Пусть <tex>Y</tex> {{---}} линейное множество в <tex>X</tex>, например, <tex>H_n</tex> (тригонометрических полиномов степени не больше <tex>n</tex>).<br />
<br />
{{Определение<br />
|definition = Для любого <tex> x \in X</tex> величина <tex>E_Y(x) = \inf\limits_{y \in Y}{\|x-y\|}</tex> называется '''наилучшим приближением точки <tex>x</tex> элементами линейного множества <tex>Y</tex>'''.<br />
Если при этом существует <tex>y^* \in Y</tex> такой, что <tex>E_Y(x)=\|x-y^*\|</tex>, то этот <tex>y^*</tex> называется '''элементом наилучшего приближения точки <tex>x</tex>'''.<br />
}}<br />
<br />
{{Теорема<br />
|statement=<br />
Пусть <tex>X</tex> {{---}} нормированное пространство, <tex>\dim Y < +\infty</tex>, тогда <tex>\forall x \in X</tex> существует элемент наилучшего приближения <tex>x</tex>.<br />
}}<br />
<br />
= 13 Наилучшее приближение в унитарном пространстве, неравенство Бесселя. =<br />
{{Утверждение<br />
|statement = Пусть <tex>x\in\mathcal{H}</tex>, <tex>\sum\limits_{j=1}^\infty \langle x, e_j\rangle e_j</tex> (причем он может быть расходящимся), <tex>s_n(x) = \sum\limits_{j=1}^n \langle x, e_j\rangle e_j</tex><br />
тогда: <tex>\|x-s_n(x)\|^2 = \inf \|x - \sum\limits_{k=1}^n \alpha_ke_k\|^2</tex>, <tex>\alpha_k \in \mathbb{R}</tex><br />
}}<br />
<br />
{{Теорема<br />
|author=<br />
Бессель<br />
|about=<br />
неравенство Бесселя<br />
|statement=<br />
<tex> \sum \limits_{k=1}^{\infty} (x, e_k)^2 \le \|x\|^2</tex>, где <tex>e_1 \dots e_n \dots \in H </tex> {{---}} ортонормированная система точек<br />
}}<br />
<br />
= 14 Определение Гильбертова пространства, сепарабельность и полнота. =<br />
{{Определение<br />
|definition=<br />
'''Гильбертовым пространством''' называют Банахово пространство, в котором норма порождена скалярным произведением.<br />
}}<br />
<br />
{{Определение<br />
|definition=<br />
'''Гильбертово пространство''' сепарабельно тогда и только тогда, когда в нём существует счётный ортонормированный базис.<br />
}}<br />
<br />
{{TODO|t= взято от сюда:http://www.nsu.ru/education/funcan/node89.html проверить на правду}}<br />
<br />
{{Теорема<br />
|about=<br />
критерий полноты ортонормированной системы в сепарабельном гильбертовом пространстве<br />
|statement=<br />
Пусть <tex>H</tex> {{---}} сепарабельное гильбертово пространство и <tex>\{e_1, e_2, \ldots, e_n, \ldots\}</tex> {{---}} ортонормированная система векторов в нем. Тогда следующие условия эквивалентны:<br />
<br />
# система <tex>\{e_1, e_2, \ldots, e_n, \ldots\}</tex> полна<br />
# система <tex>\{e_1, e_2, \ldots, e_n, \ldots\}</tex> замкнута<br />
# <tex>\forall x \in H </tex> справедливо разложение <tex>x = \sum\limits_{n = 1}^\infty \lambda_n e_n</tex>, где <tex>\lambda_n = (x, e_n)</tex> {{---}} коэффициенты Фурье вектора <tex>x</tex> относительно ортонормированной системы <tex>\{e_1, e_2, \ldots, e_n, \ldots\}</tex><br />
}}<br />
<br />
= 15 Теорема Рисса-Фишера, равенство Парсеваля. =<br />
{{Теорема<br />
|about=<br />
равенство Парсеваля<br />
|statement=<br />
<tex>\forall x: \|x\|^2 = \sum\limits_{k=1}^{\infty} \langle x, e_k \rangle ^2 </tex> тогда и только тогда, когда ортонормированная система точек, по которым строятся коэффициенты Фурье, полная или замкнутая.<br />
}}<br />
<br />
{{Теорема<br />
|author=Рисс-Фишер<br />
|statement=<br />
Пусть <tex>\{e_1, e_2, \ldots, e_n, \ldots\}</tex> {{---}} ортонормированная система в гильбертовом пространстве <tex>H</tex>, <tex>\sum\limits_{i=1}^{\infty} \alpha_i^2 < +\infty</tex>. Тогда <tex>\exists ! x \in H : \alpha_i = \langle x, e_i \rangle</tex> и выполняется '''равенство Парсеваля''': <tex>\sum\limits_{i=1}^{\infty} \alpha_i^2 = \|x\|^2</tex><br />
}}<br />
<br />
= 16 Наилучшее приближение в <tex>H</tex> для случая выпуклого,замкнутого множества, <tex>H = H_1 \oplus H_2</tex>. =<br />
{{Теорема<br />
|statement=<br />
Пусть <tex>M</tex> — выпуклое замкнутое множество в <tex>H</tex>, тогда <tex>\forall x \in H\ \exists z \in M: \| x - z \| = \inf\limits_{y \in M} \| x - y\|</tex>. <tex>z</tex> называется '''элементом наилучшего приближения'''<br />
}}<br />
{{Определение<br />
|definition=<br />
Говорят, что два элемента <tex> x, y </tex> гильбертова пространства <tex> H </tex> '''перпендикулярны''' (<tex> x \perp y </tex>), если <tex> \langle x, y \rangle = 0. </tex><br />
}}<br />
<br />
{{Определение<br />
|definition=<br />
Пусть <tex>H_1</tex> — подпространство в <tex>H</tex>, тогда '''ортогональным дополнением''' называется <tex>H_2 = H_1^{\perp} = \{ x \in H \mid \forall y \in H_1: x \perp y\}</tex>.<br />
}}<br />
<br />
= 17 Разложение гильбертова пространства в прямую сумму подпространств. =<br />
{{Теорема<br />
|statement=<br />
Пусть <tex> H_1 </tex> — подпространство в <tex>H</tex>, <tex> H_2 </tex> {{---}} его ортогональное дополнение. Тогда для любого <tex> x \in H </tex> существует единственное представление <tex> x = x_1 + x_2 </tex>, где <tex> x_1 \in H_1, x_2 \in H_2 </tex> и <tex> x_1 \perp x_2 </tex>.<br />
}}<br />
<br />
= 18 Счетно-нормированные пространства, метризуемость. =<br />
{{Определение<br />
|definition=<br />
Пусть <tex>X</tex> — линейное пространство, <tex>p_1 \dots p_n \dots</tex> — полунормы. Если <tex>\forall x \in X</tex> из того, что <tex>\forall k: p_k(x) = 0</tex> следует, что <tex>x = 0</tex>, то <tex>X</tex> называют '''счетно-нормированным пространством'''<br />
}}<br />
<br />
{{Утверждение<br />
|statement=<br />
Счетно-нормированные пространства можно метризовать как <tex>\mathbb{R}^{\infty}</tex>: <tex>\rho(x, y) = \sum\limits_{n=1}^{\infty} {1 \over 2^n} {p_n(x - y) \over 1 + p_n(x - y)}</tex>.<br />
}}<br />
<br />
= 19 Условие нормируемости СНТП. =<br />
<wikitex><br />
{{Определение<br />
|definition=<br />
Полунорма $p_n$ в системе $p$ '''существенна''', если она не мажорируется ни одной из полунорм этой системы с меньшими чем $n$ номерами.<br />
}}<br />
{{Теорема<br />
|about=критерий нормируемости счетно-нормированного пространства<br />
|statement=<br />
Пусть $X$ — счетное-нормированное пространство по монотонной системе полунорм $p$. Оно нормируется тогда и только тогда, когда в системе $p$ конечное число существенных полунорм.<br />
}}<br />
</wikitex><br />
<br />
= 20 Функционал Минковского. =<br />
<wikitex><br />
{{Определение<br />
|definition=<br />
$A$ '''поглощает''' $B$, если $\exists \lambda_0 > 0: \forall \lambda: |\lambda| > \lambda_0: B \subset \lambda A$.<br />
}}<br />
<br />
{{Определение<br />
|definition=<br />
$A$ '''радиальное/поглощающее''', если оно поглощает любую конечную систему точек. Для проверки радиальности достаточно проверить поглощение каждой конкретной точки.<br />
}}<br />
{{Определение<br />
|definition=<br />
Пусть $X$ — линейное пространство, $\mu$ — радиальное подмножество, тогда '''функционал Минковского''' $p_{\mu}$ определяется как $p_{\mu}(x) = \inf \{ \lambda > 0 \mid x \in \lambda \mu\}$.<br />
}}<br />
</wikitex><br />
<br />
= 21 Топология векторных пространств. =<br />
<wikitex><br />
{{Определение<br />
|definition=<br />
'''Топологическое векторное пространство''' — линейное пространство, наделенной такой топологией, что операции сложения векторов и умножения на скаляр в ней непрерывны в этой топологии, то есть:<br />
* непрерывность умножения на скаляр: $\alpha x \to \alpha_0 x_0$, если $\alpha \to \alpha_0$, $x \to x_0$. Означает, что для любой окрестности $U(\alpha_0 x_0)$ существует $ \varepsilon > 0$ и существует $U(x_0): |\alpha - \alpha_0| < \varepsilon, x \in U(x_0) \implies \alpha x \in U(\alpha_0 x_0)$<br />
* непрерывность сложения векторов: $x + y \to x_0 + y_0$, если $x \to x_0$, $y \to y_0$. Означает, что для любой окрестности $U(x_0 + y_0)$ существуют окрестности $U(x_0), U(y_0): \forall x \in U(x_0) \forall y \in U(y_0) \implies x + y \in U(x_0 + y_0)$.<br />
}}<br />
</wikitex><br />
<br />
= 22 Теорема Колмогорова о нормируемости ТВП. =<br />
{{Теорема<br />
|author=Колмогоров<br />
|statement=<br />
Хаусдорфово ТВП нормируемо тогда и только тогда, когда у нуля есть ограниченная выпуклая окрестность.<br />
}}<br />
<br />
= 23 Коразмерность ядра линейного функционала. =<br />
{{Определение<br />
|id=linfuncdef<br />
|definition=<br />
Пусть <tex>X</tex> — линейное множество. Отображение <tex> f\colon X \to \mathbb{R} </tex> {{---}} '''линейный функционал''', если <br />
<tex>\forall \alpha, \beta \in \mathbb{R} \ \forall x, y \in X : f(\alpha x + \beta y) = \alpha f(x) + \beta f(y)</tex>.<br />
<br />
Обозначим <tex>X^*</tex> — совокупность линейных функционалов, определенных на множестве <tex>X</tex>.<br />
<br />
<tex> \mathrm{Ker}\, f = \{x \mid f(x) = 0 \} </tex> — '''ядро функционала'''.<br />
}}<br />
<br />
{{Определение<br />
|id=factorsetdef<br />
|definition=<br />
Пусть <tex>X</tex> — линейное множество, <tex>Y</tex> линейное подмножество <tex>X</tex>. <br />
<br />
Введем отношение эквивалентности на <tex>X</tex>:<br />
<br />
<tex> x_1 \sim x_2 \stackrel{\mathrm{def}}{\iff} x_1 - x_2 \in Y </tex><br />
<br />
<tex> [x] = \{ y \in X \mid y \sim x \} </tex> — '''классы смежности''' по <tex>Y</tex>.<br />
<br />
<tex> X /_Y </tex> — совокупность всех классов смежности — '''фактор-множество''' по <tex>Y</tex>.<br />
<br />
}}<br />
<br />
{{Определение<br />
|id=codimdef<br />
|definition=<br />
<tex>\mathrm{Codim}\, Y \stackrel{\mathrm{def}}{=} \dim X /_Y </tex> — '''коразмерность''' <tex>Y</tex>. <br />
<br />
<tex> Y </tex> — '''гиперплоскость''' в <tex>X</tex>, если <tex>\mathrm{Codim}\, Y = 1</tex>.<br />
<br />
}}<br />
<br />
{{Утверждение<br />
|about=Коразмерность ядра функционала<br />
|statement=<br />
<br />
Если <tex> f </tex> не является тождественно равным нулю, то <tex>\mathrm{Codim}\, \mathrm{Ker}\, f = 1 </tex>.<br />
}}<br />
<br />
= 24 Непрерывный линейный функционал и его норма. =<br />
{{Определение<br />
|id=contfuncdef<br />
|definition=<br />
Пусть <tex>X</tex> — нормированное пространство. Линейный функционал <tex> f \in X^* </tex> {{---}} '''непрерывен''' в точке <tex> x </tex>, если <br />
<tex>x_n \to x \implies f(x_n) \to f(x) </tex>.<br />
}}<br />
Обозначение <tex> \overline{V}_1 = \{ x : \| x \| \leq 1 \} </tex><br />
<br />
Введем норму в <tex> X^* </tex>: <tex> \| f \| \stackrel{\mathrm{def}}{=} \sup\limits_{\overline{V}_1} {| f(x) |} </tex><br />
<br />
{{Утверждение<br />
|id=cont0<br />
|statement= Линейный функционал <tex>f</tex> непрерывен <tex> \iff </tex> <tex>f</tex> непрерывен в нуле.<br />
}}<br />
<br />
= 25 Связь между непрерывностью линейного функционала и замкнутостью его ядра. =<br />
<br />
{{Определение<br />
|id=finitefuncdef<br />
|definition=<br />
<tex> f </tex> — '''ограниченный''' функционал, если <tex> \| f \| < \infty </tex>.<br />
}}<br />
<br />
{{Утверждение<br />
|id=cont-finite<br />
|statement= <tex>f</tex> — непрерывен <tex> \iff </tex> <tex>f</tex> — ограничен.<br />
}}<br />
{{<br />
Теорема<br />
|about=характеристика ограниченного функционала в терминах ядра<br />
|statement=<br />
<tex>f</tex> — ограничен <tex>\iff \mathrm{Ker}\, f</tex> — замкнуто в <tex>X</tex>.<br />
}}<br />
<br />
= 26 Продолжение по непрерывности линейного функционала со всюду плотного линейного подмножества НП. =<br />
{{Утверждение<br />
|id=densefunextension<br />
|statement= Пусть <tex> Y </tex> — линейное всюду плотное в <tex> X </tex> множество.<br />
<tex> f </tex> — линейный непрерывный функционал на <tex> Y </tex>. Тогда существует единственный <tex> \widetilde f </tex> — линейный непрерывный функционал на <tex> X </tex> такой, что:<br />
<br />
1) <tex> \widetilde f |_Y = f </tex> — сужение на <tex> Y </tex> совпадает с <tex> f </tex>.<br />
2) <tex> \| \widetilde f \|_X = \| f \|_Y </tex><br />
}}<br />
<br />
= 27 Теорема Хана-Банаха для НП (сепарабельный случай). =<br />
{{Теорема<br />
|author=<br />
Хан, Банах<br />
|statement=<br />
Пусть <tex>X</tex> {{---}} [[Метрические_пространства#defdense|сепарабельное]] нормированное пространство, <tex>Y</tex> {{---}} линейное подмножество <tex>X</tex>, <tex>f: Y \to \mathbb R</tex> {{---}} линейный ограниченный функционал.<br />
Тогда существует линейный ограниченный функционал <tex>g: X \to \mathbb R</tex> такой, что <tex>g|_Y = f</tex>, <tex>\|g\| = \|f\|</tex>.<br />
}}<br />
<br />
Два следствия из теоремы Хана-Банаха.<br />
{{Утверждение<br />
|statement=<br />
Пусть <tex>X</tex> {{---}} нормированное пространство. Тогда <tex>\forall x \in X \exists f: X \to R:\ f(x) = \|x\|, \|f\| = 1</tex>.<br />
}}<br />
{{Утверждение<br />
|statement=<br />
Пусть <tex>X</tex> {{---}} нормированное пространство, <tex>e_1, e_2, \ldots, e_n</tex> {{---}} линейно независимый набор в <tex>X</tex>.<br />
Тогда в <tex>X</tex> существует биортогональная система функционалов <tex>f_1, f_2, \ldots f_n, f_i(e_j) = \delta_{ij}</tex><br />
}}<br />
<br />
= 28 Теорема Рисса об общем виде линейного непрерывного функционала в <tex>H</tex>. =<br />
{{Теорема<br />
|author=Рисс<br />
|about=об общем виде линейного непрерывного функционала в гильбертовом пространстве<br />
|statement=<br />
<tex>\forall f \in H^*\; \exists ! y \in H : f(x) = \langle x, y \rangle</tex>, причем <tex>\|f\| = \|y\|</tex><br />
}}<br />
<br />
= 29 Непрерывный линейный оператор и его норма. =<br />
{{Определение<br />
|definition=<br />
Оператор <tex>A</tex> называется '''линейным''', если <tex>A(\alpha x_1 + \beta x_2) = \alpha A(x_1) + \beta A(x_2)</tex>.<br />
}}<br />
<br />
{{Определение<br />
|definition=<br />
Оператор <tex>A</tex> '''непрерывен''' в точке <tex>x_0</tex>, если <tex>\lim\limits_{x \to x_0} Ax = Ax_0</tex>.<br />
}}<br />
<br />
{{Определение<br />
|definition=<br />
'''Нормой''' оператора <tex>A</tex> называется <tex>\|A\| = \sup\limits_{\|x\| \le 1} \| Ax \|</tex>.<br />
}}<br />
<br />
{{Определение<br />
|definition=<br />
Оператор <tex>A</tex> '''ограничен''', если <tex>\|A\| \le \infty</tex>.<br />
}}<br />
<br />
{{Теорема<br />
|statement=<br />
Линейный оператор непрерывен тогда и только тогда, когда он ограничен.<br />
}}<br />
<br />
= 30 Продолжение линейного оператора по непрерывности. =<br />
{{Теорема<br />
|statement=<br />
Пусть <tex>Y</tex> {{---}} линейное пространство, <tex>Cl Y = X</tex>, <tex>A: Y \to Z</tex> {{---}} линейный ограниченный оператор, <tex>Z</tex> {{---}} банахово.<br />
Тогда <tex>\exists! B: X \to Z</tex>:<br />
# <tex>B|_Y = A</tex><br />
# <tex>\|B\| = \|A\|</tex><br />
}}<br />
<br />
= 31 Полнота пространства <tex>L(X,Y)</tex>. =<br />
Обычно пространство линейных ограниченных операторов из <tex>X</tex> в <tex>Y</tex> обозначают как <tex>L(X, Y)</tex>.<br />
<br />
{{Теорема<br />
|statement=<br />
Пусть <tex>Y</tex> {{---}} банахово, тогда <tex>L(X, Y)</tex> тоже банахово.<br />
}}<br />
<br />
= 32 Теорема Банаха-Штейнгауза. =<br />
{{Определение<br />
|definition=<br />
Последовательность <tex>A_n</tex> '''поточечно ограничена''', если <tex>\forall x \in X \sup\limits_{n \in \mathbb N} \|A_n x\| < +\infty</tex>.<br />
}}<br />
<br />
{{Определение<br />
|definition=<br />
Последовательность <tex>A_n</tex> '''равномерно ограничена''', если <tex>\sup\limits_{n \in \mathbb N} \|A_n\| < +\infty</tex>.<br />
}}<br />
<br />
{{Теорема<br />
|author=<br />
Банах, Штейнгауз<br />
|about=<br />
принцип равномерной ограниченности<br />
|statement=<br />
Пусть <tex>X</tex> {{---}} банахово, <tex>A_n \in L(X, Y)</tex>, <tex>A_n</tex> поточечно ограничена. Тогда <tex>A_n</tex> равномерно ограничена.<br />
}}<br />
<br />
= 33 Условие замкнутости множества значений линейного оператора на базе априорной оценки решения операторного уравнения. =<br />
{{Определение<br />
|definition=<br />
Рассмотрим уравнение <tex> Ax = y </tex> при заданном <tex> y </tex>. Если для такого уравнения можно написать <tex> \| x \| \le \alpha \| y \| </tex>, где <tex> \alpha </tex> {{---}} константа, то говорят, что это уравнение '''допускает априорную оценку решений'''.<br />
}}<br />
<br />
{{Утверждение<br />
|statement=<br />
Если <tex> A </tex> непрерывен, и уравнение <tex> Ax = y </tex> допускает априорную оценку решений, то <tex> R(A) = \mathrm{Cl} R(A) </tex>.<br />
}}<br />
<br />
= 34 Условие непрерывной обратимости лин. оператора. =<br />
{{Определение<br />
|definition=<br />
Оператор <tex> A : X \to Y </tex> называется '''непрерывно обратимым''', если существует <tex> A^{-1} : Y \to X </tex> и <tex> \| A^{-1} \| < \infty </tex>, причем <tex>A^{-1}</tex> должен быть определен на всем <tex>Y</tex>.<br />
}}<br />
<br />
{{Теорема<br />
|statement=<br />
Пусть <tex> A : X \to Y </tex> {{---}} линейный ограниченный оператор, и <tex>\exists m > 0: m \| x \| \le \| Ax \| </tex>.<br />
Тогда <tex> A </tex> непрерывно обратим на <tex>R(A)</tex>.<br />
}}<br />
<br />
= 35 Теорема Банаха о непрерывной обратимости <tex>I-C</tex>. =<br />
{{Теорема<br />
|author=Банах<br />
|about=о непрерывной обратимости I-C<br />
|statement=<br />
Пусть <tex> X </tex> {{---}} B-пространство, оператор <tex> C : X \to X, C \in \mathbb{L}(X) </tex> и <tex> \| C \| < 1 </tex>.<br />
Тогда оператор <tex> I - C </tex>, где <tex> I </tex> {{---}} тождественный оператор, непрерывно обратим.<br />
}}<br />
<br />
= 36 Лемма о множествах <tex>X_n = {||Ax|| < n ||x||}</tex>. =<br />
{{Утверждение<br />
|statement=<br />
Рассмотрим линейный оператор <tex> A : X \to Y </tex>. Обозначим <tex> X_n = \{ x \in X: \| Ax \| \le n \| x \| \} </tex>.<br />
Тогда хотя бы одно <tex> X_n </tex> ''всюду плотно в <tex> X </tex>''.<br />
}}<br />
<br />
= 37 Теорема Банаха об обратном операторе. =<br />
{{Теорема<br />
|about=Банаха, о гомеоморфизме<br />
|statement=<br />
Пусть <tex> A : X \to Y </tex> {{---}} линейный ограниченный оператор, причем осуществляющий биекцию, тогда <tex> A^{-1} </tex> {{---}} линейный ограниченный оператор.<br />
}}<br />
<br />
= 38 Теорема о замкнутом графике. =<br />
{{Определение<br />
|definition=<br />
'''Графиком''' линейного оператора <tex> A: X \to Y </tex> называется множество <tex> G(A) = \{ (x, Ax) \mid x \in X \}, G(A) \subset X \times Y </tex>. <br />
}}<br />
<br />
{{Теорема<br />
|about=о замкнутом графике<br />
|statement=<br />
Линейный <tex>A : X \to Y </tex> ограничен <tex> \iff </tex> <tex> G(A) </tex> {{---}} замкнут.<br />
}}<br />
<br />
= 39 Теорема об открытом отображении. =<br />
{{Определение<br />
|definition=<br />
<tex> F : X \to Y </tex> {{---}} произвольное отображение. Если для любого открытого <tex> G \subset X </tex> <tex> F(G) </tex> открыто в <tex> Y </tex>, то <tex> F </tex> называют '''открытым отображением'''.<br />
}}<br />
<br />
{{Теорема<br />
|about=об открытом отображении<br />
|statement=<br />
Пусть <tex> A : X \to Y </tex> {{---}} линейный ограниченный оператор. Тогда <tex> A </tex> {{---}} открытое отображение.<br />
}}<br />
<br />
= 40 Теорема о резольвентном множестве. =<br />
{{Определение<br />
|definition=<br />
Рассмотрим некоторое <tex>\lambda \in \mathbb C</tex>. Если для него существует и непрерывен оператор <tex>R_\lambda(A) = R_\lambda = (A - \lambda I)^{-1}</tex> (<tex>I</tex> {{---}} единичный оператор), то он называется '''резольвентой'''. Множество <tex>\lambda</tex>, для которых существует <tex>R_\lambda</tex>, обозначается <tex>\rho(A)</tex>, и называется '''резольвентным множеством''', дополнение к нему обозначается <tex>\sigma(A)</tex> и называется '''спектром''' оператора <tex>A</tex>.<br />
}}<br />
<br />
{{Утверждение<br />
|about=замкнутость спектра<br />
|statement=<br />
<tex>\rho(A)</tex> {{---}} открытое множество в <tex>\mathbb C</tex>;<br />
}}<br />
<br />
= 41 Теорема о спектральном радиусе. =<br />
{{Определение<br />
|definition=<br />
<tex>r_\sigma(A) = \inf\limits_{n \in \mathbb N} \sqrt[n]{\|A^n\|}</tex> {{---}} спектральный радиус оператора.<br />
}}<br />
<br />
{{Утверждение<br />
|statement=<br />
<tex>r_\sigma(A) = \lim\limits_{n \to \infty} \sqrt[n]{\|A^n\|}</tex><br />
}}<br />
<br />
= 42 Аналитичность резольвенты. =<br />
{{Утверждение<br />
|about=аналитичность резольвенты в резольвентном множестве<br />
|statement=<tex>R_\lambda</tex> как функция из комплексного числа в ограниченный оператор, аналитична в <tex>\rho(A)</tex> и в бесконечно удаленной точке комплексной плоскости.<br />
}}<br />
<br />
= 43 Непустота спектра ограниченного оператора. =<br />
{{Теорема<br />
|about=непустота спектра ограниченного оператора<br />
|statement=<br />
<tex>\|A\| < +\infty \implies \sigma(A) \ne \varnothing</tex><br />
}}<br />
<br />
[[Категория: Функциональный анализ 3 курс]]</div>Kabanovhttp://neerc.ifmo.ru/wiki/index.php?title=%D0%A2%D0%B5%D0%BE%D1%80%D0%B5%D1%82%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B8%D0%B9_%D0%BC%D0%B8%D0%BD%D0%B8%D0%BC%D1%83%D0%BC_%D0%BF%D0%BE_%D1%84%D1%83%D0%BD%D0%BA%D1%86%D0%B8%D0%BE%D0%BD%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D0%BC%D1%83_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7%D1%83_%D0%B7%D0%B0_5_%D1%81%D0%B5%D0%BC%D0%B5%D1%81%D1%82%D1%80&diff=44770Теоретический минимум по функциональному анализу за 5 семестр2015-01-24T14:15:22Z<p>Kabanov: /* 17 Счетно-нормированные пространства, метризуемость. */</p>
<hr />
<div>= 1 Определение МП, замыкание в МП. =<br />
{{Определение<br />
|id=defms<br />
|definition=<br />
Для некоторого множества <tex>X</tex>, отображение <tex> \rho : X \times X \to \mathbb{R^+} </tex> {{---}} называется '''метрикой''' на <tex>X</tex>, если выполняются аксиомы<br />
# <tex> \rho (x, y) \ge 0 ;\ \rho (x, y) = 0 \iff x = y </tex><br />
# <tex> \rho (x, y) = \rho (y, x) </tex> <br />
# <tex> \rho (x, y) \le \rho (x, z) + \rho (z, y) </tex> {{---}} неравенство треугольника<br />
<br />
Пару <tex>(X, \rho)</tex> называют '''метрическим пространством'''.<br />
}}<br />
<br />
{{Определение<br />
|id=defint<br />
|definition=<br />
'''Замыкание (closure)''' множества <tex>A</tex> называется множество <tex>\mathrm{Cl} A = \bigcap\limits_{A \subset F } F</tex>, где <tex> F </tex> — замкнутые множества.<br />
}}<br />
<br />
= 2 Принцип вложенных шаров в полном МП. =<br />
{{Утверждение<br />
|about=<br />
принцип вложенных шаров<br />
|statement=<br />
Пусть <tex>(X, \rho)</tex> — полное. <tex>\overline V_n</tex> — замкнутые шары. <tex>\overline V_{n + 1} \subset \overline V_n</tex>, <tex>r_n \to 0</tex>. Тогда <tex>\bigcap\limits_{n=1}^{\infty} \overline V_n \ne \varnothing</tex>, и состоит из одной точки.<br />
}}<br />
<br />
= 3 Теорема Бэра о категориях. =<br />
{{Определение<br />
|definition=<br />
Подмножество <tex>A</tex> топологического пространства <tex>X</tex> имеет '''I категорию по Бэру в пространстве <tex>X</tex>''', если оно является не более чем счетным объединением нигде не плотных в <tex>X</tex> множеств. В противном случае оно имеет '''II категорию по Бэру'''.<br />
}}<br />
<br />
{{Теорема<br />
|author=Бэр<br />
|statement=<br />
Полное МП является множеством II категории в себе.<br />
}}<br />
<br />
= 4 Критерий компактности Хаусдорфа в МП. =<br />
{{Теорема<br />
|author=Хаусдорф<br />
|statement=<br />
Пусть <tex>X</tex> {{---}} полное метрическое пространство, <tex>K \subset X</tex>, <tex>K</tex> {{---}} замкнуто. <br />
Тогда <tex>K</tex> {{---}} компакт <tex>\iff</tex> <tex>K</tex> {{---}} вполне ограниченно.<br />
}}<br />
<br />
= 5 Пространство <tex>R^{\infty}</tex> : метрика, покоординатная сходимость. =<br />
* <tex>X = \mathbb{R}^{\infty}</tex>. Превращение в МП должно быть связано с желаемой операцией предельного перехода. В случае конечномерного пространства сходимость совпадает с покоординатной сходимостью, хотим того же самого для бесконечномерного. Введем метрику: <tex>\rho(\overline x, \overline y) = \sum\limits_{n = 1}^{\infty} {1 \over 2^n}{|x_n - y_n| \over 1 + |x_n - y_n|}</tex> (стандартный способ превратить в метрическое пространство счетное произведение метрических пространств, коим и является <tex>R^{\infty}</tex>). Проверим, что эта метрика удовлетворяет аксиомам:<br />
** этот ряд всегда сходящийся, так как мажорируется убывающей геометрической прогрессией <tex>\sum\limits_{n=1}^{\infty} {1 \over 2^n} = 1</tex>, соответственно, расстояние ограничено единицей.<br />
** первая аксиома: неотрицательность очевидна, равенство метрики нулю в обе стороны очевидно<br />
** вторая аксиома: еще очевиднее<br />
** третья аксиома легко вытекает из следующего утверждения:<br />
{{Утверждение<br />
|statement=<tex> {|x - z| \over 1 + |x - z|} \le {|x - y| \over 1 + |x - y|} + {|y - z| \over 1 + |y - z|}</tex><br />
}} <br />
<br />
{{Утверждение<br />
|statement=Сходимость в метрике <tex> \mathbb{R}^{\infty} </tex> эквивалентна покоординатной. <br />
}}<br />
<br />
= 6 Норма в линейном множестве, определение предела по норме, арифметика предела. =<br />
{{Определение<br />
|definition=<br />
Функция <tex>\| \cdot \|: L \to \mathbb{R}</tex> называется нормой в пространстве <tex>L</tex>, если для нее выполняется:<br />
# <tex>\forall x \in L: \| x \| \ge 0</tex>, <tex>\| x \| = 0 \iff x = \mathrm{0}</tex><br />
# <tex>\forall \alpha \in \mathbb{R}\ \forall x \in L: \| \alpha x \| = |\alpha |\| x \|</tex><br />
# <tex>\forall x, y \in L: \| x + y \| \le \| x \| + \| y \|</tex><br />
Пространство с введенной на нем нормой называют '''нормированным пространством'''.<br />
}}<br />
<br />
В нормированных пространствах определение предела записывается аналогично пределу вещественной последовательности, отличаясь лишь заменой знака модуля на знак нормы.<br />
<br />
Например, если <tex>E \subset X</tex>, <tex>a</tex> — предельная точка множества <tex>E</tex>, <tex>f \colon E \to Y</tex> (где <tex>X</tex> и <tex>Y</tex> — нормированные пространства), то <tex>A</tex> называется пределом функции <tex>f</tex> при <tex>x \to a</tex> и обозначается <tex>\lim\limits_{x \to a} f(x)</tex>, если для любого положительного <tex>\varepsilon</tex> найдётся <tex>\delta > 0</tex>, для которого выполняется следствие <tex>0 < \|x - a\| < \delta \implies \|f(x) - A\| < \varepsilon</tex>.<br />
<br />
Специфика нормированных пространств — структура линейного пространства на рассматриваемом множестве. То есть, точки пространства можно складывать и умножать на числа, и эти операции будут непрерывными по норме пространства.<br />
<br />
{{Утверждение<br />
|statement=<br />
Пусть <tex>x_n</tex>, <tex>y_n</tex> — последовательности точек нормированного пространства <tex>(X, \|\cdot\|)</tex>, а <tex>\alpha_n</tex> — вещественная последовательность. Известно, что <tex>x_n \to x</tex>, <tex>y_n \to y</tex>, <tex>\alpha_n \to \alpha</tex>.<br />
<br />
Тогда:<br />
# <tex>x_n + y_n \to x + y</tex><br />
# <tex>\alpha_n x_n \to \alpha x</tex><br />
# <tex>\|x_n\| \to \|x\|</tex><br />
}}<br />
<br />
= 7 Эквивалентность норм в конечномерном НП. =<br />
{{Определение<br />
|definition=<br />
Нормы <tex>\| \|_1</tex>, <tex>\| \|_2</tex> '''эквивалентны''', если существуют константы <tex>m, M > 0</tex> такие, что <tex>\forall x: m\|x\|_2 \le \|x\|_1 \le M \|x\|_2</tex>. Очевидно, что отношение эквивалентности норм является отношением эквивалентности (то есть выполняется рефлексивность, симметриченость и транзитивность).<br />
}}<br />
<br />
Это определение равносильно тому, что сходимость последовательностей в них равносильна: <tex>x_n \xrightarrow[]{\|\|_1} x \iff x_n \xrightarrow[]{\|\|_2} x</tex>.<br />
<br />
{{Определение<br />
|definition=<br />
Пространство <tex> X </tex> '''конечномерно''', если <tex> \exists n = dim X < \infty: \exists e_1, e_2, \ldots, e_n: X = \mathcal L(e_1, \ldots, e_n)</tex>.<br />
}}<br />
<br />
{{Теорема<br />
|author=Рисс<br />
|statement=<br />
В конечномерных пространствах любые две нормы эквивалентны.<br />
}}<br />
<br />
= 8 Замкнутость конечномерного линейного подмножества НП. =<br />
{{Определение<br />
|definition=Подпространство в алгебраическом смысле не обязательно замкнуто в исходном пространстве. Поэтому в функциональном анализе собственно '''подпространством''' называется именно ''замкнутое'' подпространство, а ''алгебраические'' подпространства называют '''линейными подмножествами'''.<br />
}}<br />
<br />
{{Теорема<br />
|statement=<br />
Пусть <tex>X</tex> — НП и <tex>Y</tex> — линейное конечномерное подмножество в <tex>X</tex>, тогда <tex>Y</tex> — замкнуто в <tex>X</tex>, т.е.<br />
<tex>\mathrm{Cl} Y = Y</tex>.}}<br />
<br />
= 9 Лемма Рисса о почти перпендикуляре, пример ее применения. =<br />
{{Лемма<br />
|author=Рисc<br />
|about=о почти перпендикуляре<br />
|statement=<br />
Пусть <tex>X</tex> — НП, а <tex>Y</tex> {{---}} собственное (то есть не совпадающее с <tex>X</tex>) подпространство <tex>X</tex>, тогда <tex>\forall \varepsilon \in (0, 1) \; \exists z_{\varepsilon} \in X : \|z_{\varepsilon}\| = 1,\; \rho(z_{\varepsilon}, Y) \geq 1 - \varepsilon</tex> (где <tex>\rho(z, Y) = \inf\limits_{y \in Y} \|z-y\|</tex>)<br />
}}<br />
<br />
{{Теорема<br />
|about=некомпактность шара в бесконечномерном пространстве<br />
|statement=<br />
Если <tex>X</tex> {{---}} бесконечномерное НП, то единичный шар <tex>S_1 = \{ x \in X \mid \|x \| = 1\}</tex> в нем не компактен.<br />
|proof=<br />
Возьмем <tex>x \in S_1</tex>, <tex>Y_1 = \mathcal{L}(x_1)</tex> — собственное подпространство <tex>X</tex>, применим лемму Рисса, возьмем <tex>\varepsilon = {1 \over 2}</tex>, существует <tex>x_2: \| x_2 \| = 1, \| x_2 - x_1 \| \ge {1 \over 2}</tex>, заметим, что <tex>x_2</tex> окажется в <tex>S_1</tex>.<br />
<br />
<tex>Y_2 = \mathcal{L}(x_1, x_2)</tex>, опять применим лемму Рисса, существует <tex>x_3 \in X: \| x_3 - x_j \| \ge {1 \over 2}, j = 1, 2</tex>, <tex>x_3</tex> будет в <tex>S_1</tex>.<br />
<br />
Продолжаем так же для <tex>Y_3 \dots Y_n \dots</tex>. Процесс никогда не завершится, так как <tex>X</tex> — бесконечномерное и не может быть линейной оболочкой конечного числа векторов. Таким образом построили бесконечную систему точек в <tex>S_1</tex>, но из которой нельзя выделить сходящуюся подпоследовательность, так как <tex>\| x_n - x_m \| \ge {1 \over 2}</tex>, следовательно, <tex>S_1</tex> не компактно.<br />
}}<br />
<br />
= 10 Банаховы пространства на примерах <tex>C [0,1]</tex> и <tex>L_p(E)</tex>. =<br />
Чо-то не нашёл, где это и что именно сюда надо пилить<br />
<br />
= 11 Определение скалярного произведения, равенство параллелограмма, неравенство Шварца. =<br />
Пусть <tex>H</tex> — линейное пространство. Величина <tex>(x, y) \in \mathbb R</tex> называется скалярным произведением точек множества <tex>H</tex>, если она удовлетворяет следующим трём аксиомам:<br />
# <tex>(x, x) \ge 0</tex>, <tex>(x, x) = 0 \iff x = 0</tex><br />
# <tex>(x, y) = (y, x)</tex><br />
# <tex>(\alpha x + \beta y, z) = \alpha(x, z) + \beta(y, z)</tex><br />
<br />
Основное значение для скалярного произведения имеет неравенство Шварца:<br />
{{Утверждение<br />
|statement=<br />
<tex>|(x, y)| \le \sqrt{(x, x)}\sqrt{(y, y)}</tex><br />
}}<br />
<br />
//не нашёл этого в конспектах, беру с википедии<br />
<br />
Характеристическим свойством, выделяющим гильбертовы пространства <tex>H</tex> среди прочих банаховых пространств, является равенство параллелограмма:<br />
<tex>\forall x,y\in H\ \quad \|x+y\|^2+\|x-y\|^2=2(\|x\|^2+\|y\|^2)</tex><br />
<br />
= 12 Наилучшее приближение в НП в случае конечномерного подпространства. =<br />
Пусть <tex>X</tex> {{---}} [[Нормированные_пространства#определение и примеры|нормированное пространство]], к примеру, <tex>L_p</tex>. Пусть <tex>Y</tex> {{---}} линейное множество в <tex>X</tex>, например, <tex>H_n</tex> (тригонометрических полиномов степени не больше <tex>n</tex>).<br />
<br />
{{Определение<br />
|definition = Для любого <tex> x \in X</tex> величина <tex>E_Y(x) = \inf\limits_{y \in Y}{\|x-y\|}</tex> называется '''наилучшим приближением точки <tex>x</tex> элементами линейного множества <tex>Y</tex>'''.<br />
Если при этом существует <tex>y^* \in Y</tex> такой, что <tex>E_Y(x)=\|x-y^*\|</tex>, то этот <tex>y^*</tex> называется '''элементом наилучшего приближения точки <tex>x</tex>'''.<br />
}}<br />
<br />
{{Теорема<br />
|statement=<br />
Пусть <tex>X</tex> {{---}} нормированное пространство, <tex>\dim Y < +\infty</tex>, тогда <tex>\forall x \in X</tex> существует элемент наилучшего приближения <tex>x</tex>.<br />
}}<br />
<br />
= 13 Наилучшее приближение в унитарном пространстве, неравенство Бесселя. =<br />
{{Утверждение<br />
|statement = Пусть <tex>x\in\mathcal{H}</tex>, <tex>\sum\limits_{j=1}^\infty \langle x, e_j\rangle e_j</tex> (причем он может быть расходящимся), <tex>s_n(x) = \sum\limits_{j=1}^n \langle x, e_j\rangle e_j</tex><br />
тогда: <tex>\|x-s_n(x)\|^2 = \inf \|x - \sum\limits_{k=1}^n \alpha_ke_k\|^2</tex>, <tex>\alpha_k \in \mathbb{R}</tex><br />
}}<br />
<br />
{{Теорема<br />
|author=<br />
Бессель<br />
|about=<br />
неравенство Бесселя<br />
|statement=<br />
<tex> \sum \limits_{k=1}^{\infty} (x, e_k)^2 \le \|x\|^2</tex>, где <tex>e_1 \dots e_n \dots \in H </tex> {{---}} ортонормированная система точек<br />
}}<br />
<br />
= 14 Определение Гильбертова пространства, сепарабельность и полнота. =<br />
{{Определение<br />
|definition=<br />
'''Гильбертовым пространством''' называют Банахово пространство, в котором норма порождена скалярным произведением.<br />
}}<br />
<br />
{{Определение<br />
|definition=<br />
'''Гильбертово пространство''' сепарабельно тогда и только тогда, когда в нём существует счётный ортонормированный базис.<br />
}}<br />
<br />
{{TODO|t= взято от сюда:http://www.nsu.ru/education/funcan/node89.html проверить на правду}}<br />
<br />
{{Теорема<br />
|about=<br />
критерий полноты ортонормированной системы в сепарабельном гильбертовом пространстве<br />
|statement=<br />
Пусть <tex>H</tex> {{---}} сепарабельное гильбертово пространство и <tex>\{e_1, e_2, \ldots, e_n, \ldots\}</tex> {{---}} ортонормированная система векторов в нем. Тогда следующие условия эквивалентны:<br />
<br />
# система <tex>\{e_1, e_2, \ldots, e_n, \ldots\}</tex> полна<br />
# система <tex>\{e_1, e_2, \ldots, e_n, \ldots\}</tex> замкнута<br />
# <tex>\forall x \in H </tex> справедливо разложение <tex>x = \sum\limits_{n = 1}^\infty \lambda_n e_n</tex>, где <tex>\lambda_n = (x, e_n)</tex> {{---}} коэффициенты Фурье вектора <tex>x</tex> относительно ортонормированной системы <tex>\{e_1, e_2, \ldots, e_n, \ldots\}</tex><br />
}}<br />
<br />
= 15 Теорема Рисса-Фишера, равенство Парсеваля. =<br />
{{Теорема<br />
|about=<br />
равенство Парсеваля<br />
|statement=<br />
<tex>\forall x: \|x\|^2 = \sum\limits_{k=1}^{\infty} \langle x, e_k \rangle ^2 </tex> тогда и только тогда, когда ортонормированная система точек, по которым строятся коэффициенты Фурье, полная или замкнутая.<br />
}}<br />
<br />
{{Теорема<br />
|author=Рисс-Фишер<br />
|statement=<br />
Пусть <tex>\{e_1, e_2, \ldots, e_n, \ldots\}</tex> {{---}} ортонормированная система в гильбертовом пространстве <tex>H</tex>, <tex>\sum\limits_{i=1}^{\infty} \alpha_i^2 < +\infty</tex>. Тогда <tex>\exists ! x \in H : \alpha_i = \langle x, e_i \rangle</tex> и выполняется '''равенство Парсеваля''': <tex>\sum\limits_{i=1}^{\infty} \alpha_i^2 = \|x\|^2</tex><br />
}}<br />
<br />
= 16 Наилучшее приближение в <tex>H</tex> для случая выпуклого,замкнутого множества, <tex>H = H_1 \oplus H_2</tex>. =<br />
{{Теорема<br />
|statement=<br />
Пусть <tex>M</tex> — выпуклое замкнутое множество в <tex>H</tex>, тогда <tex>\forall x \in H\ \exists z \in M: \| x - z \| = \inf\limits_{y \in M} \| x - y\|</tex>. <tex>z</tex> называется '''элементом наилучшего приближения'''<br />
}}<br />
{{Определение<br />
|definition=<br />
Говорят, что два элемента <tex> x, y </tex> гильбертова пространства <tex> H </tex> '''перпендикулярны''' (<tex> x \perp y </tex>), если <tex> \langle x, y \rangle = 0. </tex><br />
}}<br />
<br />
{{Определение<br />
|definition=<br />
Пусть <tex>H_1</tex> — подпространство в <tex>H</tex>, тогда '''ортогональным дополнением''' называется <tex>H_2 = H_1^{\perp} = \{ x \in H \mid \forall y \in H_1: x \perp y\}</tex>.<br />
}}<br />
<br />
= 17 Разложение гильбертова пространства в прямую сумму подпространств. =<br />
{{Теорема<br />
|statement=<br />
Пусть <tex> H_1 </tex> — подпространство в <tex>H</tex>, <tex> H_2 </tex> {{---}} его ортогональное дополнение. Тогда для любого <tex> x \in H </tex> существует единственное представление <tex> x = x_1 + x_2 </tex>, где <tex> x_1 \in H_1, x_2 \in H_2 </tex> и <tex> x_1 \perp x_2 </tex>.<br />
}}<br />
<br />
= 18 Счетно-нормированные пространства, метризуемость. =<br />
{{Определение<br />
|definition=<br />
Пусть <tex>X</tex> — линейное пространство, <tex>p_1 \dots p_n \dots</tex> — полунормы. Если <tex>\forall x \in X</tex> из того, что <tex>\forall k: p_k(x) = 0</tex> следует, что <tex>x = 0</tex>, то <tex>X</tex> называют '''счетно-нормированным пространством'''<br />
}}<br />
<br />
{{Утверждение<br />
|statement=<br />
Счетно-нормированные пространства можно метризовать как <tex>\mathbb{R}^{\infty}</tex>: <tex>\rho(x, y) = \sum\limits_{n=1}^{\infty} {1 \over 2^n} {p_n(x - y) \over 1 + p_n(x - y)}</tex>.<br />
}}<br />
<br />
= 18 Условие нормируемости СНТП. =<br />
<wikitex><br />
{{Определение<br />
|definition=<br />
Полунорма $p_n$ в системе $p$ '''существенна''', если она не мажорируется ни одной из полунорм этой системы с меньшими чем $n$ номерами.<br />
}}<br />
{{Теорема<br />
|about=критерий нормируемости счетно-нормированного пространства<br />
|statement=<br />
Пусть $X$ — счетное-нормированное пространство по монотонной системе полунорм $p$. Оно нормируется тогда и только тогда, когда в системе $p$ конечное число существенных полунорм.<br />
}}<br />
</wikitex><br />
<br />
= 19 Функционал Минковского. =<br />
<wikitex><br />
{{Определение<br />
|definition=<br />
$A$ '''поглощает''' $B$, если $\exists \lambda_0 > 0: \forall \lambda: |\lambda| > \lambda_0: B \subset \lambda A$.<br />
}}<br />
<br />
{{Определение<br />
|definition=<br />
$A$ '''радиальное/поглощающее''', если оно поглощает любую конечную систему точек. Для проверки радиальности достаточно проверить поглощение каждой конкретной точки.<br />
}}<br />
{{Определение<br />
|definition=<br />
Пусть $X$ — линейное пространство, $\mu$ — радиальное подмножество, тогда '''функционал Минковского''' $p_{\mu}$ определяется как $p_{\mu}(x) = \inf \{ \lambda > 0 \mid x \in \lambda \mu\}$.<br />
}}<br />
</wikitex><br />
<br />
= 20 Топология векторных пространств. =<br />
<wikitex><br />
{{Определение<br />
|definition=<br />
'''Топологическое векторное пространство''' — линейное пространство, наделенной такой топологией, что операции сложения векторов и умножения на скаляр в ней непрерывны в этой топологии, то есть:<br />
* непрерывность умножения на скаляр: $\alpha x \to \alpha_0 x_0$, если $\alpha \to \alpha_0$, $x \to x_0$. Означает, что для любой окрестности $U(\alpha_0 x_0)$ существует $ \varepsilon > 0$ и существует $U(x_0): |\alpha - \alpha_0| < \varepsilon, x \in U(x_0) \implies \alpha x \in U(\alpha_0 x_0)$<br />
* непрерывность сложения векторов: $x + y \to x_0 + y_0$, если $x \to x_0$, $y \to y_0$. Означает, что для любой окрестности $U(x_0 + y_0)$ существуют окрестности $U(x_0), U(y_0): \forall x \in U(x_0) \forall y \in U(y_0) \implies x + y \in U(x_0 + y_0)$.<br />
}}<br />
</wikitex><br />
<br />
= 21 Теорема Колмогорова о нормируемости ТВП. =<br />
{{Теорема<br />
|author=Колмогоров<br />
|statement=<br />
Хаусдорфово ТВП нормируемо тогда и только тогда, когда у нуля есть ограниченная выпуклая окрестность.<br />
}}<br />
<br />
= 22 Коразмерность ядра линейного функционала. =<br />
{{Определение<br />
|id=linfuncdef<br />
|definition=<br />
Пусть <tex>X</tex> — линейное множество. Отображение <tex> f\colon X \to \mathbb{R} </tex> {{---}} '''линейный функционал''', если <br />
<tex>\forall \alpha, \beta \in \mathbb{R} \ \forall x, y \in X : f(\alpha x + \beta y) = \alpha f(x) + \beta f(y)</tex>.<br />
<br />
Обозначим <tex>X^*</tex> — совокупность линейных функционалов, определенных на множестве <tex>X</tex>.<br />
<br />
<tex> \mathrm{Ker}\, f = \{x \mid f(x) = 0 \} </tex> — '''ядро функционала'''.<br />
}}<br />
<br />
{{Определение<br />
|id=factorsetdef<br />
|definition=<br />
Пусть <tex>X</tex> — линейное множество, <tex>Y</tex> линейное подмножество <tex>X</tex>. <br />
<br />
Введем отношение эквивалентности на <tex>X</tex>:<br />
<br />
<tex> x_1 \sim x_2 \stackrel{\mathrm{def}}{\iff} x_1 - x_2 \in Y </tex><br />
<br />
<tex> [x] = \{ y \in X \mid y \sim x \} </tex> — '''классы смежности''' по <tex>Y</tex>.<br />
<br />
<tex> X /_Y </tex> — совокупность всех классов смежности — '''фактор-множество''' по <tex>Y</tex>.<br />
<br />
}}<br />
<br />
{{Определение<br />
|id=codimdef<br />
|definition=<br />
<tex>\mathrm{Codim}\, Y \stackrel{\mathrm{def}}{=} \dim X /_Y </tex> — '''коразмерность''' <tex>Y</tex>. <br />
<br />
<tex> Y </tex> — '''гиперплоскость''' в <tex>X</tex>, если <tex>\mathrm{Codim}\, Y = 1</tex>.<br />
<br />
}}<br />
<br />
{{Утверждение<br />
|about=Коразмерность ядра функционала<br />
|statement=<br />
<br />
Если <tex> f </tex> не является тождественно равным нулю, то <tex>\mathrm{Codim}\, \mathrm{Ker}\, f = 1 </tex>.<br />
}}<br />
<br />
= 23 Непрерывный линейный функционал и его норма. =<br />
{{Определение<br />
|id=contfuncdef<br />
|definition=<br />
Пусть <tex>X</tex> — нормированное пространство. Линейный функционал <tex> f \in X^* </tex> {{---}} '''непрерывен''' в точке <tex> x </tex>, если <br />
<tex>x_n \to x \implies f(x_n) \to f(x) </tex>.<br />
}}<br />
Обозначение <tex> \overline{V}_1 = \{ x : \| x \| \leq 1 \} </tex><br />
<br />
Введем норму в <tex> X^* </tex>: <tex> \| f \| \stackrel{\mathrm{def}}{=} \sup\limits_{\overline{V}_1} {| f(x) |} </tex><br />
<br />
{{Утверждение<br />
|id=cont0<br />
|statement= Линейный функционал <tex>f</tex> непрерывен <tex> \iff </tex> <tex>f</tex> непрерывен в нуле.<br />
}}<br />
<br />
= 24 Связь между непрерывностью линейного функционала и замкнутостью его ядра. =<br />
<br />
{{Определение<br />
|id=finitefuncdef<br />
|definition=<br />
<tex> f </tex> — '''ограниченный''' функционал, если <tex> \| f \| < \infty </tex>.<br />
}}<br />
<br />
{{Утверждение<br />
|id=cont-finite<br />
|statement= <tex>f</tex> — непрерывен <tex> \iff </tex> <tex>f</tex> — ограничен.<br />
}}<br />
{{<br />
Теорема<br />
|about=характеристика ограниченного функционала в терминах ядра<br />
|statement=<br />
<tex>f</tex> — ограничен <tex>\iff \mathrm{Ker}\, f</tex> — замкнуто в <tex>X</tex>.<br />
}}<br />
<br />
= 25 Продолжение по непрерывности линейного функционала со всюду плотного линейного подмножества НП. =<br />
{{Утверждение<br />
|id=densefunextension<br />
|statement= Пусть <tex> Y </tex> — линейное всюду плотное в <tex> X </tex> множество.<br />
<tex> f </tex> — линейный непрерывный функционал на <tex> Y </tex>. Тогда существует единственный <tex> \widetilde f </tex> — линейный непрерывный функционал на <tex> X </tex> такой, что:<br />
<br />
1) <tex> \widetilde f |_Y = f </tex> — сужение на <tex> Y </tex> совпадает с <tex> f </tex>.<br />
2) <tex> \| \widetilde f \|_X = \| f \|_Y </tex><br />
}}<br />
<br />
= 26 Теорема Хана-Банаха для НП (сепарабельный случай). =<br />
{{Теорема<br />
|author=<br />
Хан, Банах<br />
|statement=<br />
Пусть <tex>X</tex> {{---}} [[Метрические_пространства#defdense|сепарабельное]] нормированное пространство, <tex>Y</tex> {{---}} линейное подмножество <tex>X</tex>, <tex>f: Y \to \mathbb R</tex> {{---}} линейный ограниченный функционал.<br />
Тогда существует линейный ограниченный функционал <tex>g: X \to \mathbb R</tex> такой, что <tex>g|_Y = f</tex>, <tex>\|g\| = \|f\|</tex>.<br />
}}<br />
<br />
= 27 Два следствия из теоремы Хана-Банаха. =<br />
{{Утверждение<br />
|statement=<br />
Пусть <tex>X</tex> {{---}} нормированное пространство. Тогда <tex>\forall x \in X \exists f: X \to R:\ f(x) = \|x\|, \|f\| = 1</tex>.<br />
}}<br />
{{Утверждение<br />
|statement=<br />
Пусть <tex>X</tex> {{---}} нормированное пространство, <tex>e_1, e_2, \ldots, e_n</tex> {{---}} линейно независимый набор в <tex>X</tex>.<br />
Тогда в <tex>X</tex> существует биортогональная система функционалов <tex>f_1, f_2, \ldots f_n, f_i(e_j) = \delta_{ij}</tex><br />
}}<br />
<br />
= 28 Теорема Рисса об общем виде линейного непрерывного функционала в <tex>H</tex>. =<br />
{{Теорема<br />
|author=Рисс<br />
|about=об общем виде линейного непрерывного функционала в гильбертовом пространстве<br />
|statement=<br />
<tex>\forall f \in H^*\; \exists ! y \in H : f(x) = \langle x, y \rangle</tex>, причем <tex>\|f\| = \|y\|</tex><br />
}}<br />
<br />
= 29 Непрерывный линейный оператор и его норма. =<br />
{{Определение<br />
|definition=<br />
Оператор <tex>A</tex> называется '''линейным''', если <tex>A(\alpha x_1 + \beta x_2) = \alpha A(x_1) + \beta A(x_2)</tex>.<br />
}}<br />
<br />
{{Определение<br />
|definition=<br />
Оператор <tex>A</tex> '''непрерывен''' в точке <tex>x_0</tex>, если <tex>\lim\limits_{x \to x_0} Ax = Ax_0</tex>.<br />
}}<br />
<br />
{{Определение<br />
|definition=<br />
'''Нормой''' оператора <tex>A</tex> называется <tex>\|A\| = \sup\limits_{\|x\| \le 1} \| Ax \|</tex>.<br />
}}<br />
<br />
{{Определение<br />
|definition=<br />
Оператор <tex>A</tex> '''ограничен''', если <tex>\|A\| \le \infty</tex>.<br />
}}<br />
<br />
{{Теорема<br />
|statement=<br />
Линейный оператор непрерывен тогда и только тогда, когда он ограничен.<br />
}}<br />
<br />
= 30 Продолжение линейного оператора по непрерывности. =<br />
{{Теорема<br />
|statement=<br />
Пусть <tex>Y</tex> {{---}} линейное пространство, <tex>Cl Y = X</tex>, <tex>A: Y \to Z</tex> {{---}} линейный ограниченный оператор, <tex>Z</tex> {{---}} банахово.<br />
Тогда <tex>\exists! B: X \to Z</tex>:<br />
# <tex>B|_Y = A</tex><br />
# <tex>\|B\| = \|A\|</tex><br />
}}<br />
<br />
= 31 Полнота пространства <tex>L(X,Y)</tex>. =<br />
Обычно пространство линейных ограниченных операторов из <tex>X</tex> в <tex>Y</tex> обозначают как <tex>L(X, Y)</tex>.<br />
<br />
{{Теорема<br />
|statement=<br />
Пусть <tex>Y</tex> {{---}} банахово, тогда <tex>L(X, Y)</tex> тоже банахово.<br />
}}<br />
<br />
= 32 Теорема Банаха-Штейнгауза. =<br />
{{Определение<br />
|definition=<br />
Последовательность <tex>A_n</tex> '''поточечно ограничена''', если <tex>\forall x \in X \sup\limits_{n \in \mathbb N} \|A_n x\| < +\infty</tex>.<br />
}}<br />
<br />
{{Определение<br />
|definition=<br />
Последовательность <tex>A_n</tex> '''равномерно ограничена''', если <tex>\sup\limits_{n \in \mathbb N} \|A_n\| < +\infty</tex>.<br />
}}<br />
<br />
{{Теорема<br />
|author=<br />
Банах, Штейнгауз<br />
|about=<br />
принцип равномерной ограниченности<br />
|statement=<br />
Пусть <tex>X</tex> {{---}} банахово, <tex>A_n \in L(X, Y)</tex>, <tex>A_n</tex> поточечно ограничена. Тогда <tex>A_n</tex> равномерно ограничена.<br />
}}<br />
<br />
= 33 Условие замкнутости множества значений линейного оператора на базе априорной оценки решения операторного уравнения. =<br />
{{Определение<br />
|definition=<br />
Рассмотрим уравнение <tex> Ax = y </tex> при заданном <tex> y </tex>. Если для такого уравнения можно написать <tex> \| x \| \le \alpha \| y \| </tex>, где <tex> \alpha </tex> {{---}} константа, то говорят, что это уравнение '''допускает априорную оценку решений'''.<br />
}}<br />
<br />
{{Утверждение<br />
|statement=<br />
Если <tex> A </tex> непрерывен, и уравнение <tex> Ax = y </tex> допускает априорную оценку решений, то <tex> R(A) = \mathrm{Cl} R(A) </tex>.<br />
}}<br />
<br />
= 34 Условие непрерывной обратимости лин. оператора. =<br />
{{Определение<br />
|definition=<br />
Оператор <tex> A : X \to Y </tex> называется '''непрерывно обратимым''', если существует <tex> A^{-1} : Y \to X </tex> и <tex> \| A^{-1} \| < \infty </tex>, причем <tex>A^{-1}</tex> должен быть определен на всем <tex>Y</tex>.<br />
}}<br />
<br />
{{Теорема<br />
|statement=<br />
Пусть <tex> A : X \to Y </tex> {{---}} линейный ограниченный оператор, и <tex>\exists m > 0: m \| x \| \le \| Ax \| </tex>.<br />
Тогда <tex> A </tex> непрерывно обратим на <tex>R(A)</tex>.<br />
}}<br />
<br />
= 35 Теорема Банаха о непрерывной обратимости <tex>I-C</tex>. =<br />
{{Теорема<br />
|author=Банах<br />
|about=о непрерывной обратимости I-C<br />
|statement=<br />
Пусть <tex> X </tex> {{---}} B-пространство, оператор <tex> C : X \to X, C \in \mathbb{L}(X) </tex> и <tex> \| C \| < 1 </tex>.<br />
Тогда оператор <tex> I - C </tex>, где <tex> I </tex> {{---}} тождественный оператор, непрерывно обратим.<br />
}}<br />
<br />
= 36 Лемма о множествах <tex>X_n = {||Ax|| < n ||x||}</tex>. =<br />
{{Утверждение<br />
|statement=<br />
Рассмотрим линейный оператор <tex> A : X \to Y </tex>. Обозначим <tex> X_n = \{ x \in X: \| Ax \| \le n \| x \| \} </tex>.<br />
Тогда хотя бы одно <tex> X_n </tex> ''всюду плотно в <tex> X </tex>''.<br />
}}<br />
<br />
= 37 Теорема Банаха об обратном операторе. =<br />
{{Теорема<br />
|about=Банаха, о гомеоморфизме<br />
|statement=<br />
Пусть <tex> A : X \to Y </tex> {{---}} линейный ограниченный оператор, причем осуществляющий биекцию, тогда <tex> A^{-1} </tex> {{---}} линейный ограниченный оператор.<br />
}}<br />
<br />
= 38 Теорема о замкнутом графике. =<br />
{{Определение<br />
|definition=<br />
'''Графиком''' линейного оператора <tex> A: X \to Y </tex> называется множество <tex> G(A) = \{ (x, Ax) \mid x \in X \}, G(A) \subset X \times Y </tex>. <br />
}}<br />
<br />
{{Теорема<br />
|about=о замкнутом графике<br />
|statement=<br />
Линейный <tex>A : X \to Y </tex> ограничен <tex> \iff </tex> <tex> G(A) </tex> {{---}} замкнут.<br />
}}<br />
<br />
= 39 Теорема об открытом отображении. =<br />
{{Определение<br />
|definition=<br />
<tex> F : X \to Y </tex> {{---}} произвольное отображение. Если для любого открытого <tex> G \subset X </tex> <tex> F(G) </tex> открыто в <tex> Y </tex>, то <tex> F </tex> называют '''открытым отображением'''.<br />
}}<br />
<br />
{{Теорема<br />
|about=об открытом отображении<br />
|statement=<br />
Пусть <tex> A : X \to Y </tex> {{---}} линейный ограниченный оператор. Тогда <tex> A </tex> {{---}} открытое отображение.<br />
}}<br />
<br />
= 40 Теорема о резольвентном множестве. =<br />
{{Определение<br />
|definition=<br />
Рассмотрим некоторое <tex>\lambda \in \mathbb C</tex>. Если для него существует и непрерывен оператор <tex>R_\lambda(A) = R_\lambda = (A - \lambda I)^{-1}</tex> (<tex>I</tex> {{---}} единичный оператор), то он называется '''резольвентой'''. Множество <tex>\lambda</tex>, для которых существует <tex>R_\lambda</tex>, обозначается <tex>\rho(A)</tex>, и называется '''резольвентным множеством''', дополнение к нему обозначается <tex>\sigma(A)</tex> и называется '''спектром''' оператора <tex>A</tex>.<br />
}}<br />
<br />
{{Утверждение<br />
|about=замкнутость спектра<br />
|statement=<br />
<tex>\rho(A)</tex> {{---}} открытое множество в <tex>\mathbb C</tex>;<br />
}}<br />
<br />
= 41 Теорема о спектральном радиусе. =<br />
{{Определение<br />
|definition=<br />
<tex>r_\sigma(A) = \inf\limits_{n \in \mathbb N} \sqrt[n]{\|A^n\|}</tex> {{---}} спектральный радиус оператора.<br />
}}<br />
<br />
{{Утверждение<br />
|statement=<br />
<tex>r_\sigma(A) = \lim\limits_{n \to \infty} \sqrt[n]{\|A^n\|}</tex><br />
}}<br />
<br />
= 42 Аналитичность резольвенты. =<br />
{{Утверждение<br />
|about=аналитичность резольвенты в резольвентном множестве<br />
|statement=<tex>R_\lambda</tex> как функция из комплексного числа в ограниченный оператор, аналитична в <tex>\rho(A)</tex> и в бесконечно удаленной точке комплексной плоскости.<br />
}}<br />
<br />
= 43 Непустота спектра ограниченного оператора. =<br />
{{Теорема<br />
|about=непустота спектра ограниченного оператора<br />
|statement=<br />
<tex>\|A\| < +\infty \implies \sigma(A) \ne \varnothing</tex><br />
}}<br />
<br />
[[Категория: Функциональный анализ 3 курс]]</div>Kabanovhttp://neerc.ifmo.ru/wiki/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Kabanov&diff=44749Участник:Kabanov2015-01-21T18:43:42Z<p>Kabanov: </p>
<hr />
<div>{{nohate}}<br />
== Определение ==<br />
{{Определение<br />
|definition=<br />
'''Подразбиение Делоне множества точек''' — такое разбиение выпуклой оболочки множества точек на множество выпуклых фигур, что в окружности, описанной вокруг любой из фигур, не находится никаких точек из множества.<br />
}}<br />
{{Определение<br />
|definition=<br />
'''Триангуляция Делоне множества точек''' — триангуляция, являющаяся подразбиением Делоне.<br />
}}<br />
<br />
== Существование триангуляции Делоне ==<br />
{{Лемма<br />
|about=1<br />
|statement=<br />
Окружность, спроецированная на параболоид, находится в одной плоскости. Все точки, лежащие внутри окружности, будут лежать под этой плоскостью. Точки, лежащие вне окружности, будут лежать над плоскостью.<br />
}}<br />
{{Теорема<br />
|statement=<br />
Подразбиение Делоне существует, причём для каждого набора точек оно единственно.<br />
|proof=<br />
Спроецируем все точки на параболоид и построим выпуклую оболочку.<br />
<br />
Все грани выпуклой оболочки окажутся внутри параболоида из-за его выпуклости. При этом точки лежат на параболоиде. Поэтому не найдётся точек, которые будут лежать за гранями выпуклой оболочки. То есть все точки, спроецированные на параболоид, будут принадлежать выпуклой оболочке.<br />
<br />
По лемме 1 очевидно, что внутри окружностей, описанных вокруг проекций граней выпуклой оболочки, не будет лежать никаких точек. Значит, проекции граней — фигуры подразбиения Делоне. Значит, такое подразбиение существует. <br />
<br />
Из единственности выпуклой оболочки следует, что такое подразбиение единственно.<br />
}}<br />
<br />
== Критерий Делоне для рёбер ==<br />
{{Определение<br />
|definition='''Критерий Делоне для ребра''': на ребре можно построить такую окружность, что внутри неё не будет лежать никаких точек.<br />
}}<br />
{{Лемма<br />
|about=2<br />
|statement=Триангуляции Делоне принадлежат те и только те рёбра (с поправкой на точки, лежащие на одной окружности), которые удовлетворяют критерию Делоне.<br />
|proof=<br />
[[Файл:Good edges.png|200px|right]]<br />
То, что для рёбер, принадлежащих триангуляции Делоне, выполняется критерий Делоне для рёбер, очевидно (вокруг каждого ребра можно описать окружность, проходящую через противолежащую ему точку в смежном треугольнике, причём в окружности не будет никаких точек по критерию Делоне).<br />
<br />
Докажем, что если для ребра выполняется критерий Делоне, то оно принадлежит триангуляции Делоне.<br />
<br />
Предположим, что это ребро (назовём его <tex>AB</tex>) не принадлежит триангуляции Делоне. Тогда существует пересекающее его ребро <tex>CD</tex>, принадлежащее триангуляции. Рассмотрим четырёхугольник <tex>ACBD</tex>. Точки <tex>C</tex> и <tex>D</tex> лежат вне окружности, построенной на <tex>AB</tex> как на хорде, поэтому сумма углов <tex>C</tex> и <tex>D</tex> меньше 180°. Аналогичным образом доказывается, что сумма углов <tex>A</tex> и <tex>B</tex> тоже меньше 180°. Значит, сумма углов четырёхугольника <tex>ACBD</tex> меньше 360°, что невозможно. Противоречие. Значит, ребро <tex>AB</tex> принадлежит триангуляции Делоне.<br />
}}<br />
<br />
== Локальный критерий Делоне ==<br />
{{Определение<br />
|definition='''Локальный критерий Делоне''': для пары треугольников, которым принадлежит это ребро, выполняется критерий Делоне (то есть вершина, противолежащая ребру в одном треугольнике, не лежит в окружности, описанной вокруг другого, и наоборот).<br />
}}<br />
Будем называть '''хорошими''' те рёбра, для которых выполняется локальный критерий Делоне.<br />
{{Лемма<br />
|about=3<br />
|id=fliplemma<br />
|statement=<br />
Из двух рёбер, которые можно провести для пары треугольников, как минимум одно хорошее.<br />
|proof=<br />
[[Файл:Bad edges.png|200px|right]]<br />
Предположим, что это не так, то есть оба ребра (назовём их <tex>AB</tex> и <tex>CD</tex>) плохие. Рассмотрим четырёхугольник <tex>ACBD</tex> и окружность, описанную вокруг треугольника <tex>ABC</tex>. Точка <tex>D</tex> лежит внутри этой окружности, значит, сумма углов <tex>C</tex> и <tex>D</tex> больше 180°. Аналогично доказывается, что сумма углов <tex>A</tex> и <tex>B</tex> больше 180°. Значит, сумма углов четырёхугольника <tex>ACBD</tex> больше 360°, что невозможно.<br />
}}<br />
{{Лемма<br />
|about=4<br />
|statement=<br />
Если для всех рёбер выполняется локальный критерий Делоне, то выполняется и глобальный критерий Делоне.<br />
|proof=<br />
[[Файл:Bad triangle.png|200px|right]]<br />
Пример:Все рёбра треугольника хорошие, но описанная окружность содержит точки.<br />
<br />
Предположим, что это не так, то есть все рёбра хорошие, но существуют треугольники, описанная окружность которых содержат какие-либо точки триангуляции. Возьмём какую-либо конфликтную точку <tex>E</tex>. Рассмотрим такой треугольник <tex>ABC</tex> из тех, в описанную окружность которых попадает <tex>E</tex>, что угол <tex>BEC</tex> максимален, если <tex>BC</tex> — ближайшая к точке <tex>E</tex> сторона. Пусть треугольник <tex>BDC</tex> — смежный с <tex>ABC</tex>.<br />
<br />
Докажем, что точка <tex>E</tex> лежит в окружности, описанной вокруг <tex>BDC</tex>. Предположим, что это не так. Посмотрим на окружность, описанную вокруг треугольника <tex>ABC</tex>: <tex>\angle BAC + \angle BEC > 180^\circ</tex> и <tex>\angle BAC + \angle BDC < 180^\circ</tex>. Если точка <tex>E</tex> не лежит в окружности, описанной вокруг треугольника <tex>BDC</tex>, то <tex>\angle BEC < \angle BDC</tex>, что противоречит предыдущим двум неравенствам.<br />
<br />
Очевидно, что угол <tex>BED</tex> больше, чем угол <tex>BEC</tex>. При этом точка <tex>E</tex> лежит в окружности, описанной вокруг <tex>BDC</tex>. Значит, при выборе треугольника нужно было взять не <tex>ABC</tex>, а <tex>BDC</tex>. Противоречие.<br />
}}<br />
<br />
== Динамическая триангуляция ==<br />
{{Определение<br />
|definition=<br />
Рассмотрим пару смежных треугольников. Рёбра этих треугольников образуют четырёхугольник с проведённой в нём диагональю. Операция замены этой диагонали на другую называется '''flip''' ('''флип''').<br />
}}<br />
[[Файл:Flip.png|200px|thumb|right|Красное ребро — до флипа, синее — после]]<br />
Из [[#fliplemma|леммы 3]] следует, что если ребро плохое, то флип сделает его хорошим.<br />
{{Лемма<br />
|about=5<br />
|statement=Флип плохого ребра уменьшает разность объёмов параболоида и триангуляции, спроецированной на него.<br />
|id=volumelemma<br />
|proof=<br />
Рассмотрим два таких смежных треугольника, что ребро между ними является плохим. Спроецируем их на параболоид. Четыре точки, принадлежащие смежным треугольникам, при проекции на параболоид образуют тетраэдр.<br />
<br />
Проведём через какой-нибудь из двух треугольников плоскость. Вершина, противолежащая основанию тетраэдра, являющегося этим треугольником, лежит ниже этой плоскости (так как не выполняется локальный критерий Делоне), то есть тетраэдр лежит ниже тела, образующегося при проекции всей триангуляции на параболоид.<br />
<br />
После флипа станет выполняться локальный критерий Делоне, то есть тело станет включать в себя тетраэдр. Поэтому после флипа плохого ребра объём тела увеличится на объём этого тетраэдра.<br />
}}<br />
{{Лемма<br />
|about=6<br />
|statement=<br />
Флипами можно достичь хорошей триангуляции за конечное время.<br />
|proof=<br />
Всего триангуляций заданного множества точек конечное число, и среди них есть триангуляция Делоне. Последовательность флипов плохих рёбер триангуляции образует такую последовательность триангуляций, что разность объёмов параболоида и спроецированной на него триангуляции убывает ([[#volumelemma|по лемме 5]]). Эта последовательность конечна (при этом последней в последовательности является триангуляция Делоне), значит, число флипов, требуемых для достижения триангуляции Делоне, тоже конечно.<br />
}}<br />
{{Лемма<br />
|about=7<br />
|statement=<br />
Если в триангуляцию Делоне вставить точку в некоторый треугольник и соединить его вершины с этой точкой, то получившиеся рёбра будут хорошими.<br />
|id=newedgeslemma<br />
|proof=<br />
[[Файл:Good edge.png|200px|thumb|right|Точка V вставлена в треугольник ABC]]<br />
Предположим, точка была вставлена не на ребро. Рассмотрим любое из рёбер — пусть это будет ребро <tex>VC</tex>. Проведём окружность, описывающую треугольник <tex>ABC</tex>. По критерию Делоне в ней не будет никаких точек триангуляции. На ребре <tex>VC</tex> можно построить окружность, изнутри касающуюся окружности, описанной вокруг треугольника. В ней тоже нет никаких точек. Значит, для <tex>VC</tex> выполняется критерий Делоне для рёбер, значит, ребро должно принадлежать триангуляции с добавленной точкой <tex>V</tex>, значит, оно хорошее.<br />
<br />
Случай, когда точка вставляется на ребро, рассматривается аналогично.<br />
}}<br />
=== Вставка точки ===<br />
==== Вставка точки, лежащей внутри триангуляции ====<br />
[[Файл:Insert in triangle.png|200px|thumb|left|Вставка в треугольник]]<br />
[[Файл:Insert on edge.png|200px|thumb|right|Вставка на ребро]]<br />
<br />
Для начала локализуемся: поймём, в каком фейсе лежит точка (или на каком ребре).<br />
<br />
Если точка лежит внутри фейса, добавляем три ребра, сам фейс превращаем в один из новых смежных с вставляемой точкой и добавялем ещё два фейса.<br />
<br />
Если же точка лежит на ребре, два смежных с ребром фейса превращаем в два новых, добавляем ещё два, а так же превращаем ребро, на которое вставляется точка, в ребро, которое заканчивается в этой точке, и вставляем три новых.<br />
<br />
Итого у нас появилось несколько новых рёбер. Они все хорошие (по [[#newedgeslemma|лемме 7]]), плохими могут оказаться только рёбра, противолежащие вставленной точке. Флипаем рёбра, пока триангуляция не станет хорошей.<br />
<br />
==== Вставка точки, лежащей снаружи триангуляции ====<br />
Представим, что вне триангуляции — бесконечные треугольники, основания которых — рёбра выпуклой оболочки триангуляции, а противолежащая ребру вершина — это бесконечно удалённая точка. Тогда понятно, что вставка точки, не лежащей в триангуляции, сведётся к вставке точки внутрь триангуляции, если мы научимся обрабатывать бесконечные фейсы.<br />
<br />
Бесконечно удалённая точка имеет координаты <tex>(0,0,1,0)</tex> (последняя координата — однородная).<br />
<br />
Тогда проверка на то, является ли хорошим ребро, инцидентное бесконечно удалённой точке, упрощается:<br />
<tex><br />
\begin{vmatrix}<br />
a_x & a_y & a_x^2 + a_y^2 & 1 \\<br />
b_x & b_y & b_y^2 + b_y^2 & 1 \\<br />
c_x & c_y & c_x^2 + c_y^2 & 1 \\<br />
0 & 0 & 1 & 0<br />
\end{vmatrix} = \begin{vmatrix}<br />
a_x & a_y & 1 \\<br />
b_x & b_y & 1 \\<br />
c_x & c_y & 1<br />
\end{vmatrix}<br />
</tex>, то есть достаточно проверить поворот трёх остальных точек образованного двумя бесконечными треугольниками четырёхугольника.<br />
<br />
Проверка, принадлежит ли точка бесконечному треугольнику, тоже проста: нужно, чтобы из точки было видно ребро, противолежащее бесконечно удалённой точке, в бесконечном треугольнике. Это проверяется предикатом поворота.<br />
<br />
==== Время работы ====<br />
{{Лемма<br />
|about=8<br />
|statement=<br />
При вставке точки будут флипаться только рёбра, противолежащие вставленной точке.<br />
|proof=<br />
[[Файл:Flip edges.png|400px|thumb|right|V — вставленная точка, ребро AC — плохое]]<br />
Доказательство по индукции.<br />
<br />
База. По [[#newedgeslemma|лемме 7]] изначально не будут флипаться новые рёбра, инцидентные точке, то есть плохими могут оказаться только рёбра, противолежащие точке.<br />
<br />
Переход. Рассмотрим, что произойдёт с противолежащим точке <tex>V</tex> ребром <tex>AC</tex> после флипа, если оно плохое. До вставки точки <tex>V</tex> для триангуляции выполнялся глобальный критерий Делоне, поэтому в окружности, описанной вокруг треугольника <tex>ACD</tex>, не будет лежать никаких точек, кроме точки <tex>V</tex>. Можно построить окружность, касающуюся её изнутри в точке <tex>D</tex> и проходящую через точку <tex>V</tex>. В ней тоже не окажется никаких точек, так как она касается изнутри. Значит, для ребра <tex>VD</tex> выполняется критерий Делоне. Значит, после флипа ребро <tex>AC</tex> уже не будет флипаться. Так как для рёбер <tex>AV</tex> и <tex>CV</tex> выполняется критерий Делоне, то плохими после флипа могут стать только рёбра <tex>AD</tex> и <tex>CD</tex> — то есть рёбра, противолежащие точке <tex>V</tex>.<br />
}}<br />
{{Лемма<br />
|about=9<br />
|statement=<br />
Средняя степень вершины после вставки её в триангуляцию Делоне равна <tex>O(1)</tex>.<br />
|id=deglemma<br />
|proof=<br />
Предположим, что мы вставляем <tex>i+1</tex>-ую точку из последовательности из <tex>n</tex> точек. Рассмотрим все перестановки из этих <tex>i+1</tex> точек, означающие порядок вставки этих точек. Всего таких перестановок <tex>(i+1)!</tex>. Тогда средняя степень последней вершины среди перестановок равна:<br />
<br />
<tex>E(\operatorname{deg}(v_{i+1}))=\frac {\sum_{p=perm(v_1, v_2, ..., v_{i+1})} \operatorname{deg} (p[i+1])} {(i+1)!}</tex><br />
<br />
Каждая из <tex>i+1</tex> вершин побывает последней ровно <tex>i!</tex> раз, поэтому:<br />
<br />
<tex>E(\operatorname{deg} (v_{i+1}))=\frac {\sum_{k=0}^{i} i! \operatorname{deg} (v_k)} {(i+1)!} = \frac {\sum_{k=0}^i \operatorname{deg}(v_k)} {i+1} = \frac {O(i+1)} {i+1} = O(1)</tex><br />
}}<br />
{{Теорема<br />
|statement=<br />
При вставке точки в триангуляцию Делоне в среднем придётся сделать <tex>O(1)</tex> флипов.<br />
|id=flipnumberlemma<br />
|proof=<br />
Все флипнутые рёбра окажутся инцидентными вставленной точке (по лемме 8), а [[#deglemma|степень вершины — <tex>O(1)</tex> (по лемме 9)]]. Поэтому будет сделано <tex>O(1)</tex> флипов.<br />
}}<br />
Так как среднее число флипов — <tex>O(1)</tex>, то время вставки целиком зависит от времени локализации.<br />
<br />
=== Удаление точки ===<br />
==== Алгоритм ====<br />
При удалении точки получится {{Acronym|звёздный многоугольник, который можно затриангулировать за линию|Общеизвестный факт}}. При этом все рёбра, полученные в результате триангуляции звёздного многоугольника, могут оказаться плохими, поэтому необходимо пройтись по ним и пофлипать, если нужно.<br />
==== Время работы ====<br />
{{Acronym|Средняя степень вершины в триангуляции — <tex>O(1)</tex>|Общеизвестный факт}}, поэтому триангуляция звёздного многоугольника будет тоже за <tex>O(1)</tex>. Новых рёбер получится <tex>O(1)</tex>, проверить их на локальный критерий Делоне и пофлипать тоже можно за <tex>O(1)</tex>. Итого удаление точки работает за <tex>O(1)</tex>.<br />
<br />
== Constraints ==<br />
{{Определение<br />
|definition=<br />
'''Констрейнты''' — рёбра, которые нельзя флипать.<br />
}}<br />
{{Утверждение<br />
|statement=<br />
Хорошая триангуляция с констрейнтом может быть хорошей с точностью до видимости через констрейнт.<br />
}}<br />
=== Вставка ===<br />
[[Файл:Constraint.png|400px|thumb|right|Красным выделен вставляемый констрейнт]]<br />
Смотрим на список рёбер, пересечённых ещё не вставленным констрейнтом, и флипаем их. Последнее флипнутое ребро и будет констрейнтом {{Acronym|(по понятным причинам)|Рёбра, пересечённые констрейнтом, после флипа будут начинаться в той же точке, что и констрейнт, а заканчиваться в точке, в которой начинается ещё одно пересекающее ребро. Последнее же ребро будет начинаться и заканчиваться в начале и конце констрейнта}}, после флипа пометим его как констрейнт. Затем флипаем всё, что могло стать плохим (кроме констрейнта), пока триангуляция вновь не станет хорошей.<br />
=== Удаление ===<br />
Аналогично: помечаем ребро как не-констрейнт и флипаем, пока не дойдём до хорошей триангуляции.</div>Kabanovhttp://neerc.ifmo.ru/wiki/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Kabanov&diff=44748Участник:Kabanov2015-01-21T18:40:41Z<p>Kabanov: </p>
<hr />
<div>{{nohate}}<br />
== Определение ==<br />
{{Определение<br />
|definition=<br />
'''Подразбиение Делоне множества точек''' — такое разбиение выпуклой оболочки множества точек на множество выпуклых фигур, что в окружности, описанной вокруг любой из фигур, не находится никаких точек из множества.<br />
}}<br />
{{Определение<br />
|definition=<br />
'''Триангуляция Делоне множества точек''' — триангуляция, являющаяся подразбиением Делоне.<br />
}}<br />
<br />
== Существование триангуляции Делоне ==<br />
{{Лемма<br />
|about=1<br />
|statement=<br />
Окружность, спроецированная на параболоид, находится в одной плоскости. Все точки, лежащие внутри окружности, будут лежать под этой плоскостью. Точки, лежащие вне окружности, будут лежать над плоскостью.<br />
}}<br />
{{Теорема<br />
|statement=<br />
Подразбиение Делоне существует, причём для каждого набора точек оно единственно.<br />
|proof=<br />
Спроецируем все точки на параболоид и построим выпуклую оболочку.<br />
<br />
Все грани выпуклой оболочки окажутся внутри параболоида из-за его выпуклости. При этом точки лежат на параболоиде. Поэтому не найдётся точек, которые будут лежать за гранями выпуклой оболочки. То есть все точки, спроецированные на параболоид, будут принадлежать выпуклой оболочке.<br />
<br />
По лемме 1 очевидно, что внутри окружностей, описанных вокруг проекций граней выпуклой оболочки, не будет лежать никаких точек. Значит, проекции граней — фигуры подразбиения Делоне. Значит, такое подразбиение существует. <br />
<br />
Из единственности выпуклой оболочки следует, что такое подразбиение единственно.<br />
}}<br />
<br />
== Критерий Делоне для рёбер ==<br />
{{Определение<br />
|definition='''Критерий Делоне для ребра''': на ребре можно построить такую окружность, что внутри неё не будет лежать никаких точек.<br />
}}<br />
{{Лемма<br />
|about=2<br />
|statement=Триангуляции Делоне принадлежат те и только те рёбра (с поправкой на точки, лежащие на одной окружности), которые удовлетворяют критерию Делоне.<br />
|proof=<br />
[[Файл:Good edges.png|200px|right]]<br />
То, что для рёбер, принадлежащих триангуляции Делоне, выполняется критерий Делоне для рёбер, очевидно (вокруг каждого ребра можно описать окружность, проходящую через противолежащую ему точку в смежном треугольнике, причём в окружности не будет никаких точек по критерию Делоне).<br />
<br />
Докажем, что если для ребра выполняется критерий Делоне, то оно принадлежит триангуляции Делоне.<br />
<br />
Предположим, что это ребро (назовём его <tex>AB</tex>) не принадлежит триангуляции Делоне. Тогда существует пересекающее его ребро <tex>CD</tex>, принадлежащее триангуляции. Рассмотрим четырёхугольник <tex>ACBD</tex>. Точки <tex>C</tex> и <tex>D</tex> лежат вне окружности, построенной на <tex>AB</tex> как на хорде, поэтому сумма углов <tex>C</tex> и <tex>D</tex> меньше 180°. Аналогичным образом доказывается, что сумма углов <tex>A</tex> и <tex>B</tex> тоже меньше 180°. Значит, сумма углов четырёхугольника <tex>ACBD</tex> меньше 360°, что невозможно. Противоречие. Значит, ребро <tex>AB</tex> принадлежит триангуляции Делоне.<br />
}}<br />
<br />
== Локальный критерий Делоне ==<br />
{{Определение<br />
|definition='''Локальный критерий Делоне''': для пары треугольников, которым принадлежит это ребро, выполняется критерий Делоне (то есть вершина, противолежащая ребру в одном треугольнике, не лежит в окружности, описанной вокруг другого, и наоборот).<br />
}}<br />
Будем называть '''хорошими''' те рёбра, для которых выполняется локальный критерий Делоне.<br />
{{Лемма<br />
|about=3<br />
|id=fliplemma<br />
|statement=<br />
Из двух рёбер, которые можно провести для пары треугольников, как минимум одно хорошее.<br />
|proof=<br />
[[Файл:Bad edges.png|200px|thumb|right|Рёбра AB и BC плохие]]<br />
Предположим, что это не так, то есть оба ребра (назовём их <tex>AB</tex> и <tex>CD</tex>) плохие. Рассмотрим четырёхугольник <tex>ACBD</tex> и окружность, описанную вокруг треугольника <tex>ABC</tex>. Точка <tex>D</tex> лежит внутри этой окружности, значит, сумма углов <tex>C</tex> и <tex>D</tex> больше 180°. Аналогично доказывается, что сумма углов <tex>A</tex> и <tex>B</tex> больше 180°. Значит, сумма углов четырёхугольника <tex>ACBD</tex> больше 360°, что невозможно.<br />
}}<br />
{{Лемма<br />
|about=4<br />
|statement=<br />
Если для всех рёбер выполняется локальный критерий Делоне, то выполняется и глобальный критерий Делоне.<br />
|proof=<br />
[[Файл:Bad triangle.png|200px|thumb|right|Все рёбра треугольника хорошие, но описанная окружность содержит точки]]<br />
Предположим, что это не так, то есть все рёбра хорошие, но существуют треугольники, описанная окружность которых содержат какие-либо точки триангуляции. Возьмём какую-либо конфликтную точку <tex>E</tex>. Рассмотрим такой треугольник <tex>ABC</tex> из тех, в описанную окружность которых попадает <tex>E</tex>, что угол <tex>BEC</tex> максимален, если <tex>BC</tex> — ближайшая к точке <tex>E</tex> сторона. Пусть треугольник <tex>BDC</tex> — смежный с <tex>ABC</tex>.<br />
<br />
Докажем, что точка <tex>E</tex> лежит в окружности, описанной вокруг <tex>BDC</tex>. Предположим, что это не так. Посмотрим на окружность, описанную вокруг треугольника <tex>ABC</tex>: <tex>\angle BAC + \angle BEC > 180^\circ</tex> и <tex>\angle BAC + \angle BDC < 180^\circ</tex>. Если точка <tex>E</tex> не лежит в окружности, описанной вокруг треугольника <tex>BDC</tex>, то <tex>\angle BEC < \angle BDC</tex>, что противоречит предыдущим двум неравенствам.<br />
<br />
Очевидно, что угол <tex>BED</tex> больше, чем угол <tex>BEC</tex>. При этом точка <tex>E</tex> лежит в окружности, описанной вокруг <tex>BDC</tex>. Значит, при выборе треугольника нужно было взять не <tex>ABC</tex>, а <tex>BDC</tex>. Противоречие.<br />
}}<br />
<br />
== Динамическая триангуляция ==<br />
{{Определение<br />
|definition=<br />
Рассмотрим пару смежных треугольников. Рёбра этих треугольников образуют четырёхугольник с проведённой в нём диагональю. Операция замены этой диагонали на другую называется '''flip''' ('''флип''').<br />
}}<br />
[[Файл:Flip.png|200px|thumb|right|Красное ребро — до флипа, синее — после]]<br />
Из [[#fliplemma|леммы 3]] следует, что если ребро плохое, то флип сделает его хорошим.<br />
{{Лемма<br />
|about=5<br />
|statement=Флип плохого ребра уменьшает разность объёмов параболоида и триангуляции, спроецированной на него.<br />
|id=volumelemma<br />
|proof=<br />
Рассмотрим два таких смежных треугольника, что ребро между ними является плохим. Спроецируем их на параболоид. Четыре точки, принадлежащие смежным треугольникам, при проекции на параболоид образуют тетраэдр.<br />
<br />
Проведём через какой-нибудь из двух треугольников плоскость. Вершина, противолежащая основанию тетраэдра, являющегося этим треугольником, лежит ниже этой плоскости (так как не выполняется локальный критерий Делоне), то есть тетраэдр лежит ниже тела, образующегося при проекции всей триангуляции на параболоид.<br />
<br />
После флипа станет выполняться локальный критерий Делоне, то есть тело станет включать в себя тетраэдр. Поэтому после флипа плохого ребра объём тела увеличится на объём этого тетраэдра.<br />
}}<br />
{{Лемма<br />
|about=6<br />
|statement=<br />
Флипами можно достичь хорошей триангуляции за конечное время.<br />
|proof=<br />
Всего триангуляций заданного множества точек конечное число, и среди них есть триангуляция Делоне. Последовательность флипов плохих рёбер триангуляции образует такую последовательность триангуляций, что разность объёмов параболоида и спроецированной на него триангуляции убывает ([[#volumelemma|по лемме 5]]). Эта последовательность конечна (при этом последней в последовательности является триангуляция Делоне), значит, число флипов, требуемых для достижения триангуляции Делоне, тоже конечно.<br />
}}<br />
{{Лемма<br />
|about=7<br />
|statement=<br />
Если в триангуляцию Делоне вставить точку в некоторый треугольник и соединить его вершины с этой точкой, то получившиеся рёбра будут хорошими.<br />
|id=newedgeslemma<br />
|proof=<br />
[[Файл:Good edge.png|200px|thumb|right|Точка V вставлена в треугольник ABC]]<br />
Предположим, точка была вставлена не на ребро. Рассмотрим любое из рёбер — пусть это будет ребро <tex>VC</tex>. Проведём окружность, описывающую треугольник <tex>ABC</tex>. По критерию Делоне в ней не будет никаких точек триангуляции. На ребре <tex>VC</tex> можно построить окружность, изнутри касающуюся окружности, описанной вокруг треугольника. В ней тоже нет никаких точек. Значит, для <tex>VC</tex> выполняется критерий Делоне для рёбер, значит, ребро должно принадлежать триангуляции с добавленной точкой <tex>V</tex>, значит, оно хорошее.<br />
<br />
Случай, когда точка вставляется на ребро, рассматривается аналогично.<br />
}}<br />
=== Вставка точки ===<br />
==== Вставка точки, лежащей внутри триангуляции ====<br />
[[Файл:Insert in triangle.png|200px|thumb|left|Вставка в треугольник]]<br />
[[Файл:Insert on edge.png|200px|thumb|right|Вставка на ребро]]<br />
<br />
Для начала локализуемся: поймём, в каком фейсе лежит точка (или на каком ребре).<br />
<br />
Если точка лежит внутри фейса, добавляем три ребра, сам фейс превращаем в один из новых смежных с вставляемой точкой и добавялем ещё два фейса.<br />
<br />
Если же точка лежит на ребре, два смежных с ребром фейса превращаем в два новых, добавляем ещё два, а так же превращаем ребро, на которое вставляется точка, в ребро, которое заканчивается в этой точке, и вставляем три новых.<br />
<br />
Итого у нас появилось несколько новых рёбер. Они все хорошие (по [[#newedgeslemma|лемме 7]]), плохими могут оказаться только рёбра, противолежащие вставленной точке. Флипаем рёбра, пока триангуляция не станет хорошей.<br />
<br />
==== Вставка точки, лежащей снаружи триангуляции ====<br />
Представим, что вне триангуляции — бесконечные треугольники, основания которых — рёбра выпуклой оболочки триангуляции, а противолежащая ребру вершина — это бесконечно удалённая точка. Тогда понятно, что вставка точки, не лежащей в триангуляции, сведётся к вставке точки внутрь триангуляции, если мы научимся обрабатывать бесконечные фейсы.<br />
<br />
Бесконечно удалённая точка имеет координаты <tex>(0,0,1,0)</tex> (последняя координата — однородная).<br />
<br />
Тогда проверка на то, является ли хорошим ребро, инцидентное бесконечно удалённой точке, упрощается:<br />
<tex><br />
\begin{vmatrix}<br />
a_x & a_y & a_x^2 + a_y^2 & 1 \\<br />
b_x & b_y & b_y^2 + b_y^2 & 1 \\<br />
c_x & c_y & c_x^2 + c_y^2 & 1 \\<br />
0 & 0 & 1 & 0<br />
\end{vmatrix} = \begin{vmatrix}<br />
a_x & a_y & 1 \\<br />
b_x & b_y & 1 \\<br />
c_x & c_y & 1<br />
\end{vmatrix}<br />
</tex>, то есть достаточно проверить поворот трёх остальных точек образованного двумя бесконечными треугольниками четырёхугольника.<br />
<br />
Проверка, принадлежит ли точка бесконечному треугольнику, тоже проста: нужно, чтобы из точки было видно ребро, противолежащее бесконечно удалённой точке, в бесконечном треугольнике. Это проверяется предикатом поворота.<br />
<br />
==== Время работы ====<br />
{{Лемма<br />
|about=8<br />
|statement=<br />
При вставке точки будут флипаться только рёбра, противолежащие вставленной точке.<br />
|proof=<br />
[[Файл:Flip edges.png|400px|thumb|right|V — вставленная точка, ребро AC — плохое]]<br />
Доказательство по индукции.<br />
<br />
База. По [[#newedgeslemma|лемме 7]] изначально не будут флипаться новые рёбра, инцидентные точке, то есть плохими могут оказаться только рёбра, противолежащие точке.<br />
<br />
Переход. Рассмотрим, что произойдёт с противолежащим точке <tex>V</tex> ребром <tex>AC</tex> после флипа, если оно плохое. До вставки точки <tex>V</tex> для триангуляции выполнялся глобальный критерий Делоне, поэтому в окружности, описанной вокруг треугольника <tex>ACD</tex>, не будет лежать никаких точек, кроме точки <tex>V</tex>. Можно построить окружность, касающуюся её изнутри в точке <tex>D</tex> и проходящую через точку <tex>V</tex>. В ней тоже не окажется никаких точек, так как она касается изнутри. Значит, для ребра <tex>VD</tex> выполняется критерий Делоне. Значит, после флипа ребро <tex>AC</tex> уже не будет флипаться. Так как для рёбер <tex>AV</tex> и <tex>CV</tex> выполняется критерий Делоне, то плохими после флипа могут стать только рёбра <tex>AD</tex> и <tex>CD</tex> — то есть рёбра, противолежащие точке <tex>V</tex>.<br />
}}<br />
{{Лемма<br />
|about=9<br />
|statement=<br />
Средняя степень вершины после вставки её в триангуляцию Делоне равна <tex>O(1)</tex>.<br />
|id=deglemma<br />
|proof=<br />
Предположим, что мы вставляем <tex>i+1</tex>-ую точку из последовательности из <tex>n</tex> точек. Рассмотрим все перестановки из этих <tex>i+1</tex> точек, означающие порядок вставки этих точек. Всего таких перестановок <tex>(i+1)!</tex>. Тогда средняя степень последней вершины среди перестановок равна:<br />
<br />
<tex>E(\operatorname{deg}(v_{i+1}))=\frac {\sum_{p=perm(v_1, v_2, ..., v_{i+1})} \operatorname{deg} (p[i+1])} {(i+1)!}</tex><br />
<br />
Каждая из <tex>i+1</tex> вершин побывает последней ровно <tex>i!</tex> раз, поэтому:<br />
<br />
<tex>E(\operatorname{deg} (v_{i+1}))=\frac {\sum_{k=0}^{i} i! \operatorname{deg} (v_k)} {(i+1)!} = \frac {\sum_{k=0}^i \operatorname{deg}(v_k)} {i+1} = \frac {O(i+1)} {i+1} = O(1)</tex><br />
}}<br />
{{Теорема<br />
|statement=<br />
При вставке точки в триангуляцию Делоне в среднем придётся сделать <tex>O(1)</tex> флипов.<br />
|id=flipnumberlemma<br />
|proof=<br />
Все флипнутые рёбра окажутся инцидентными вставленной точке (по лемме 8), а [[#deglemma|степень вершины — <tex>O(1)</tex> (по лемме 9)]]. Поэтому будет сделано <tex>O(1)</tex> флипов.<br />
}}<br />
Так как среднее число флипов — <tex>O(1)</tex>, то время вставки целиком зависит от времени локализации.<br />
<br />
=== Удаление точки ===<br />
==== Алгоритм ====<br />
При удалении точки получится {{Acronym|звёздный многоугольник, который можно затриангулировать за линию|Общеизвестный факт}}. При этом все рёбра, полученные в результате триангуляции звёздного многоугольника, могут оказаться плохими, поэтому необходимо пройтись по ним и пофлипать, если нужно.<br />
==== Время работы ====<br />
{{Acronym|Средняя степень вершины в триангуляции — <tex>O(1)</tex>|Общеизвестный факт}}, поэтому триангуляция звёздного многоугольника будет тоже за <tex>O(1)</tex>. Новых рёбер получится <tex>O(1)</tex>, проверить их на локальный критерий Делоне и пофлипать тоже можно за <tex>O(1)</tex>. Итого удаление точки работает за <tex>O(1)</tex>.<br />
<br />
== Constraints ==<br />
{{Определение<br />
|definition=<br />
'''Констрейнты''' — рёбра, которые нельзя флипать.<br />
}}<br />
{{Утверждение<br />
|statement=<br />
Хорошая триангуляция с констрейнтом может быть хорошей с точностью до видимости через констрейнт.<br />
}}<br />
=== Вставка ===<br />
[[Файл:Constraint.png|400px|thumb|right|Красным выделен вставляемый констрейнт]]<br />
Смотрим на список рёбер, пересечённых ещё не вставленным констрейнтом, и флипаем их. Последнее флипнутое ребро и будет констрейнтом {{Acronym|(по понятным причинам)|Рёбра, пересечённые констрейнтом, после флипа будут начинаться в той же точке, что и констрейнт, а заканчиваться в точке, в которой начинается ещё одно пересекающее ребро. Последнее же ребро будет начинаться и заканчиваться в начале и конце констрейнта}}, после флипа пометим его как констрейнт. Затем флипаем всё, что могло стать плохим (кроме констрейнта), пока триангуляция вновь не станет хорошей.<br />
=== Удаление ===<br />
Аналогично: помечаем ребро как не-констрейнт и флипаем, пока не дойдём до хорошей триангуляции.</div>Kabanovhttp://neerc.ifmo.ru/wiki/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Kabanov&diff=44747Участник:Kabanov2015-01-21T18:39:21Z<p>Kabanov: Делоне</p>
<hr />
<div>{{nohate}}<br />
== Определение ==<br />
{{Определение<br />
|definition=<br />
'''Подразбиение Делоне множества точек''' — такое разбиение выпуклой оболочки множества точек на множество выпуклых фигур, что в окружности, описанной вокруг любой из фигур, не находится никаких точек из множества.<br />
}}<br />
{{Определение<br />
|definition=<br />
'''Триангуляция Делоне множества точек''' — триангуляция, являющаяся подразбиением Делоне.<br />
}}<br />
<br />
== Существование триангуляции Делоне ==<br />
{{Лемма<br />
|about=1<br />
|statement=<br />
Окружность, спроецированная на параболоид, находится в одной плоскости. Все точки, лежащие внутри окружности, будут лежать под этой плоскостью. Точки, лежащие вне окружности, будут лежать над плоскостью.<br />
}}<br />
{{Теорема<br />
|statement=<br />
Подразбиение Делоне существует, причём для каждого набора точек оно единственно.<br />
|proof=<br />
Спроецируем все точки на параболоид и построим выпуклую оболочку.<br />
<br />
Все грани выпуклой оболочки окажутся внутри параболоида из-за его выпуклости. При этом точки лежат на параболоиде. Поэтому не найдётся точек, которые будут лежать за гранями выпуклой оболочки. То есть все точки, спроецированные на параболоид, будут принадлежать выпуклой оболочке.<br />
<br />
По лемме 1 очевидно, что внутри окружностей, описанных вокруг проекций граней выпуклой оболочки, не будет лежать никаких точек. Значит, проекции граней — фигуры подразбиения Делоне. Значит, такое подразбиение существует. <br />
<br />
Из единственности выпуклой оболочки следует, что такое подразбиение единственно.<br />
}}<br />
<br />
== Критерий Делоне для рёбер ==<br />
{{Определение<br />
|definition='''Критерий Делоне для ребра''': на ребре можно построить такую окружность, что внутри неё не будет лежать никаких точек.<br />
}}<br />
{{Лемма<br />
|about=2<br />
|statement=Триангуляции Делоне принадлежат те и только те рёбра (с поправкой на точки, лежащие на одной окружности), которые удовлетворяют критерию Делоне.<br />
|proof=<br />
[[Файл:Good edges.png|400px|thumb|right|Для рёбер AB и CD выполняется критерий Делоне, на них построены окружности]]<br />
То, что для рёбер, принадлежащих триангуляции Делоне, выполняется критерий Делоне для рёбер, очевидно (вокруг каждого ребра можно описать окружность, проходящую через противолежащую ему точку в смежном треугольнике, причём в окружности не будет никаких точек по критерию Делоне).<br />
<br />
Докажем, что если для ребра выполняется критерий Делоне, то оно принадлежит триангуляции Делоне.<br />
<br />
Предположим, что это ребро (назовём его <tex>AB</tex>) не принадлежит триангуляции Делоне. Тогда существует пересекающее его ребро <tex>CD</tex>, принадлежащее триангуляции. Рассмотрим четырёхугольник <tex>ACBD</tex>. Точки <tex>C</tex> и <tex>D</tex> лежат вне окружности, построенной на <tex>AB</tex> как на хорде, поэтому сумма углов <tex>C</tex> и <tex>D</tex> меньше 180°. Аналогичным образом доказывается, что сумма углов <tex>A</tex> и <tex>B</tex> тоже меньше 180°. Значит, сумма углов четырёхугольника <tex>ACBD</tex> меньше 360°, что невозможно. Противоречие. Значит, ребро <tex>AB</tex> принадлежит триангуляции Делоне.<br />
}}<br />
<br />
== Локальный критерий Делоне ==<br />
{{Определение<br />
|definition='''Локальный критерий Делоне''': для пары треугольников, которым принадлежит это ребро, выполняется критерий Делоне (то есть вершина, противолежащая ребру в одном треугольнике, не лежит в окружности, описанной вокруг другого, и наоборот).<br />
}}<br />
Будем называть '''хорошими''' те рёбра, для которых выполняется локальный критерий Делоне.<br />
{{Лемма<br />
|about=3<br />
|id=fliplemma<br />
|statement=<br />
Из двух рёбер, которые можно провести для пары треугольников, как минимум одно хорошее.<br />
|proof=<br />
[[Файл:Bad edges.png|400px|thumb|right|Рёбра AB и BC плохие]]<br />
Предположим, что это не так, то есть оба ребра (назовём их <tex>AB</tex> и <tex>CD</tex>) плохие. Рассмотрим четырёхугольник <tex>ACBD</tex> и окружность, описанную вокруг треугольника <tex>ABC</tex>. Точка <tex>D</tex> лежит внутри этой окружности, значит, сумма углов <tex>C</tex> и <tex>D</tex> больше 180°. Аналогично доказывается, что сумма углов <tex>A</tex> и <tex>B</tex> больше 180°. Значит, сумма углов четырёхугольника <tex>ACBD</tex> больше 360°, что невозможно.<br />
}}<br />
{{Лемма<br />
|about=4<br />
|statement=<br />
Если для всех рёбер выполняется локальный критерий Делоне, то выполняется и глобальный критерий Делоне.<br />
|proof=<br />
[[Файл:Bad triangle.png|400px|thumb|right|Все рёбра треугольника хорошие, но описанная окружность содержит точки]]<br />
Предположим, что это не так, то есть все рёбра хорошие, но существуют треугольники, описанная окружность которых содержат какие-либо точки триангуляции. Возьмём какую-либо конфликтную точку <tex>E</tex>. Рассмотрим такой треугольник <tex>ABC</tex> из тех, в описанную окружность которых попадает <tex>E</tex>, что угол <tex>BEC</tex> максимален, если <tex>BC</tex> — ближайшая к точке <tex>E</tex> сторона. Пусть треугольник <tex>BDC</tex> — смежный с <tex>ABC</tex>.<br />
<br />
Докажем, что точка <tex>E</tex> лежит в окружности, описанной вокруг <tex>BDC</tex>. Предположим, что это не так. Посмотрим на окружность, описанную вокруг треугольника <tex>ABC</tex>: <tex>\angle BAC + \angle BEC > 180^\circ</tex> и <tex>\angle BAC + \angle BDC < 180^\circ</tex>. Если точка <tex>E</tex> не лежит в окружности, описанной вокруг треугольника <tex>BDC</tex>, то <tex>\angle BEC < \angle BDC</tex>, что противоречит предыдущим двум неравенствам.<br />
<br />
Очевидно, что угол <tex>BED</tex> больше, чем угол <tex>BEC</tex>. При этом точка <tex>E</tex> лежит в окружности, описанной вокруг <tex>BDC</tex>. Значит, при выборе треугольника нужно было взять не <tex>ABC</tex>, а <tex>BDC</tex>. Противоречие.<br />
}}<br />
<br />
== Динамическая триангуляция ==<br />
{{Определение<br />
|definition=<br />
Рассмотрим пару смежных треугольников. Рёбра этих треугольников образуют четырёхугольник с проведённой в нём диагональю. Операция замены этой диагонали на другую называется '''flip''' ('''флип''').<br />
}}<br />
[[Файл:Flip.png|400px|thumb|right|Красное ребро — до флипа, синее — после]]<br />
Из [[#fliplemma|леммы 3]] следует, что если ребро плохое, то флип сделает его хорошим.<br />
{{Лемма<br />
|about=5<br />
|statement=Флип плохого ребра уменьшает разность объёмов параболоида и триангуляции, спроецированной на него.<br />
|id=volumelemma<br />
|proof=<br />
Рассмотрим два таких смежных треугольника, что ребро между ними является плохим. Спроецируем их на параболоид. Четыре точки, принадлежащие смежным треугольникам, при проекции на параболоид образуют тетраэдр.<br />
<br />
Проведём через какой-нибудь из двух треугольников плоскость. Вершина, противолежащая основанию тетраэдра, являющегося этим треугольником, лежит ниже этой плоскости (так как не выполняется локальный критерий Делоне), то есть тетраэдр лежит ниже тела, образующегося при проекции всей триангуляции на параболоид.<br />
<br />
После флипа станет выполняться локальный критерий Делоне, то есть тело станет включать в себя тетраэдр. Поэтому после флипа плохого ребра объём тела увеличится на объём этого тетраэдра.<br />
}}<br />
{{Лемма<br />
|about=6<br />
|statement=<br />
Флипами можно достичь хорошей триангуляции за конечное время.<br />
|proof=<br />
Всего триангуляций заданного множества точек конечное число, и среди них есть триангуляция Делоне. Последовательность флипов плохих рёбер триангуляции образует такую последовательность триангуляций, что разность объёмов параболоида и спроецированной на него триангуляции убывает ([[#volumelemma|по лемме 5]]). Эта последовательность конечна (при этом последней в последовательности является триангуляция Делоне), значит, число флипов, требуемых для достижения триангуляции Делоне, тоже конечно.<br />
}}<br />
{{Лемма<br />
|about=7<br />
|statement=<br />
Если в триангуляцию Делоне вставить точку в некоторый треугольник и соединить его вершины с этой точкой, то получившиеся рёбра будут хорошими.<br />
|id=newedgeslemma<br />
|proof=<br />
[[Файл:Good edge.png|400px|thumb|right|Точка V вставлена в треугольник ABC]]<br />
Предположим, точка была вставлена не на ребро. Рассмотрим любое из рёбер — пусть это будет ребро <tex>VC</tex>. Проведём окружность, описывающую треугольник <tex>ABC</tex>. По критерию Делоне в ней не будет никаких точек триангуляции. На ребре <tex>VC</tex> можно построить окружность, изнутри касающуюся окружности, описанной вокруг треугольника. В ней тоже нет никаких точек. Значит, для <tex>VC</tex> выполняется критерий Делоне для рёбер, значит, ребро должно принадлежать триангуляции с добавленной точкой <tex>V</tex>, значит, оно хорошее.<br />
<br />
Случай, когда точка вставляется на ребро, рассматривается аналогично.<br />
}}<br />
=== Вставка точки ===<br />
==== Вставка точки, лежащей внутри триангуляции ====<br />
[[Файл:Insert in triangle.png|200px|thumb|left|Вставка в треугольник]]<br />
[[Файл:Insert on edge.png|200px|thumb|right|Вставка на ребро]]<br />
<br />
Для начала локализуемся: поймём, в каком фейсе лежит точка (или на каком ребре).<br />
<br />
Если точка лежит внутри фейса, добавляем три ребра, сам фейс превращаем в один из новых смежных с вставляемой точкой и добавялем ещё два фейса.<br />
<br />
Если же точка лежит на ребре, два смежных с ребром фейса превращаем в два новых, добавляем ещё два, а так же превращаем ребро, на которое вставляется точка, в ребро, которое заканчивается в этой точке, и вставляем три новых.<br />
<br />
Итого у нас появилось несколько новых рёбер. Они все хорошие (по [[#newedgeslemma|лемме 7]]), плохими могут оказаться только рёбра, противолежащие вставленной точке. Флипаем рёбра, пока триангуляция не станет хорошей.<br />
<br />
==== Вставка точки, лежащей снаружи триангуляции ====<br />
Представим, что вне триангуляции — бесконечные треугольники, основания которых — рёбра выпуклой оболочки триангуляции, а противолежащая ребру вершина — это бесконечно удалённая точка. Тогда понятно, что вставка точки, не лежащей в триангуляции, сведётся к вставке точки внутрь триангуляции, если мы научимся обрабатывать бесконечные фейсы.<br />
<br />
Бесконечно удалённая точка имеет координаты <tex>(0,0,1,0)</tex> (последняя координата — однородная).<br />
<br />
Тогда проверка на то, является ли хорошим ребро, инцидентное бесконечно удалённой точке, упрощается:<br />
<tex><br />
\begin{vmatrix}<br />
a_x & a_y & a_x^2 + a_y^2 & 1 \\<br />
b_x & b_y & b_y^2 + b_y^2 & 1 \\<br />
c_x & c_y & c_x^2 + c_y^2 & 1 \\<br />
0 & 0 & 1 & 0<br />
\end{vmatrix} = \begin{vmatrix}<br />
a_x & a_y & 1 \\<br />
b_x & b_y & 1 \\<br />
c_x & c_y & 1<br />
\end{vmatrix}<br />
</tex>, то есть достаточно проверить поворот трёх остальных точек образованного двумя бесконечными треугольниками четырёхугольника.<br />
<br />
Проверка, принадлежит ли точка бесконечному треугольнику, тоже проста: нужно, чтобы из точки было видно ребро, противолежащее бесконечно удалённой точке, в бесконечном треугольнике. Это проверяется предикатом поворота.<br />
<br />
==== Время работы ====<br />
{{Лемма<br />
|about=8<br />
|statement=<br />
При вставке точки будут флипаться только рёбра, противолежащие вставленной точке.<br />
|proof=<br />
[[Файл:Flip edges.png|400px|thumb|right|V — вставленная точка, ребро AC — плохое]]<br />
Доказательство по индукции.<br />
<br />
База. По [[#newedgeslemma|лемме 7]] изначально не будут флипаться новые рёбра, инцидентные точке, то есть плохими могут оказаться только рёбра, противолежащие точке.<br />
<br />
Переход. Рассмотрим, что произойдёт с противолежащим точке <tex>V</tex> ребром <tex>AC</tex> после флипа, если оно плохое. До вставки точки <tex>V</tex> для триангуляции выполнялся глобальный критерий Делоне, поэтому в окружности, описанной вокруг треугольника <tex>ACD</tex>, не будет лежать никаких точек, кроме точки <tex>V</tex>. Можно построить окружность, касающуюся её изнутри в точке <tex>D</tex> и проходящую через точку <tex>V</tex>. В ней тоже не окажется никаких точек, так как она касается изнутри. Значит, для ребра <tex>VD</tex> выполняется критерий Делоне. Значит, после флипа ребро <tex>AC</tex> уже не будет флипаться. Так как для рёбер <tex>AV</tex> и <tex>CV</tex> выполняется критерий Делоне, то плохими после флипа могут стать только рёбра <tex>AD</tex> и <tex>CD</tex> — то есть рёбра, противолежащие точке <tex>V</tex>.<br />
}}<br />
{{Лемма<br />
|about=9<br />
|statement=<br />
Средняя степень вершины после вставки её в триангуляцию Делоне равна <tex>O(1)</tex>.<br />
|id=deglemma<br />
|proof=<br />
Предположим, что мы вставляем <tex>i+1</tex>-ую точку из последовательности из <tex>n</tex> точек. Рассмотрим все перестановки из этих <tex>i+1</tex> точек, означающие порядок вставки этих точек. Всего таких перестановок <tex>(i+1)!</tex>. Тогда средняя степень последней вершины среди перестановок равна:<br />
<br />
<tex>E(\operatorname{deg}(v_{i+1}))=\frac {\sum_{p=perm(v_1, v_2, ..., v_{i+1})} \operatorname{deg} (p[i+1])} {(i+1)!}</tex><br />
<br />
Каждая из <tex>i+1</tex> вершин побывает последней ровно <tex>i!</tex> раз, поэтому:<br />
<br />
<tex>E(\operatorname{deg} (v_{i+1}))=\frac {\sum_{k=0}^{i} i! \operatorname{deg} (v_k)} {(i+1)!} = \frac {\sum_{k=0}^i \operatorname{deg}(v_k)} {i+1} = \frac {O(i+1)} {i+1} = O(1)</tex><br />
}}<br />
{{Теорема<br />
|statement=<br />
При вставке точки в триангуляцию Делоне в среднем придётся сделать <tex>O(1)</tex> флипов.<br />
|id=flipnumberlemma<br />
|proof=<br />
Все флипнутые рёбра окажутся инцидентными вставленной точке (по лемме 8), а [[#deglemma|степень вершины — <tex>O(1)</tex> (по лемме 9)]]. Поэтому будет сделано <tex>O(1)</tex> флипов.<br />
}}<br />
Так как среднее число флипов — <tex>O(1)</tex>, то время вставки целиком зависит от времени локализации.<br />
<br />
=== Удаление точки ===<br />
==== Алгоритм ====<br />
При удалении точки получится {{Acronym|звёздный многоугольник, который можно затриангулировать за линию|Общеизвестный факт}}. При этом все рёбра, полученные в результате триангуляции звёздного многоугольника, могут оказаться плохими, поэтому необходимо пройтись по ним и пофлипать, если нужно.<br />
==== Время работы ====<br />
{{Acronym|Средняя степень вершины в триангуляции — <tex>O(1)</tex>|Общеизвестный факт}}, поэтому триангуляция звёздного многоугольника будет тоже за <tex>O(1)</tex>. Новых рёбер получится <tex>O(1)</tex>, проверить их на локальный критерий Делоне и пофлипать тоже можно за <tex>O(1)</tex>. Итого удаление точки работает за <tex>O(1)</tex>.<br />
<br />
== Constraints ==<br />
{{Определение<br />
|definition=<br />
'''Констрейнты''' — рёбра, которые нельзя флипать.<br />
}}<br />
{{Утверждение<br />
|statement=<br />
Хорошая триангуляция с констрейнтом может быть хорошей с точностью до видимости через констрейнт.<br />
}}<br />
=== Вставка ===<br />
[[Файл:Constraint.png|400px|thumb|right|Красным выделен вставляемый констрейнт]]<br />
Смотрим на список рёбер, пересечённых ещё не вставленным констрейнтом, и флипаем их. Последнее флипнутое ребро и будет констрейнтом {{Acronym|(по понятным причинам)|Рёбра, пересечённые констрейнтом, после флипа будут начинаться в той же точке, что и констрейнт, а заканчиваться в точке, в которой начинается ещё одно пересекающее ребро. Последнее же ребро будет начинаться и заканчиваться в начале и конце констрейнта}}, после флипа пометим его как констрейнт. Затем флипаем всё, что могло стать плохим (кроме констрейнта), пока триангуляция вновь не станет хорошей.<br />
=== Удаление ===<br />
Аналогично: помечаем ребро как не-констрейнт и флипаем, пока не дойдём до хорошей триангуляции.</div>Kabanovhttp://neerc.ifmo.ru/wiki/index.php?title=%D0%97%D0%B0%D0%B3%D0%BB%D0%B0%D0%B2%D0%BD%D0%B0%D1%8F_%D1%81%D1%82%D1%80%D0%B0%D0%BD%D0%B8%D1%86%D0%B0&diff=44746Заглавная страница2015-01-21T16:12:43Z<p>Kabanov: /* Преподаватель Ковалев Антон */</p>
<hr />
<div>Добро пожаловать на сайт [[Вики-конспекты|вики-конспектов]]!<br />
<br />
= Проверяемые конспекты =<br />
<br />
==Преподаватель [[Станкевич Андрей Сергеевич]]==<br />
<br />
* [[Дискретная математика, алгоритмы и структуры данных|Дискретная математика, алгоритмы и структуры данных — 1, 2, 3 и 4 семестры]]<br />
* [[Теория формальных языков|Теория формальных языков — 5 семестр]]<br />
* [[Теория сложности|Теория сложности — 6 семестр]]<br />
* [[Методы трансляции|Методы трансляции — 6 семестр]]<br />
<br />
==Преподаватель [[Царев Федор Николаевич]]==<br />
* [[Эволюционные алгоритмы|Эволюционные алгоритмы — 10 семестр]]<br />
<br />
==Преподаватель [[Корнеев Георгий Александрович]]==<br />
* [[Язык программирования Java|Язык программирования Java — 2 семестр]]<br />
<br />
==Преподаватель [[Ковалев Антон Сергеевич]]==<br />
* [[Вычислительная геометрия|Вычислительная геометрия — 4 и 5 семестры]]<br />
<br />
= Непроверяемые конспекты =<br />
<br />
*[[Алгебра и геометрия 1 курс | Алгебра и геометрия — 1, 2 семестр]]<br />
*[[Математический анализ 1 курс | Математический анализ — 1, 2 семестр]]<br />
*[[Математический анализ 2 курс | Математический анализ — 3, 4 семестр]]<br />
*[[Математическая логика|Математическая логика — 3 семестр]]<br />
*[[Участник:Qwerty787788/плюсы3сем | С++ — 3 семестр]]<br />
*[[Assembler|Assembler — 4 семестр]]<br />
*[[Алгоритмы алгебры и теории чисел|Алгоритмы алгебры и теории чисел — 4 семестр]]<br />
*[[Функциональный_анализ_3_курс | Функциональный анализ — 5, 6 семестр]]<br />
*[[Параллельное программирование|Параллельное программирование — 6 семестр]]</div>Kabanovhttp://neerc.ifmo.ru/wiki/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Kabanov&diff=44745Участник:Kabanov2015-01-21T11:07:33Z<p>Kabanov: Kd-tree</p>
<hr />
<div>[[Файл:Kd-tree.png | 400px | right]]<br />
<br />
'''K-d дерево''' (short for k-dimensional tree) {{---}} статическая структура данных для хранения точек в <tex>k</tex>-мерном пространстве. Позволяет отвечать на запрос, какие точки лежат в данном прямоугольнике.<br />
Строится это дерево следующим образом: разобьём все точки вертикальной прямой так, чтобы слева (нестрого) и справа (строго) от неё было примерно поровну точек (для этого посчитаем медиану первых координат). Получим подмножества для левого и правого ребёнка. Далее построим для этих подмножеств деревья, но разбивать будем уже не вертикальной, а горизонтальной прямой (для этого посчитаем медиану вторых координат). И так далее (будем считать, что <tex>k = 2</tex> (случай бОльших размерностей обрабатывается аналогично), поэтому на следующем уровне вновь будем разбивать вертикальными прямыми).<br />
<br />
''Замечание: проблемы могут возникнуть, если много точек имеют одинаковую координату, тогда разбить примерно поровну не получится (почти все точки будут лежать на медиане и попадут в левую часть). Лучший способ борьбы с этим {{---}} не вспоминать о данной проблеме совсем. Но вообще с этим борются, используя composite numbers, то есть сравнивая ещё и по другой (другим) координате.''<br />
<br />
Реализовывать построение можно рекурсивно с помощью функции <tex>BuildKdTree(P, Depth)</tex>, принимающей множество точек и глубину. В зависимости от остатка при делении на размерность (при <tex> k = 2 </tex> от чётности размерности) сплитим множество на два подмножества и делаем рекурсивные вызовы. Для лучшего понимания приведём псевдокод:<br />
<code><br />
BuildKdTree(P, Depth)<br />
//Input. A set of points P and the current depth Depth.<br />
//Output. The root of a kd-tree storing P.<br />
if P contains only one point<br />
return a leaf storing this point<br />
else if depth is even<br />
Split P into two subsets <tex>P_1</tex> and <tex>P_2</tex> with a vertical line <tex>l</tex> through the median x-coordinate of the points in P<br />
else <br />
Split P into two subsets <tex>P_1</tex> and <tex>P_2</tex> with a horizontal line <tex>l</tex> through the median y-coordinate of the points in P. <br />
<tex>V_{left}</tex> <- BuildKdTree(<tex>P_1</tex>, Depth + 1)<br />
<tex>V_{right}</tex> <- BuildKdTree(<tex>P_2</tex>, Depth + 1)<br />
Create a node v storing <tex>l</tex>, make <tex>V_{left}</tex> the left child of v, and make <tex>V_{right}</tex> the right child of v.<br />
return v <br />
</code><br />
<br />
{{Лемма<br />
|about=<br />
О времени построения<br />
|statement=<br />
Построение выполняется за <tex>O(n \log n)</tex>.<br />
|proof=<br />
Время построения обозначим <tex>T(n)</tex>. Поиск медианы можно сделать за линейное время, поэтому достаточно очевидно, что:<br />
<br />
<tex>T(n) = O(1)</tex> if <tex>n = 1</tex>.<br />
<br />
<tex>T(n) = O(n) + 2 \cdot T(n / 2)</tex>, otherwise.<br />
<br />
Решением этого является <tex>T(n) = O(n \log n)</tex>.<br />
<br />
Также стоит отметить, что можно и не искать медиану за линейное время, а просто посортить все точки в самом начале и дальше использовать это. В реализации попроще, асимптотика та же.<br />
}}<br />
<br />
{{Лемма<br />
|about=<br />
О занимаемой памяти<br />
|statement=<br />
K-d дерево требует <tex>O(n)</tex> памяти.<br />
|proof=<br />
Высота дерева, очевидно, логарифмическая, а листьев всего <tex>O(n)</tex>. Поэтому будет <tex>O(n)</tex> вершин, каждая занимает <tex>O(1)</tex> памяти.<br />
}}<br />
<br />
By the way, если считать <tex>k</tex> константой, то и для случая большей размерности эти оценки будут такими же (доказывается аналогично).<br />
<br />
== Запрос ==<br />
Пусть нам поступил какой-то прямоугольник <tex>R</tex>. Нужно вернуть все точки, которые в нём лежат. Будем это делать рекурсивно, получая на вход корень дерева и сам прямоугольник <tex>R</tex>. Обозначим область, соответствующую вершине <tex>v</tex>, как <tex>region(v)</tex>. Она будет прямоугольником, одна или более границ которого могут быть на бесконечности. <tex>region(v)</tex> можно явно хранить в узлах, записав при построении, или же считать при рекурсивном спуске. Если корень дерева является листом, то просто проверяем одну точку и при необходимости репортим её. Если нет, то смотрим пересекают ли регионы детей прямоугольник <tex>R</tex>. Если да, то запускаемся рекурсивно от такого ребёнка. При этом, если регион полностью содержится в <tex>R</tex>, то можно репортить сразу все точки из него. Тем самым мы, очевидно, вернём все нужные точки и только их. Чтобы стало совсем понятно, приведём псевдокод:<br />
<br />
<code><br />
SearchKdTree(v, R)<br />
//Input. The root of (a subtree of) a kd-tree, and a range R.<br />
//Output. All points at leaves below v that lie in the range.<br />
if v is a leaf <br />
Report the point stored at v if it lies in R.<br />
else <br />
if region(v.left) is fully contained in R<br />
ReportSubtree(v.left)<br />
else if region(v.left) intersects R<br />
SearchKdTree(v.left, R)<br />
if region(v.right) is fully contained in R<br />
ReportSubtree(v.right)<br />
else if region(v.right) intersects R<br />
SearchKdTree(v.right, R) <br />
</code><br />
<br />
Здесь <tex>ReportSubtree</tex> репортит все точки в поддереве.<br />
<br />
By the way, точно так же можно перечислять точки в любом множестве, ведь нигде не используется, что <tex>R</tex> {{---}} прямоугольник.<br />
<br />
{{Теорема<br />
|about=<br />
О времени на запрос<br />
|statement=<br />
Перечисление точек в прямоугольнике выполняется за <tex>O(\sqrt n + ans)</tex>, где <tex>ans</tex> {{---}} размер ответа.<br />
|proof=<br />
Сперва заметим, что все <tex>ReportSubtree</tex> суммарно выполняются за <tex>O(ans)</tex>. Поэтому достаточно доказать оценку для числа рекурсивных вызовов. А рекурсивные вызовы выполняются только для тех вершин, регионы которых пересекают <tex>R</tex>, но не содержатся в нём. Такие регионы обязательно пересекают хотя бы одну (axis-parallel) сторону заданного прямоугольника. Оценим количество регионов, которые могут пересекаться произвольной вертикальной прямой. Для горизонтальной прямой это будет аналогично. <br />
<br />
Обозначим максимально возможное количество регионов, пересекаемых какой-либо вертикальной прямой, в дереве для <tex>n</tex> точек, у которого первое разбиение делается вертикальной прямой, как <tex>Q(n)</tex>. Рассмотрим произвольную вертикальную прямую <tex>l</tex>. Она будет пересекать регион корня и какого-то одного из его детей (например, левого). При этом ни один из регионов в другом (правом) поддереве пересекать она не может. Левая половина разбита ещё на 2 части горизонтальной прямой, в каждой из них примерно <tex>n / 4</tex> вершин, и они хранятся в поддереве, у которого первое разбиение делается вертикальной прямой. Это даёт нам следующее соотношение:<br />
<br />
<tex>Q(n) = O(1)</tex> if <tex>n = 1</tex>.<br />
<br />
<tex>Q(n) = 2 + 2 \cdot Q(n / 4)</tex>, otherwise.<br />
<br />
Нетрудно заметить, что <tex>Q(n) = O(\sqrt n)</tex> является решением. Принимая во внимание всё, что писалось выше, получаем требуемое.<br />
}}<br />
By the way, в общем случае время на запрос <tex>O(n^{1 - 1/k} + ans)</tex> из соотношения <tex>Q(n) = k + 2^{k - 1} \cdot Q(n / 2^k)</tex>.</div>Kabanovhttp://neerc.ifmo.ru/wiki/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Kabanov&diff=44744Участник:Kabanov2015-01-21T10:23:18Z<p>Kabanov: </p>
<hr />
<div>Есть множество точек на плоскости. Нужно найти две самые удалённые из них.<br />
<br />
[[Статические выпуклые оболочки: Джарвис, Грэхем, Эндрю, Чен, QuickHull|Найдём выпуклую оболочку]] исходного множества и получим более простую задачу: найти две наиболее удалённые вершины в выпуклом многоугольнике. Сделать это можно за линейное время с помощью метода, который называется '''вращающиеся калиперы''' (англ. ''rotating calipers'').<br />
<br />
''Обоснование: Пусть диаметр с одной стороны содержит точку, которая не принадлежит выпуклой оболочке. Тогда продлим диаметр в эту сторону до пересечения с выпуклой оболочкой. Очевидно, он станет длиннее. А потом заметим, что диаметр станет еще больше, если сдвинуть его к одному из концов ребра, в которое мы уперлись. Конец.''<br />
<br />
== Постановка задачи ==<br />
Пусть <tex>P = (p_1, p_2, ... ,p_n)</tex> {{---}} выпуклый многоугольник, в котором порядок обхода вершин направлен против часовой стрелки, и никакие три последовательные точки не лежат на одной прямой. Найти пару чисел <tex>\langle i, j \rangle</tex>, такие, что <tex>d(p_i, p_j)</tex> максимально.<br />
<br />
=== Опорные прямые ===<br />
{{Определение<br />
|definition=<br />
Прямая <tex>L</tex> называется '''опорной прямой''' (англ. ''line of support'') для многоугольника <tex>P</tex>, если его внутренность лежит по одну сторону от <tex>L</tex>, при этом <tex>L</tex> проходит хотя бы через одну из вершин <tex>P</tex>.<br />
}}<br />
{{Теорема<br />
|statement=<br />
Пусть <tex>L_1</tex> и <tex>L_2</tex> {{---}} две параллельные опорные прямые фигуры <tex>\Phi</tex>, расстояние между которыми имеет максимальное значение. <tex>A_1</tex> и <tex>A_2</tex> {{---}} граничные точки фигуры <tex>\Phi</tex>, принадлежащие соответственно прямым <tex>L_1</tex> и <tex>L_2</tex>. Тогда отрезок <tex>A_1A_2</tex> перпендикулярен обеим прямым <tex>L_1</tex> и <tex>L_2</tex>.<br />
|proof=<br />
[[Файл:perpendicular.png|250px|right]]<br />
Предположим, что это не так. Тогда расстояние между прямыми <tex>L_1</tex> и <tex>L_2</tex> было бы меньше, чем отрезок <tex>A_1A_2</tex>, и тем более меньше, чем расстояние между двумя опорными прямыми <tex>L'_1</tex> и <tex>L'_2</tex> фигуры <tex>\Phi</tex>, перпендикулярными к отрезку <tex>A_1A_2</tex>, что противоречит условию.<br />
}}<br />
Так как <tex>A_1</tex> и <tex>A_2</tex> {{---}} какие угодно граничные точки фигуры <tex>\Phi</tex>, принадлежащие соответственно прямым <tex>L_1</tex> и <tex>L_2</tex>, то из перпендикулярности отрезка <tex>A_1A_2</tex> к прямым <tex>L_1</tex> и <tex>L_2</tex> следует, что ни одна из прямых <tex>L_1</tex>, <tex>L_2</tex> не может иметь с фигурой <tex>\Phi</tex> целый общий отрезок. Другими словами, каждая из этих прямых содержит единственную граничную точку фигуры <tex>\Phi</tex>.<br />
<br />
{{Теорема<br />
|statement=<br />
Диаметр выпуклого многоугольника равен максимальному расстоянию между параллельными опорными прямыми.<br />
|proof=<br />
[[Файл:max_parallel.png|170px|right]]<br />
Пусть <tex>\Phi</tex> {{---}} выпуклая фигура, <tex>L_1</tex> и <tex>L_2</tex> {{---}} параллельные опорные прямые, расстояние между которыми имеет наибольшее возможное значение <tex>d</tex>, <tex>A_1</tex> и <tex>A_2</tex> {{---}} общие точки фигуры <tex>\Phi</tex> и прямых <tex>L_1</tex> и <tex>L_2</tex> соответственно. По предыдущей теореме <tex>A_1A_2</tex> перпендикулярен к прямым <tex>L_1</tex>, <tex>L_2</tex>, следовательно, его длина равна <tex>d</tex>. Докажем, что расстояние между любыми двумя точками фигуры <tex>\Phi</tex> не преводходит <tex>d</tex>. Действительно, если <tex>B</tex> и <tex>C</tex> {{---}} какие-либо две точки фигуры <tex>\Phi</tex>, а <tex>m</tex> и <tex>n</tex> {{---}} опорные прямые, перпендикулярные к отрезку <tex>BC</tex>, то отрезок <tex>BC</tex> не превосходит расстояния между прямыми <tex>m</tex> и <tex>n</tex>, которое в свою очередь не превосходит <tex>d</tex>. Следовательно, длина <tex>BC</tex> не может быть больше <tex>d</tex>.<br />
}}<br />
<br />
=== Алгоритм ===<br />
Заметим, что параллельные опорные прямые можно провести не через любую пару точек.<br />
<br />
{{Определение<br />
|definition=<br />
Точки, через которые можно провести параллельные опорные прямые, будем называть '''противолежащими''' (англ. ''antipodal'').<br />
}}<br />
<br />
Благодаря предыдущей теореме нам нужно рассмотреть только противолежащие точки. Задача в том, чтобы рассмотреть их, не перебирая все пары точек.<br />
<br />
[[Файл:calipers.png|250px|right]]<br />
Рассмотрим рисунок справа. <tex>L</tex> и <tex>M</tex> {{---}} опорные прямые, проходящие через вершины <tex>A</tex> и <tex>D</tex> соответственно. Значит, <tex>\langle A,D \rangle</tex> {{---}} противолежащая пара точек. Если начать вращать прямые против часовой стрелки вокруг данных точек, они будут оставаться опорными прямыми, пока одна из прямых не станет накладываться на сторону многоугольника. В нашем примере, при вращении <tex>M</tex> к позиции <tex>M'</tex>, <tex>M</tex> коснётся точки <tex>E</tex> раньше, чем <tex>L</tex> коснётся <tex>B</tex>, поэтому <tex>\langle A,E \rangle</tex> становится новой парой противолежащих точек. <br />
<br />
Теперь <tex>M'</tex> будет вращаться вокруг <tex>E</tex>, а <tex>L'</tex> продолжит вращаться вокруг <tex>A</tex>, и следующей парой противолежащих точек станет <tex>\langle B,E \rangle</tex>. Продолжая таким образом, мы сгенерируем все пары противолежащих точек, так как параллельные линии пройдут под всеми возможными углами. Определение новой пары противолежащих точек требует только одного сравнения углов, которое можно выполнить с помощью предиката поворота.<br />
<br />
=== Асимптотика ===<br />
{{Теорема<br />
|statement=<br />
Представленный выше алгоритм генерирует все пары противолежащих точек в многоугольнике <tex>P</tex>, состоящем из <tex>N</tex> вершин, за время <tex>O(N)</tex>.<br />
|proof=<br />
Данный алгоритм можно реализовать следующим образом: хранить указатели на противолежащие вершины, и на каждой итерации алгоритма увеличивать либо один из данных указателей, либо сразу оба (когда обе прямые проходят через сторону многоугольника), и заканчивать работу, когда опорные прямые сделают полный круг. Таким образом, каждая из вершин будет посещена каждой из прямых не более двух раз.<br />
}}</div>Kabanovhttp://neerc.ifmo.ru/wiki/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Kabanov&diff=44743Участник:Kabanov2015-01-21T10:15:59Z<p>Kabanov: Диаметр множества точек</p>
<hr />
<div>Есть множество точек на плоскости. Нужно найти две самые удалённые из них.<br />
<br />
[[Статические выпуклые оболочки: Джарвис, Грэхем, Эндрю, Чен, QuickHull|Найдём выпуклую оболочку]] исходного множества и получим более простую задачу: найти две наиболее удалённые вершины в выпуклом многоугольнике. Сделать это можно за линейное время с помощью метода, который называется '''вращающиеся калиперы''' (англ. ''rotating calipers'').<br />
<br />
''Обоснование: Пусть диаметр с одной стороны содержит точку, которая не принадлежит выпуклой оболочке. Тогда продлим диаметр в эту сторону до пересечения с выпуклой оболочкой. Очевидно, он станет длиннее. А потом заметим, что диаметр станет еще больше, если сдвинуть его к одному из концов ребра, в которое мы уперлись. Конец.''<br />
<br />
== Постановка задачи ==<br />
Пусть <tex>P = (p_1, p_2, ... ,p_n)</tex> {{---}} выпуклый многоугольник, в котором порядок обхода вершин направлен против часовой стрелки, и никакие три последовательные точки не лежат на одной прямой. Найти пару чисел <tex>\langle i, j \rangle</tex>, такие, что <tex>d(p_i, p_j)</tex> максимально.<br />
<br />
== Вращающиеся калиперы ==<br />
=== Опорные прямые ===<br />
{{Определение<br />
|definition=<br />
Прямая <tex>L</tex> называется '''опорной прямой''' (англ. ''line of support'') для многоугольника <tex>P</tex>, если его внутренность лежит по одну сторону от <tex>L</tex>, при этом <tex>L</tex> проходит хотя бы через одну из вершин <tex>P</tex>.<br />
}}<br />
<br />
{|border=0 width="100%"<br />
|{{Теорема<br />
|statement=<br />
Пусть <tex>L_1</tex> и <tex>L_2</tex> {{---}} две параллельные опорные прямые фигуры <tex>\Phi</tex>, расстояние между которыми имеет максимальное значение. <tex>A_1</tex> и <tex>A_2</tex> {{---}} граничные точки фигуры <tex>\Phi</tex>, принадлежащие соответственно прямым <tex>L_1</tex> и <tex>L_2</tex>. Тогда отрезок <tex>A_1A_2</tex> перпендикулярен обеим прямым <tex>L_1</tex> и <tex>L_2</tex>.<br />
|proof=<br />
Предположим, что это не так. Тогда расстояние между прямыми <tex>L_1</tex> и <tex>L_2</tex> было бы меньше, чем отрезок <tex>A_1A_2</tex>, и тем более меньше, чем расстояние между двумя опорными прямыми <tex>L'_1</tex> и <tex>L'_2</tex> фигуры <tex>\Phi</tex>, перпендикулярными к отрезку <tex>A_1A_2</tex>, что противоречит условию.<br />
}}<br />
|[[Файл:perpendicular.png|250px|thumb|right]]<br />
|}<br />
<br />
Так как <tex>A_1</tex> и <tex>A_2</tex> {{---}} какие угодно граничные точки фигуры <tex>\Phi</tex>, принадлежащие соответственно прямым <tex>L_1</tex> и <tex>L_2</tex>, то из перпендикулярности отрезка <tex>A_1A_2</tex> к прямым <tex>L_1</tex> и <tex>L_2</tex> следует, что ни одна из прямых <tex>L_1</tex>, <tex>L_2</tex> не может иметь с фигурой <tex>\Phi</tex> целый общий отрезок. Другими словами, каждая из этих прямых содержит единственную граничную точку фигуры <tex>\Phi</tex>.<br />
<br />
{| border="0" width="100%"<br />
|{{Теорема<br />
|statement=<br />
Диаметр выпуклого многоугольника равен максимальному расстоянию между параллельными опорными прямыми.<br />
|proof=<br />
Пусть <tex>\Phi</tex> {{---}} выпуклая фигура, <tex>L_1</tex> и <tex>L_2</tex> {{---}} параллельные опорные прямые, расстояние между которыми имеет наибольшее возможное значение <tex>d</tex>, <tex>A_1</tex> и <tex>A_2</tex> {{---}} общие точки фигуры <tex>\Phi</tex> и прямых <tex>L_1</tex> и <tex>L_2</tex> соответственно. По предыдущей теореме <tex>A_1A_2</tex> перпендикулярен к прямым <tex>L_1</tex>, <tex>L_2</tex>, следовательно, его длина равна <tex>d</tex>. Докажем, что расстояние между любыми двумя точками фигуры <tex>\Phi</tex> не преводходит <tex>d</tex>. Действительно, если <tex>B</tex> и <tex>C</tex> {{---}} какие-либо две точки фигуры <tex>\Phi</tex>, а <tex>m</tex> и <tex>n</tex> {{---}} опорные прямые, перпендикулярные к отрезку <tex>BC</tex>, то отрезок <tex>BC</tex> не превосходит расстояния между прямыми <tex>m</tex> и <tex>n</tex>, которое в свою очередь не превосходит <tex>d</tex>. Следовательно, длина <tex>BC</tex> не может быть больше <tex>d</tex>.<br />
}}<br />
|[[Файл:max_parallel.png|170px|thumb|right]]<br />
|}<br />
=== Алгоритм ===<br />
Заметим, что параллельные опорные прямые можно провести не через любую пару точек.<br />
<br />
{{Определение<br />
|definition=<br />
Точки, через которые можно провести параллельные опорные прямые, будем называть '''противолежащими''' (англ. ''antipodal'').<br />
}}<br />
<br />
Благодаря предыдущей теореме нам нужно рассмотреть только противолежащие точки. Задача в том, чтобы рассмотреть их, не перебирая все пары точек.<br />
<br />
{|border="0" width="100%"<br />
|Рассмотрим рисунок справа. <tex>L</tex> и <tex>M</tex> {{---}} опорные прямые, проходящие через вершины <tex>A</tex> и <tex>D</tex> соответственно. Значит, <tex>\langle A,D \rangle</tex> {{---}} противолежащая пара точек. Если начать вращать прямые против часовой стрелки вокруг данных точек, они будут оставаться опорными прямыми, пока одна из прямых не станет накладываться на сторону многоугольника. В нашем примере, при вращении <tex>M</tex> к позиции <tex>M'</tex>, <tex>M</tex> коснётся точки <tex>E</tex> раньше, чем <tex>L</tex> коснётся <tex>B</tex>, поэтому <tex>\langle A,E \rangle</tex> становится новой парой противолежащих точек. <br />
<br />
Теперь <tex>M'</tex> будет вращаться вокруг <tex>E</tex>, а <tex>L'</tex> продолжит вращаться вокруг <tex>A</tex>, и следующей парой противолежащих точек станет <tex>\langle B,E \rangle</tex>. Продолжая таким образом, мы сгенерируем все пары противолежащих точек, так как параллельные линии пройдут под всеми возможными углами. Определение новой пары противолежащих точек требует только одного сравнения углов, которое можно выполнить с помощью предиката поворота.<br />
|[[Файл:calipers.png|250px|thumb|right]]<br />
|}<br />
=== Асимптотика ===<br />
{{Теорема<br />
|statement=<br />
Представленный выше алгоритм генерирует все пары противолежащих точек в многоугольнике <tex>P</tex>, состоящем из <tex>N</tex> вершин, за время <tex>O(N)</tex>.<br />
|proof=<br />
Данный алгоритм можно реализовать следующим образом: хранить указатели на противолежащие вершины, и на каждой итерации алгоритма увеличивать либо один из данных указателей, либо сразу оба (когда обе прямые проходят через сторону многоугольника), и заканчивать работу, когда опорные прямые сделают полный круг. Таким образом, каждая из вершин будет посещена каждой из прямых не более двух раз.<br />
}}<br />
<br />
== Ссылки ==<br />
* ''M.I. Shamos'' Computational geometry, 1978 {{---}} С. 76.<br />
* ''Яглом И.М., Болтянский В.Г.'' Выпуклые фигуры, 1951 {{---}} С. 20, 144.<br />
* [https://github.com/Megabyte777/cg/blob/master/include/cg/operations/diameter.h Реализация - Github.com]<br />
<br />
[[Категория: Вычислительная геометрия]]</div>Kabanovhttp://neerc.ifmo.ru/wiki/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Kabanov&diff=44742Участник:Kabanov2015-01-20T19:40:01Z<p>Kabanov: </p>
<hr />
<div>=== Монотонный метод ===<br />
<br />
{{Определение<br />
|id=def_monotone_polygon<br />
|definition=<br />
Простой многоугольник <tex>P</tex> называется '''монотонным''' относительно прямой <tex>l</tex>, если любая <tex>l'</tex>, такая что <tex>l' \perp l</tex>, пересекает стороны <tex>P</tex> не более двух раз (результатом пересечения <tex>l'</tex> и <tex>P</tex> может быть только один отрезок или точка).<br />
}}<br />
<br />
{{Определение<br />
|definition=<br />
Многоугольник, монотонный относительно <tex>y</tex>-оси называется '''<tex>y</tex>-монотонным'''.<br />
}}<br />
<br />
<br />
Суть данного метода заключается в том, чтобы разбить многоугольник на монотонные части, а затем триангулировать каждую из них.<br />
=== Разбиение многоугольника на монотонные части ====<br />
[[Файл:Split-merge.png|500px|thumb||Пять типов вершин]]<br />
<br />
Рассмотрим самую верхнюю — максимальную по координате <tex>y</tex> вершину. Будем идти вниз по рёбрам до самой нижней — соотвественно минимальной по <tex>y</tex> вершине, то есть таким образом, что для некоторой вершины <tex>j</tex>: <tex>y_j > y_{j+1}</tex>. '''Поворотной''' назовём вершину <tex>i</tex>, на которой направление обхода будет меняется: <tex>y_{i-1} > y_i</tex> и <tex>y_i < y_{i+1}</tex>. Опишем более подробно этот тип вершин. <br />
Уточним понятния ''выше'' и ''ниже'': точка <tex>p</tex> лежит ''ниже'' точки <tex>q</tex>, если <tex>p_y < q_y</tex> или если <tex>p_y = q_y</tex> и <tex>p_x > q_x</tex>, соответственно точка <tex>p</tex> лежит ''выше'' точки <tex>q</tex>, если <tex>p_y > q_y</tex> или если <tex>p_y = q_y</tex> и <tex>p_x < q_x</tex>. Это было сделано для того, чтобы избежать неопределённых ситуаций с вершинами, у которых <tex>y</tex>-координаты равны.<br />
<br />
Обозначим за <tex>\phi</tex> внутренний угол при некоторой вершине и определим далее пять типов вершин, четыре из которых являются поворотными:<br />
* '''''start вершина''''' — два её соседа лежат ниже её самой и <tex> \phi < \pi </tex><br />
* '''''split вершина''''' — два её соседа лежат ниже её самой и <tex> \phi > \pi </tex><br />
* '''''end вершина''''' — два её соседа лежат выше её самой и <tex> \phi < \pi </tex><br />
* '''''merge вершина''''' — два её соседа лежат выше её самой и <tex> \phi > \pi </tex><br />
* '''''regular вершина''''' — не является поворотной, в отличие от остальных, другими словами один её сосед находится выше, а другой ниже её самой.<br />
<br />
{{Лемма<br />
|statement=<br />
Многоугольник <tex>P</tex> является <tex>y</tex>-монотонным, если в нём отсутствуют split и merge вершины.<br />
|proof=<br />
Предположим, что <tex>P</tex> не <tex>y</tex>-монотонный. Тогда докажем, что <tex>P</tex> содержит split и merge вершины. Поскольку <tex>P</tex> не <tex>y</tex>-монотонный, существует горизонтальная прямая <tex>l</tex>, которая пересекает его стороны более двух раз. Выберем <tex>l</tex> таким образом, чтобы самой левой компонентой пересечения <tex>l</tex> и <tex>P</tex> был бы отрезок <tex>pq</tex>. Далее будем двигаться наверх по сторонам <tex>P</tex>, начиная от точки <tex>q</tex>. В результате в некоторой точке <tex>r</tex>, где <tex>r \neq p</tex> (случай '''(a)''' на рисунке), прямая <tex>l</tex> снова пересечёт одну из сторон <tex>P</tex>. Отсюда самая высокая точка, которую мы достигли во время движения по сторонам <tex>P</tex>, будет split вершиной.<br />
<br />
[[Файл:Proof_lemma.jpg|450px]]<br />
<br />
Если же <tex>r = p</tex> (случай '''(b)''' на рисунке), начём опять двигаться по сторонам <tex>P</tex> теперь уже вниз. Как и в предыдущем случае найдётся некоторая точка <tex>r'</tex>, которая будет результатом пересечения <tex>l</tex> и <tex>P</tex>. При этом <tex>r' \neq p</tex>, в противном случае <tex>l</tex> будет пересекать <tex>P</tex> только два раза, что противоречит выбору <tex>l</tex>. Аналогично предыдущему случаю, выберем теперь самую низкую точку, которую мы достигли во время движения по сторонам P. Она будет merge вершиной.<br />
}}<br />
<br />
=== Алгоритм ===<br />
Чтобы сделать многоугольник монотонным, нужно избавиться от split и merge вершин путём проведения непересекающихся дигоналей из таких вершин.<br />
<br />
Рассмотрим горизонтальную заметающую прямую <tex>l</tex>, будем перемещать её сверху вниз вдоль плоскости на которой лежит исходный многоугольник <tex>P</tex>. Будем останавливать её в каждой вершине многоугольника. В тот момент, когда на пути заметающей прямой встречается split или merge вершина её нужно соединить с вершиной, у которой расстояние до <tex>l</tex> минимально, при этом она должна лежать соответственно выше или ниже <tex>l</tex>.<br />
[[Файл:Split_case.jpg|200px|thumb|right|Обработка ''split'' вершины <tex>v_i</tex>]] Рассмотрим каждый случай подробнее:<br />
<br />
# '''''Split вершина'''''. Пусть <tex>e_j</tex> и <tex>e_k</tex> — ближайшее левое и правое ребро относительно split вершины <tex>v_i</tex>, которые <tex>l</tex> пересекает в данный момент. Нам нужно найти вершину, лежащую между <tex>e_j</tex> и <tex>e_k</tex>, наиболее приближённую к <tex>l</tex>, либо если такой точки не существет выбрать минимальную из верхних вершин <tex>e_j</tex> и <tex>e_k</tex>. Для этого будем хранить указатель на искомую вершину у левого ребра <tex>e_j</tex>, который можно заранее вычислить. Тип вершины, хранящийся в <tex>helper</tex> не имеет значения. Таким образом, чтобы построить диагональ для split вершины нужно обратиться к указателю <tex>helper(e_j)</tex> её левого ребра, которое <tex>l</tex> пересекает в данный момент.<br />
# '''''Merge вершина'''''. В отличие от случая со split вершиной заранее вычислить указатель <tex>helper</tex> нельзя, поскольку merge вершина <tex>v_i</tex> должна быть соединена с вершиной, лежащей ниже заметающей прямой <tex>l</tex>. Для этого в <tex>helper(e_j)</tex> - левого относительно <tex>v_i</tex> ребра запишем саму <tex>v_i</tex>. Далее спускаем заметающую прямую вниз к следующей вершине <tex>v_m</tex>, обращаемся к <tex>helper</tex>'у её левого ребра. Проверяем, если там хранится merge вершина, строим диагональ <tex>v_{i}v_{m}</tex>. Последняя проверка осуществляется для любого типа вершины, кроме split, согласно п.1.<br />
[[Файл:Merge_case_1_2.jpg|500px|thumb|center|Обработка ''merge'' вершины <tex>v_i</tex>. На рисунке слева <tex>v_i</tex> записывается в качестве <tex>helper</tex>'а своего левого ребра. На правом рисунке ближайшая вершина <tex>v_m</tex> при обращении к своему левому ребру <tex>helper(e_j)</tex> находит <tex>v_i</tex> и образует диагональ <tex>v_{i}v_m</tex>]]<br />
<br />
=== Структуры данных ===<br />
В подходе, описанном выше, требуется находить пересечения заметающей прямой и левых ребёр многоугольника. Создадим двоичное дерево поиска <tex>T</tex>, в листьях которого будем хранить рёбра, пересекающие <tex>l</tex>, такие, что внутренняя область многоугольника будет лежать справа от них самих. С каждым таким ребром будем хранить его <tex>helper</tex>. Порядок следования листьев в дереве соответствует порядку следования рёбер в многоугольнике: слева направо. Дерево изменяется в зависимости от текущего состояния заметающей прямой. Создадим приоритетную очередь <tex>Q</tex> из вершин, в которой приоритетом будет <tex>y</tex>-координата вершины. Если две вершины имеют одинаковые <tex>y</tex>-координаты, больший приоритет у левой. Вершины будут добавляться на "остановках" заметающей прямой.<br />
<br />
Многоугольник <tex>P</tex> и добавленные в процессе диагонали удобно хранить в виде списка <tex>D</tex> рёбер с двойными связями (''DCEL — doubly-connected edge list''), так как потом это обеспечит эффективный доступ к каждой из частей, которые нужно будет триангулировать.<br />
<br />
=== Псевдокод ===<br />
MakeMonotone(P)<br />
Construct(D);<br />
Construct(Q); // функция Construct создаёт объекты <tex>D</tex> и <tex>Q</tex> , описанные выше.<br />
bst T = new bst();<br />
while Q <tex> \neq \varnothing </tex><br />
Remove <tex>v_{max}</tex> from Q // удаление вершины с наивысшим приоритетом из <tex>Q</tex> <br />
switch (Type_of_vertex(<tex>v_{max}</tex>)): // определение типа вершины<br />
case 'start':<br />
HandleStartVertex(<tex>v_{max}</tex>);<br />
case 'end':<br />
HandleEndVertex(<tex>v_{max}</tex>);<br />
case 'split':<br />
HandleSplitVertex(<tex>v_{max}</tex>);<br />
case 'merge':<br />
HandleMergeVertex(<tex>v_{max}</tex>);<br />
case 'regular':<br />
HandleRegularVertex(<tex>v_{max}</tex>);<br />
<br />
[[Файл:Split-merge - result.png|470px|thumb|right]]<br />
<br />
Опишем теперь каждый метод из последнего switch:<br />
<br />
HandleStartVertex(<tex>v_{i}</tex>)<br />
Insert <tex>e_{i}</tex> in T<br />
<tex>helper(e_{i}) \leftarrow v_i</tex><br />
<br />
HandleSplitVertex(<tex>v_{i}</tex>)<br />
edge <tex>e_j</tex> = <tex>l \cap P</tex><br />
Search <tex>e_j</tex> in T<br />
Insert edge(<tex>v_{i}</tex>, <tex>helper(e_{j})</tex>) in D<br />
<tex>helper(e_{j}) \leftarrow v_i</tex> <br />
Insert <tex>e_{i}</tex> in T<br />
<tex>helper(e_{i}) \leftarrow v_i</tex><br />
<br />
В последующих трех функциях обработки вершины <tex>v_i</tex> происходит обращение к смежному ребру <tex>e_{i-1}</tex>. Это сделано для вершин, относительно которых внутренняя область <tex>P</tex> лежит справа от них самих (вершина <tex>v_6</tex>), либо для двух подряд идущих merge вершин, таких как <tex>v_2</tex> и <tex>v_8</tex>.<br />
<br />
HandleEndVertex(<tex>v_{i}</tex>)<br />
if (Type_of_vertex(<tex>helper(e_{i-1})</tex> = 'merge')<br />
Insert edge(<tex>v_{i}</tex>, <tex>helper(e_{i-1})</tex>) in D<br />
Delete <tex>e_{i-1}</tex> from T<br />
<br />
HandleMergeVertex(<tex>v_{i}</tex>)<br />
if (Type_of_vertex(<tex>helper(e_{i-1})</tex> = 'merge')<br />
Insert edge(<tex>v_{i}</tex>, <tex>helper(e_{i-1})</tex>) in D<br />
Delete <tex>e_{i-1}</tex> from T<br />
edge <tex>e_j</tex> = <tex>l \cap P</tex><br />
Search <tex>e_j</tex> in T<br />
if (Type_of_vertex(<tex>helper(e_{j})</tex> = 'merge')<br />
Insert edge(<tex>v_{i}</tex>, <tex>helper(e_{j})</tex>) in D<br />
<tex>helper(e_{j}) \leftarrow v_i</tex><br />
<br />
HandleRegularVertex(<tex>v_{i}</tex>)<br />
if (interior of <tex>P</tex> lies to the right of <tex>v_{i}</tex>)<br />
then<br />
if (Type_of_vertex(<tex>helper(e_{i-1})</tex> = 'merge')<br />
Insert edge(<tex>v_{i}</tex>, <tex>helper(e_{i-1})</tex>) in D<br />
Delete <tex>e_{i-1}</tex> from T<br />
Insert <tex>e_{i}</tex> in T<br />
<tex>helper(e_{i}) \leftarrow v_i</tex><br />
else<br />
edge <tex>e_j</tex> = <tex>l \cap P</tex><br />
Search <tex>e_j</tex> in T<br />
if (Type_of_vertex(<tex>helper(e_{j})</tex> = 'merge')<br />
Insert edge(<tex>v_{i}</tex>, <tex>helper(e_{j})</tex>) in D<br />
<tex>helper(e_{j}) \leftarrow v_i</tex><br />
===== Корректность =====<br />
{{Лемма<br />
|statement=<br />
Функция ''MakeMonotone(P)'' корректно выполняет разбиение многоугольника <tex>P</tex>. Другими словами эта функция добавляет в <tex>P</tex> множество непересекающихся диагоналей, которые разбивают <tex>P</tex> на монотонные части.<br />
|proof=Тот факт, что <tex>P</tex> разбивается на монотонные части следует из предыдущей леммы.<br />
Остаётся доказать, что диагонали, построенные в процессе выполнения алгоритма, попарно не пересекаются и не пересекают стороны <tex>P</tex>. <br />
<br />
Рассмотрим случай выполнения функции ''HandleSplitVertex'', поскольку это наиболее общий случай: split вершина может быть соединена со всеми типами вершин, в отличие от остальных функций (в них рассматриваемая в данный момент вершина может быть соединена только с merge вершиной). <br />
<br />
Допустим, что диагональ <tex>v_{i}v_{m}</tex> была построена с помощью ''HandleSplitVertex'' по достижению split вершины <tex>v_i</tex>. Рассмотрим четырёхугольник <tex>H</tex>, заключённый между <tex>e_j</tex> и <tex>e_k</tex> - левым и правым ребром относительно <tex>v_i</tex> и горизонтальными прямыми, проведёнными через <tex>v_i</tex> и <tex>v_m</tex>. Внутри <tex>H</tex>, не может находиться ни одной из вершин <tex>P</tex>, в противном случае <tex>helper(e_j)</tex> не равнялся бы <tex>v_m</tex>. Предположим теперь, что <tex>v_{i}v_{m}</tex> пересекает <tex>e_s</tex> одну из сторон <tex>P</tex>. Учитывая, что никаких вершин <tex>P</tex> не лежит внутри <tex>H</tex> и стороны <tex>P</tex> не пересекаются, то <tex>e_s</tex> должна пересечь либо отрезок, соединяющий <tex>e_j</tex> и <tex>v_m</tex>, либо <tex>e_j</tex> и <tex>v_i</tex>.[[Файл:Pic_of_correctness.jpg|400px|thumb|right|1) Вершин внутри <tex>H</tex> находиться не может; 2) <tex>v_{i}v_m</tex> может пересекать только рёбра, помеченные зелёным]] Такое возможно только в случае, когда точками пересечения будут являться <tex>v_i</tex> или <tex>v_m</tex>, что не противоречит условию. Отсюда <tex>v_{i}v_{m}</tex> не пересекает ни одну из сторон <tex>P</tex> в посторонних точках.<br />
<br />
<br />
Теперь рассмотрим случай с пересечением добавленной ранее диагональю. Поскольку внутри <tex>H</tex> никаких вершин вершин находиться не может, и оба конца любой добавленной ранее диагонали должны лежать выше <tex>v_i</tex>, диагональ <tex>v_{i}v_m</tex> не может пересекать никакую из ранее добавленных диагоналей.<br />
}}<br />
===== Оценка работы =====<br />
Построение описанной выше приоритетной очереди <tex>Q</tex> происходит за линейное время. Когда заметающая прямая останавливается в вершине: операции с очередью занимают константу по времени, операции с деревом <tex>T</tex> на запросы и обновления требуют <tex>\mathcal{O}(\mathcal \log n)</tex>. Добавление диагонали в <tex>D</tex> требует <tex>\mathcal{O}(1)</tex>. В итоге обработка каждой вершины требует <tex>\mathcal{O}(\log n)</tex>, а весь алгоритм соответственно <tex>\mathcal{O}(n \log n)</tex>. Что касается памяти, она очевидно составляет <tex>\mathcal{O}(n) </tex>. Очередь <tex>Q</tex> и дерево <tex>T</tex> занимают линейную память.<br />
<br />
[[Файл:Triangulationg intro.jpg|170px|thumb|right|Зелёным помечена так называемая воронка, которая образуется, когда мы достигнем красной вершины]]<br />
<br />
== Триангуляция монотонного многоугольника ==<br />
Будем проходить сверху вниз по вершинам многоугольника проводя диагонали где это возможно.<br />
<br />
Отсортируем все вершины многоугольника <tex>P</tex> в порядке убывания их <tex>y</tex>-координаты. Заведём стек вершин <tex>S</tex>. В стеке будем хранить вершины в отсортированном порядке, которые были обработаны, но не были отрезаны от многоугольника, то есть находятся в той части многоугольника, которая ещё не была триангулирована. В момент обработки некоторой вершины, будем пытаться провести из неё как можно больше диагоналей к вершинам, содержащимся в стеке. Эти диагонали отрезают треугольники от <tex>P</tex>. На вершине стека будет храниться вершина, которая будет обрабатываться последней.<br />
<br />
Часть многоугольника <tex>P</tex>, лежащая выше последней обработанной вершины <tex>v_i</tex> и которая ещё не была триангулирована имеет форму перевёрнутой воронки (см. рисунки). Одна сторона воронки состоит из одной из сторон <tex>P</tex>, а другая состоит из цепи вершин, которые лежат выше <tex>v_i</tex> и внутренние углы которых не меньше <tex>\pi</tex>. Несложно догадаться, что самая нижняя вершина стека является единственной выпуклой. Несложно также заметить, что при обработке следующей вершины свойство перевёрнутой воронки сохранится, то есть оно является инвариантом алгоритма.<br />
<br />
[[Файл:Triang_alg_case1.jpg|200px|thumb|left|Первый случай. Синим помечены стороны воронки, зелёным — диагонали, а жёлтым границы новой ещё не протриангулированной области]]<br />
[[Файл:Triang alg case2.jpg|300px|thumb|right|Второй случай. Синим помечена цепь из вершин, которая содержится в стеке <tex>S</tex> на момент достижения вершины <tex>v_j</tex>, рыжей помечена первая вершина, до которой невозможно провести диагональ, жёлтой помечена новая нетриангулированная область <tex>P</tex> в форме воронки]]<br />
=== Алгоритм ===<br />
Рассмотрим процесс обработки вершины более подробно. Возможны два случая:<br />
* Текущая вершина <tex>v_j</tex> является нижним концом стороны <tex>e</tex>, ограничивающего воронку. Вершины противоположной цепи уже были положены в стек. В этом случае можно просто построить диагонали, соединяющие <tex>v_j</tex> со всеми вершинами, находящимися в стеке, кроме последней. Последняя вершина в стеке уже соединена с <tex>v_j</tex> стороной <tex>e</tex>. Часть многоугольника <tex>P</tex>, лежащая выше <tex>v_j</tex>, которая не была триангулирована, ограничена диагональю, которая соединяет <tex>v_j</tex> с вершиной <tex>v_{s1}</tex>, которая была первой в стеке. Сторона многоугольника <tex>P</tex>, выходящая из <tex>v_{s1}</tex> направлена вниз. Снова образуется фигура c одним выпуклым углом, похожая на воронку — инвариант сохраняется. Вершины <tex>v_j</tex> и <tex>v_{s1}</tex> кладутся в стек, поскольку они были были обработаны, но по прежнему являются вершинами непротриангулированной части <tex>P</tex>.<br />
<br />
* Вершина <tex>v_j</tex> принадлежит последовательной цепи вершин, добавленных в <tex>S</tex>. Вынем из стека верхнюю вершину <tex>v_{s1}</tex> — она уже соединена с <tex>v_{j}</tex> одной из сторон <tex>P</tex>. Затем будем пытаться выстраивать диагонали, соединяющие <tex>v_{j}</tex> c вынимаемыми из стека вершинами пока это возможно. Проверку на возможность построения диагонали <tex>v_{j}v_{k}</tex>, где <tex>v_{k}</tex> — текущая верхняя вершина стека, можно осуществлять посредством изучения взаимного расположения предыдущей вершины, вынутой из <tex>S</tex>, относительно <tex>v_{j}v_{k}</tex>. Когда мы достигнем вершины <tex>v_{k}</tex>, до которой невозможно провести диагональ, положим предыдущую вершину <tex>v_{k-1}</tex> обратно в стек. Вершина <tex>v_{k-1}</tex> является либо последней, до которой было возможно провести диагональ, либо, если ни одной диагонали из <tex>v_{j}</tex> провести не удалось, — соседом <tex>v_{j}</tex>. Далее положим <tex>v_{j}</tex> в стек. Опять же инвариант непротриангулированной части <tex>P</tex> сохраняется: одна сторона воронки ограничена частью стороны многоугольника, а другая цепью невыпуклых вершин.<br />
<br />
=== Псевдокод ===<br />
Как ранее уже было отмечено, задаём <tex>P</tex> в виде рёберного списка c двойными связями <tex>D</tex>.<br />
TriangulateMonotonePolygon(P)<br />
vertex [n] V = new vertex(P); // массив вершин <tex>P</tex>, отсортированный по y-координате в порядке убывания.<br />
stack S = new stack();<br />
S.push(V[1]);<br />
S.push(V[2]);<br />
for j <tex>\leftarrow</tex> 3 to n - 1<br />
if (V[j] = S.peek())<br />
while (S <tex>\neq \varnothing </tex>)<br />
if (S.size() <tex>\neq</tex> 1)<br />
Insert edge(V[j], S.peek()) in D<br />
S.pop()<br />
S.push(V[j-1])<br />
S.push(V[j]);<br />
else<br />
vertex last <tex>\leftarrow</tex> S.peek();<br />
S.pop();<br />
while (IsValidDiagonal(edge(V[j], S.peek()), last)) //проверка возможности построения <br />
//диагонали — предикат "левый поворот"<br />
last <tex>\leftarrow</tex> S.peek();<br />
S.pop();<br />
Insert edge(V[j], last) in D<br />
S.push(last);<br />
S.push(V[j]);<br />
S.pop()<br />
while (S <tex>\neq \varnothing </tex>)<br />
if (S.size() <tex>\neq</tex> 1)<br />
Insert edge(V[j], S.peek()) in D<br />
S.pop()<br />
=== Корректность ===<br />
* Все построенные диагонали попарно не пересекаются. Это гарантируется тем, что при каждом просмотре определённой вершины рассматривается только та часть <tex>P'</tex> многоугольника <tex>P</tex>, которая не была протриангулирована, следовательно внутри этой области по определению не может лежать ни одной из уже построенных диагоналей. Несложно заметить, что в стеке <tex>S</tex> на каждой итерации главного цикла хранятся вершины, которые принадлежат именно <tex>P'</tex> и лежат выше рассматриваемой вершины.<br />
* Количество построенных диагоналей всегда будет <tex>n-3</tex>, поэтому непротриангулированных частей в многоугольнике не останется.<br />
=== Оценка работы ===<br />
Построение массива вершин требует линейное время и занимает линейную память. Главный цикл ''for'' выполняется <tex>n-3</tex> раза. Каждая его итерация может потребовать линейное время. Однако заметим, что на каждой итерации главного цикла в стек кладутся максимум две вершины, следовательно общее число выполнения операции ''push'', включая первые две вершины, положенные в начале алгоритма, ограничено <tex>2n-4</tex>. Количество операций ''pop'' за время работы алгоритма не превысит количества операций ''push''. Отсюда общее время работы цикла ''for'' <tex>\mathcal{O}(n)</tex>. В итоге общее время работы <tex>\mathcal{O}(n)</tex>.<br />
=== Общая оценка ===<br />
Разбиение многоугольника на монотонные части занимает <tex>\mathcal{O}(n \log n)</tex> времени и <tex>\mathcal{O}(n)</tex> памяти. Триангуляция каждой из частей занимает линейную память и время. Учитывая то, что суммарное количество вершин во всех частях <tex>\mathcal{O}(n)</tex>, триангуляция всех частей займёт <tex>\mathcal{O}(n)</tex> по времени и по памяти.<br />
<br />
В итоге общая оценка составляет <tex>\mathcal{O}(n \log n)</tex> по времени и <tex>\mathcal{O}(n)</tex> по памяти.</div>Kabanovhttp://neerc.ifmo.ru/wiki/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Kabanov&diff=44741Участник:Kabanov2015-01-20T19:27:46Z<p>Kabanov: </p>
<hr />
<div>==Решение методом полос==<br />
[[Файл:cgslabs.png|200px|right]]<br />
Проведём через каждую вершину вертикальную прямую. Получим полосы (slabs). Пусть каждой полосе соответствует точка, через которую проведён левый край полосы. Будем хранить отсортированный массив <tex>x</tex>-координат, тогда за <tex>O(\log n)</tex> можно найти, в какой полосе лежит <tex>P</tex>.<br />
<br />
В каждой полосе отрезки, составляющие ППЛГ, могут пересекаться только в концах, причём эти точки пересечения могут лежать только на прямых, ограничивающих полосы (по построению). Получается, что внутри каждой полосы можно отсортировать отрезки, которые лежат в ней, например, снизу вверх. Тогда, найдя нужную полосу, можно быстро найти нужный отрезок.<br />
<br />
===Персистентные деревья===<br />
Персистентные структуры данных — это структуры, боже царя хранящие историю своих изменений. Персистентность бывает полная (когда можем изменять любую версию) и частичная (когда можем изменить только последнюю версию, но запросы можем делать на всех).<br />
<br />
Один из способов сделать дерево частично персистентным — node-copying (или path-copying, в разных источниках по-разному). Храним массив корней дерева. Когда нам нужно изменить ноду, мы создаём в этом массиве новый корень, но его поля left и right совпадают с таковыми в предыдущем корне. Далее мы идём от корня к ноде, которую хотим изменить. Все вершины по пути мы «копируем» так же, как и корень, при этом у предка меняем соответствующий указатель на новый. После этого мы меняем нужную нам ноду. Таким образом, для такого дерева нам нужно <tex>O(n \log n)</tex> памяти.<br />
<br />
Можно усовершенствовать этот способ. Теперь в каждой ноде будет храниться номер версии и поля для ленивого изменения дерева: фиксированное количество запасных указателей left и right и номера версий для них. Когда мы хотим изменить ноду, вместо копирования записываем изменения в запасные указатели, если они ещё есть, иначе создаём новую ноду и соответственно исправляем её предка. Для поиска по версиям используем бинпоиск. Этот способ называется limited node copying, для него нужно O(n) памяти, потому что амортизированно за один апдейт копируем O(1) нод.<br />
<br />
===Локализация в полосе===<br />
Воспользуемся сбалансированным частично персистентным деревом для хранения отрезков в полосах. Каждая полоса — это новая версия дерева.<br />
<br />
==Время и память==<br />
На запрос нужно <tex>O(\log n)</tex>, на препроцессинг — <tex>O(n \log n)</tex>; памяти нужно <tex>O(n)</tex>.<br />
<br />
==Алгоритм Киркпатрика==<br />
Существует ли метод локализации со временем поиска за <tex>O(\log n)</tex>, использующий менее чем квадратичную память? Эта задача оставалась не решенной довольно долго. Но все же была решена Липтоном и Тарьяном в 1977-1980 гг. Но их метод оказался на столько громоздким, а оценки времени его эффективности содержат слишком большую константу, что сами авторы не считали этот метод практичным, но его существование заставляет думать, что может найтись практичный алгоритм с временной оценкой <tex>O(\log n)</tex> и линейной памятью.<br />
<br />
Недавно Киркпатриком был предложен оптимальный метод, дающий ответ на ожидания Липтона и Тарьяна, {{---}} детализация триангуляции.<br />
===Предобработка===<br />
<wikitex>[[Файл:кирк1.png|right|200px]]Пусть планарный N-вершинный граф задает триангуляцию нашего многоугольника (если это не так, то воспользуемся методом триангуляции многоугольника за время $O (n \log n)$. Напомним, что триангуляция на множестве вершин $V$ есть планарный граф с не более чем $3 |V| - 6$ ребрами ([[Формула_Эйлера |формула Эйлера]]). Для удобства описания алгоритма поместим нашу триангуляцию в охватывающий треугольник и построим триангуляцию области между нашими объектами. После этого преобразования все триангуляции будут обладать тремя границами и ровно $3 |V| - 6$ ребрами.<br />
</wikitex><br />
<br />
===Структура данных===<br />
<wikitex>[[Файл:кирк2.png|right|200px]]<br />
[[Файл:кирк3.png|right|300px]]<br />
Итак, имеется N-вершинная триангуляция $G$, и пусть строится последовательность триангуляций $S_1, S_2, \dots, S_{h(N)}$, где $S_1 = G$, а $S_i$ получается из $S_{i - 1}$ по следующим правилам:<br />
* Шаг 1. Удалим некоторое количество неграничных и независимых (попарно несмежных друг с другом) вершин и инцидентные им ребра (от выбора этого множества напрямую зависит оптимальность алгоритма).<br />
* Шаг 2. Построить триангуляцию получившихся в результате шага 1 многоугольников.<br />
Таким образом $S_{h(N)}$ состоит из одного треугольника. Заметим, что все триангуляции имеют одну общую границу, так как удаляются только внутренние узлы. Далее, будем обозначать все треугольники как $R$, а также будем говорить, что треугольник $R_ij$ принадлежит триангуляции $S_i$, если <br />
он был создан на шаге (2) при построении этой триангуляции.<br />
<br />
Теперь построим структуру данных $T$ для поиска. Эта структура представляет собой направленный ацикличный граф, вершинами которого будут наши треугольники. Определим эту структуру следующим образом: из треугольника $R_k$ будет вести ребро в треугольник $R_j$, если при построении $S_i$ из $S_{i-1}$ мы имеем<br />
* $R_j$ удалятся из $S_{i - 1}$ на шаге (1)<br />
* $R_k$ создается в $S_{i}$ на шаге (2)<br />
* $R_j \cap R_k \ne \varnothing $<br />
<br />
Очевидно, что треугольники из $S_1$ (и только они) не имеют исходящих ребер.<br />
<br />
Для ясности удобно изобразить $T$ в рассмотренном виде, то есть помещая его узлы в горизонтальные строки, каждая из <br />
которых соответствует какой-нибудь триангуляции. Последовательность триангуляций и соответствующая ей структура $T$ показаны на рисунке. Треугольники пронумерованы в порядке их появления. Кружком обведены вершины, которые удалены на данном шаге. </wikitex><br />
====Выбор множества удаляемых вершин====<br />
<wikitex>Как уже упоминалось, от выбора множества вершит триангуляции, которые будут удалены при построении $S_i$ по $S_{i-1}$ существенно зависит эффективность метода. Предположим, что можно выбрать это множество так, чтобы выполнялись следующие ''свойства'' ($N_i$ обозначает число вершин в $S_i$):<br />
<br />
'''Свойство 1'''. $N_i = a_i N_{i-1}$, где $a_i \le a < 1$ для $i = 2,\dots , h(N)$.<br />
<br />
'''Свойство 2'''. Каждый треугольник $R_i \in S_i$ пересекается не более чем с $H$ треугольниками из $S_{i-1}$ и наоборот.<br />
Первое свойство немедленно влечет за собой следствие, что $h(N) \le \left \lceil \log_{1/a}N \right \rceil = O(log N)$, поскольку при переходе от $S_{i-1}$ к $S_i$ удаляется по меньшей мере фиксированная доля вершин.<br />
<br />
Также из этих свойств следует, что память для $T$ равна $O(N)$. Действительно, заметим, что эта память используется для хранения узлов и указателей на их потомков. Из [[Формула_Эйлера|теоремы Эйлера]] о плоских графах следует, что $S_i$ содержит $F_i < 2N_i$ треугольников. Число узлов в $T$, представляющих треугольники из $S_i$, не превосходит $F_i$ (только те треугольники, которые действительно принадлежат $S_i$, появляются на соответствующем «ярусе» $T$). Отсюда следует, что общее число узлов в $T$ меньше, чем<br />
$2(N_1 + N_2 + \dots + N_{h(N)}) \le 2N_1(1 + a + a^2 + \dots + a^{h(N) - 1}) < \frac{2N}{1 - a}$.<br />
Что касается памяти, используемой под указатели, то по свойству 2 каждый узел имеет не более $H$ указателей, поэтому не более $\frac{2NH}{1-a}$ указателей появится в $T$. Это доказывает последнее утверждение.<br />
<br />
Покажем теперь, что критерий выбора множества удаляемых вершин, удовлетворяющий вышеописанным свойствам, существует.<br />
{{Теорема<br />
|about=<br />
критерий выбора множества удаляемых вершин<br />
|statement=<br />
Если на шаге (1) построения последовательности триангуляции удалять несмежные вершины со степенью меньше некоторого целого (будет указано позже) числа $K$, то свойства, описанные выше, будут выполнены.<br />
|proof=<br />
'''1. ''' Для проверки первого свойства воспользуемся некоторыми особенностями плоских графов. Из [[Формула_Эйлера | формулы Эйлера]] для плоских графов, в частном случае триангуляции, ограниченной тремя ребрами, следует, что число вершин $N$ и число ребер $e$ связаны соотношением <br />
$e = 3N - 6$.<br />
Пока в триангнуляции есть внутренние вершины (в противном случае задача тривиальна), степень каждой из трех граничных вершин не меньше трех. Поскольку существует $3N - 6$ ребер, а каждое ребро инцидентно двум вершинам, то сумма степеней всех вершин меньше $6N$. Отсюда сразу следует, что не менее $ \frac{N}{2}$ вершин имеет степень меньше 12. Следовательно, пусть $K = 12$. Пусть также $v$ {{---}} число выбранных вершин. Поскольку каждой из них инцидентно не более $K-1 = 11$ ребер, а три граничные вершины не выбираются, то мы имеем <br />
$v \ge \left \lfloor \frac{1}{12}(\frac{N}{2} - 3) \right \rfloor $.<br />
Следовательно, $a \cong 1 - \frac{1}{24} < 0,959 < 1$, что доказывает справедливость свойства 1.<br />
'''2. ''' Выполнение второго свойства обеспечивается тривиально. Поскольку удаление вершины со степенью меньше $K$ приводит к образованию многоугольника с числом ребер менее $K$, то каждый из удаленных треугольников пересекает не более $K - 2 = H$ новых треугольников.<br />
}}</wikitex><br />
<br />
===Поиск===<br />
<wikitex>После построения структуры легко понять, как в ней происходит поиск. Элементарной операцией здесь является определение принадлежности треугольнику. Очевидно, что она выполняется константное время. Сначала мы локализуемся в треугольнике $S_1$. После этого мы строим путь от корневой вершины до листа следующим образом: находясь в какой-либо вершине $z$, просмотрим всех ее детей на принадлежность точки соответствующему треугольнику и, так как точка может находиться лишь в одном треугольнике конкретной триангуляции, перейдем в эту вершину, и продолжим поиск.<br />
Этот поиск также можно рассматривать как последовательную локализацию в триангуляциях $S_1, \dots, S_{h(N)}$, откуда и происходит название самого метода.<br />
</wikitex><br />
====Псевдокод====<br />
<wikitex>Пусть все потомки узла $u$ из $T$ собраны в список successors(u), а triangle(u) обозначает треугольник, соответствующий узлу $u$. Тогда алгоритм поиска может выглядеть следующим образом: </wikitex><br />
procedure localization(z)<br />
if (z not in triangle(root))<br />
z in infinite region<br />
else<br />
u = root<br />
while (successors(u) != null)<br />
for (v in successors(u))<br />
if (z in triangle(v))<br />
u = v<br />
return u</div>Kabanovhttp://neerc.ifmo.ru/wiki/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Kabanov&diff=44740Участник:Kabanov2015-01-20T19:25:48Z<p>Kabanov: Локализация в ППЛГ</p>
<hr />
<div>==Решение методом полос==<br />
[[Файл:cgslabs.png|200px|right]]<br />
Проведём через каждую вершину вертикальную прямую. Получим полосы (slabs). Пусть каждой полосе соответствует точка, через которую проведён левый край полосы. Будем хранить отсортированный массив <tex>x</tex>-координат, тогда за <tex>O(\log n)</tex> можно найти, в какой полосе лежит <tex>P</tex>.<br />
<br />
В каждой полосе отрезки, составляющие ППЛГ, могут пересекаться только в концах, причём эти точки пересечения могут лежать только на прямых, ограничивающих полосы (по построению). Получается, что внутри каждой полосы можно отсортировать отрезки, которые лежат в ней, например, снизу вверх. Тогда, найдя нужную полосу, можно быстро найти нужный отрезок.<br />
<br />
===Персистентные деревья===<br />
Персистентные структуры данных — это структуры, боже царя хранящие историю своих изменений. Персистентность бывает полная (когда можем изменять любую версию) и частичная (когда можем изменить только последнюю версию, но запросы можем делать на всех).<br />
<br />
Один из способов сделать дерево частично персистентным — node-copying (или path-copying, в разных источниках по-разному). Храним массив корней дерева. Когда нам нужно изменить ноду, мы создаём в этом массиве новый корень, но его поля left и right совпадают с таковыми в предыдущем корне. Далее мы идём от корня к ноде, которую хотим изменить. Все вершины по пути мы «копируем» так же, как и корень, при этом у предка меняем соответствующий указатель на новый. После этого мы меняем нужную нам ноду. Таким образом, для такого дерева нам нужно <tex>O(n \log n)</tex> памяти.<br />
<br />
Можно усовершенствовать этот способ. Теперь в каждой ноде будет храниться номер версии и поля для ленивого изменения дерева: фиксированное количество запасных указателей left и right и номера версий для них. Когда мы хотим изменить ноду, вместо копирования записываем изменения в запасные указатели, если они ещё есть, иначе создаём новую ноду и соответственно исправляем её предка. Для поиска по версиям используем бинпоиск. Этот способ называется limited node copying, для него нужно O(n) памяти, потому что амортизированно за один апдейт копируем O(1) нод.<br />
<br />
===Локализация в полосе===<br />
Воспользуемся сбалансированным частично персистентным деревом для хранения отрезков в полосах. Каждая полоса — это новая версия дерева.<br />
<br />
==Время и память==<br />
На запрос нужно <tex>O(\log n)</tex>, на препроцессинг — <tex>O(n \log n)</tex>; памяти нужно <tex>O(n)</tex>.<br />
<br />
==Алгоритм Киркпатрика==<br />
Существует ли метод локализации со временем поиска за <tex>O(\log n)</tex>, использующий менее чем квадратичную память? Эта задача оставалась не решенной довольно долго. Но все же была решена Липтоном и Тарьяном в 1977-1980 гг. Но их метод оказался на столько громоздким, а оценки времени его эффективности содержат слишком большую константу, что сами авторы не считали этот метод практичным, но его существование заставляет думать, что может найтись практичный алгоритм с временной оценкой <tex>O(\log n)</tex> и линейной памятью.<br />
<br />
Недавно Киркпатриком был предложен оптимальный метод, дающий ответ на ожидания Липтона и Тарьяна, {{---}} детализация триангуляции.<br />
===Предобработка===<br />
<wikitex>[[Файл:кирк1.png|right|300px]]Пусть планарный N-вершинный граф задает триангуляцию нашего многоугольника (если это не так, то воспользуемся методом триангуляции многоугольника за время $O (n \log n)$. Напомним, что триангуляция на множестве вершин $V$ есть планарный граф с не более чем $3 |V| - 6$ ребрами ([[Формула_Эйлера |формула Эйлера]]). Для удобства описания алгоритма поместим нашу триангуляцию в охватывающий треугольник и построим триангуляцию области между нашими объектами. После этого преобразования все триангуляции будут обладать тремя границами и ровно $3 |V| - 6$ ребрами.<br />
</wikitex><br />
<br />
===Структура данных===<br />
<wikitex>[[Файл:кирк2.png|right|300px]]<br />
[[Файл:кирк3.png|right|300px]]<br />
Итак, имеется N-вершинная триангуляция $G$, и пусть строится последовательность триангуляций $S_1, S_2, \dots, S_{h(N)}$, где $S_1 = G$, а $S_i$ получается из $S_{i - 1}$ по следующим правилам:<br />
* Шаг 1. Удалим некоторое количество неграничных и независимых (попарно несмежных друг с другом) вершин и инцидентные им ребра (от выбора этого множества напрямую зависит оптимальность алгоритма).<br />
* Шаг 2. Построить триангуляцию получившихся в результате шага 1 многоугольников.<br />
Таким образом $S_{h(N)}$ состоит из одного треугольника. Заметим, что все триангуляции имеют одну общую границу, так как удаляются только внутренние узлы. Далее, будем обозначать все треугольники как $R$, а также будем говорить, что треугольник $R_ij$ принадлежит триангуляции $S_i$, если <br />
он был создан на шаге (2) при построении этой триангуляции.<br />
<br />
Теперь построим структуру данных $T$ для поиска. Эта структура представляет собой направленный ацикличный граф, вершинами которого будут наши треугольники. Определим эту структуру следующим образом: из треугольника $R_k$ будет вести ребро в треугольник $R_j$, если при построении $S_i$ из $S_{i-1}$ мы имеем<br />
* $R_j$ удалятся из $S_{i - 1}$ на шаге (1)<br />
* $R_k$ создается в $S_{i}$ на шаге (2)<br />
* $R_j \cap R_k \ne \varnothing $<br />
<br />
Очевидно, что треугольники из $S_1$ (и только они) не имеют исходящих ребер.<br />
<br />
Для ясности удобно изобразить $T$ в рассмотренном виде, то есть помещая его узлы в горизонтальные строки, каждая из <br />
которых соответствует какой-нибудь триангуляции. Последовательность триангуляций и соответствующая ей структура $T$ показаны на рисунке. Треугольники пронумерованы в порядке их появления. Кружком обведены вершины, которые удалены на данном шаге. </wikitex><br />
====Выбор множества удаляемых вершин====<br />
<wikitex>Как уже упоминалось, от выбора множества вершит триангуляции, которые будут удалены при построении $S_i$ по $S_{i-1}$ существенно зависит эффективность метода. Предположим, что можно выбрать это множество так, чтобы выполнялись следующие ''свойства'' ($N_i$ обозначает число вершин в $S_i$):<br />
<br />
'''Свойство 1'''. $N_i = a_i N_{i-1}$, где $a_i \le a < 1$ для $i = 2,\dots , h(N)$.<br />
<br />
'''Свойство 2'''. Каждый треугольник $R_i \in S_i$ пересекается не более чем с $H$ треугольниками из $S_{i-1}$ и наоборот.<br />
Первое свойство немедленно влечет за собой следствие, что $h(N) \le \left \lceil \log_{1/a}N \right \rceil = O(log N)$, поскольку при переходе от $S_{i-1}$ к $S_i$ удаляется по меньшей мере фиксированная доля вершин.<br />
<br />
Также из этих свойств следует, что память для $T$ равна $O(N)$. Действительно, заметим, что эта память используется для хранения узлов и указателей на их потомков. Из [[Формула_Эйлера|теоремы Эйлера]] о плоских графах следует, что $S_i$ содержит $F_i < 2N_i$ треугольников. Число узлов в $T$, представляющих треугольники из $S_i$, не превосходит $F_i$ (только те треугольники, которые действительно принадлежат $S_i$, появляются на соответствующем «ярусе» $T$). Отсюда следует, что общее число узлов в $T$ меньше, чем<br />
$2(N_1 + N_2 + \dots + N_{h(N)}) \le 2N_1(1 + a + a^2 + \dots + a^{h(N) - 1}) < \frac{2N}{1 - a}$.<br />
Что касается памяти, используемой под указатели, то по свойству 2 каждый узел имеет не более $H$ указателей, поэтому не более $\frac{2NH}{1-a}$ указателей появится в $T$. Это доказывает последнее утверждение.<br />
<br />
Покажем теперь, что критерий выбора множества удаляемых вершин, удовлетворяющий вышеописанным свойствам, существует.<br />
{{Теорема<br />
|about=<br />
критерий выбора множества удаляемых вершин<br />
|statement=<br />
Если на шаге (1) построения последовательности триангуляции удалять несмежные вершины со степенью меньше некоторого целого (будет указано позже) числа $K$, то свойства, описанные выше, будут выполнены.<br />
|proof=<br />
'''1. ''' Для проверки первого свойства воспользуемся некоторыми особенностями плоских графов. Из [[Формула_Эйлера | формулы Эйлера]] для плоских графов, в частном случае триангуляции, ограниченной тремя ребрами, следует, что число вершин $N$ и число ребер $e$ связаны соотношением <br />
$e = 3N - 6$.<br />
Пока в триангнуляции есть внутренние вершины (в противном случае задача тривиальна), степень каждой из трех граничных вершин не меньше трех. Поскольку существует $3N - 6$ ребер, а каждое ребро инцидентно двум вершинам, то сумма степеней всех вершин меньше $6N$. Отсюда сразу следует, что не менее $ \frac{N}{2}$ вершин имеет степень меньше 12. Следовательно, пусть $K = 12$. Пусть также $v$ {{---}} число выбранных вершин. Поскольку каждой из них инцидентно не более $K-1 = 11$ ребер, а три граничные вершины не выбираются, то мы имеем <br />
$v \ge \left \lfloor \frac{1}{12}(\frac{N}{2} - 3) \right \rfloor $.<br />
Следовательно, $a \cong 1 - \frac{1}{24} < 0,959 < 1$, что доказывает справедливость свойства 1.<br />
'''2. ''' Выполнение второго свойства обеспечивается тривиально. Поскольку удаление вершины со степенью меньше $K$ приводит к образованию многоугольника с числом ребер менее $K$, то каждый из удаленных треугольников пересекает не более $K - 2 = H$ новых треугольников.<br />
}}</wikitex><br />
<br />
===Поиск===<br />
<wikitex>После построения структуры легко понять, как в ней происходит поиск. Элементарной операцией здесь является определение принадлежности треугольнику. Очевидно, что она выполняется константное время. Сначала мы локализуемся в треугольнике $S_1$. После этого мы строим путь от корневой вершины до листа следующим образом: находясь в какой-либо вершине $z$, просмотрим всех ее детей на принадлежность точки соответствующему треугольнику и, так как точка может находиться лишь в одном треугольнике конкретной триангуляции, перейдем в эту вершину, и продолжим поиск.<br />
Этот поиск также можно рассматривать как последовательную локализацию в триангуляциях $S_1, \dots, S_{h(N)}$, откуда и происходит название самого метода.<br />
</wikitex><br />
====Псевдокод====<br />
<wikitex>Пусть все потомки узла $u$ из $T$ собраны в список successors(u), а triangle(u) обозначает треугольник, соответствующий узлу $u$. Тогда алгоритм поиска может выглядеть следующим образом: </wikitex><br />
procedure localization(z)<br />
if (z not in triangle(root))<br />
z in infinite region<br />
else<br />
u = root<br />
while (successors(u) != null)<br />
for (v in successors(u))<br />
if (z in triangle(v))<br />
u = v<br />
return u</div>Kabanovhttp://neerc.ifmo.ru/wiki/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Kabanov&diff=44739Участник:Kabanov2015-01-20T18:45:26Z<p>Kabanov: </p>
<hr />
<div>== Описание ==<br />
Сумма Минковского позволяет решать задачу Motion Planning, в случае, когда робота нельзя поворачивать. Таким образом, каждой точке <tex>(x, y)</tex> ставится в соответствие фигура робота <tex>R(x, y)</tex>, с точкой привязки помещенной в точку <tex>(x, y)</tex>.<br />
{{Определение<br />
|definition=Для заданного робота <tex>R</tex> и препятствия <tex>P</tex>, '''К-препятствием''' называется множество точек, будучи помещенным в которые, робот заденет препятствие: <br />
<tex>CP := \{(x, y) : R(x, y) \cap P \neq \varnothing\}</tex><br />
}}<br />
{{Определение<br />
|definition='''Суммой Минковского''' двух множеств <tex>S_1 \subset R^2, S_2 \subset R^2</tex> называется множество <tex>S_1 \oplus S_2 := \{p + q : p \in S_1, q \in S_2\}</tex>, где <tex>p + q</tex> обозначает векторную сумму.<br />
}}{{Определение<br />
|definition=<br />
'''Отрицанием''' множества <tex>S \subset R^2</tex> называется множество <tex>-S := \{-p : p \in S\}</tex>, где <tex>-p</tex> обозначает векторное отрицание.<br />
}}<br />
{{Теорема<br />
|statement=Для заданного робота <tex>R</tex> и препятствия <tex>P</tex>, К-препятствием является множество <tex>P \oplus (-R(0, 0))</tex>.<br />
|proof=<br />
[[Файл:minkowski_sum.png | right | 150px]]<br />
Необходимо доказать, что робот <tex>R(x, y)</tex> пересекает препятствие <tex>P</tex> в том и только в том случае, если <tex>(x, y) \in P \oplus (-R(0, 0))</tex>.<br />
<br />
Пусть робот задевает препятствие, и точка <tex>q = (q_x , q_y)</tex> является точкой пересечения. Тогда, так как <tex>q \in R(x, y)</tex>, то <tex>(q_x - x, q_y - y) \in R(0,0)</tex>, или <tex>(x - q_x , y - q_y) \in -R(0,0)</tex>. А заметив, что <tex>q \in P</tex>, получаем <tex>(x, y) \in P \oplus (-R(0,0))</tex>.<br />
В обратную сторону, пусть <tex>(x, y) \in P \oplus (-R(0,0))</tex>, тогда существуют точки <tex>(r_x , r_y) \in R(0, 0)</tex> и <tex>(p_x , p_y) \in P</tex> такие, что <tex>(x, y) = (p_x - r_x , p_y - r_y)</tex> или <tex>(p_x , p_y) = (x + r_x , y + r_y)</tex>, а это означает, что <tex>R(x, y)</tex> пересекает <tex>P</tex>.<br />
}}<br />
<br />
Допустим, для простоты, что робот и все препятствия выпуклые, а позже обобщим для невыпуклых фигур.<br />
{{Теорема<br />
|statement=<br />
Пусть заданы две выпуклые фигуры <tex>P</tex> и <tex>R</tex>, с числом вершин <tex>n</tex> и <tex>m</tex> соответственно. Тогда суммой Минковского <tex>P \oplus R</tex> является выпуклая фигура с не более чем <tex>m + n</tex> вершинами.<br />
|proof=<br />
[[Файл:minkowski_extreme.png | right | 200px]]<br />
Для начала заметим, что любая крайняя точка в направлении вектора <tex>\vec{d}</tex> есть сумма крайних точек фигур в этом направлении. Убедиться в этом можно спроецировав обе фигуры на вектор <tex>\vec{d}</tex>.<br />
<br />
Теперь рассмотрим произвольное ребро <tex>e</tex> из <tex>P \oplus R</tex>. Оно является крайним в направлении к своей нормали, а значит оно образовано крайними точками фигур, и хотя бы у одной из фигур должно быть ребро, которое является крайним в этом направлении. Сопоставим <tex>e</tex> с этим ребром. Тогда сопоставив таким образом всем ребрам <tex>P \oplus R</tex> ребра исходных фигур, получаем что всего ребер в <tex>P \oplus R</tex> не более чем <tex>m + n</tex>, так как каждое ребро исходных фигур использовалось не более раза.<br />
}}<br />
<br />
== Псевдокод ==<br />
i = j = 0<br />
V[n] = V[0], V[n+1] = V[1], W[n] = W[0], W[n+1] = W[1]<br />
while i < n or j < m do<br />
add V[i]+W[j] to answer<br />
if angle(V[i], V[i+1]) < angle(W[j], W[j+1])<br />
++i<br />
else if angle(V[i], V[i+1]) > angle(W[j], W[j+1])<br />
++j<br />
else<br />
++i, ++j<br />
Здесь множества точек <tex>V</tex> и <tex>W</tex> отсортированы в порядке обхода против часовой стрелки, причем первым элементом в обоих массивах является самая левая нижняя точка множества. Функция angle возвращает значение полярного угла вектора, заданного ее аргументами.<br />
<br />
Корректность алгоритма следует из доказательства предыдущей теоремы, а время работы равно <tex>O(n + m)</tex>, так как на каждой итерации хотя бы один из индексов <tex>i</tex> и <tex>j</tex> увеличивается.<br />
== Случай невыпуклых фигур ==<br />
Для начала заметим следующий факт: <br />
<tex>S_1 \oplus (S_2 \cup S_3) = (S_1 \oplus S_2) \cup (S_1 \oplus S_3)</tex><br />
<br />
В случае, когда одна из фигур невыпукла, её сначала надо затриангулировать, получив <tex>n-2</tex> треугольников. После этого, уже известным алгоритмом, надо построить <tex>n-2</tex> выпуклых фигур с не более чем <tex>m+3</tex> вершинами, которые будут суммами Минковского соответствующих треугольников. Объединение этих выпуклых фигур будет состоять из <tex>O(nm)</tex> вершин.<br />
<br />
В случае, когда обе фигуры невыпуклы, обе эти фигуры надо затриангулировать, получив <tex>n-2</tex> и <tex>m-2</tex> треугольников соответственно. Построив суммы Минковского множеств этих треугольников получим <tex>(n-2)(m-2)</tex> выпуклых фигур, объединение которых состоит из <tex>O(n^2m^2)</tex> вершин.</div>Kabanovhttp://neerc.ifmo.ru/wiki/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Kabanov&diff=44738Участник:Kabanov2015-01-20T18:44:43Z<p>Kabanov: Сумма</p>
<hr />
<div>== Описание ==<br />
Сумма Минковского позволяет решать задачу Motion Planning, в случае, когда робота нельзя поворачивать. Таким образом, каждой точке <tex>(x, y)</tex> ставится в соответствие фигура робота <tex>R(x, y)</tex>, с точкой привязки помещенной в точку <tex>(x, y)</tex>.<br />
{{Определение<br />
|definition=Для заданного робота <tex>R</tex> и препятствия <tex>P</tex>, '''К-препятствием''' называется множество точек, будучи помещенным в которые, робот заденет препятствие: <br />
<tex>CP := \{(x, y) : R(x, y) \cap P \neq \varnothing\}</tex><br />
}}<br />
{{Определение<br />
|definition='''Суммой Минковского''' двух множеств <tex>S_1 \subset R^2, S_2 \subset R^2</tex> называется множество <tex>S_1 \oplus S_2 := \{p + q : p \in S_1, q \in S_2\}</tex>, где <tex>p + q</tex> обозначает векторную сумму.<br />
}}{{Определение<br />
|definition=<br />
'''Отрицанием''' множества <tex>S \subset R^2</tex> называется множество <tex>-S := \{-p : p \in S\}</tex>, где <tex>-p</tex> обозначает векторное отрицание.<br />
}}<br />
{{Теорема<br />
|statement=Для заданного робота <tex>R</tex> и препятствия <tex>P</tex>, К-препятствием является множество <tex>P \oplus (-R(0, 0))</tex>.<br />
|proof=<br />
[[Файл:minkowski_sum.png | right | 100px]]<br />
Необходимо доказать, что робот <tex>R(x, y)</tex> пересекает препятствие <tex>P</tex> в том и только в том случае, если <tex>(x, y) \in P \oplus (-R(0, 0))</tex>.<br />
<br />
Пусть робот задевает препятствие, и точка <tex>q = (q_x , q_y)</tex> является точкой пересечения. Тогда, так как <tex>q \in R(x, y)</tex>, то <tex>(q_x - x, q_y - y) \in R(0,0)</tex>, или <tex>(x - q_x , y - q_y) \in -R(0,0)</tex>. А заметив, что <tex>q \in P</tex>, получаем <tex>(x, y) \in P \oplus (-R(0,0))</tex>.<br />
В обратную сторону, пусть <tex>(x, y) \in P \oplus (-R(0,0))</tex>, тогда существуют точки <tex>(r_x , r_y) \in R(0, 0)</tex> и <tex>(p_x , p_y) \in P</tex> такие, что <tex>(x, y) = (p_x - r_x , p_y - r_y)</tex> или <tex>(p_x , p_y) = (x + r_x , y + r_y)</tex>, а это означает, что <tex>R(x, y)</tex> пересекает <tex>P</tex>.<br />
}}<br />
<br />
Допустим, для простоты, что робот и все препятствия выпуклые, а позже обобщим для невыпуклых фигур.<br />
{{Теорема<br />
|statement=<br />
Пусть заданы две выпуклые фигуры <tex>P</tex> и <tex>R</tex>, с числом вершин <tex>n</tex> и <tex>m</tex> соответственно. Тогда суммой Минковского <tex>P \oplus R</tex> является выпуклая фигура с не более чем <tex>m + n</tex> вершинами.<br />
|proof=<br />
[[Файл:minkowski_extreme.png | right | 200px]]<br />
Для начала заметим, что любая крайняя точка в направлении вектора <tex>\vec{d}</tex> есть сумма крайних точек фигур в этом направлении. Убедиться в этом можно спроецировав обе фигуры на вектор <tex>\vec{d}</tex>.<br />
<br />
Теперь рассмотрим произвольное ребро <tex>e</tex> из <tex>P \oplus R</tex>. Оно является крайним в направлении к своей нормали, а значит оно образовано крайними точками фигур, и хотя бы у одной из фигур должно быть ребро, которое является крайним в этом направлении. Сопоставим <tex>e</tex> с этим ребром. Тогда сопоставив таким образом всем ребрам <tex>P \oplus R</tex> ребра исходных фигур, получаем что всего ребер в <tex>P \oplus R</tex> не более чем <tex>m + n</tex>, так как каждое ребро исходных фигур использовалось не более раза.<br />
}}<br />
<br />
== Псевдокод ==<br />
i = j = 0<br />
V[n] = V[0], V[n+1] = V[1], W[n] = W[0], W[n+1] = W[1]<br />
while i < n or j < m do<br />
add V[i]+W[j] to answer<br />
if angle(V[i], V[i+1]) < angle(W[j], W[j+1])<br />
++i<br />
else if angle(V[i], V[i+1]) > angle(W[j], W[j+1])<br />
++j<br />
else<br />
++i, ++j<br />
Здесь множества точек <tex>V</tex> и <tex>W</tex> отсортированы в порядке обхода против часовой стрелки, причем первым элементом в обоих массивах является самая левая нижняя точка множества. Функция angle возвращает значение полярного угла вектора, заданного ее аргументами.<br />
<br />
Корректность алгоритма следует из доказательства предыдущей теоремы, а время работы равно <tex>O(n + m)</tex>, так как на каждой итерации хотя бы один из индексов <tex>i</tex> и <tex>j</tex> увеличивается.<br />
== Случай невыпуклых фигур ==<br />
Для начала заметим следующий факт: <br />
<tex>S_1 \oplus (S_2 \cup S_3) = (S_1 \oplus S_2) \cup (S_1 \oplus S_3)</tex><br />
<br />
В случае, когда одна из фигур невыпукла, её сначала надо затриангулировать, получив <tex>n-2</tex> треугольников. После этого, уже известным алгоритмом, надо построить <tex>n-2</tex> выпуклых фигур с не более чем <tex>m+3</tex> вершинами, которые будут суммами Минковского соответствующих треугольников. Объединение этих выпуклых фигур будет состоять из <tex>O(nm)</tex> вершин.<br />
<br />
В случае, когда обе фигуры невыпуклы, обе эти фигуры надо затриангулировать, получив <tex>n-2</tex> и <tex>m-2</tex> треугольников соответственно. Построив суммы Минковского множеств этих треугольников получим <tex>(n-2)(m-2)</tex> выпуклых фигур, объединение которых состоит из <tex>O(n^2m^2)</tex> вершин.</div>Kabanovhttp://neerc.ifmo.ru/wiki/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Kabanov&diff=44737Участник:Kabanov2015-01-20T18:32:44Z<p>Kabanov: /* Ушной метод */</p>
<hr />
<div>'''Триангуляция полигона ''' — декомпозиция многоугольника <tex>P</tex> на множество треугольников, внутренние области которых попарно не пересекаются и объединение которых в совокупности составляет <tex>P</tex>. В строгом смысле слова, вершины этих треугольников должны совпадать с вершинами исходного многоугольника. Триангуляция любого многоугольника не единственна. В этом можно убедиться из примера на рисунке. <br />
<br />
На плоскости задан произвольный многоугольник. Стороны многоугольника не пересекаются. Требуется найти его триангуляцию.<br />
<br />
== Теорема о существовании триангуляции ==<br />
<br />
'''Простым многоугольником''' является фигура, ограниченная одной замкнутой ломаной, стороны которой не пересекаются. Таким образом, случаи многоугольников с дырками в теореме исключаются.<br />
{{Теорема<br />
|about = О существовании триангуляции многоугольника<br />
|statement =<br />
У любого простого <tex>n</tex>-вершинного многоугольника <tex>P</tex> всегда существует триангуляция, причём количество треугольников в ней <tex>n - 2</tex> независимо от самой триангуляции.<br />
|proof=<br />
[[Файл:Proof theorem.jpg|100px|thumb|right]]<br />
Доказательство ведётся индуктивно по <tex>n</tex>. При <tex>n = 3</tex> теорема тривиальна. Рассмотрим случай при <tex>n > 3</tex> и предположим, что теорема выполняется при всех <tex>m < n</tex>. Докажем существование диагонали в многоугольнике <tex>P</tex>. Возьмём самую левую по оси <tex>x</tex> вершину <tex>v</tex> многоугольника <tex>P</tex> и две смежных с ней вершины <tex>u</tex> и <tex>w</tex>. Если отрезок <tex>uw</tex> принадлежит внутренней области <tex>P</tex> — мы нашли диагональ. В противном случае, во внутренней области треугольника <tex>\Delta uwv</tex> или на самом отрезке <tex>uw</tex> содержится одна или несколько вершин <tex>P</tex>. Выберем из них наиболее удалённую от <tex>uw</tex> вершину <tex>v'</tex>. Отрезок, соединяющий <tex>v</tex> и <tex>v'</tex> не может пересекать ребро <tex>P</tex>, поскольку в противном случае одна из вершин этого ребра будет располагаться дальше от <tex>uw</tex>, чем <tex>v'</tex>. Это противоречит условию выбора <tex>v'</tex>. В итоге получаем, что <tex>v'v</tex> — диагональ. <br />
Любая диагональ делит <tex>P</tex> на два многоугольника <tex>P_1</tex> и <tex>P_2</tex>. За <tex>m_1</tex> и <tex>m_2</tex> обозначим количество вершин в <tex>P_1</tex> и <tex>P_2</tex> соответственно. <tex>m_1 < n</tex> и <tex>m_2 < n</tex>, поэтому по предположению индукции у <tex>P_1</tex> и <tex>P_2</tex> существует триангуляция, следовательно и у <tex>P</tex> она существует.<br />
<br />
Докажем, что триангуляция <tex>P</tex> состоит из <tex>n - 2</tex> треугольников. Рассмотрим произвольную диагональ <tex>d</tex> в триангуляции <tex>T_P</tex>. <tex>d</tex> делит <tex>P</tex> на два многоугольника <tex>P_1</tex> и <tex>P_2</tex>, количество вершин в которых <tex>m_1</tex> и <tex>m_2</tex> соответственно. Каждая вершина <tex>P</tex> встречается только в одном из двух многоугольников <tex>P_1</tex> и <tex>P_2</tex>, за исключением тех, которые являются концами <tex>d</tex>, поэтому справедливо следующее: <tex>m_1 + m_2 = n + 2</tex>. По индукции, любая триангуляция <tex>P_i</tex> состоит из <tex>m_i - 2</tex> треугольников, откуда следует, что <tex>T_P</tex>. состоит из <tex>(m_1 - 2) + (m_2 - 2) = n - 2</tex> треугольников.<br />
}}<br />
<br />
== Ушной метод ==<br />
{{Определение<br />
|definition=<br />
Вершина <tex>v_i</tex> называется '''ухом''', если диагональ <tex>v_{i-1}v_{i+1}</tex> лежит строго во внутренней области многоугольника <tex>P</tex><br />
}}<br />
<br />
{{Теорема<br />
|about = О существовании двух ушей многоугольника<br />
|statement =<br />
У любого простого <tex>n</tex>-вершинного многоугольника <tex>P</tex> всегда существует два не пересекающихся между собой уха.<br />
|proof=<br />
[[Файл:Ear case1.jpg|200px|thumb|left]]<br />
[[Файл:Ear_case2.jpg|200px|thumb|right]]<br />
Доказательство будем вести по индукции. Базовый случай: <tex>n = 4</tex>. Предположим для всех многоугольников, количество вершин в которых не больше <tex>n</tex>, теорема верна. Рассмотрим многоугольник <tex>P</tex>, в котором <tex>n+1</tex> вершина. Далее возможны два случая:<br />
* Произвольная выпуклая вершина <tex>v_i</tex> многоугольника <tex>P</tex> является ухом. Отрезав это ухо, мы уменьшим число вершин <tex>P</tex> на одну. В результате, получиv <tex>n</tex>-вершинный многоугольник <tex>P'</tex>. По предположению индукции у него существует два непересекающихся уха. Учитывая, что уши <tex>P'</tex> являются ушами и <tex>P</tex>, несложно заметить, что для <tex>P</tex> теорема верна.<br />
* Произвольная выпуклая вершина <tex>v_i</tex> многоугольника <tex>P</tex> не является ухом. В таком случае в треугольнике <tex>\Delta v_{i-1}v_{i}v_{i+1}</tex> лежат вершины, принадлежащие <tex>P</tex>. Из этих вершин выберем вершину <tex>q</tex>, которая будет ближе всего к <tex>v_i</tex>. Проведём отрезок <tex>Q</tex>, который разделит <tex>P</tex> на два многоугольника: <tex>P_1</tex> и <tex>P_2</tex>. В каждом из них будет не более <tex>n</tex> вершин, следовательно у каждого будет по два непересекающихся уха. Даже если предположить, что ухо из <tex>P_1</tex> и ухо из <tex>P_2</tex> будут пересекаться по стороне <tex>v_{i}q</tex>, в <tex>P</tex> всё равно будет не менее двух непересекающихся ушей.<br />
}}<br />
<br />
==== Идея ====<br />
Рассмотрим все вершины многоугольника <tex>P</tex>, и где возможно, будем отрезать уши до тех пор, пока <tex>P</tex> не станет треугольником.<br />
<br />
Будем рассматривать вершины многоугольника в порядке обхода. Индексирование вершин для удобства будем вести по модулю <tex>n</tex>, т.е. <tex>v_{-1} = v_{n-1}</tex> и <tex>v_0 = v_n</tex>. Если вершина <tex>v_i</tex> является ухом, построим диагональ <tex>v_{i+1}v_{i-1}</tex> и отрежем треугольник <tex>\Delta v_{i-1}v_{i}v_{i+1}</tex> от <tex>P</tex>. В противном случае переходим к следующей вершине <tex>v_{i+1}</tex> в порядке обхода. <br />
<br />
==== Алгоритм ====<br />
При проверке каждой вершину следует для начала проверить, является ли она выпуклой, в противном случае её просто нет надобности рассматривать в качестве уха. Это несложно сделать, воспользовавшись [[Предикат_"левый_поворот"|левым поворотом]]. "Ушную" проверку вершины будем осуществлять алгоритмом принадлежности точки <tex>n</tex>-угольнику (в нашем случае треугольнику). В качестве поддерживаемых структур удобно хранить DCEL, в котором будем строить новые диагонали, и список вершин с двойными связями, аналогичный DCEL по построению.<br />
<br />
==== Псевдокод ====<br />
DCVL D1 //список вершин doubly connected vertex list по аналогии с DCEL<br />
DCEL D2<br />
Construct(D1);<br />
Construct(D2);<br />
vertex v = random_vertex_of(D1);<br />
while size_of(D1) <tex> \neq </tex> 3<br />
if IsConvex(v) //проверка на выпуклость<br />
for each Vertex v_i in D1<br />
if v_i <tex> \neq </tex> v, v.prev(), v.next() //проверка всех вершин на <br />
and v_i <tex>\in </tex> Triangle(v, v.prev(), v.next())//принадлежность треугольнику, <br />
//одной из вершин которого<br />
//является потенциальное ухо v.<br />
edge e = new edge(v.prev, v.next)<br />
Insert e in D2;<br />
v = v.next<br />
Delete v.prev from D1<br />
<br />
==== Корректность ====<br />
При нахождении каждого уха от многоугольника <tex>P</tex> отрезается треугольник, состоящий из самого уха и его двух смежных вершин. Существование ушей в свою очередь следует из теоремы, доказанной выше. В конце алгоритма, когда все уши от <tex>P</tex> отрезаны, остается только один треугольник. Как несложно видеть, триангуляция выстраивается корректно.<br />
<br />
==== Оценка работы ====<br />
Изначально в многоугольнике содержится <tex>\mathcal{O}(n)</tex> ушей. Нетрудно понять, что в процессе отрезания ушей, смежные точки могут тоже становиться ушами. В результате триангуляции образуется <tex>n - 3</tex> диагонали, соответственно максимальное количество вершин, которые в процессе могут становиться ушами <tex>2n - 6</tex>. Итого общее количество ушей будет <tex>\mathcal{O}(n)</tex>. Определить, является ли вершина ухом можно за <tex>\mathcal{O}(n)</tex>, поскольку используется алгоритм определения принадлежности точки треугольнику — это <tex>\mathcal{O}(1)</tex>. Таким образом общий процесс отрезания ушей займёт <tex>\mathcal{O}(n^2)</tex>. Невыпуклых вершин всего <tex>\mathcal{O}(n)</tex>, каждая из них обрабатывается за константу, поэтому общее время для их обработки <tex>\mathcal{O}(n)</tex>. Списки рёбер и вершин строятся за линейное время, добавление ребра и удаление вершины в каждом из них работает за константу. Общее время <tex>\mathcal{O}(n^2)</tex>. Поскольку храним только два списка — память линейная.<br />
<br />
[[Категория: Вычислительная геометрия]]</div>Kabanovhttp://neerc.ifmo.ru/wiki/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Kabanov&diff=44736Участник:Kabanov2015-01-20T18:31:39Z<p>Kabanov: </p>
<hr />
<div>'''Триангуляция полигона ''' — декомпозиция многоугольника <tex>P</tex> на множество треугольников, внутренние области которых попарно не пересекаются и объединение которых в совокупности составляет <tex>P</tex>. В строгом смысле слова, вершины этих треугольников должны совпадать с вершинами исходного многоугольника. Триангуляция любого многоугольника не единственна. В этом можно убедиться из примера на рисунке. <br />
<br />
На плоскости задан произвольный многоугольник. Стороны многоугольника не пересекаются. Требуется найти его триангуляцию.<br />
<br />
== Теорема о существовании триангуляции ==<br />
<br />
'''Простым многоугольником''' является фигура, ограниченная одной замкнутой ломаной, стороны которой не пересекаются. Таким образом, случаи многоугольников с дырками в теореме исключаются.<br />
{{Теорема<br />
|about = О существовании триангуляции многоугольника<br />
|statement =<br />
У любого простого <tex>n</tex>-вершинного многоугольника <tex>P</tex> всегда существует триангуляция, причём количество треугольников в ней <tex>n - 2</tex> независимо от самой триангуляции.<br />
|proof=<br />
[[Файл:Proof theorem.jpg|100px|thumb|right]]<br />
Доказательство ведётся индуктивно по <tex>n</tex>. При <tex>n = 3</tex> теорема тривиальна. Рассмотрим случай при <tex>n > 3</tex> и предположим, что теорема выполняется при всех <tex>m < n</tex>. Докажем существование диагонали в многоугольнике <tex>P</tex>. Возьмём самую левую по оси <tex>x</tex> вершину <tex>v</tex> многоугольника <tex>P</tex> и две смежных с ней вершины <tex>u</tex> и <tex>w</tex>. Если отрезок <tex>uw</tex> принадлежит внутренней области <tex>P</tex> — мы нашли диагональ. В противном случае, во внутренней области треугольника <tex>\Delta uwv</tex> или на самом отрезке <tex>uw</tex> содержится одна или несколько вершин <tex>P</tex>. Выберем из них наиболее удалённую от <tex>uw</tex> вершину <tex>v'</tex>. Отрезок, соединяющий <tex>v</tex> и <tex>v'</tex> не может пересекать ребро <tex>P</tex>, поскольку в противном случае одна из вершин этого ребра будет располагаться дальше от <tex>uw</tex>, чем <tex>v'</tex>. Это противоречит условию выбора <tex>v'</tex>. В итоге получаем, что <tex>v'v</tex> — диагональ. <br />
Любая диагональ делит <tex>P</tex> на два многоугольника <tex>P_1</tex> и <tex>P_2</tex>. За <tex>m_1</tex> и <tex>m_2</tex> обозначим количество вершин в <tex>P_1</tex> и <tex>P_2</tex> соответственно. <tex>m_1 < n</tex> и <tex>m_2 < n</tex>, поэтому по предположению индукции у <tex>P_1</tex> и <tex>P_2</tex> существует триангуляция, следовательно и у <tex>P</tex> она существует.<br />
<br />
Докажем, что триангуляция <tex>P</tex> состоит из <tex>n - 2</tex> треугольников. Рассмотрим произвольную диагональ <tex>d</tex> в триангуляции <tex>T_P</tex>. <tex>d</tex> делит <tex>P</tex> на два многоугольника <tex>P_1</tex> и <tex>P_2</tex>, количество вершин в которых <tex>m_1</tex> и <tex>m_2</tex> соответственно. Каждая вершина <tex>P</tex> встречается только в одном из двух многоугольников <tex>P_1</tex> и <tex>P_2</tex>, за исключением тех, которые являются концами <tex>d</tex>, поэтому справедливо следующее: <tex>m_1 + m_2 = n + 2</tex>. По индукции, любая триангуляция <tex>P_i</tex> состоит из <tex>m_i - 2</tex> треугольников, откуда следует, что <tex>T_P</tex>. состоит из <tex>(m_1 - 2) + (m_2 - 2) = n - 2</tex> треугольников.<br />
}}<br />
<br />
== Ушной метод ==<br />
{{Определение<br />
|definition=<br />
Вершина <tex>v_i</tex> называется '''ухом''', если диагональ <tex>v_{i-1}v_{i+1}</tex> лежит строго во внутренней области многоугольника <tex>P</tex><br />
}}<br />
[[Файл:Ear1.jpg|350px|thumb|left|В первом случае выделенная вершина является ухом, в остальных нет]]<br />
<br />
{{Теорема<br />
|about = О существовании двух ушей многоугольника<br />
|statement =<br />
У любого простого <tex>n</tex>-вершинного многоугольника <tex>P</tex> всегда существует два не пересекающихся между собой уха.<br />
|proof=<br />
[[Файл:Ear case1.jpg|200px|thumb|left]]<br />
[[Файл:Ear_case2.jpg|200px|thumb|right]]<br />
Доказательство будем вести по индукции. Базовый случай: <tex>n = 4</tex>. Предположим для всех многоугольников, количество вершин в которых не больше <tex>n</tex>, теорема верна. Рассмотрим многоугольник <tex>P</tex>, в котором <tex>n+1</tex> вершина. Далее возможны два случая:<br />
* Произвольная выпуклая вершина <tex>v_i</tex> многоугольника <tex>P</tex> является ухом. Отрезав это ухо, мы уменьшим число вершин <tex>P</tex> на одну. В результате, получиv <tex>n</tex>-вершинный многоугольник <tex>P'</tex>. По предположению индукции у него существует два непересекающихся уха. Учитывая, что уши <tex>P'</tex> являются ушами и <tex>P</tex>, несложно заметить, что для <tex>P</tex> теорема верна.<br />
* Произвольная выпуклая вершина <tex>v_i</tex> многоугольника <tex>P</tex> не является ухом. В таком случае в треугольнике <tex>\Delta v_{i-1}v_{i}v_{i+1}</tex> лежат вершины, принадлежащие <tex>P</tex>. Из этих вершин выберем вершину <tex>q</tex>, которая будет ближе всего к <tex>v_i</tex>. Проведём отрезок <tex>Q</tex>, который разделит <tex>P</tex> на два многоугольника: <tex>P_1</tex> и <tex>P_2</tex>. В каждом из них будет не более <tex>n</tex> вершин, следовательно у каждого будет по два непересекающихся уха. Даже если предположить, что ухо из <tex>P_1</tex> и ухо из <tex>P_2</tex> будут пересекаться по стороне <tex>v_{i}q</tex>, в <tex>P</tex> всё равно будет не менее двух непересекающихся ушей.<br />
}}<br />
<br />
==== Идея ====<br />
Рассмотрим все вершины многоугольника <tex>P</tex>, и где возможно, будем отрезать уши до тех пор, пока <tex>P</tex> не станет треугольником.<br />
<br />
Будем рассматривать вершины многоугольника в порядке обхода. Индексирование вершин для удобства будем вести по модулю <tex>n</tex>, т.е. <tex>v_{-1} = v_{n-1}</tex> и <tex>v_0 = v_n</tex>. Если вершина <tex>v_i</tex> является ухом, построим диагональ <tex>v_{i+1}v_{i-1}</tex> и отрежем треугольник <tex>\Delta v_{i-1}v_{i}v_{i+1}</tex> от <tex>P</tex>. В противном случае переходим к следующей вершине <tex>v_{i+1}</tex> в порядке обхода. <br />
<br />
==== Алгоритм ====<br />
При проверке каждой вершину следует для начала проверить, является ли она выпуклой, в противном случае её просто нет надобности рассматривать в качестве уха. Это несложно сделать, воспользовавшись [[Предикат_"левый_поворот"|левым поворотом]]. "Ушную" проверку вершины будем осуществлять алгоритмом принадлежности точки <tex>n</tex>-угольнику (в нашем случае треугольнику). В качестве поддерживаемых структур удобно хранить DCEL, в котором будем строить новые диагонали, и список вершин с двойными связями, аналогичный DCEL по построению.<br />
<br />
==== Псевдокод ====<br />
DCVL D1 //список вершин doubly connected vertex list по аналогии с DCEL<br />
DCEL D2<br />
Construct(D1);<br />
Construct(D2);<br />
vertex v = random_vertex_of(D1);<br />
while size_of(D1) <tex> \neq </tex> 3<br />
if IsConvex(v) //проверка на выпуклость<br />
for each Vertex v_i in D1<br />
if v_i <tex> \neq </tex> v, v.prev(), v.next() //проверка всех вершин на <br />
and v_i <tex>\in </tex> Triangle(v, v.prev(), v.next())//принадлежность треугольнику, <br />
//одной из вершин которого<br />
//является потенциальное ухо v.<br />
edge e = new edge(v.prev, v.next)<br />
Insert e in D2;<br />
v = v.next<br />
Delete v.prev from D1<br />
<br />
==== Корректность ====<br />
При нахождении каждого уха от многоугольника <tex>P</tex> отрезается треугольник, состоящий из самого уха и его двух смежных вершин. Существование ушей в свою очередь следует из теоремы, доказанной выше. В конце алгоритма, когда все уши от <tex>P</tex> отрезаны, остается только один треугольник. Как несложно видеть, триангуляция выстраивается корректно.<br />
<br />
==== Оценка работы ====<br />
Изначально в многоугольнике содержится <tex>\mathcal{O}(n)</tex> ушей. Нетрудно понять, что в процессе отрезания ушей, смежные точки могут тоже становиться ушами. В результате триангуляции образуется <tex>n - 3</tex> диагонали, соответственно максимальное количество вершин, которые в процессе могут становиться ушами <tex>2n - 6</tex>. Итого общее количество ушей будет <tex>\mathcal{O}(n)</tex>. Определить, является ли вершина ухом можно за <tex>\mathcal{O}(n)</tex>, поскольку используется алгоритм определения принадлежности точки треугольнику — это <tex>\mathcal{O}(1)</tex>. Таким образом общий процесс отрезания ушей займёт <tex>\mathcal{O}(n^2)</tex>. Невыпуклых вершин всего <tex>\mathcal{O}(n)</tex>, каждая из них обрабатывается за константу, поэтому общее время для их обработки <tex>\mathcal{O}(n)</tex>. Списки рёбер и вершин строятся за линейное время, добавление ребра и удаление вершины в каждом из них работает за константу. Общее время <tex>\mathcal{O}(n^2)</tex>. Поскольку храним только два списка — память линейная.<br />
<br />
[[Категория: Вычислительная геометрия]]</div>Kabanovhttp://neerc.ifmo.ru/wiki/index.php?title=%D0%A1%D1%83%D0%BC%D0%BC%D0%B0_%D0%9C%D0%B8%D0%BD%D0%BA%D0%BE%D0%B2%D1%81%D0%BA%D0%BE%D0%B3%D0%BE_(%D0%BE%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5,_%D0%B2%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5)&diff=44735Сумма Минковского (определение, вычисление)2015-01-20T17:31:21Z<p>Kabanov: </p>
<hr />
<div>== Описание ==<br />
{|align="center"<br />
|-valign="top"<br />
|Сумма Минковского позволяет решать задачу Motion Planning, в случае, когда робота нельзя поворачивать. Таким образом, каждой точке <tex>(x, y)</tex> ставится в соответствие фигура робота <tex>R(x, y)</tex>, с точкой привязки помещенной в точку <tex>(x, y)</tex>.<br />
<br />
{{Определение<br />
|definition=<br />
Для заданного робота <tex>R</tex> и препятствия <tex>P</tex>, '''К-препятствием''' называется множество точек, будучи помещенным в которые, робот заденет препятствие: <br />
<tex>CP := \{(x, y) : R(x, y) \cap P \neq \varnothing\}</tex>}}<br />
|[[Файл:minkowski_sum.png | 230px]]<br />
|}<br />
{{Определение<br />
|definition=<br />
'''Суммой Минковского''' двух множеств <tex>S_1 \subset R^2, S_2 \subset R^2</tex> называется множество <tex>S_1 \oplus S_2 := \{p + q : p \in S_1, q \in S_2\}</tex>, где <tex>p + q</tex> обозначает векторную сумму.<br />
}}{{Определение<br />
|definition=<br />
'''Отрицанием''' множества <tex>S \subset R^2</tex> называется множество <tex>-S := \{-p : p \in S\}</tex>, где <tex>-p</tex> обозначает векторное отрицание.<br />
}}<br />
<br />
{{Теорема<br />
|statement=<br />
Для заданного робота <tex>R</tex> и препятствия <tex>P</tex>, К-препятствием является множество <tex>P \oplus (-R(0, 0))</tex>.<br />
|proof=<br />
Необходимо доказать, что робот <tex>R(x, y)</tex> пересекает препятствие <tex>P</tex> в том и только в том случае, если <tex>(x, y) \in P \oplus (-R(0, 0))</tex>.<br />
<br />
Пусть робот задевает препятствие, и точка <tex>q = (q_x , q_y)</tex> является точкой пересечения. Тогда, так как <tex>q \in R(x, y)</tex>, то <tex>(q_x - x, q_y - y) \in R(0,0)</tex>, или <tex>(x - q_x , y - q_y) \in -R(0,0)</tex>. А заметив, что <tex>q \in P</tex>, получаем <tex>(x, y) \in P \oplus (-R(0,0))</tex>.<br />
В обратную сторону, пусть <tex>(x, y) \in P \oplus (-R(0,0))</tex>, тогда существуют точки <tex>(r_x , r_y) \in R(0, 0)</tex> и <tex>(p_x , p_y) \in P</tex> такие, что <tex>(x, y) = (p_x - r_x , p_y - r_y)</tex> или <tex>(p_x , p_y) = (x + r_x , y + r_y)</tex>, а это означает, что <tex>R(x, y)</tex> пересекает <tex>P</tex>.<br />
}}<br />
<br />
<br />
Допустим, для простоты, что робот и все препятствия выпуклые, а позже обобщим для невыпуклых фигур.<br />
<br />
{{Теорема<br />
|statement=<br />
Пусть заданы две выпуклые фигуры <tex>P</tex> и <tex>R</tex>, с числом вершин <tex>n</tex> и <tex>m</tex> соответственно. Тогда суммой Минковского <tex>P \oplus R</tex> является выпуклая фигура с не более чем <tex>m + n</tex> вершинами.<br />
|proof=<br />
[[Файл:minkowski_extreme.png | right | 350px]]<br />
Для начала заметим, что любая крайняя точка в направлении вектора <tex>\vec{d}</tex> есть сумма крайних точек фигур в этом направлении. Убедиться в этом можно спроецировав обе фигуры на вектор <tex>\vec{d}</tex>.<br />
<br />
Теперь рассмотрим произвольное ребро <tex>e</tex> из <tex>P \oplus R</tex>. Оно является крайним в направлении к своей нормали, а значит оно образовано крайними точками фигур, и хотя бы у одной из фигур должно быть ребро, которое является крайним в этом направлении. Сопоставим <tex>e</tex> с этим ребром. Тогда сопоставив таким образом всем ребрам <tex>P \oplus R</tex> ребра исходных фигур, получаем что всего ребер в <tex>P \oplus R</tex> не более чем <tex>m + n</tex>, так как каждое ребро исходных фигур использовалось не более раза.<br />
}}<br />
<br />
== Псевдокод ==<br />
<br />
i = j = 0<br />
V[n] = V[0], V[n+1] = V[1], W[n] = W[0], W[n+1] = W[1]<br />
while i < n or j < m do<br />
add V[i]+W[j] to answer<br />
if angle(V[i], V[i+1]) < angle(W[j], W[j+1])<br />
++i<br />
else if angle(V[i], V[i+1]) > angle(W[j], W[j+1])<br />
++j<br />
else<br />
++i, ++j<br />
<br />
Здесь множества точек <tex>V</tex> и <tex>W</tex> отсортированы в порядке обхода против часовой стрелки, причем первым элементом в обоих массивах является самая левая нижняя точка множества. Функция angle возвращает значение полярного угла вектора, заданного ее аргументами.<br />
<br />
Корректность алгоритма следует из доказательства предыдущей теоремы, а время работы равно <tex>O(n + m)</tex>, так как на каждой итерации хотя бы один из индексов <tex>i</tex> и <tex>j</tex> увеличивается.<br />
<br />
== Случай невыпуклых фигур ==<br />
<br />
Для начала заметим следующий факт: <br />
<tex>S_1 \oplus (S_2 \cup S_3) = (S_1 \oplus S_2) \cup (S_1 \oplus S_3)</tex><br />
<br />
В случае, когда одна из фигур невыпукла, её сначала надо затриангулировать, получив <tex>n-2</tex> треугольников. После этого, уже известным алгоритмом, надо построить <tex>n-2</tex> выпуклых фигур с не более чем <tex>m+3</tex> вершинами, которые будут суммами Минковского соответствующих треугольников. Объединение этих выпуклых фигур будет состоять из <tex>O(nm)</tex> вершин.<br />
<br />
В случае, когда обе фигуры невыпуклы, обе эти фигуры надо затриангулировать, получив <tex>n-2</tex> и <tex>m-2</tex> треугольников соответственно. Построив суммы Минковского множеств этих треугольников получим <tex>(n-2)(m-2)</tex> выпуклых фигур, объединение которых состоит из <tex>O(n^2m^2)</tex> вершин.<br />
<br />
{|align="center"<br />
|-valign="top"<br />
|[[Файл:minkowski_medium_example.png | thumb | 550px | Пример суммы Минковского с O(nm) вершинами]]<br />
|[[Файл:minkowski_hard_example.png | thumb | 550px | Пример суммы Минковского с O(n<sup>2</sup>m<sup>2</sup>) вершинами]]<br />
|}<br />
<br />
== Ссылки ==<br />
* [https://en.wikipedia.org/wiki/Minkowski_addition Английская википедия]<br />
* de Berg, van Kreveld, Overmars, Schwarzkopf "Computational Geometry Algorithms and Applications", p. 290</div>Kabanovhttp://neerc.ifmo.ru/wiki/index.php?title=%D0%94%D0%B2%D1%83%D1%81%D1%82%D0%BE%D1%80%D0%BE%D0%BD%D0%BD%D0%B8%D0%B9_%D0%B4%D0%B5%D1%82%D0%B5%D1%80%D0%BC%D0%B8%D0%BD%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BA%D0%BE%D0%BD%D0%B5%D1%87%D0%BD%D1%8B%D0%B9_%D0%B0%D0%B2%D1%82%D0%BE%D0%BC%D0%B0%D1%82&diff=44023Двусторонний детерминированный конечный автомат2015-01-10T20:32:54Z<p>Kabanov: sta</p>
<hr />
<div>{{Определение<br />
|definition=<br />
'''Двусторонний детерминированный конечный автомат (2ДКА)''' (англ. ''Two-way deterministic finite automaton (2DFA)'') {{---}} набор из восьми элементов <tex>M = \langle \Sigma , Q, L, R, s, t, r, \delta \rangle</tex>, где<br />
* <tex>\Sigma</tex> {{---}} алфавит,<br />
* <tex>Q</tex> {{---}} множество состояний,<br />
* <tex>L \notin \Sigma</tex> {{---}} левый маркер конца строки (англ. ''left endmarker''),<br />
* <tex>R \notin \Sigma</tex> {{---}} правый маркер конца строки (англ. ''right endmarker''),<br />
* <tex>s \in Q</tex> — начальное (стартовое) состояние,<br />
* <tex>t \in Q</tex> {{---}} допускающее состояние,<br />
* <tex>r \in Q</tex> — отвергающее состояние,<br />
* <tex>\delta : Q \times \{\Sigma \cup \{L, R\}\} \to Q \times \{left, right\}</tex> {{---}} функция переходов.<br />
}}<br />
Также должны быть удовлетворены следующие условия:<br />
<br />
<tex>\forall q \in Q</tex><br />
* <tex>\delta(q, L) = (u, right)</tex> для некоторого <tex>u \in Q</tex>,<br />
* <tex>\delta(q, R) = (v, left)</tex> для некоторого <tex>v \in Q</tex>,<br />
и <tex>\forall b \in \Sigma \cup \{L\}</tex><br />
* <tex>\delta(t, b) = (t, right)</tex>,<br />
* <tex>\delta(r, b) = (r, right)</tex>,<br />
* <tex>\delta(t, R) = (t, left)</tex>,<br />
* <tex>\delta(r, R) = (r, left)</tex>.<br />
<br />
== Регулярность языка ==<br />
{{Теорема<br />
|statement=Классы языков ДКА и 2ДКА совпадают.<br />
|proof=<br />
Рассмотрим длинную входную строку <tex>w_1</tex> и разобьем на две подстроки <tex>w_1=xz</tex>. Будем считать, что <tex>w_1 = a_1 a_2 a_3 \dots a_n</tex>. Пусть <tex>a_0 = L</tex> и <tex>a_{n+1}=R</tex>. Так как у нас наш автомат может производить чтение в любом направлении, то граница <tex>x</tex> и <tex>z</tex> может быть пересечена несколько раз. Каждый раз, когда автомат пересекает границу справа налево (то есть из <tex>z</tex> в <tex>x</tex>), он выходит из <tex>z</tex> в состояние <tex>q</tex>. Когда пересечение происходит снова слева направо (если оно вообще происходит), то автомат выходит из <tex>x</tex> в состояние <tex>p</tex>. Теперь, если 2ДКА перейдет в <tex>x</tex> в состояние <tex>q</tex> заново, то он снова может появиться в состоянии <tex>p</tex>. Более того, состояние <tex>p</tex> зависит исключительно от <tex>q</tex> и <tex>x</tex>. Обозначим такое отношение через <tex>T_x(q) = p</tex>. Мы может записать все такие отношения в виде конечной таблицы <tex>T_x : Q \cup \{d\} \to Q \cup \{h\}</tex>, где <tex>Q</tex> {{---}} множество состояний 2ДКА, а <tex>d</tex> и <tex>h</tex> будут описаны ниже.<br />
<br />
На входной строке <tex>xz</tex> 2ДКА начнет чтение с левого маркера конца строки. В процессе работы автомата позиция чтения будет меняться. В конце концов это позиция пересечет слева направо границу между <tex>x</tex> и <tex>z</tex>. В первый раз это произойдет в каком-нибудь состоянии, которое будем называть <tex>T_x(d)</tex> (для этого мы и выделили <tex>d</tex>). Так же автомат может никогда не выйти из <tex>x</tex>. В таком случае мы запишем <tex>T_x(d) = h</tex>. Состояние <tex>T_x(d)</tex> дает немного информации о <tex>x</tex>, но только конечное количество, поскольку существует только конечное количество вариантов для <tex>T_x(d)</tex>. Отметим, что <tex>T_x(d)</tex> зависит только от <tex>x</tex> и не зависит от <tex>z</tex>. Если на вход подавалась бы строка <tex>xw</tex> вместо <tex>xz</tex>, то в таком случае при пересечении границы из <tex>x</tex> в <tex>w</tex> автомат также был бы в состоянии <tex>T_x(d)</tex>, потому что его значение до того момента определялось только <tex>x</tex> и до тех пор все, что находится справа от границы никак не влияет.<br />
<br />
Если <tex>T_x(d) = h</tex>, то 2ДКА в бесконечном цикле внутри <tex>x</tex>, и он никогда не допустит и не отвергнет входную строку.<br />
<br />
Предположим, что 2ДКА переходит из <tex>x</tex> в <tex>z</tex> и спустя время переходит обратно в состояние <tex>q</tex>. Если это происходит, то потом:<br />
* либо снова произойдет переход из <tex>x</tex> в некоторое состояние <tex>p</tex>. В таком случае мы определим <tex>T_x(q)=p</tex>.<br />
* либо никогда не перейдет. В таком случае <tex>T_x(q) = h</tex>.<br />
<br />
Ещё раз отметим, что <tex>T_x(q)</tex> зависит только от <tex>x</tex> и <tex>q</tex> и не зависит от <tex>z</tex>. Если автомат переходит в <tex>x</tex> справа в состояние <tex>q</tex>, то тогда он появится заново в состоянии <tex>T_x(q)</tex> (или никогда не перейдет, если <tex>T_x(q) = h</tex>), потому что автомат детерминированный, и его поведение полностью определяется <tex>x</tex> и состоянием, в которое он вошел.<br />
<br />
Если мы запишем <tex>T_x(q)</tex> для каждого состояния <tex>q</tex> вместе с <tex>T_x(d)</tex>, это даст нам всю информацию о <tex>x</tex>, которую можно перенести через границу между <tex>x</tex> и <tex>z</tex>. Все это позволит узнать <tex>T_x(d)</tex> сразу после пересечения границы, а также посмотреть значения <tex>T_x(q)</tex>. Если <tex>y</tex> {{---}} другая строка, такая что <tex>T_y=T_x</tex>, то тогда <tex>x</tex> и <tex>y</tex> будут неразличимы.<br />
<br />
Заметим, что у нас конечное число возможных таблиц<br />
<tex>T_x : Q \cup \{d\} \to Q \cup \{h\}</tex>,<br />
а именно <tex>(k+1)^{k+1}</tex>, где <tex>k</tex> {{---}} размер множество <tex>Q</tex>. Таким образом, у нас конечное количество информации о <tex>x</tex>, которое мы может перенести через границу справа от <tex>x</tex>, и которое закодировано у нас в таблицe <tex>T_x</tex>.<br />
<br />
Отметим также, что если <tex>T_x=T_y</tex> и автомат допускает строку <tex>xz</tex>, то тогда он допускает и строку <tex>yz</tex>, потому что последовательность состояний, перенесенных через границу <tex>x</tex> и <tex>z</tex> (либо <tex>y</tex> и <tex>z</tex>) в любом направлении, полностью определяется таблицами <tex>T_x=T_y</tex> и строкой <tex>z</tex>. Чтобы допустить строку <tex>xz</tex>, автомат должен в какой-то момент прочитать правый маркер конца строки, находясь в допускающем состоянии <tex>t</tex>.<br />
<br />
Теперь мы может использовать теорему Майхилла-Нероуда, чтобы показать, что язык <tex>L(M)</tex> нашего автомата <tex>M</tex> [[Регулярные_языки:_два_определения_и_их_эквивалентность|регулярный]].<br />
<br />
<tex>T_x = T_y \Rightarrow \forall z (M\ \text{accepts}\ xz \Leftrightarrow M\ \text{accepts}\ yz)</tex> <tex> \Leftrightarrow \forall z (xz \in L(M)</tex> <tex> \Leftrightarrow yz \in L(M)) \Leftrightarrow x \equiv_{L(M)} y</tex>, где <tex>\equiv_{L(M)}</tex> {{---}} [[Отношение_эквивалентности|отношение эквивалентности]] на множестве слов в алфавите. Таким образом, если 2 строки имеют одинаковые таблицы, то тогда они эквивалентны отношением <tex>\equiv_{L(M)}</tex>. Поскольку у нас только конечное число таблиц, отношение <tex>\equiv_{L(M)}</tex> имеет только конечное количество [[Отношение_эквивалентности|классов эквивалентности]], самое большее один для каждой таблицы. Следовательно, по теореме Майхилла-Нероуда, <tex>L(M)</tex> {{---}} регулярный язык, что согласно теореме Клини, совпадает с классом и автоматных языков, ч.т.д.<br />
}}<br />
<br />
== Пример ==<br />
Рассмотрим следующий язык <tex>L_n = (a+b)^∗a(a+b)^{n-1}a(a+b)^∗</tex> при <tex>\forall n > 0</tex>.<br />
<br />
Он может быть легко распознан с помощью следующего [[Недетерминированные_конечные_автоматы|недетерменированного конечного автомата]].<br />
<br />
[[Файл:2dfa_example_1.png|600px]]<br />
<br />
Теперь построим на его основе [[Детерминированные_конечные_автоматы|ДКА]]. Мы можем построить автомат <tex>A_n</tex>, который запоминает последние <tex>n</tex> входных символов. Следовательно, когда мы находимся в состоянии, соответствующему подстроке <tex>\sigma_1 \sigma_2 \dots \sigma_n</tex>, и читаем очередной символ <tex>\gamma</tex>, то мы переходим в состояние, которому уже будет соответствовать подстрока <tex>\sigma_2 \sigma_3 \dots \sigma_n \gamma</tex>. Однако, в случае <tex>\sigma_1 = \gamma = a</tex> автомат переходит в допускающее состояние, где в цикле может переходить на любому символу. Следует отметить, что такая стратегия строит ДКА c <tex>2^n + 1</tex> состояниями. Ниже представлен автомат <tex>A_3</tex>, который распознает язык <tex>L_3</tex>.<br />
<br />
[[Файл:2dfa_example_2.png|500px]]<br />
<br />
Покажем, что построенные таким образом автоматы будут минимальными.<br />
* Каждые две попарно различных строки <tex>x</tex> и <tex>y</tex> длины <tex>n</tex> различимы. Чтобы доказать это, достаточно рассмотреть строку <tex>b^{i-1}a</tex>, где <tex>i : 1 \leqslant i \leqslant n</tex> {{---}} самая левая позиция символа, в которой начинают различаться строки <tex>x</tex> и <tex>y</tex>, и проверить, что ровно одна строка <tex>xb^{i-1}a</tex> или <tex>yb^{i-1}a</tex> принадлежит <tex>L_n</tex>.<br />
* Каждая строка длины <tex>n</tex> не принадлежит <tex>L_n</tex> и, следовательно, различима со строкой <tex>a^{n+1}</tex>, которая принадлежит <tex>L_n</tex>.<br />
* Таким образом, <tex>2^n + 1</tex> строк в <tex>\Sigma^n \cup \{a^{n+1}\}</tex> являются попарно различимыми для <tex>L_n</tex>. Как следствие, <tex>2^n + 1</tex> {{---}} минимальное количество состояний для ДКА, который способен распознать язык <tex>L_n</tex>.<br />
<br />
Чтобы определить, что строка <tex>w=c_1 \dots c_n</tex> принадлежит языку <tex>L_n</tex>, нужно для <tex>\forall i = 1, \dots, |w|-n</tex> проверить, что <tex>c_i=c_{i+n}=a</tex>. Строка будет допустимой, если условие сработает хотя бы для одного <tex>i</tex>. Этот алгоритм может быть просто реализован с помощью 2ДКА. Будем для каждого <tex>i</tex> двигаться на <tex>n</tex> позиций вперед, а потом на <tex>n-1</tex> позиций назад до позиции <tex>i+1</tex>. Кроме того, при движении с позиции <tex>i</tex> до <tex>i+n</tex> автомат должен помнить сохраняется ли условие <tex>c_i=a</tex>. Такой подход требует <tex>O(n)</tex> состояний.<br />
<br />
== См. также ==<br />
* [[Локальные автоматы]]<br />
* [[Теорема Клини (совпадение классов автоматных и регулярных языков)]]<br />
<br />
== Источники информации==<br />
* [[wikipedia:Two-way_deterministic_finite_automaton|Wikipedia {{---}} Two-way deterministic finite automaton]]<br />
* [[wikipedia:ru:Теорема_Майхилла_—_Нероуда|Википедия {{---}} Теорема Майхилла-Нероуда]]<br />
* [http://arxiv.org/pdf/1208.2755.pdf Giovanni Pighizzini, ''Two-Way Finite Automata: Old and Recent Results'']<br />
* [http://www.cs.cornell.edu/Courses/cs682/2008sp/Handouts/2DFA.pdf Cornell University, ''Two-Way Finite Automata'']<br />
* [http://webcourse.cs.technion.ac.il/236807/Spring2012/ho/WCFiles/Two-way%20finite%20automata%20new.pdf Liat Peterfreund, ''Two-way finite automata & pebble automata'']<br />
[[Категория: Теория формальных языков]]<br />
[[Категория: Автоматы и регулярные языки]]<br />
[[Категория: Другие автоматы]]</div>Kabanovhttp://neerc.ifmo.ru/wiki/index.php?title=%D0%94%D0%B2%D1%83%D1%81%D1%82%D0%BE%D1%80%D0%BE%D0%BD%D0%BD%D0%B8%D0%B9_%D0%B4%D0%B5%D1%82%D0%B5%D1%80%D0%BC%D0%B8%D0%BD%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BA%D0%BE%D0%BD%D0%B5%D1%87%D0%BD%D1%8B%D0%B9_%D0%B0%D0%B2%D1%82%D0%BE%D0%BC%D0%B0%D1%82&diff=43933Двусторонний детерминированный конечный автомат2015-01-10T09:00:45Z<p>Kabanov: /* Регулярность языка */</p>
<hr />
<div>{{Определение<br />
|definition=<br />
'''Двусторонний детерминированный конечный автомат (2ДКА)''' (англ. ''Two-way deterministic finite automaton (2DFA)'') {{---}} набор из восьми элементов <tex>M = \langle \Sigma , Q, L, R, s, t, r, \delta \rangle</tex>, где<br />
* <tex>\Sigma</tex> {{---}} алфавит,<br />
* <tex>Q</tex> {{---}} множество состояний,<br />
* <tex>L \notin \Sigma</tex> {{---}} левый маркер конца строки (англ. ''left endmarker''),<br />
* <tex>R \notin \Sigma</tex> {{---}} правый маркер конца строки (англ. ''right endmarker''),<br />
* <tex>s \in Q</tex> — начальное (стартовое) состояние,<br />
* <tex>t \in Q</tex> {{---}} допускающее состояние,<br />
* <tex>r \in Q</tex> — отвергающее состояние,<br />
* <tex>\delta : Q \times \{\Sigma \cup \{L, R\}\} \to Q \times \{left, right\}</tex> {{---}} функция переходов.<br />
}}<br />
Также должны быть удовлетворены следующие условия:<br />
<br />
<tex>\forall q \in Q</tex><br />
* <tex>\delta(q, L) = (u, right)</tex> для некоторого <tex>u \in Q</tex>,<br />
* <tex>\delta(q, R) = (v, left)</tex> для некоторого <tex>v \in Q</tex>,<br />
и <tex>\forall b \in \Sigma \cup \{L\}</tex><br />
* <tex>\delta(t, b) = (t, right)</tex>,<br />
* <tex>\delta(r, b) = (r, right)</tex>,<br />
* <tex>\delta(t, R) = (t, left)</tex>,<br />
* <tex>\delta(r, R) = (r, left)</tex>.<br />
<br />
== Регулярность языка ==<br />
{{Теорема<br />
|statement=Классы языков ДКА и 2ДКА совпадают.<br />
|proof=<br />
Рассмотрим длинную входную строку <tex>w_1</tex> и разобьем на две подстроки <tex>w_1=xz</tex>. Будем считать, что <tex>w_1 = a_1 a_2 a_3 \dots a_n</tex>. Пусть <tex>a_0 = L</tex> и <tex>a_{n+1}=R</tex>. Так как у нас наш автомат может производить чтение в любом направлении, то граница <tex>x</tex> и <tex>z</tex> может быть пересечена несколько раз. Каждый раз, когда автомат пересекает границу справа налево (то есть из <tex>z</tex> в <tex>x</tex>), он выходит из <tex>z</tex> в состояние <tex>q</tex>. Когда пересечение происходит снова слева направо (если оно вообще происходит), то автомат выходит из <tex>x</tex> в состояние <tex>p</tex>. Теперь, если 2ДКА перейдет в <tex>x</tex> в состояние <tex>q</tex> заново, то он снова может появиться в состоянии <tex>p</tex>. Более того, состояние <tex>p</tex> зависит исключительно от <tex>q</tex> и <tex>x</tex>. Обозначим такое отношение через <tex>T_x(q) = p</tex>. Мы может записать все такие отношения в виде конечной таблицы <tex>T_x : Q \cup \{d\} \to Q \cup \{h\}</tex>, где <tex>Q</tex> {{---}} множество состояний 2ДКА, а <tex>d</tex> и <tex>h</tex> будут описаны ниже.<br />
<br />
На входной строке <tex>xz</tex> 2ДКА начнет чтение с левого маркера конца строки. В процессе работы автомата позиция чтения будет меняться. В конце концов это позиция пересечет слева направо границу между <tex>x</tex> и <tex>z</tex>. В первый раз это произойдет в каком-нибудь состоянии, которое будем называть <tex>T_x(d)</tex> (для этого мы и выделили <tex>d</tex>). Так же автомат может никогда не выйти из <tex>x</tex>. В таком случае мы запишем <tex>T_x(d) = h</tex>. Состояние <tex>T_x(d)</tex> дает немного информации о <tex>x</tex>, но только конечное количество, поскольку существует только конечное количество вариантов для <tex>T_x(d)</tex>. Отметим, что <tex>T_x(d)</tex> зависит только от <tex>x</tex> и не зависит от <tex>z</tex>. Если на вход подавалась бы строка <tex>xw</tex> вместо <tex>xz</tex>, то в таком случае при пересечении границы из <tex>x</tex> в <tex>w</tex> автомат также был бы в состоянии <tex>T_x(d)</tex>, потому что его значение до того момента определялось только <tex>x</tex> и до тех пор все, что находится справа от границы никак не влияет.<br />
<br />
Если <tex>T_x(d) = h</tex>, то 2ДКА в бесконечном цикле внутри <tex>x</tex>, и он никогда не допустит и не отвергнет входную строку.<br />
<br />
Предположим, что 2ДКА переходит из <tex>x</tex> в <tex>z</tex> и спустя время переходит обратно в состояние <tex>q</tex>. Если это происходит, то потом:<br />
* либо снова произойдет переход из <tex>x</tex> в некоторое состояние <tex>p</tex>. В таком случае мы определим <tex>T_x(q)=p</tex>.<br />
* либо никогда не перейдет. В таком случае <tex>T_x(q) = h</tex>.<br />
<br />
Ещё раз отметим, что <tex>T_x(q)</tex> зависит только от <tex>x</tex> и <tex>q</tex> и не зависит от <tex>z</tex>. Если автомат переходит в <tex>x</tex> справа в состояние <tex>q</tex>, то тогда он появится заново в состоянии <tex>T_x(q)</tex> (или никогда не перейдет, если <tex>T_x(q) = h</tex>), потому что автомат детерминированный, и его поведение полностью определяется <tex>x</tex> и состоянием, в которое он вошел.<br />
<br />
Если мы запишем <tex>T_x(q)</tex> для каждого состояния <tex>q</tex> вместе с <tex>T_x(d)</tex>, это даст нам всю информацию о <tex>x</tex>, которую можно перенести через границу между <tex>x</tex> и <tex>z</tex>. Все это позволит узнать <tex>T_x(d)</tex> сразу после пересечения границы, а также посмотреть значения <tex>T_x(q)</tex>. Если <tex>y</tex> {{---}} другая строка, такая что <tex>T_y=T_x</tex>, то тогда <tex>x</tex> и <tex>y</tex> будут неразличимы.<br />
<br />
Заметим, что у нас конечное число возможных таблиц<br />
<tex>T_x : Q \cup \{d\} \to Q \cup \{h\}</tex>,<br />
а именно <tex>(k+1)^{k+1}</tex>, где <tex>k</tex> {{---}} размер множество <tex>Q</tex>. Таким образом, у нас конечное количество информации о <tex>x</tex>, которое мы может перенести через границу справа от <tex>x</tex>, и которое закодировано у нас в таблицe <tex>T_x</tex>.<br />
<br />
Отметим также, что если <tex>T_x=T_y</tex> и автомат допускает строку <tex>xz</tex>, то тогда он допускает и строку <tex>yz</tex>, потому что последовательность состояний, перенесенных через границу <tex>x</tex> и <tex>z</tex> (либо <tex>y</tex> и <tex>z</tex>) в любом направлении, полностью определяется таблицами <tex>T_x=T_y</tex> и строкой <tex>z</tex>. Чтобы допустить строку <tex>xz</tex>, автомат должен в какой-то момент прочитать правый маркер конца строки, находясь в допускающем состоянии <tex>t</tex>.<br />
<br />
Теперь мы может использовать теорему Майхилла-Нероуда, чтобы показать, что язык <tex>L(M)</tex> нашего автомата <tex>M</tex> [[Регулярные_языки:_два_определения_и_их_эквивалентность|регулярный]].<br />
<br />
<tex>T_x = T_y \Rightarrow \forall z (M\ \text{accepts}\ xz \Leftrightarrow M\ \text{accepts}\ yz)</tex> <tex> \Leftrightarrow \forall z (xz \in L(M)</tex> <tex> \Leftrightarrow yz \in L(M)) \Leftrightarrow x \equiv_{L(M)} y</tex>, где <tex>\equiv_{L(M)}</tex> {{---}} [[Отношение_эквивалентности|отношение эквивалентности]] на множестве слов в алфавите. Таким образом, если 2 строки имеют одинаковые таблицы, то тогда они эквивалентны отношением <tex>\equiv_{L(M)}</tex>. Поскольку у нас только конечное число таблиц, отношение <tex>\equiv_{L(M)}</tex> имеет только конечное количество [[Отношение_эквивалентности|классов эквивалентности]], самое большее один для каждой таблицы. Следовательно, по теореме Майхилла-Нероуда, <tex>L(M)</tex> {{---}} регулярный язык, что согласно теореме Клини, совпадает с классом и автоматных языков, ч.т.д.<br />
}}<br />
<br />
== Пример ==<br />
Рассмотрим следующий язык <tex>L_n = (a+b)^∗a(a+b)^{n-1}a(a+b)^∗</tex> при <tex>\forall n > 0</tex>.<br />
<br />
Он может быть легко распознан с помощью следующего [[Недетерминированные_конечные_автоматы|недетерменированного конечного автомата]].<br />
<br />
[[Файл:2dfa_example_1.png|600px]]<br />
<br />
Теперь построим на его основе [[Детерминированные_конечные_автоматы|ДКА]]. Мы можем построить автомат <tex>A_n</tex>, который запоминает последние <tex>n</tex> входных символов. Следовательно, когда мы находимся в состоянии, соответствующему подстроке <tex>\sigma_1 \sigma_2 \dots \sigma_n</tex>, и читаем очередной символ <tex>\gamma</tex>, то мы переходим в состояние, которому уже будет соответствовать подстрока <tex>\sigma_2 \sigma_3 \dots \sigma_n \gamma</tex>. Однако, в случае <tex>\sigma_1 = \gamma = a</tex> автомат переходит в допускающее состояние, где в цикле может переходить на любому символу. Следует отметить, что такая стратегия строит ДКА c <tex>2^n + 1</tex> состояниями. Ниже представлен автомат <tex>A_3</tex>, который распознает язык <tex>L_3</tex>.<br />
<br />
[[Файл:2dfa_example_2.png|500px]]<br />
<br />
Покажем, что построенные таким образом автоматы будут минимальными.<br />
* Каждые две попарно различных строки <tex>x</tex> и <tex>y</tex> длины <tex>n</tex> различимы. Чтобы доказать это, достаточно рассмотреть строку <tex>b^{i-1}a</tex>, где <tex>i : 1 \leqslant i \leqslant n</tex> {{---}} самая левая позиция символа, в которой начинают различаться строки <tex>x</tex> и <tex>y</tex>, и проверить, что ровно одна строка <tex>xb^{i-1}a</tex> или <tex>yb^{i-1}a</tex> принадлежит <tex>L_n</tex>.<br />
* Каждая строка длины <tex>n</tex> не принадлежит <tex>L_n</tex> и, следовательно, различима со строкой <tex>a^{n+1}</tex>, которая принадлежит <tex>L_n</tex>.<br />
* Таким образом, <tex>2^n + 1</tex> строк в <tex>\Sigma^n \cup \{a^{n+1}\}</tex> являются попарно различимыми для <tex>L_n</tex>. Как следствие, <tex>2^n + 1</tex> {{---}} минимальное количество состояний для ДКА, который способен распознать язык <tex>L_n</tex>.<br />
<br />
Чтобы определить, что строка <tex>w=c_1 \dots c_n</tex> принадлежит языку <tex>L_n</tex>, нужно для <tex>\forall i = 1, \dots, |w|-n</tex> проверить, что <tex>c_i=c_{i+n}=a</tex>. Строка будет допустимой, если условие сработает хотя бы для одного <tex>i</tex>. Этот алгоритм может быть просто реализован с помощью 2ДКА. Будем для каждого <tex>i</tex> двигаться на <tex>n</tex> позиций вперед, а потом на <tex>n-1</tex> позиций назад до позиции <tex>i+1</tex>. Кроме того, при движении с позиции <tex>i</tex> до <tex>i+n</tex> автомат должен помнить сохраняется ли условие <tex>c_i=a</tex>. Такой подход требует <tex>O(n)</tex> состояний.<br />
<br />
== См. также ==<br />
* [[Детерминированные конечные автоматы]]<br />
* [[Локальные автоматы]]<br />
* [[Теорема Клини (совпадение классов автоматных и регулярных языков)]]<br />
<br />
== Источники информации==<br />
* [[wikipedia:Two-way_deterministic_finite_automaton|Wikipedia {{---}} Two-way deterministic finite automaton]]<br />
* [[wikipedia:ru:Теорема_Майхилла_—_Нероуда|Википедия {{---}} Теорема Майхилла-Нероуда]]<br />
* [http://arxiv.org/pdf/1208.2755.pdf Giovanni Pighizzini, ''Two-Way Finite Automata: Old and Recent Results'']<br />
* [http://www.cs.cornell.edu/Courses/cs682/2008sp/Handouts/2DFA.pdf Cornell University, ''Two-Way Finite Automata'']<br />
* [http://webcourse.cs.technion.ac.il/236807/Spring2012/ho/WCFiles/Two-way%20finite%20automata%20new.pdf Liat Peterfreund, ''Two-way finite automata & pebble automata'']<br />
[[Категория: Теория формальных языков]]<br />
[[Категория: Автоматы и регулярные языки]]</div>Kabanovhttp://neerc.ifmo.ru/wiki/index.php?title=%D0%94%D0%B2%D1%83%D1%81%D1%82%D0%BE%D1%80%D0%BE%D0%BD%D0%BD%D0%B8%D0%B9_%D0%B4%D0%B5%D1%82%D0%B5%D1%80%D0%BC%D0%B8%D0%BD%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BA%D0%BE%D0%BD%D0%B5%D1%87%D0%BD%D1%8B%D0%B9_%D0%B0%D0%B2%D1%82%D0%BE%D0%BC%D0%B0%D1%82&diff=43932Двусторонний детерминированный конечный автомат2015-01-10T08:58:00Z<p>Kabanov: /* Регулярность языка */</p>
<hr />
<div>{{Определение<br />
|definition=<br />
'''Двусторонний детерминированный конечный автомат (2ДКА)''' (англ. ''Two-way deterministic finite automaton (2DFA)'') {{---}} набор из восьми элементов <tex>M = \langle \Sigma , Q, L, R, s, t, r, \delta \rangle</tex>, где<br />
* <tex>\Sigma</tex> {{---}} алфавит,<br />
* <tex>Q</tex> {{---}} множество состояний,<br />
* <tex>L \notin \Sigma</tex> {{---}} левый маркер конца строки (англ. ''left endmarker''),<br />
* <tex>R \notin \Sigma</tex> {{---}} правый маркер конца строки (англ. ''right endmarker''),<br />
* <tex>s \in Q</tex> — начальное (стартовое) состояние,<br />
* <tex>t \in Q</tex> {{---}} допускающее состояние,<br />
* <tex>r \in Q</tex> — отвергающее состояние,<br />
* <tex>\delta : Q \times \{\Sigma \cup \{L, R\}\} \to Q \times \{left, right\}</tex> {{---}} функция переходов.<br />
}}<br />
Также должны быть удовлетворены следующие условия:<br />
<br />
<tex>\forall q \in Q</tex><br />
* <tex>\delta(q, L) = (u, right)</tex> для некоторого <tex>u \in Q</tex>,<br />
* <tex>\delta(q, R) = (v, left)</tex> для некоторого <tex>v \in Q</tex>,<br />
и <tex>\forall b \in \Sigma \cup \{L\}</tex><br />
* <tex>\delta(t, b) = (t, right)</tex>,<br />
* <tex>\delta(r, b) = (r, right)</tex>,<br />
* <tex>\delta(t, R) = (t, left)</tex>,<br />
* <tex>\delta(r, R) = (r, left)</tex>.<br />
<br />
== Регулярность языка ==<br />
{{Теорема<br />
|statement=Классы языков ДКА и 2ДКА совпадают.<br />
|proof=<br />
Рассмотрим длинную входную строку <tex>w_1</tex> и разобьем на две подстроки <tex>w_1=xz</tex>. Будем считать, что <tex>w_1 = a_1 a_2 a_3 \dots a_n</tex>. Пусть <tex>a_0 = L</tex> и <tex>a_{n+1}=R</tex>. Так как у нас наш автомат может производить чтение в любом направлении, то граница <tex>x</tex> и <tex>z</tex> может быть пересечена несколько раз. Каждый раз, когда автомат пересекает границу справа налево (то есть из <tex>z</tex> в <tex>x</tex>), он выходит из <tex>z</tex> в состояние <tex>q</tex>. Когда пересечение происходит снова слева направо (если оно вообще происходит), то автомат выходит из <tex>x</tex> в состояние <tex>p</tex>. Теперь, если 2ДКА перейдет в <tex>x</tex> в состояние <tex>q</tex> заново, то он снова может появиться в состоянии <tex>p</tex>. Более того, состояние <tex>p</tex> зависит исключительно от <tex>q</tex> и <tex>x</tex>. Обозначим такое отношение через <tex>T_x(q) = p</tex>. Мы может записать все такие отношения в виде конечной таблицы <tex>T_x : Q \cup \{d\} \to Q \cup \{h\}</tex>, где <tex>Q</tex> {{---}} множество состояний 2ДКА, а <tex>d</tex> и <tex>h</tex> будут описаны ниже.<br />
<br />
На входной строке <tex>xz</tex> 2ДКА начнет чтение с левого маркера конца строки. В процессе работы автомата позиция чтения будет меняться. В конце концов это позиция пересечет слева направо границу между <tex>x</tex> и <tex>z</tex>. В первый раз это произойдет в каком-нибудь состоянии, которое будем называть <tex>T_x(d)</tex> (для этого мы и выделили <tex>d</tex>). Так же автомат может никогда не выйти из <tex>x</tex>. В таком случае мы запишем <tex>T_x(d) = h</tex>. Состояние <tex>T_x(d)</tex> дает немного информации о <tex>x</tex>, но только конечное количество, поскольку существует только конечное количество вариантов для <tex>T_x(d)</tex>. Отметим, что <tex>T_x(d)</tex> зависит только от <tex>x</tex> и не зависит от <tex>z</tex>. Если на вход подавалась бы строка <tex>xw</tex> вместо <tex>xz</tex>, то в таком случае при пересечении границы из <tex>x</tex> в <tex>w</tex> автомат также был бы в состоянии <tex>T_x(d)</tex>, потому что его значение до того момента определялось только <tex>x</tex> и до тех пор все, что находится справа от границы никак не влияет.<br />
<br />
Если <tex>T_x(d) = h</tex>, то 2ДКА в бесконечном цикле внутри <tex>x</tex>, и он никогда не допустит и не отвергнет входную строку.<br />
<br />
Предположим, что 2ДКА переходит из <tex>x</tex> в <tex>z</tex> и спустя время переходит обратно в состояние <tex>q</tex>. Если это происходит, то потом:<br />
* либо снова произойдет переход из <tex>x</tex> в некоторое состояние <tex>p</tex>. В таком случае мы определим <tex>T_x(q)=p</tex>.<br />
* либо никогда не перейдет. В таком случае <tex>T_x(q) = h</tex>.<br />
<br />
Ещё раз отметим, что <tex>T_x(q)</tex> зависит только от <tex>x</tex> и <tex>q</tex> и не зависит от <tex>z</tex>. Если автомат переходит в <tex>x</tex> справа в состояние <tex>q</tex>, то тогда он появится заново в состоянии <tex>T_x(q)</tex> (или никогда не перейдет, если <tex>T_x(q) = h</tex>), потому что автомат детерминированный, и его поведение полностью определяется <tex>x</tex> и состоянием, в которое он вошел.<br />
<br />
Если мы запишем <tex>T_x(q)</tex> для каждого состояния <tex>q</tex> вместе с <tex>T_x(d)</tex>, это даст нам всю информацию о <tex>x</tex>, которую можно перенести через границу между <tex>x</tex> и <tex>z</tex>. Все это позволит узнать <tex>T_x(d)</tex> сразу после пересечения границы, а также посмотреть значения <tex>T_x(q)</tex>. Если <tex>y</tex> {{---}} другая строка, такая что <tex>T_y=T_x</tex>, то тогда <tex>x</tex> и <tex>y</tex> будут неразличимы.<br />
<br />
Заметим, что у нас конечное число возможных таблиц<br />
<tex>T_x : Q \cup \{d\} \to Q \cup \{h\}</tex>,<br />
а именно <tex>(k+1)^{k+1}</tex>, где <tex>k</tex> {{---}} размер множество <tex>Q</tex>. Таким образом, у нас конечное количество информации о <tex>x</tex>, которое мы может перенести через границу справа от <tex>x</tex>, и которое закодировано у нас в таблицe <tex>T_x</tex>.<br />
<br />
Отметим также, что если <tex>T_x=T_y</tex> и автомат допускает строку <tex>xz</tex>, то тогда он допускает и строку <tex>yz</tex>, потому что последовательность состояний перенесенных через границу <tex>x</tex> и <tex>z</tex> (либо <tex>y</tex> и <tex>z</tex>) в любом направлении полностью определяется таблицами <tex>T_x=T_y</tex> и строкой <tex>z</tex>. Чтобы допустить строку <tex>xz</tex>, автомат должен в какой-то момент прочитать правый маркер конца строки, находясь в допускающем состоянии <tex>t</tex>.<br />
<br />
Теперь мы может использовать теорему Майхилла-Нероуда, чтобы показать, что язык <tex>L(M)</tex> нашего автомата <tex>M</tex> [[Регулярные_языки:_два_определения_и_их_эквивалентность|регулярный]].<br />
<br />
<tex>T_x = T_y \Rightarrow \forall z (M\ \text{accepts}\ xz \Leftrightarrow M\ \text{accepts}\ yz)</tex> <tex> \Leftrightarrow \forall z (xz \in L(M)</tex> <tex> \Leftrightarrow yz \in L(M)) \Leftrightarrow x \equiv_{L(M)} y</tex>, где <tex>\equiv_{L(M)}</tex> {{---}} [[Отношение_эквивалентности|отношение эквивалентности]] на множестве слов в алфавите. Таким образом, если 2 строки имеют одинаковые таблицы, то тогда они эквивалентны отношением <tex>\equiv_{L(M)}</tex>. Поскольку у нас только конечное число таблиц, отношение <tex>\equiv_{L(M)}</tex> имеет только конечное количество [[Отношение_эквивалентности|классов эквивалентности]], самое большее один для каждой таблицы. Следовательно, по теореме Майхилла-Нероуда, <tex>L(M)</tex> {{---}} регулярный язык, что согласно теореме Клини, совпадает с классом и автоматных языков, ч.т.д.<br />
}}<br />
<br />
== Пример ==<br />
Рассмотрим следующий язык <tex>L_n = (a+b)^∗a(a+b)^{n-1}a(a+b)^∗</tex> при <tex>\forall n > 0</tex>.<br />
<br />
Он может быть легко распознан с помощью следующего [[Недетерминированные_конечные_автоматы|недетерменированного конечного автомата]].<br />
<br />
[[Файл:2dfa_example_1.png|600px]]<br />
<br />
Теперь построим на его основе [[Детерминированные_конечные_автоматы|ДКА]]. Мы можем построить автомат <tex>A_n</tex>, который запоминает последние <tex>n</tex> входных символов. Следовательно, когда мы находимся в состоянии, соответствующему подстроке <tex>\sigma_1 \sigma_2 \dots \sigma_n</tex>, и читаем очередной символ <tex>\gamma</tex>, то мы переходим в состояние, которому уже будет соответствовать подстрока <tex>\sigma_2 \sigma_3 \dots \sigma_n \gamma</tex>. Однако, в случае <tex>\sigma_1 = \gamma = a</tex> автомат переходит в допускающее состояние, где в цикле может переходить на любому символу. Следует отметить, что такая стратегия строит ДКА c <tex>2^n + 1</tex> состояниями. Ниже представлен автомат <tex>A_3</tex>, который распознает язык <tex>L_3</tex>.<br />
<br />
[[Файл:2dfa_example_2.png|500px]]<br />
<br />
Покажем, что построенные таким образом автоматы будут минимальными.<br />
* Каждые две попарно различных строки <tex>x</tex> и <tex>y</tex> длины <tex>n</tex> различимы. Чтобы доказать это, достаточно рассмотреть строку <tex>b^{i-1}a</tex>, где <tex>i : 1 \leqslant i \leqslant n</tex> {{---}} самая левая позиция символа, в которой начинают различаться строки <tex>x</tex> и <tex>y</tex>, и проверить, что ровно одна строка <tex>xb^{i-1}a</tex> или <tex>yb^{i-1}a</tex> принадлежит <tex>L_n</tex>.<br />
* Каждая строка длины <tex>n</tex> не принадлежит <tex>L_n</tex> и, следовательно, различима со строкой <tex>a^{n+1}</tex>, которая принадлежит <tex>L_n</tex>.<br />
* Таким образом, <tex>2^n + 1</tex> строк в <tex>\Sigma^n \cup \{a^{n+1}\}</tex> являются попарно различимыми для <tex>L_n</tex>. Как следствие, <tex>2^n + 1</tex> {{---}} минимальное количество состояний для ДКА, который способен распознать язык <tex>L_n</tex>.<br />
<br />
Чтобы определить, что строка <tex>w=c_1 \dots c_n</tex> принадлежит языку <tex>L_n</tex>, нужно для <tex>\forall i = 1, \dots, |w|-n</tex> проверить, что <tex>c_i=c_{i+n}=a</tex>. Строка будет допустимой, если условие сработает хотя бы для одного <tex>i</tex>. Этот алгоритм может быть просто реализован с помощью 2ДКА. Будем для каждого <tex>i</tex> двигаться на <tex>n</tex> позиций вперед, а потом на <tex>n-1</tex> позиций назад до позиции <tex>i+1</tex>. Кроме того, при движении с позиции <tex>i</tex> до <tex>i+n</tex> автомат должен помнить сохраняется ли условие <tex>c_i=a</tex>. Такой подход требует <tex>O(n)</tex> состояний.<br />
<br />
== См. также ==<br />
* [[Детерминированные конечные автоматы]]<br />
* [[Локальные автоматы]]<br />
* [[Теорема Клини (совпадение классов автоматных и регулярных языков)]]<br />
<br />
== Источники информации==<br />
* [[wikipedia:Two-way_deterministic_finite_automaton|Wikipedia {{---}} Two-way deterministic finite automaton]]<br />
* [[wikipedia:ru:Теорема_Майхилла_—_Нероуда|Википедия {{---}} Теорема Майхилла-Нероуда]]<br />
* [http://arxiv.org/pdf/1208.2755.pdf Giovanni Pighizzini, ''Two-Way Finite Automata: Old and Recent Results'']<br />
* [http://www.cs.cornell.edu/Courses/cs682/2008sp/Handouts/2DFA.pdf Cornell University, ''Two-Way Finite Automata'']<br />
* [http://webcourse.cs.technion.ac.il/236807/Spring2012/ho/WCFiles/Two-way%20finite%20automata%20new.pdf Liat Peterfreund, ''Two-way finite automata & pebble automata'']<br />
[[Категория: Теория формальных языков]]<br />
[[Категория: Автоматы и регулярные языки]]</div>Kabanovhttp://neerc.ifmo.ru/wiki/index.php?title=%D0%94%D0%B2%D1%83%D1%81%D1%82%D0%BE%D1%80%D0%BE%D0%BD%D0%BD%D0%B8%D0%B9_%D0%B4%D0%B5%D1%82%D0%B5%D1%80%D0%BC%D0%B8%D0%BD%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BA%D0%BE%D0%BD%D0%B5%D1%87%D0%BD%D1%8B%D0%B9_%D0%B0%D0%B2%D1%82%D0%BE%D0%BC%D0%B0%D1%82&diff=43930Двусторонний детерминированный конечный автомат2015-01-10T08:52:02Z<p>Kabanov: </p>
<hr />
<div>{{Определение<br />
|definition=<br />
'''Двусторонний детерминированный конечный автомат (2ДКА)''' (англ. ''Two-way deterministic finite automaton (2DFA)'') {{---}} набор из восьми элементов <tex>M = \langle \Sigma , Q, L, R, s, t, r, \delta \rangle</tex>, где<br />
* <tex>\Sigma</tex> {{---}} алфавит,<br />
* <tex>Q</tex> {{---}} множество состояний,<br />
* <tex>L \notin \Sigma</tex> {{---}} левый маркер конца строки (англ. ''left endmarker''),<br />
* <tex>R \notin \Sigma</tex> {{---}} правый маркер конца строки (англ. ''right endmarker''),<br />
* <tex>s \in Q</tex> — начальное (стартовое) состояние,<br />
* <tex>t \in Q</tex> {{---}} допускающее состояние,<br />
* <tex>r \in Q</tex> — отвергающее состояние,<br />
* <tex>\delta : Q \times \{\Sigma \cup \{L, R\}\} \to Q \times \{left, right\}</tex> {{---}} функция переходов.<br />
}}<br />
Также должны быть удовлетворены следующие условия:<br />
<br />
<tex>\forall q \in Q</tex><br />
* <tex>\delta(q, L) = (u, right)</tex> для некоторого <tex>u \in Q</tex>,<br />
* <tex>\delta(q, R) = (v, left)</tex> для некоторого <tex>v \in Q</tex>,<br />
и <tex>\forall b \in \Sigma \cup \{L\}</tex><br />
* <tex>\delta(t, b) = (t, right)</tex>,<br />
* <tex>\delta(r, b) = (r, right)</tex>,<br />
* <tex>\delta(t, R) = (t, left)</tex>,<br />
* <tex>\delta(r, R) = (r, left)</tex>.<br />
<br />
== Регулярность языка ==<br />
{{Теорема<br />
|statement=Классы языков ДКА и 2ДКА совпадают.<br />
|proof=<br />
Рассмотрим длинную входную строку <tex>w_1</tex> и разобьем на две подстроки <tex>w_1=xz</tex>. Будем считать, что <tex>w_1 = a_1 a_2 a_3 \dots a_n</tex>. Пусть <tex>a_0 = L</tex> и <tex>a_{n+1}=R</tex>. Так как у нас наш автомат может производить чтение в любом направлении, то граница <tex>x</tex> и <tex>z</tex> может быть пересечена несколько раз. Каждый раз, когда автомат пересекает границу справа налево (то есть из <tex>z</tex> в <tex>x</tex>), он выходит из <tex>z</tex> в состояние <tex>q</tex>. Когда пересечение происходит снова слева направо (если оно вообще происходит), то автомат выходит из <tex>x</tex> в состояние <tex>p</tex>. Теперь, если 2ДКА перейдет в <tex>x</tex> в состояние <tex>q</tex> заново, то он снова может появиться в состоянии <tex>p</tex>. Более того, состояние <tex>p</tex> зависит исключительно от <tex>q</tex> и <tex>x</tex>. Обозначим такое отношение через <tex>T_x(q) = p</tex>. Мы может записать все такие отношения в виде конечной таблицы <tex>T_x : Q \cup \{d\} \to Q \cup \{h\}</tex>, где <tex>Q</tex> {{---}} множество состояний 2ДКА, а <tex>d</tex> и <tex>h</tex> будут описаны ниже.<br />
<br />
На входной строке <tex>xz</tex> 2ДКА начнет чтение с левого маркера конца строки. В процессе работы автомата позиция чтения будет меняться. В конце концов это позиция пересечет слева направо границу между <tex>x</tex> и <tex>z</tex>. В первый раз это произойдет в каком-нибудь состоянии, которое будем называть <tex>T_x(d)</tex> (для этого мы и выделили <tex>d</tex>). Так же автомат может никогда не выйти из <tex>x</tex>. В таком случае мы запишем <tex>T_x(d) = h</tex>. Состояние <tex>T_x(d)</tex> дает немного информации о <tex>x</tex>, но только конечное количество, поскольку существует только конечное количество вариантов для <tex>T_x(d)</tex>. Отметим, что <tex>T_x(d)</tex> зависит только от <tex>x</tex> и не зависит от <tex>z</tex>. Если на вход подавалась строка <tex>xw</tex> вместо <tex>xz</tex>, то в таком случае при пересечении границы из <tex>x</tex> в <tex>w</tex> автомат также был бы в состоянии <tex>T_x(d)</tex>, потому что его значение до того момента определялось только <tex>x</tex> и до тех пор все, что находится справа от границы никак не влияет.<br />
<br />
Если <tex>T_x(d) = h</tex>, то 2ДКА в бесконечном цикле внутри <tex>x</tex>, и он никогда не допустит и не отвергнет входную строку.<br />
<br />
Предположим, что 2ДКА переходит из <tex>x</tex> в <tex>z</tex> и спустя время перейти обратно в состояние <tex>q</tex>. Если это происходит, то потом:<br />
* либо снова произойдет переход из <tex>x</tex> в некоторое состояние <tex>p</tex>. В таком случае мы определим <tex>T_x(q)=p</tex>.<br />
* либо никогда не перейдет. В таком случае <tex>T_x(q) = h</tex>.<br />
<br />
Ещё раз отметим, что <tex>T_x(q)</tex> зависит только от <tex>x</tex> и <tex>q</tex> и не зависит от <tex>z</tex>. Если автомат переходит в <tex>x</tex> справа в состояние <tex>q</tex>, то тогда он появится заново в состоянии <tex>T_x(q)</tex> (или никогда не перейдет, если <tex>T_x(q) = h</tex>), потому что автомат детерминированный, и его поведение полностью определяется <tex>x</tex> и состоянием, в которое он вошел.<br />
<br />
Если мы запишем <tex>T_x(q)</tex> для каждого состояния <tex>q</tex> вместе с <tex>T_x(d)</tex>, это даст нам всю информацию о <tex>x</tex>, которую можно перенести через границу между <tex>x</tex> и <tex>z</tex>. Все это позволит узнать <tex>T_x(d)</tex> сразу после пересечения границы, а также посмотреть значения <tex>T_x(q)</tex>. Если <tex>y</tex> {{---}} другая строка, такая что <tex>T_y=T_x</tex>, то тогда <tex>x</tex> и <tex>y</tex> будут неразличимы.<br />
<br />
Заметим, что у нас конечное число возможных таблиц<br />
<tex>T_x : Q \cup \{d\} \to Q \cup \{h\}</tex>,<br />
а именно <tex>(k+1)^{k+1}</tex>, где <tex>k</tex> {{---}} размер множество <tex>Q</tex>. Таким образом, у нас конечное количество информации о <tex>x</tex>, которое мы может перенести через границу справа от <tex>x</tex>, и которое закодировано у нас в таблицe <tex>T_x</tex>.<br />
<br />
Отметим также, что если <tex>T_x=T_y</tex> и автомат допускает строку <tex>xz</tex>, то тогда он допускает и строку <tex>yz</tex>, потому что последовательность состояний перенесенных через границу <tex>x</tex> и <tex>z</tex> (либо <tex>y</tex> и <tex>z</tex>) в любом направлении полностью определяется таблицами <tex>T_x=T_y</tex> и строкой <tex>z</tex>. Чтобы допустить строку <tex>xz</tex>, автомат должен в какой-то момент прочитать правый маркер конца строки, находясь в допускающем состоянии <tex>t</tex>.<br />
<br />
Теперь мы может использовать теорему Майхилла-Нероуда, чтобы показать, что язык <tex>L(M)</tex> нашего автомата <tex>M</tex> [[Регулярные_языки:_два_определения_и_их_эквивалентность|регулярный]].<br />
<br />
<tex>T_x = T_y \Rightarrow \forall z (M\ \text{accepts}\ xz \Leftrightarrow M\ \text{accepts}\ yz)</tex> <tex> \Leftrightarrow \forall z (xz \in L(M)</tex> <tex> \Leftrightarrow yz \in L(M)) \Leftrightarrow x \equiv_{L(M)} y</tex>, где <tex>\equiv_{L(M)}</tex> {{---}} [[Отношение_эквивалентности|отношение эквивалентности]] на множестве слов в алфавите. Таким образом, если 2 строки имеют одинаковые таблицы, то тогда они эквивалентны отношением <tex>\equiv_{L(M)}</tex>. Поскольку у нас только конечное число таблиц, отношение <tex>\equiv_{L(M)}</tex> имеет только конечное количество [[Отношение_эквивалентности|классов эквивалентности]], самое большее один для каждой таблицы. Следовательно, по теореме Майхилла-Нероуда, <tex>L(M)</tex> {{---}} регулярный язык, что согласно теореме Клини, совпадает с классом и автоматных языков, ч.т.д.<br />
}}<br />
<br />
== Пример ==<br />
Рассмотрим следующий язык <tex>L_n = (a+b)^∗a(a+b)^{n-1}a(a+b)^∗</tex> при <tex>\forall n > 0</tex>.<br />
<br />
Он может быть легко распознан с помощью следующего [[Недетерминированные_конечные_автоматы|недетерменированного конечного автомата]].<br />
<br />
[[Файл:2dfa_example_1.png|600px]]<br />
<br />
Теперь построим на его основе [[Детерминированные_конечные_автоматы|ДКА]]. Мы можем построить автомат <tex>A_n</tex>, который запоминает последние <tex>n</tex> входных символов. Следовательно, когда мы находимся в состоянии, соответствующему подстроке <tex>\sigma_1 \sigma_2 \dots \sigma_n</tex>, и читаем очередной символ <tex>\gamma</tex>, то мы переходим в состояние, которому уже будет соответствовать подстрока <tex>\sigma_2 \sigma_3 \dots \sigma_n \gamma</tex>. Однако, в случае <tex>\sigma_1 = \gamma = a</tex> автомат переходит в допускающее состояние, где в цикле может переходить на любому символу. Следует отметить, что такая стратегия строит ДКА c <tex>2^n + 1</tex> состояниями. Ниже представлен автомат <tex>A_3</tex>, который распознает язык <tex>L_3</tex>.<br />
<br />
[[Файл:2dfa_example_2.png|500px]]<br />
<br />
Покажем, что построенные таким образом автоматы будут минимальными.<br />
* Каждые две попарно различных строки <tex>x</tex> и <tex>y</tex> длины <tex>n</tex> различимы. Чтобы доказать это, достаточно рассмотреть строку <tex>b^{i-1}a</tex>, где <tex>i : 1 \leqslant i \leqslant n</tex> {{---}} самая левая позиция символа, в которой начинают различаться строки <tex>x</tex> и <tex>y</tex>, и проверить, что ровно одна строка <tex>xb^{i-1}a</tex> или <tex>yb^{i-1}a</tex> принадлежит <tex>L_n</tex>.<br />
* Каждая строка длины <tex>n</tex> не принадлежит <tex>L_n</tex> и, следовательно, различима со строкой <tex>a^{n+1}</tex>, которая принадлежит <tex>L_n</tex>.<br />
* Таким образом, <tex>2^n + 1</tex> строк в <tex>\Sigma^n \cup \{a^{n+1}\}</tex> являются попарно различимыми для <tex>L_n</tex>. Как следствие, <tex>2^n + 1</tex> {{---}} минимальное количество состояний для ДКА, который способен распознать язык <tex>L_n</tex>.<br />
<br />
Чтобы определить, что строка <tex>w=c_1 \dots c_n</tex> принадлежит языку <tex>L_n</tex>, нужно для <tex>\forall i = 1, \dots, |w|-n</tex> проверить, что <tex>c_i=c_{i+n}=a</tex>. Строка будет допустимой, если условие сработает хотя бы для одного <tex>i</tex>. Этот алгоритм может быть просто реализован с помощью 2ДКА. Будем для каждого <tex>i</tex> двигаться на <tex>n</tex> позиций вперед, а потом на <tex>n-1</tex> позиций назад до позиции <tex>i+1</tex>. Кроме того, при движении с позиции <tex>i</tex> до <tex>i+n</tex> автомат должен помнить сохраняется ли условие <tex>c_i=a</tex>. Такой подход требует <tex>O(n)</tex> состояний.<br />
<br />
== См. также ==<br />
* [[Детерминированные конечные автоматы]]<br />
* [[Локальные автоматы]]<br />
* [[Теорема Клини (совпадение классов автоматных и регулярных языков)]]<br />
<br />
== Источники информации==<br />
* [[wikipedia:Two-way_deterministic_finite_automaton|Wikipedia {{---}} Two-way deterministic finite automaton]]<br />
* [[wikipedia:ru:Теорема_Майхилла_—_Нероуда|Википедия {{---}} Теорема Майхилла-Нероуда]]<br />
* [http://arxiv.org/pdf/1208.2755.pdf Giovanni Pighizzini, ''Two-Way Finite Automata: Old and Recent Results'']<br />
* [http://www.cs.cornell.edu/Courses/cs682/2008sp/Handouts/2DFA.pdf Cornell University, ''Two-Way Finite Automata'']<br />
* [http://webcourse.cs.technion.ac.il/236807/Spring2012/ho/WCFiles/Two-way%20finite%20automata%20new.pdf Liat Peterfreund, ''Two-way finite automata & pebble automata'']<br />
[[Категория: Теория формальных языков]]<br />
[[Категория: Автоматы и регулярные языки]]</div>Kabanovhttp://neerc.ifmo.ru/wiki/index.php?title=%D0%94%D0%B2%D1%83%D1%81%D1%82%D0%BE%D1%80%D0%BE%D0%BD%D0%BD%D0%B8%D0%B9_%D0%B4%D0%B5%D1%82%D0%B5%D1%80%D0%BC%D0%B8%D0%BD%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BA%D0%BE%D0%BD%D0%B5%D1%87%D0%BD%D1%8B%D0%B9_%D0%B0%D0%B2%D1%82%D0%BE%D0%BC%D0%B0%D1%82&diff=43892Двусторонний детерминированный конечный автомат2015-01-09T23:19:35Z<p>Kabanov: </p>
<hr />
<div>{{Определение<br />
|definition=<br />
'''Двусторонний детерминированный конечный автомат (2ДКА)''' (англ. ''Two-way deterministic finite automaton (2DFA)'') {{---}} набор из восьми элементов <tex>M = \langle \Sigma , Q, L, R, s, t, r, \delta \rangle</tex>, где <tex>\Sigma</tex> {{---}} алфавит (англ. ''alphabet''), <tex>Q</tex> {{---}} множество состояний (англ. ''finite set of states''), <tex>L \notin \Sigma</tex> {{---}} левый маркер конца строки (англ. ''left endmarker''), <tex>R \notin \Sigma</tex> {{---}} правый маркер конца строки (англ. ''right endmarker''), <tex>s \in Q</tex> — начальное (стартовое) состояние (англ. ''start state''), <tex>t \in Q</tex> {{---}} допускающее состояние (англ. ''accept state''), <tex>r \in Q</tex> — отвергающее состояние (англ. ''reject state''), <tex>\delta : Q \times \{\Sigma \cup \{L, R\}\} \to Q \times \{left, right\}</tex> {{---}} функция переходов (англ. ''transition function'').<br />
}}<br />
Также должны быть удовлетворены следующие условия:<br />
<br />
<tex>\forall q \in Q</tex><br />
* <tex>\delta(q, L) = (u, right)</tex> для некоторого <tex>u \in Q</tex>,<br />
* <tex>\delta(q, R) = (v, left)</tex> для некоторого <tex>v \in Q</tex>,<br />
и <tex>\forall b \in \Sigma \cup \{L\}</tex><br />
* <tex>\delta(t, b) = (t, right)</tex>,<br />
* <tex>\delta(r, b) = (r, right)</tex>,<br />
* <tex>\delta(t, R) = (t, left)</tex>,<br />
* <tex>\delta(r, R) = (r, left)</tex>.<br />
<br />
== Регулярность языка ==<br />
Рассмотрим длинную входную строку <tex>w_1</tex> и разобьем на две подстроки <tex>w_1=xz</tex>. Будем считать, что <tex>w_1 = a_1 a_2 a_3 \dots a_n</tex>. Пусть <tex>a_0 = L</tex> и <tex>a_{n+1}=R</tex>. Так как у нас наш автомат может производить чтение в любом направлении, то граница <tex>x</tex> и <tex>z</tex> может быть пересечена несколько раз. Каждый раз, когда автомат пересекает границу справа налево (то есть из <tex>z</tex> в <tex>x</tex>), он выходит из <tex>z</tex> в состояние <tex>q</tex>. Когда пересечение происходит снова слева направо (если оно вообще происходит), то автомат выходит из <tex>x</tex> в состояние <tex>p</tex>. Теперь, если 2ДКА перейдет в <tex>x</tex> в состояние <tex>q</tex> заново, то он снова может появиться в состоянии <tex>p</tex>. Более того, состояние <tex>p</tex> зависит исключительно от <tex>q</tex> и <tex>x</tex>. Обозначим такое отношение через <tex>T_x(q) = p</tex>. Мы может записать все такие отношения в виде конечной таблицы <tex>T_x : Q \cup \{d\} \to Q \cup \{h\}</tex>, где <tex>Q</tex> {{---}} множество состояний 2ДКА, а <tex>d</tex> и <tex>h</tex> будут описаны ниже.<br />
<br />
На входной строке <tex>xz</tex> 2ДКА начнет чтение с левого маркера конца строки. В процессе работы автомата позиция чтения будет меняться. В конце концов это позиция пересечет слева направо границу между <tex>x</tex> и <tex>z</tex>. В первый раз это произойдет в каком-нибудь состоянии, которое будем называть <tex>T_x(d)</tex> (для этого мы и выделили <tex>d</tex>). Так же автомат может никогда не выйти из <tex>x</tex>. В таком случае мы запишем <tex>T_x(d) = h</tex>. Состояние <tex>T_x(d)</tex> дает немного информации о <tex>x</tex>, но только конечное количество, поскольку существует только конечное количество вариантов для <tex>T_x(d)</tex>. Отметим, что <tex>T_x(d)</tex> зависит только от <tex>x</tex> и не зависит от <tex>z</tex>. Если на вход подавалась строка <tex>xw</tex> вместо <tex>xz</tex>, то в таком случае при пересечении границы из <tex>x</tex> в <tex>w</tex> автомат также был бы в состоянии <tex>T_x(d)</tex>, потому что его значение до того момента определялось только <tex>x</tex> и до тех пор все, что находится справа от границы никак не влияет.<br />
<br />
Если <tex>T_x(d) = h</tex>, то 2ДКА в бесконечном цикле внутри <tex>x</tex>, и он никогда не допустит и не отвергнет входную строку.<br />
<br />
Предположим, что 2ДКА переходит из <tex>x</tex> в <tex>z</tex> и спустя время перейти обратно в состояние <tex>q</tex>. Если это происходит, то потом:<br />
* либо снова произойдет переход из <tex>x</tex> в некоторое состояние <tex>p</tex>. В таком случае мы определим <tex>T_x(q)=p</tex>.<br />
* либо никогда не перейдет. В таком случае <tex>T_x(q) = h</tex>.<br />
<br />
Ещё раз отметим, что <tex>T_x(q)</tex> зависит только от <tex>x</tex> и <tex>q</tex> и не зависит от <tex>z</tex>. Если автомат переходит в <tex>x</tex> справа в состояние <tex>q</tex>, то тогда он появится заново в состоянии <tex>T_x(q)</tex> (или никогда не перейдет, если <tex>T_x(q) = h</tex>), потому что автомат детерминированный, и его поведение полностью определяется <tex>x</tex> и состоянием, в которое он вошел.<br />
<br />
Если мы запишем <tex>T_x(q)</tex> для каждого состояния <tex>q</tex> вместе с <tex>T_x(d)</tex>, это даст нам всю информацию о <tex>x</tex>, которую можно перенести через границу между <tex>x</tex> и <tex>z</tex>. Все это позволит узнать <tex>T_x(d)</tex> сразу после пересечения границы, а также посмотреть значения <tex>T_x(q)</tex>. Если <tex>y</tex> {{---}} другая строка, такая что <tex>T_y=T_x</tex>, то тогда <tex>x</tex> и <tex>y</tex> будут неразличимы.<br />
<br />
Заметим, что у нас конечное число возможных таблиц<br />
<tex>T_x : Q \cup \{d\} \to Q \cup \{h\}</tex>,<br />
а именно <tex>(k+1)^{k+1}</tex>, где <tex>k</tex> {{---}} размер множество <tex>Q</tex>. Таким образом, у нас конечное количество информации о <tex>x</tex>, которое мы может перенести через границу справа от <tex>x</tex>, и которое закодировано у нас в таблицe <tex>T_x</tex>.<br />
<br />
Отметим также, что если <tex>T_x=T_y</tex> и автомат допускает строку <tex>xz</tex>, то тогда он допускает и строку <tex>yz</tex>, потому что последовательность состояний перенесенных через границу <tex>x</tex> и <tex>z</tex> (либо <tex>y</tex> и <tex>z</tex>) в любом направлении полностью определяется таблицами <tex>T_x=T_y</tex> и строкой <tex>z</tex>. Чтобы допустить строку <tex>xz</tex>, автомат должен в какой-то момент прочитать правый маркер конца строки, находясь в допускающем состоянии <tex>t</tex>.<br />
<br />
Теперь мы может использовать теорему Майхилла-Нероуда, чтобы показать, что язык <tex>L(M)</tex> нашего автомата <tex>M</tex> [[Регулярные_языки:_два_определения_и_их_эквивалентность|регулярный]].<br />
<br />
<tex>T_x = T_y \Rightarrow \forall z (M\ \text{accepts}\ xz \Leftrightarrow M\ \text{accepts}\ yz)</tex> <tex> \Leftrightarrow \forall z (xz \in L(M)</tex> <tex> \Leftrightarrow yz \in L(M)) \Leftrightarrow x \equiv_{L(M)} y</tex>, где <tex>\equiv_{L(M)}</tex> {{---}} [[Отношение_эквивалентности|отношение эквивалентности]] на множестве слов в алфавите. Таким образом, если 2 строки имеют одинаковые таблицы, то тогда они эквивалентны отношением <tex>\equiv_{L(M)}</tex>. Поскольку у нас только конечное число таблиц, отношение <tex>\equiv_{L(M)}</tex> имеет только конечное количество [[Отношение_эквивалентности|классов эквивалентности]], самое большее один для каждой таблицы. Следовательно, по теореме <tex>L(M)</tex> {{---}} регулярный язык, ч.т.д.<br />
<br />
== Пример ==<br />
Рассмотрим следующий язык <tex>L_n = (a+b)^∗a(a+b)^{n-1}a(a+b)^∗</tex> при <tex>\forall n > 0</tex>.<br />
<br />
Он может быть легко распознан с помощью следующего [[Недетерминированные_конечные_автоматы|недетерменированного конечного автомата]].<br />
<br />
[[Файл:2dfa_example_1.png|600px]]<br />
<br />
Теперь построим на его основе [[Детерминированные_конечные_автоматы|ДКА]]. Мы можем построить автомат <tex>A_n</tex>, который запоминает последние <tex>n</tex> входных символов. Следовательно, когда мы находимся в состоянии, соответствующему подстроке <tex>\sigma_1 \sigma_2 \dots \sigma_n</tex>, и читаем очередной символ <tex>\gamma</tex>, то мы переходим в состояние, которому уже будет соответствовать подстрока <tex>\sigma_2 \sigma_3 \dots \sigma_n \gamma</tex>. Однако, в случае <tex>\sigma_1 = \gamma = a</tex> автомат переходит в допускающее состояние, где в цикле может переходить на любому символу. Следует отметить, что такая стратегия строит ДКА c <tex>2^n + 1</tex> состояниями. Ниже представлен автомат <tex>A_3</tex>, который распознает язык <tex>L_3</tex>.<br />
<br />
[[Файл:2dfa_example_2.png|500px]]<br />
<br />
Покажем, что построенные таким образом автоматы будут минимальными.<br />
* Каждые две попарно различных строки <tex>x</tex> и <tex>y</tex> длины <tex>n</tex> различимы. Чтобы доказать это, достаточно рассмотреть строку <tex>b^{i-1}a</tex>, где <tex>i : 1 \leqslant i \leqslant n</tex> {{---}} самая левая позиция символа, в которой начинают различаться строки <tex>x</tex> и <tex>y</tex>, и проверить, что ровно одна строка <tex>xb^{i-1}a</tex> или <tex>yb^{i-1}a</tex> принадлежит <tex>L_n</tex>.<br />
* Каждая строка длины <tex>n</tex> не принадлежит <tex>L_n</tex> и, следовательно, различима со строкой <tex>a^{n+1}</tex>, которая принадлежит <tex>L_n</tex>.<br />
* Таким образом, <tex>2^n + 1</tex> строк в <tex>\Sigma^n \cup \{a^{n+1}\}</tex> являются попарно различимыми для <tex>L_n</tex>. Как следствие, <tex>2^n + 1</tex> {{---}} минимальное количество состояний для ДКА, который способен распознать язык <tex>L_n</tex>.<br />
<br />
Чтобы определить, что строка <tex>w=c_1 \dots c_n</tex> принадлежит языку <tex>L_n</tex>, нужно для <tex>\forall i = 1, \dots, |w|-n</tex> проверить, что <tex>c_i=c_{i+n}=a</tex>. Строка будет допустимой, если условие сработает хотя бы для одного <tex>i</tex>. Этот алгоритм может быть просто реализован с помощью 2ДКА. Будем для каждого <tex>i</tex> двигаться на <tex>n</tex> позиций вперед, а потом на <tex>n-1</tex> позиций назад до позиции <tex>i+1</tex>. Кроме того, при движении с позиции <tex>i</tex> до <tex>i+n</tex> автомат должен помнить сохраняется ли условие <tex>c_i=a</tex>. Такой подход требует <tex>O(n)</tex> состояний.<br />
<br />
== См. также ==<br />
* [[wikipedia:ru:Теорема_Майхилла_—_Нероуда|Википедия {{---}} Теорема Майхилла-Нероуда]]<br />
* [[Детерминированные конечные автоматы]]<br />
<br />
== Источники информации==<br />
* [[wikipedia:Two-way_deterministic_finite_automaton|Wikipedia {{---}} Two-way deterministic finite automaton]]<br />
* [http://arxiv.org/pdf/1208.2755.pdf Giovanni Pighizzini, ''Two-Way Finite Automata: Old and Recent Results'']<br />
* [http://www.cs.cornell.edu/Courses/cs682/2008sp/Handouts/2DFA.pdf Cornell University, ''Two-Way Finite Automata'']<br />
* [http://webcourse.cs.technion.ac.il/236807/Spring2012/ho/WCFiles/Two-way%20finite%20automata%20new.pdf Liat Peterfreund, ''Two-way finite automata & pebble automata'']<br />
[[Категория: Теория формальных языков]]<br />
[[Категория: Автоматы и регулярные языки]]</div>Kabanovhttp://neerc.ifmo.ru/wiki/index.php?title=%D0%A2%D0%B5%D0%BE%D1%80%D0%B8%D1%8F_%D1%84%D0%BE%D1%80%D0%BC%D0%B0%D0%BB%D1%8C%D0%BD%D1%8B%D1%85_%D1%8F%D0%B7%D1%8B%D0%BA%D0%BE%D0%B2&diff=43880Теория формальных языков2015-01-09T23:05:23Z<p>Kabanov: /* Регулярные языки и ДКА */</p>
<hr />
<div>[[Категория: Теория формальных языков]]<br />
== Автоматы и регулярные языки ==<br />
=== Регулярные языки и ДКА ===<br />
*[[Основные определения: алфавит, слово, язык, конкатенация, свободный моноид слов; операции над языками]]<br />
*[[Регулярные языки: два определения и их эквивалентность | Регулярные языки: два определения и их эквивалентность, регулярные выражения]]<br />
*[[Детерминированные конечные автоматы]]<br />
*[[Двусторонний детерминированный конечный автомат]]<br />
*[[Прямое произведение ДКА]]<br />
<br />
=== НКА ===<br />
*[[Недетерминированные конечные автоматы]]<br />
*[[Построение по НКА эквивалентного ДКА, алгоритм Томпсона]]<br />
*[[Автоматы с eps-переходами. Eps-замыкание]]<br />
*[[Теорема Клини (совпадение классов автоматных и регулярных языков)]]<br />
*[[Альтернативное доказательство теоремы Клини (через систему уравнений в регулярных выражениях)]]<br />
=== Минимизация ДКА ===<br />
*[[Эквивалентность состояний ДКА]]<br />
*[[Минимизация ДКА, алгоритм за O(n^2) с построением пар различимых состояний]]<br />
*[[Минимизация ДКА, алгоритм Хопкрофта (сложность O(n log n))]]<br />
*[[Алгоритм Бржозовского]]<br />
=== Другие свойства конечных автоматов ===<br />
*[[Доказательство нерегулярности языков: лемма о разрастании]]<br />
*[[Интерпретация булевых формул с кванторами как игр для двух игроков]]<br />
*[[Решение уравнений в регулярных выражениях]]<br />
*[[Замкнутость регулярных языков относительно различных операций]]<br />
*[[Анализ свойств регулярных языков (пустота, совпадение, включение, конечность, подсчет числа слов)]]<br />
*[[Контексты и синтаксические моноиды]]<br />
*[[Локальные автоматы]]<br />
=== Абстрактные автоматы ===<br />
*[[Автоматы Мура и Мили]]<br />
<br />
== Контекстно-свободные грамматики ==<br />
=== Базовые понятия о грамматиках ===<br />
*[[Формальные грамматики]]<br />
*[[Иерархия Хомского формальных грамматик]]<br />
*[[Неукорачивающие и контекстно-зависимые грамматики, эквивалентность]]<br />
*[[Правоконтекстные грамматики, эквивалентность автоматам]]<br />
*[[Контекстно-свободные грамматики, вывод, лево- и правосторонний вывод, дерево разбора]]<br />
*[[Замкнутость КС-языков относительно различных операций]]<br />
*[[Регулярная аппроксимация КС-языков]]<br />
=== Нормальные формы КС-грамматик ===<br />
*[[Удаление бесполезных символов из грамматики]]<br />
*[[Удаление длинных правил из грамматики]]<br />
*[[Удаление eps-правил из грамматики]]<br />
*[[Удаление цепных правил из грамматики]]<br />
*[[Нормальная форма Хомского]]<br />
*[[Устранение левой рекурсии]]<br />
*[[Приведение грамматики к ослабленной нормальной форме Грейбах]]<br />
*[[Нормальная форма Куроды]]<br />
<br />
=== Алгоритмы разбора ===<br />
*[[Алгоритм Кока-Янгера-Касами разбора грамматики в НФХ]]<br />
*[[Алгоритм Кока-Янгера-Касами, модификация для произвольной грамматики]]<br />
*[[Алгоритм Эрли]]<br />
*[[Алгоритм Эрли, доказательство оценки O(n^2) для однозначной грамматики]]<br />
=== Опровержение контекстно-свободности языка ===<br />
*[[Лемма о разрастании для КС-грамматик]]<br />
*[[Лемма Огдена]]<br />
*[[Существенно неоднозначные языки]]<br />
=== МП-автоматы ===<br />
*[[Автоматы с магазинной памятью]]<br />
*[[МП-автоматы, допуск по пустому стеку и по допускающему состоянию, эквивалентность]]<br />
*[[Совпадение множества языков МП-автоматов и контекстно-свободных языков]]<br />
*[[Детерминированные автоматы с магазинной памятью]]<br />
*[[Детерминированные автоматы с магазинной памятью, допуск по пустому стеку]]<br />
*[[Нормальная форма ДМП-автомата]]<br />
*[[Несовпадение класса языков, распознаваемых ДМП автоматами и произвольными МП автоматами]]<br />
*[[ДМП-автоматы и неоднозначность]]<br />
<br />
== Теория вычислимости ==<br />
=== Разрешимые и перечислимые языки ===<br />
*[[Разрешимые (рекурсивные) языки]]<br />
*[[Перечислимые языки]]<br />
*[[Замкнутость разрешимых и перечислимых языков относительно теоретико-множественных и алгебраических операций]]<br />
*[[Вычислимые функции]]<br />
*[[Вычислимые числа]]<br />
*[[Универсальная функция|Универсальная функция и главные нумерации]] <br />
*[[Свойства перечислимых языков. Теорема Успенского-Райса]]<br />
*[[Неотделимые множества]]<br />
*[[Иммунные и простые множества]]<br />
*[[Теорема о рекурсии]]<br />
*[[Квайны]]<br />
*[[Busy beaver]]<br />
*[[Колмогоровская сложность]]<br />
<br />
=== Вычислительные формализмы ===<br />
*[[Машина Тьюринга]]<br />
*[[Лямбда-исчисление]]<br />
*[[Примитивно рекурсивные функции]]<br />
*[[Частично рекурсивные функции]]<br />
*[[Стековые машины, эквивалентность двухстековой машины МТ]]<br />
*[[Счетчиковые машины, эквивалентность двухсчетчиковой машины МТ]]<br />
*[[Линейный клеточный автомат, эквивалентность МТ]]<br />
*[[Возможность порождения формальной грамматикой произвольного перечислимого языка]]<br />
*[[Линейный ограниченный автомат]]<br />
*[[Сверхтьюринговые вычисления (гипервычисления)]]<br />
<br />
=== Примеры неразрешимых задач ===<br />
*[[m-сводимость]]<br />
*[[Примеры неразрешимых задач: проблема соответствий Поста |Проблема соответствий Поста]]<br />
*[[Примеры неразрешимых задач: однозначность грамматики|Однозначность КС-грамматики]]<br />
*[[Неразрешимость задачи об эквивалентности КС-грамматик|Эквивалентность КС-грамматик]]<br />
*[[Примеры неразрешимых задач: задача о замощении|Задача о замощении полимино]]<br />
*[[Примеры неразрешимых задач: задача о выводе в полусистеме Туэ|Задача о выводе в полусистеме Туэ]]<br />
*[[Неразрешимость исчисления предикатов первого порядка]]<br />
*[[Неразрешимость проблемы существования решения диофантова уравнения в целых числах]]<br />
*[[Теорема Райса-Шапиро]]</div>Kabanovhttp://neerc.ifmo.ru/wiki/index.php?title=%D0%94%D0%B2%D1%83%D1%81%D1%82%D0%BE%D1%80%D0%BE%D0%BD%D0%BD%D0%B8%D0%B9_%D0%B4%D0%B5%D1%82%D0%B5%D1%80%D0%BC%D0%B8%D0%BD%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BA%D0%BE%D0%BD%D0%B5%D1%87%D0%BD%D1%8B%D0%B9_%D0%B0%D0%B2%D1%82%D0%BE%D0%BC%D0%B0%D1%82&diff=43874Двусторонний детерминированный конечный автомат2015-01-09T23:02:01Z<p>Kabanov: </p>
<hr />
<div>{{Определение<br />
|definition=<br />
'''Двусторонний детерминированный конечный автомат (2ДКА)''' (англ. ''Two-way deterministic finite automaton (2DFA)'') {{---}} набор из восьми элементов <tex>\langle \Sigma , Q, L, R, s, t, r, \delta \rangle</tex>, где <tex>\Sigma</tex> {{---}} алфавит (англ. ''alphabet''), <tex>Q</tex> {{---}} множество состояний (англ. ''finite set of states''), <tex>L \notin \Sigma</tex> {{---}} левый маркер конца строки, <tex>R \notin \Sigma</tex> {{---}} правый маркер конца строки, <tex>s \in Q</tex> — начальное (стартовое) состояние (англ. ''start state''), <tex>t \in Q</tex> {{---}} допускающее состояние (англ. ''accept state''), <tex>r \in Q</tex> — отвергающее состояние (англ. ''reject state''), <tex>\delta : Q \times \{\Sigma \cup \{L, R\}\} \to Q \times \{left, right\}</tex> {{---}} функция переходов (англ. ''transition function'').<br />
}}<br />
Также должны быть удовлетворены следующие условия:<br />
<br />
<tex>\forall q \in Q</tex><br />
* <tex>\delta(q, L) = (u, right)</tex> для некоторого <tex>u \in Q</tex>,<br />
* <tex>\delta(q, R) = (v, left)</tex> для некоторого <tex>v \in Q</tex>,<br />
и <tex>\forall b \in \Sigma \cup \{L\}</tex><br />
* <tex>\delta(t, b) = (t, right)</tex>,<br />
* <tex>\delta(r, b) = (r, right)</tex>,<br />
* <tex>\delta(t, R) = (t, left)</tex>,<br />
* <tex>\delta(r, R) = (r, left)</tex>.<br />
<br />
== Регулярность языка ==<br />
Рассмотрим длинную входную строку <tex>w_1</tex> и разобьем на две подстроки <tex>w_1=xz</tex>. Будем считать, что <tex>w_1 = a_1 a_2 a_3 \dots a_n</tex>. Пусть <tex>a_0 = L</tex> и <tex>a_{n+1}=R</tex>. Так как у нас наш автомат может производить чтение в любом направлении, то граница <tex>x</tex> и <tex>z</tex> может быть пересечена несколько раз. Каждый раз, когда автомат пересекает границу справа налево (то есть из <tex>z</tex> в <tex>x</tex>), он выходит из <tex>z</tex> в состояние <tex>q</tex>. Когда пересечение происходит снова слева направо (если оно вообще происходит), то автомат выходит из <tex>x</tex> в состояние <tex>p</tex>. Теперь, если 2ДКА перейдет в <tex>x</tex> в состояние <tex>q</tex> заново, то он снова может появиться в состоянии <tex>p</tex>. Более того, состояние <tex>p</tex> зависит исключительно от <tex>q</tex> и <tex>x</tex>. Обозначим такое отношение через <tex>T_x(q) = p</tex>. Мы может записать все такие отношения в виде конечной таблицы <tex>T_x : Q \cup \{d\} \to Q \cup \{h\}</tex>, где <tex>Q</tex> {{---}} множество состояний 2ДКА, а <tex>d</tex> и <tex>h</tex> будут описаны ниже.<br />
<br />
На входной строке <tex>xz</tex> 2ДКА начнет чтение с левого маркера конца строки. В процессе работы автомата позиция чтения будет меняться. В конце концов это позиция пересечет слева направо границу между <tex>x</tex> и <tex>z</tex>. В первый раз, когда это произойдет в каком-нибудь состоянии <tex>T_x(d)</tex> (для этого мы и выделили <tex>d</tex>). Так же автомат может никогда не выйти из <tex>x</tex>. В таком случае мы запишем <tex>T_x(d) = h</tex>. Состояние <tex>T_x(d)</tex> дает немного информации о <tex>x</tex>, но только конечное количество, поскольку существует только конечное количество вариантов для <tex>T_x(d)</tex>. Отметим, что <tex>T_x(d)</tex> зависит только от <tex>x</tex> и не зависит от <tex>z</tex>. Если на вход подавалась строка <tex>xw</tex> вместо <tex>xz</tex>, то в таком случае при пересечении границы из <tex>x</tex> в <tex>w</tex> автомат также был бы в состоянии <tex>T_x(d)</tex>, потому что его значение до того момента определялось только <tex>x</tex> и до тех пор все, что находится справа от границы никак не влияет.<br />
<br />
Если <tex>T_x(d) = h</tex>, то 2ДКА в бесконечном цикле внутри <tex>x</tex>, и он никогда не допустит и не отвергнет входную строку.<br />
<br />
Предположим, что 2ДКА переходит из <tex>x</tex> в <tex>z</tex> и спустя время перейти обратно в состояние <tex>q</tex>. Если это происходит, то потом:<br />
* либо снова произойдет переход из <tex>x</tex> в некоторое состояние <tex>p</tex>. В таком случае мы определим <tex>T_x(q)=p</tex>.<br />
* либо никогда не перейдет. В таком случае <tex>T_x(q) = h</tex>.<br />
<br />
Ещё раз отметим, что <tex>T_x(q)</tex> зависит только от <tex>x</tex> и <tex>q</tex> и не зависит от <tex>z</tex>. Если автомат переходит в <tex>x</tex> справа в состояние <tex>q</tex>, то тогда он появится заново в состоянии <tex>T_x(q)</tex> (или никогда не перейдет, если <tex>T_x(q) = h</tex>), потому что автомат детерминированный, и его поведение полностью определяется <tex>x</tex> и состоянием, в которое он вошел.<br />
<br />
Если мы запишем <tex>T_x(q)</tex> для каждого состояния <tex>q</tex> вместе с <tex>T_x(d)</tex>, это даст нам всю информацию о <tex>x</tex>, которую можно перенести через границу между <tex>x</tex> и <tex>z</tex>. Все это позволит узнать <tex>T_x(d)</tex> сразу после пересечения границы, а также посмотреть значения <tex>T_x(q)</tex>. Если <tex>y</tex> {{---}} другая строка, такая что <tex>T_y=T_x</tex>, то тогда <tex>x</tex> и <tex>y</tex> будут неразличимы.<br />
<br />
Заметим, что у нас конечное число возможных таблиц<br />
<tex>T_x : Q \cup \{d\} \to Q \cup \{h\}</tex>,<br />
а именно <tex>(k+1)^{k+1}</tex>, где <tex>k</tex> {{---}} размер множество <tex>Q</tex>. Таким образом, у нас конечное количество информации о <tex>x</tex>, которое мы может перенести через границу справа от <tex>x</tex>, и которое закодировано у нас в таблицe <tex>T_x</tex>.<br />
<br />
Отметим также, что если <tex>T_x=T_y</tex> и автомат допускает строку <tex>xz</tex>, то тогда он допускает и строку <tex>yz</tex>, потому что последовательность состояний перенесенных через границу <tex>x</tex> и <tex>z</tex> (либо <tex>y</tex> и <tex>z</tex>) в любом направлении полностью определяется таблицами <tex>T_x=T_y</tex> и строкой <tex>z</tex>. Чтобы допустить строку <tex>xz</tex>, автомат должен в какой-то момент прочитать правый маркер конца строки, находясь в допускающем состоянии <tex>t</tex>.<br />
<br />
Теперь мы может использовать теорему Майхилла-Нероуда, чтобы показать, что язык <tex>L(M)</tex> нашего автомата <tex>M</tex> [[Регулярные_языки:_два_определения_и_их_эквивалентность|регулярный]].<br />
<br />
<tex>T_x = T_y \Rightarrow \forall z (M\ \text{accepts}\ xz \Leftrightarrow M\ \text{accepts}\ yz)</tex> <tex> \Leftrightarrow \forall z (xz \in L(M)</tex> <tex> \Leftrightarrow yz \in L(M)) \Leftrightarrow x \equiv_{L(M)} y</tex>, где <tex>\equiv_{L(M)}</tex> {{---}} [[Отношение_эквивалентности|отношение эквивалентности]] на множестве слов в алфавите. Таким образом, если 2 строки имеют одинаковые таблицы, то тогда они эквивалентны отношением <tex>\equiv_{L(M)}</tex>. Поскольку у нас только конечное число таблиц, отношение <tex>\equiv_{L(M)}</tex> имеет только конечное количество [[Отношение_эквивалентности|классов эквивалентности]], самое большее один для каждой таблицы. По теореме <tex>L(M)</tex> {{---}} регулярный язык, ч.т.д.<br />
<br />
== Пример ==<br />
Рассмотрим следующий язык <tex>L_n = (a+b)^∗a(a+b)^{n-1}a(a+b)^∗</tex> при <tex>\forall n > 0</tex>.<br />
<br />
Он может быть легко распознан с помощью следующего недетерменированного конечного автомата.<br />
<br />
[[Файл:2dfa_example_1.png|600px]]<br />
<br />
Теперь построим на его основе ДКА. Мы можем построить автомат <tex>A_n</tex>, который запоминает последние <tex>n</tex> входных символов. Следовательно, когда мы находимся с состоянии, соответствующему подстроке <tex>\sigma_1 \sigma_2 \dots \sigma_n</tex>, и читаем очередной символ <tex>\gamma</tex>, то мы переходим в состояние, которому уже будет соответствовать подстрока <tex>\sigma_2 \sigma_3 \dots \sigma_n \gamma</tex>. Однако, в случае <tex>\sigma_1 = \gamma = a</tex> автомат переходит в допускающее состояние, где в цикле может переходить на любому символу. Следует отметить, что такая стратегия строит ДКА c <tex>2^n + 1</tex> состояниями. Ниже представлен автомат <tex>A_3</tex>, который распознает язык <tex>L_3</tex>.<br />
<br />
[[Файл:2dfa_example_2.png|500px]]<br />
<br />
Покажем, что построенные таким образом автоматы будут минимальными.<br />
* Каждые две попарно различных строки <tex>x</tex> и <tex>y</tex> длины <tex>n</tex> различимы. Чтобы доказать это, достаточно рассмотреть строку <tex>b^{i-1}a</tex>, где <tex>i : 1 \leqslant i \leqslant n</tex> {{---}} самая левая позиция символа, в которой начинают различаться строки <tex>x</tex> и <tex>y</tex>, и проверить, что ровно одна строка <tex>xb^{i-1}a</tex> или <tex>yb^{i-1}a</tex> принадлежит <tex>L_n</tex>.<br />
* Каждая строка длины <tex>n</tex> не принадлежит <tex>L_n</tex> и, следовательно, различима от строки <tex>a^{n+1}</tex>, которая принадлежит <tex>L_n</tex>.<br />
* Таким образом, <tex>2^n + 1</tex> строк в <tex>\Sigma^n \cup \{a^{n+1}\}</tex> являются попарно различимыми для <tex>L_n</tex>. Как следствие, <tex>2^n + 1</tex> {{---}} минимальное количество состояний для ДКА, который способен распознать язык <tex>L_n</tex>.<br />
<br />
Чтобы определить, что строка <tex>w=c_1 \dots c_n</tex> принадлежит языку <tex>L_n</tex>, нужно для <tex>\forall i = 1, \dots, |w|-n</tex> проверить, что <tex>c_i=c_{i+n}=a</tex>. Строка будет допустимой, если условие сработает хотя бы для одного <tex>i</tex>. Этот алгоритм может быть просто реализован с помощью 2ДКА. Будем для каждого <tex>i</tex> двигаться на <tex>n</tex> позиций вперед, а потом на <tex>n-1</tex> позиций назад до позиции <tex>i+1</tex>. Кроме того, при движении с позиции <tex>i</tex> до <tex>i+n</tex> автомат должен помнить сохраняется ли условие <tex>c_i=a</tex>. Такой подход требует <tex>O(n)</tex> состояний.<br />
<br />
== См. также ==<br />
* [[wikipedia:ru:Теорема_Майхилла_—_Нероуда|Википедия {{---}} Теорема Майхилла-Нероуда]]<br />
* [[Детерминированные конечные автоматы]]<br />
<br />
== Источники информации==<br />
* [[wikipedia:Two-way_deterministic_finite_automaton|Wikipedia {{---}} Two-way deterministic finite automaton]]<br />
* [http://arxiv.org/pdf/1208.2755.pdf Giovanni Pighizzini, ''Two-Way Finite Automata: Old and Recent Results'']<br />
* [http://www.cs.cornell.edu/Courses/cs682/2008sp/Handouts/2DFA.pdf Cornell University, ''Two-Way Finite Automata'']<br />
* [http://webcourse.cs.technion.ac.il/236807/Spring2012/ho/WCFiles/Two-way%20finite%20automata%20new.pdf Liat Peterfreund, ''Two-way finite automata & pebble automata'']<br />
[[Категория: Теория формальных языков]]<br />
[[Категория: Автоматы и регулярные языки]]</div>Kabanovhttp://neerc.ifmo.ru/wiki/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:2dfa_example_2.png&diff=43858Файл:2dfa example 2.png2015-01-09T22:38:51Z<p>Kabanov: </p>
<hr />
<div></div>Kabanovhttp://neerc.ifmo.ru/wiki/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:2dfa_example_1.png&diff=43852Файл:2dfa example 1.png2015-01-09T22:34:13Z<p>Kabanov: </p>
<hr />
<div></div>Kabanovhttp://neerc.ifmo.ru/wiki/index.php?title=%D0%94%D0%B2%D1%83%D1%81%D1%82%D0%BE%D1%80%D0%BE%D0%BD%D0%BD%D0%B8%D0%B9_%D0%B4%D0%B5%D1%82%D0%B5%D1%80%D0%BC%D0%B8%D0%BD%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BA%D0%BE%D0%BD%D0%B5%D1%87%D0%BD%D1%8B%D0%B9_%D0%B0%D0%B2%D1%82%D0%BE%D0%BC%D0%B0%D1%82&diff=43844Двусторонний детерминированный конечный автомат2015-01-09T21:33:42Z<p>Kabanov: /* Регулярность языков */</p>
<hr />
<div>{{Определение<br />
|definition=<br />
'''Двусторонний детерминированный конечный автомат (2ДКА)''' (англ. ''Two-way deterministic finite automaton (2DFA)'') {{---}} набор из восьми элементов <tex>\langle \Sigma , Q, L, R, s, t, r, \delta \rangle</tex>, где <tex>\Sigma</tex> {{---}} алфавит (англ. ''alphabet''), <tex>Q</tex> {{---}} множество состояний (англ. ''finite set of states''), <tex>L \notin \Sigma</tex> {{---}} левый маркер конца строки, <tex>R \notin \Sigma</tex> {{---}} правый маркер конца строки, <tex>s \in Q</tex> — начальное (стартовое) состояние (англ. ''start state''), <tex>t \in Q</tex> {{---}} допускающее состояние (англ. ''accept state''), <tex>r \in Q</tex> — отвергающее состояние (англ. ''reject state''), <tex>\delta : Q \times \{\Sigma \cup \{L, R\}\} \to Q \times \{left, right\}</tex> {{---}} функция переходов (англ. ''transition function'').<br />
}}<br />
Также должны быть удовлетворены следующие условия:<br />
<br />
<tex>\forall q \in Q</tex><br />
* <tex>\delta(q, L) = (u, right)</tex> для некоторого <tex>u \in Q</tex>,<br />
* <tex>\delta(q, R) = (v, left)</tex> для некоторого <tex>v \in Q</tex>,<br />
и <tex>\forall b \in \Sigma \cup \{L\}</tex><br />
* <tex>\delta(t, b) = (t, right)</tex>,<br />
* <tex>\delta(r, b) = (r, right)</tex>,<br />
* <tex>\delta(t, R) = (t, left)</tex>,<br />
* <tex>\delta(r, R) = (r, left)</tex>.<br />
== Описание ==<br />
Зафиксируем <tex>x \in \Sigma^*</tex>, где <tex>x = a_1 a_2 a_3 \dots a_n</tex>. Пусть <tex>a_0 = L</tex> и <tex>a_{n+1}=R</tex>. Тогда <tex>a_0 a_1 a_2 \dots a_n a_{n+1} = L x R</tex>.<br />
<br />
Пусть дан 2ДКА <tex>A = \langle \Sigma , Q, L, R, s, t, r, \delta \rangle</tex> с <tex>n</tex> состояниями. Тогда можно построить ДКА, распознающий язык тот же язык, с <tex>O(e^n)</tex> состояниями.<br />
<br />
Для доказательства приведем ход построения требуемого автомата <tex>B = \langle \Sigma , Q, s \in Q, T \subset Q, \delta : Q \times \Sigma \to Q \rangle</tex>.<br />
<br />
== Регулярность языков ==<br />
Рассмотрим длинную входную строку <tex>w_1</tex> и разобьем на две подстроки <tex>w_1=xz</tex>. Так как у нас наш автомат может производить чтение в любом направлении, то граница <tex>x</tex> и <tex>z</tex> может быть пересечена несколько раз. Каждый раз, когда автомат пересекает границу справа налево (то есть из <tex>z</tex> в <tex>x</tex>), он выходит из состояния <tex>q</tex>. Когда пересечение происходит снова слева направо (если оно вообще происходит), то автомат выходит из состояния <tex>p</tex>. Теперь, если 2ДКА перейдет в <tex>x</tex> в состояние <tex>q</tex> заново, то он снова может появиться в состоянии <tex>p</tex>. Более того, состояние <tex>p</tex> зависит исключительно от <tex>q</tex> и <tex>x</tex>. Обозначим такое отношение через <tex>T_x(q) = p</tex>. Мы может записать все такие отношения в виде конечной таблицы <tex>T_x : Q \cup \{d\} \to Q \cup \{h\}</tex>, где <tex>Q</tex> {{---}} множество состояний 2ДКА, а <tex>d</tex> и <tex>h</tex> будут описаны ниже.<br />
<br />
На входной строке <tex>xz</tex> 2ДКА начнет чтение с левого маркера конца строки. В процессе работы автомата позиция чтения будет меняться. В конце концов это позиция пересечет слева направо границу между <tex>x</tex> и <tex>z</tex>. В первый раз, когда это произойдет в каком-нибудь состоянии <tex>T_x(d)</tex> (для этого мы и выделили <tex>d</tex>). Так же автомат может никогда не выйти из <tex>x</tex>. В таком случае мы запишем <tex>T_x(d) = h</tex>. Состояние <tex>T_x(d)</tex> дает немного информации о <tex>x</tex>, но только конечное количество, поскольку существует только конечное количество вариантов для <tex>T_x(d)</tex>. Отметим, что <tex>T_x(d)</tex> зависит только от <tex>x</tex> и не зависит от <tex>z</tex>. Если на вход подавалась строка <tex>xw</tex> вместо <tex>xz</tex>, то в таком случае при пересечении границы из <tex>x</tex> в <tex>w</tex> автомат также был бы в состоянии <tex>T_x(d)</tex>, потому что его значение до того момента определялось только <tex>x</tex> и до тех пор не все, что находится справа от границы никак не влияет.<br />
<br />
Если <tex>T_x(d) = h</tex>, то 2ДКА должен быть в бесконечном цикле внутри <tex>x</tex> и никогда не допустит или отвергнет входную строку.<br />
<br />
Предположим, что 2ДКА переходит из <tex>x</tex> в <tex>z</tex> и спустя время перейти обратно в состояние <tex>q</tex>. Если это происходит, то потом:<br />
* либо снова произойдет переход из <tex>x</tex> в некоторое состояние <tex>p</tex>. В таком случае мы определим <tex>T_x(q)=p</tex>.<br />
* либо никогда не перейдет. В таком случае <tex>T_x(q) = h</tex>.<br />
<br />
Ещё раз отметим, что <tex>T_x(q)</tex> зависит только от <tex>x</tex> и <tex>q</tex> и не зависит от <tex>z</tex>. Если автомат переходит в <tex>x</tex> справа в состояние <tex>q</tex>, то тогда он появится заново в состоянии <tex>T_x(q)</tex> (или никогда не перейдет, если <tex>T_x(q) = h</tex>), потому что автомат детерминированный, и поведение его поведение полностью определяется <tex>x</tex> и состоянием, в которое он вошел.<br />
<br />
Если мы запишем <tex>T_x(q)</tex> для каждого состояния <tex>q</tex> вместе с <tex>T_x(d)</tex>, это даст нам всю информацию о <tex>x</tex>, которую можно перенести через границу между <tex>x</tex> и <tex>z</tex>. Все это позволит узнать <tex>T_x(d)</tex> сразу после пересечения границы, а также посмотреть значения <tex>T_x(q)</tex>. Если <tex>y</tex> {{---}} другая строка, такая что <tex>T_y=T_x</tex>, то тогда <tex>x</tex> и <tex>y</tex> будут неразличимы.<br />
<br />
Заметим, что у нас конечное число возможных таблиц<br />
<tex>T_x : Q \cup \{d\} \to Q \cup \{h\}</tex>,<br />
а именно <tex>(k+1)^{k+1}</tex>, где <tex>k</tex> {{---}} размер множество <tex>Q</tex>. Таким образом, у нас конечное количество информации о <tex>x</tex>, которое мы может перенести через границу справа от <tex>x</tex>, и которое закодировано у нас в таблицe <tex>T_x</tex>.<br />
<br />
Отметим также, что если <tex>T_x=T_y</tex> и автомат допускает строку <tex>xz</tex>, то тогда он допускает и строку <tex>yz</tex>, потому что последовательность состояний перенесенных через границу <tex>x</tex> и <tex>z</tex> (либо <tex>y</tex> и <tex>z</tex>) в любом направлении полностью определяется таблицами <tex>T_x=T_y</tex> и строкой <tex>z</tex>. Чтобы допустить строку <tex>xz</tex>, автомат должен в какой-то момент прочитать правый маркер конца строки, находясь в допускающем состоянии <tex>t</tex>.<br />
<br />
Теперь мы может использовать теорему Майхилла-Нероуда, чтобы показать, что язык <tex>L(M)</tex> нашего автомата <tex>M</tex> [[Регулярные_языки:_два_определения_и_их_эквивалентность|регулярный]].<br />
<br />
<tex>T_x = T_y \Rightarrow \forall z (M\ \text{accepts}\ xz \Leftrightarrow M\ \text{accepts}\ yz)</tex> <tex> \Leftrightarrow \forall z (xz \in L(M)</tex> <tex> \Leftrightarrow yz \in L(M)) \Leftrightarrow x \equiv_{L(M)} y</tex>, где <tex>\equiv_{L(M)}</tex> {{---}} [[Отношение_эквивалентности|отношение эквивалентности]] на множестве слов в алфавите. Таким образом, если 2 строки имеют одинаковые таблицы, то тогда они эквивалентны отношением <tex>\equiv_{L(M)}</tex>. Поскольку у нас только конечное число таблиц, отношение <tex>\equiv_{L(M)}</tex> имеет только конечное количество [[Отношение_эквивалентности|классов эквивалентности]], самое большее один для каждой таблицы. По теореме <tex>L(M)</tex> {{---}} регулярный язык.<br />
<br />
== Пример ==<br />
Рассмотрим следующий язык <tex>L_n = (a+b)^∗a(a+b)^{n-1}a(a+b)^∗</tex> при <tex>\forall n > 0</tex>.<br />
<br />
Он может быть легко распознан с помощью следующего недетерменированного конечного автомата.<br />
<br />
Теперь построим на его основе ДКА. Мы можем построить автомат <tex>A_n</tex>, который запоминает последние <tex>n</tex> входных символов. Следовательно, когда мы находимся с состоянии, соответствующему подстроке <tex>\sigma_1 \sigma_2 \dots \sigma_n</tex>, и читаем очередной символ <tex>\gamma</tex>, то мы переходим в состояние, которому уже будет соответствовать подстрока <tex>\sigma_2 \sigma_3 \dots \sigma_n \gamma</tex>. Однако, в случае <tex>\sigma_1 = \gamma = a</tex> автомат переходит в допускающее состояние, где в цикле может переходить на любому символу. Следует отметить, что такая стратегия строит ДКА c <tex>2^n + 1</tex> состояниями. Ниже представлен автомат <tex>A_3</tex>, который распознает язык <tex>L_3</tex>.<br />
<br />
Покажем, что построенные таким образом автоматы будут минимальными.<br />
* Каждые две попарно различных строки <tex>x</tex> и <tex>y</tex> длины <tex>n</tex> различимы. Чтобы доказать это, достаточно рассмотреть строку <tex>b^{i-1}a</tex>, где <tex>i : 1 \leqslant i \leqslant n</tex> {{---}} самая левая позиция символа, в которой начинают различаться строки <tex>x</tex> и <tex>y</tex>, и проверить, что ровно одна строка <tex>xb^{i-1}a</tex> или <tex>yb^{i-1}a</tex> принадлежит <tex>L_n</tex>.<br />
* Каждая строка длины <tex>n</tex> не принадлежит <tex>L_n</tex> и, следовательно, различима от строки <tex>a^{n+1}</tex>, которая принадлежит <tex>L_n</tex>.<br />
* Таким образом, <tex>2^n + 1</tex> строк в <tex>\Sigma^n \cup \{a^{n+1}\}</tex> являются попарно различимыми для <tex>L_n</tex>. Как следствие, <tex>2^n + 1</tex> {{---}} минимальное количество состояний для ДКА, который способен распознать язык <tex>L_n</tex>.<br />
<br />
== См. также ==<br />
* [[Детерминированные конечные автоматы]]<br />
<br />
== Источники информации==<br />
<br />
[[Категория: Теория формальных языков]]<br />
[[Категория: Автоматы и регулярные языки]]</div>Kabanovhttp://neerc.ifmo.ru/wiki/index.php?title=%D0%94%D0%B2%D1%83%D1%81%D1%82%D0%BE%D1%80%D0%BE%D0%BD%D0%BD%D0%B8%D0%B9_%D0%B4%D0%B5%D1%82%D0%B5%D1%80%D0%BC%D0%B8%D0%BD%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BA%D0%BE%D0%BD%D0%B5%D1%87%D0%BD%D1%8B%D0%B9_%D0%B0%D0%B2%D1%82%D0%BE%D0%BC%D0%B0%D1%82&diff=43839Двусторонний детерминированный конечный автомат2015-01-09T21:22:03Z<p>Kabanov: /* Регулярность языков */</p>
<hr />
<div>{{Определение<br />
|definition=<br />
'''Двусторонний детерминированный конечный автомат (2ДКА)''' (англ. ''Two-way deterministic finite automaton (2DFA)'') {{---}} набор из восьми элементов <tex>\langle \Sigma , Q, L, R, s, t, r, \delta \rangle</tex>, где <tex>\Sigma</tex> {{---}} алфавит (англ. ''alphabet''), <tex>Q</tex> {{---}} множество состояний (англ. ''finite set of states''), <tex>L \notin \Sigma</tex> {{---}} левый маркер конца строки, <tex>R \notin \Sigma</tex> {{---}} правый маркер конца строки, <tex>s \in Q</tex> — начальное (стартовое) состояние (англ. ''start state''), <tex>t \in Q</tex> {{---}} допускающее состояние (англ. ''accept state''), <tex>r \in Q</tex> — отвергающее состояние (англ. ''reject state''), <tex>\delta : Q \times \{\Sigma \cup \{L, R\}\} \to Q \times \{left, right\}</tex> {{---}} функция переходов (англ. ''transition function'').<br />
}}<br />
Также должны быть удовлетворены следующие условия:<br />
<br />
<tex>\forall q \in Q</tex><br />
* <tex>\delta(q, L) = (u, right)</tex> для некоторого <tex>u \in Q</tex>,<br />
* <tex>\delta(q, R) = (v, left)</tex> для некоторого <tex>v \in Q</tex>,<br />
и <tex>\forall b \in \Sigma \cup \{L\}</tex><br />
* <tex>\delta(t, b) = (t, right)</tex>,<br />
* <tex>\delta(r, b) = (r, right)</tex>,<br />
* <tex>\delta(t, R) = (t, left)</tex>,<br />
* <tex>\delta(r, R) = (r, left)</tex>.<br />
== Описание ==<br />
Зафиксируем <tex>x \in \Sigma^*</tex>, где <tex>x = a_1 a_2 a_3 \dots a_n</tex>. Пусть <tex>a_0 = L</tex> и <tex>a_{n+1}=R</tex>. Тогда <tex>a_0 a_1 a_2 \dots a_n a_{n+1} = L x R</tex>.<br />
<br />
Пусть дан 2ДКА <tex>A = \langle \Sigma , Q, L, R, s, t, r, \delta \rangle</tex> с <tex>n</tex> состояниями. Тогда можно построить ДКА, распознающий язык тот же язык, с <tex>O(e^n)</tex> состояниями.<br />
<br />
Для доказательства приведем ход построения требуемого автомата <tex>B = \langle \Sigma , Q, s \in Q, T \subset Q, \delta : Q \times \Sigma \to Q \rangle</tex>.<br />
<br />
== Регулярность языков ==<br />
Рассмотрим длинную входную строку <tex>w_1</tex> и разобьем на две подстроки <tex>w_1=xz</tex>. Так как у нас наш автомат может производить чтение в любом направлении, то граница <tex>x</tex> и <tex>z</tex> может быть пересечена несколько раз. Каждый раз, когда автомат пересекает границу справа налево (то есть из <tex>z</tex> в <tex>x</tex>), он выходит из состояния <tex>q</tex>. Когда пересечение происходит снова слева направо (если оно вообще происходит), то автомат выходит из состояния <tex>p</tex>. Теперь, если 2ДКА перейдет в <tex>x</tex> в состояние <tex>q</tex> заново, то он снова может появиться в состоянии <tex>p</tex>. Более того, состояние <tex>p</tex> зависит исключительно от <tex>q</tex> и <tex>x</tex>. Обозначим такое отношение через записать <tex>T_x(q) = p</tex>. Мы может записать все такие отношения в виде конечной таблицы <tex>T_x : Q \cup \{d\} \to Q \cup \{h\}</tex>, где <tex>Q</tex> {{---}} множество состояний 2ДКА, а <tex>d</tex> и <tex>h</tex> будут описаны ниже.<br />
<br />
На входной строке <tex>xz</tex> 2ДКА начнет чтение с левого маркера конца строки. В процессе работы автомата позиция чтения будет меняться. В конце концов это позиция пересечет слева направо границу между <tex>x</tex> и <tex>z</tex>. В первый раз, когда это произойдет в каком-нибудь состоянии <tex>T_x(d)</tex> (для этого мы и выделили <tex>d</tex>). Так же автомат может никогда не выйти из <tex>x</tex>. В таком случае мы запишем <tex>T_x(d) = h</tex>. Состояние <tex>T_x(d)</tex> дает немного информации о <tex>x</tex>, но только конечное количество, поскольку существует только конечное количество вариантов для <tex>T_x(d)</tex>. Отметим, что <tex>T_x(d)</tex> зависит только от <tex>x</tex> и не зависит от <tex>z</tex>. Если на вход подавалась строка <tex>xw</tex> вместо <tex>xz</tex>, то в таком случае при пересечении границы из <tex>x</tex> в <tex>w</tex> автомат также был бы в состоянии <tex>T_x(d)</tex>, потому что его значение до того момента определялось только <tex>x</tex> и до тех пор не все, что находится справа от границы никак не влияет.<br />
<br />
Если <tex>T_x(d) = h</tex>, то 2ДКА должен быть в бесконечном цикле внутри <tex>x</tex> и никогда не допустит или отвергнет входную строку.<br />
<br />
Предположим, что 2ДКА переходит из <tex>x</tex> в <tex>z</tex> и спустя время перейти обратно в состояние <tex>q</tex>. Если это происходит, то потом:<br />
* либо снова произойдет переход из <tex>x</tex> в некоторое состояние <tex>p</tex>. В таком случае мы определим <tex>T_x(q)=p</tex>.<br />
* либо никогда не перейдет. В таком случае <tex>T_x(q) = h</tex>.<br />
<br />
Ещё раз отметим, что <tex>T_x(q)</tex> зависит только от <tex>x</tex> и <tex>q</tex> и не зависит от <tex>z</tex>. Если автомат переходит в <tex>x</tex> справа в состояние <tex>q</tex>, то тогда он появится заново в состоянии <tex>T_x(q)</tex> (или никогда не перейдет, если <tex>T_x(q) = h</tex>), потому что автомат детерминированный, и поведение его поведение полностью определяется <tex>x</tex> и состоянием, в которое он вошел.<br />
<br />
Если мы запишем <tex>T_x(q)</tex> для каждого состояния <tex>q</tex> вместе с <tex>T_x(d)</tex>, это даст нам всю информацию о <tex>x</tex>, которую можно перенести через границу между <tex>x</tex> и <tex>z</tex>. Все это позволит узнать <tex>T_x(d)</tex> сразу после пересечения границы, а также посмотреть значения <tex>T_x(q)</tex>. Если <tex>y</tex> {{---}} другая строка, такая что <tex>T_y=T_x</tex>, то тогда <tex>x</tex> и <tex>y</tex> будут неразличимы.<br />
<br />
Заметим, что у нас конечное число возможных таблиц<br />
<tex>T_x : Q \cup \{d\} \to Q \cup \{h\}</tex>,<br />
а именно <tex>(k+1)^{k+1}</tex>, где <tex>k</tex> {{---}} размер множество <tex>Q</tex>. Таким образом, у нас конечное количество информации о <tex>x</tex>, которое мы может перенести через границу справа от <tex>x</tex>, и которое закодировано у нас в таблицe <tex>T_x</tex>.<br />
<br />
Отметим также, что если <tex>T_x=T_y</tex> и автомат допускает строку <tex>xz</tex>, то тогда он допускает и строку <tex>yz</tex>, потому что последовательность состояний перенесенных через границу <tex>x</tex> и <tex>z</tex> (либо <tex>y</tex> и <tex>z</tex>) в любом направлении полностью определяется таблицами <tex>T_x=T_y</tex> и строкой <tex>z</tex>. Чтобы допустить строку <tex>xz</tex>, автомат должен в какой-то момент прочитать правый маркер конца строки, находясь в допускающем состоянии <tex>t</tex>.<br />
<br />
Теперь мы может использовать теорему Майхилла — Нероуда, чтобы показать, что язык <tex>L(M)</tex> нашего автомата <tex>M</tex> регулярный.<br />
<br />
<tex>T_x = T_y => \forall z ( M accepts xz <=> M accepts yz) <=> \forall z (xz \in L(M) <=> yz \in L(M)) <=> x =_{L(M)} y</tex>, где <tex>=_{L(M)}</tex> {{---}} отношение эквивалентности на множестве слов в алфавите. Таким образом, если 2 строки имеют одинаковые таблицы, то тогда они эквивалентны отношением <tex>=_{L(M)}</tex>. Поскольку у нас только конечное число таблиц, отношение <tex>=_{L(M)}</tex> имеет только конечное количество классов эквивалентности, самое большее один для каждой таблицы. По теореме <tex>L(M)</tex> {{---}} регулярный язык.<br />
<br />
== Пример ==<br />
Рассмотрим следующий язык <tex>L_n = (a+b)^∗a(a+b)^{n-1}a(a+b)^∗</tex> при <tex>\forall n > 0</tex>.<br />
<br />
Он может быть легко распознан с помощью следующего недетерменированного конечного автомата.<br />
<br />
Теперь построим на его основе ДКА. Мы можем построить автомат <tex>A_n</tex>, который запоминает последние <tex>n</tex> входных символов. Следовательно, когда мы находимся с состоянии, соответствующему подстроке <tex>\sigma_1 \sigma_2 \dots \sigma_n</tex>, и читаем очередной символ <tex>\gamma</tex>, то мы переходим в состояние, которому уже будет соответствовать подстрока <tex>\sigma_2 \sigma_3 \dots \sigma_n \gamma</tex>. Однако, в случае <tex>\sigma_1 = \gamma = a</tex> автомат переходит в допускающее состояние, где в цикле может переходить на любому символу. Следует отметить, что такая стратегия строит ДКА c <tex>2^n + 1</tex> состояниями. Ниже представлен автомат <tex>A_3</tex>, который распознает язык <tex>L_3</tex>.<br />
<br />
Покажем, что построенные таким образом автоматы будут минимальными.<br />
* Каждые две попарно различных строки <tex>x</tex> и <tex>y</tex> длины <tex>n</tex> различимы. Чтобы доказать это, достаточно рассмотреть строку <tex>b^{i-1}a</tex>, где <tex>i : 1 \leqslant i \leqslant n</tex> {{---}} самая левая позиция символа, в которой начинают различаться строки <tex>x</tex> и <tex>y</tex>, и проверить, что ровно одна строка <tex>xb^{i-1}a</tex> или <tex>yb^{i-1}a</tex> принадлежит <tex>L_n</tex>.<br />
* Каждая строка длины <tex>n</tex> не принадлежит <tex>L_n</tex> и, следовательно, различима от строки <tex>a^{n+1}</tex>, которая принадлежит <tex>L_n</tex>.<br />
* Таким образом, <tex>2^n + 1</tex> строк в <tex>\Sigma^n \cup \{a^{n+1}\}</tex> являются попарно различимыми для <tex>L_n</tex>. Как следствие, <tex>2^n + 1</tex> {{---}} минимальное количество состояний для ДКА, который способен распознать язык <tex>L_n</tex>.<br />
<br />
== См. также ==<br />
* [[Детерминированные конечные автоматы]]<br />
<br />
== Источники информации==<br />
<br />
[[Категория: Теория формальных языков]]<br />
[[Категория: Автоматы и регулярные языки]]</div>Kabanovhttp://neerc.ifmo.ru/wiki/index.php?title=%D0%94%D0%B2%D1%83%D1%81%D1%82%D0%BE%D1%80%D0%BE%D0%BD%D0%BD%D0%B8%D0%B9_%D0%B4%D0%B5%D1%82%D0%B5%D1%80%D0%BC%D0%B8%D0%BD%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BA%D0%BE%D0%BD%D0%B5%D1%87%D0%BD%D1%8B%D0%B9_%D0%B0%D0%B2%D1%82%D0%BE%D0%BC%D0%B0%D1%82&diff=43833Двусторонний детерминированный конечный автомат2015-01-09T21:12:37Z<p>Kabanov: /* Регулярность языков */</p>
<hr />
<div>{{Определение<br />
|definition=<br />
'''Двусторонний детерминированный конечный автомат (2ДКА)''' (англ. ''Two-way deterministic finite automaton (2DFA)'') {{---}} набор из восьми элементов <tex>\langle \Sigma , Q, L, R, s, t, r, \delta \rangle</tex>, где <tex>\Sigma</tex> {{---}} алфавит (англ. ''alphabet''), <tex>Q</tex> {{---}} множество состояний (англ. ''finite set of states''), <tex>L \notin \Sigma</tex> {{---}} левый маркер конца строки, <tex>R \notin \Sigma</tex> {{---}} правый маркер конца строки, <tex>s \in Q</tex> — начальное (стартовое) состояние (англ. ''start state''), <tex>t \in Q</tex> {{---}} допускающее состояние (англ. ''accept state''), <tex>r \in Q</tex> — отвергающее состояние (англ. ''reject state''), <tex>\delta : Q \times \{\Sigma \cup \{L, R\}\} \to Q \times \{left, right\}</tex> {{---}} функция переходов (англ. ''transition function'').<br />
}}<br />
Также должны быть удовлетворены следующие условия:<br />
<br />
<tex>\forall q \in Q</tex><br />
* <tex>\delta(q, L) = (u, right)</tex> для некоторого <tex>u \in Q</tex>,<br />
* <tex>\delta(q, R) = (v, left)</tex> для некоторого <tex>v \in Q</tex>,<br />
и <tex>\forall b \in \Sigma \cup \{L\}</tex><br />
* <tex>\delta(t, b) = (t, right)</tex>,<br />
* <tex>\delta(r, b) = (r, right)</tex>,<br />
* <tex>\delta(t, R) = (t, left)</tex>,<br />
* <tex>\delta(r, R) = (r, left)</tex>.<br />
== Описание ==<br />
Зафиксируем <tex>x \in \Sigma^*</tex>, где <tex>x = a_1 a_2 a_3 \dots a_n</tex>. Пусть <tex>a_0 = L</tex> и <tex>a_{n+1}=R</tex>. Тогда <tex>a_0 a_1 a_2 \dots a_n a_{n+1} = L x R</tex>.<br />
<br />
Пусть дан 2ДКА <tex>A = \langle \Sigma , Q, L, R, s, t, r, \delta \rangle</tex> с <tex>n</tex> состояниями. Тогда можно построить ДКА, распознающий язык тот же язык, с <tex>O(e^n)</tex> состояниями.<br />
<br />
Для доказательства приведем ход построения требуемого автомата <tex>B = \langle \Sigma , Q, s \in Q, T \subset Q, \delta : Q \times \Sigma \to Q \rangle</tex>.<br />
<br />
== Регулярность языков ==<br />
Рассмотрим длинную входную строку <tex>w_1</tex> и разобьем на две подстроки <tex>w_1=xz</tex>. Так как у нас наш автомат может производить чтение в любом направлении, то граница <tex>x</tex> и <tex>z</tex> может быть пересечена несколько раз. Каждый раз, когда автомат пересекает границу справа налево (то есть из <tex>z</tex> в <tex>x</tex>), он выходит из состояния <tex>q</tex>. Когда пересечение происходит снова слева направо (если оно вообще происходит), то автомат выходит из состояния <tex>p</tex>. Теперь, если 2ДКА перейдет в <tex>x</tex> в состояние <tex>q</tex> заново, то он снова может появиться в состоянии <tex>p</tex>. Более того, состояние <tex>p</tex> зависит исключительно от <tex>q</tex> и <tex>x</tex>. Обозначим такое отношение через записать <tex>T_x(q) = p</tex>. Мы может записать все такие отношения в виде конечной таблицы <tex>T_x : Q \cup \{d\} \to Q \cup \{h\}</tex>, где <tex>Q</tex> {{---}} множество состояний 2ДКА, а <tex>d</tex> и <tex>h</tex> будут описаны ниже.<br />
<br />
На входной строке <tex>xz</tex> 2ДКА начнет чтение с левого маркера конца строки. В процессе работы автомата позиция чтения будет меняться. В конце концов это позиция пересечет слева направо границу между <tex>x</tex> и <tex>z</tex>. В первый раз, когда это произойдет в каком-нибудь состоянии <tex>T_x(d)</tex> (для этого мы и выделили <tex>d</tex>). Так же автомат может никогда не выйти из <tex>x</tex>. В таком случае мы запишем <tex>T_x(d) = h</tex>. Состояние <tex>T_x(d)</tex> дает немного информации о <tex>x</tex>, но только конечное количество, поскольку существует только конечное количество вариантов для <tex>T_x(d)</tex>. Отметим, что <tex>T_x(d)</tex> зависит только от <tex>x</tex> и не зависит от <tex>z</tex>. Если на вход подавалась строка <tex>xw</tex> вместо <tex>xz</tex>, то в таком случае при пересечении границы из <tex>x</tex> в <tex>w</tex> автомат также был бы в состоянии <tex>T_x(d)</tex>, потому что его значение до того момента определялось только <tex>x</tex> и до тех пор не все, что находится справа от границы никак не влияет.<br />
<br />
Если <tex>T_x(d) = h</tex>, то 2ДКА должен быть в бесконечном цикле внутри <tex>x</tex> и никогда не допустит или отвергнет входную строку.<br />
<br />
Предположим, что 2ДКА переходит из <tex>x</tex> в <tex>z</tex> и спустя время перейти обратно в состояние <tex>q</tex>. Если это происходит, то потом:<br />
* либо снова произойдет переход из <tex>x</tex> в некоторое состояние <tex>p</tex>. В таком случае мы определим <tex>T_x(q)=p</tex>.<br />
* либо никогда не перейдет. В таком случае <tex>T_x(q) = h</tex>.<br />
<br />
Ещё раз отметим, что <tex>T_x(q)</tex> зависит только от <tex>x</tex> и <tex>q</tex> и не зависит от <tex>z</tex>. Если автомат переходит в <tex>x</tex> справа в состояние <tex>q</tex>, то тогда он появится заново в состоянии <tex>T_x(q)</tex> (или никогда не перейдет, если <tex>T_x(q) = h</tex>), потому что автомат детерминированный, и поведение его поведение полностью определяется <tex>x</tex> и состоянием, в которое он вошел.<br />
<br />
Если мы запишем <tex>T_x(q)</tex> для каждого состояния <tex>q</tex> вместе с <tex>T_x(d)</tex>, это даст нам всю информацию о <tex>x</tex>, которую можно перенести через границу между <tex>x</tex> и <tex>z</tex>. Все это позволит узнать <tex>T_x(d)</tex> сразу после пересечения границы, а также посмотреть значения <tex>T_x(q)</tex>. Если <tex>y</tex> {{---}} другая строка, такая что <tex>T_y=T_x</tex>, то тогда <tex>x</tex> и <tex>y</tex> будут неразличимы.<br />
<br />
Заметим, что у нас конечное число возможных таблиц<br />
<tex>T_x : Q \cup \{d\} \to Q \cup \{h\}</tex>,<br />
а именно <tex>(k+1)^{k+1}</tex>, где <tex>k</tex> {{---}} размер множество <tex>Q</tex>. Таким образом, у нас конечное количество информации о <tex>x</tex>, которое мы может перенести через границу справа от <tex>x</tex>, и которое закодировано у нас в таблицe <tex>T_x</tex>.<br />
<br />
Отметим также, что если <tex>T_x=T_y</tex> и автомат допускает строку <tex>xz</tex>, то тогда он допускает и строку <tex>yz</tex>, потому что последовательность состояний перенесенных через границу <tex>x</tex> и <tex>z</tex> (либо <tex>y</tex> и <tex>z</tex>) в любом направлении полностью определяется таблицами <tex>T_x=T_y</tex> и строкой <tex>z</tex>. Чтобы допустить строку <tex>xz</tex>, автомат должен в какой-то момент прочитать правый маркер конца строки, находясь в допускающем состоянии <tex>t</tex>.<br />
<br />
== Пример ==<br />
Рассмотрим следующий язык <tex>L_n = (a+b)^∗a(a+b)^{n-1}a(a+b)^∗</tex> при <tex>\forall n > 0</tex>.<br />
<br />
Он может быть легко распознан с помощью следующего недетерменированного конечного автомата.<br />
<br />
Теперь построим на его основе ДКА. Мы можем построить автомат <tex>A_n</tex>, который запоминает последние <tex>n</tex> входных символов. Следовательно, когда мы находимся с состоянии, соответствующему подстроке <tex>\sigma_1 \sigma_2 \dots \sigma_n</tex>, и читаем очередной символ <tex>\gamma</tex>, то мы переходим в состояние, которому уже будет соответствовать подстрока <tex>\sigma_2 \sigma_3 \dots \sigma_n \gamma</tex>. Однако, в случае <tex>\sigma_1 = \gamma = a</tex> автомат переходит в допускающее состояние, где в цикле может переходить на любому символу. Следует отметить, что такая стратегия строит ДКА c <tex>2^n + 1</tex> состояниями. Ниже представлен автомат <tex>A_3</tex>, который распознает язык <tex>L_3</tex>.<br />
<br />
Покажем, что построенные таким образом автоматы будут минимальными.<br />
* Каждые две попарно различных строки <tex>x</tex> и <tex>y</tex> длины <tex>n</tex> различимы. Чтобы доказать это, достаточно рассмотреть строку <tex>b^{i-1}a</tex>, где <tex>i : 1 \leqslant i \leqslant n</tex> {{---}} самая левая позиция символа, в которой начинают различаться строки <tex>x</tex> и <tex>y</tex>, и проверить, что ровно одна строка <tex>xb^{i-1}a</tex> или <tex>yb^{i-1}a</tex> принадлежит <tex>L_n</tex>.<br />
* Каждая строка длины <tex>n</tex> не принадлежит <tex>L_n</tex> и, следовательно, различима от строки <tex>a^{n+1}</tex>, которая принадлежит <tex>L_n</tex>.<br />
* Таким образом, <tex>2^n + 1</tex> строк в <tex>\Sigma^n \cup \{a^{n+1}\}</tex> являются попарно различимыми для <tex>L_n</tex>. Как следствие, <tex>2^n + 1</tex> {{---}} минимальное количество состояний для ДКА, который способен распознать язык <tex>L_n</tex>.<br />
<br />
== См. также ==<br />
* [[Детерминированные конечные автоматы]]<br />
<br />
== Источники информации==<br />
<br />
[[Категория: Теория формальных языков]]<br />
[[Категория: Автоматы и регулярные языки]]</div>Kabanovhttp://neerc.ifmo.ru/wiki/index.php?title=%D0%94%D0%B2%D1%83%D1%81%D1%82%D0%BE%D1%80%D0%BE%D0%BD%D0%BD%D0%B8%D0%B9_%D0%B4%D0%B5%D1%82%D0%B5%D1%80%D0%BC%D0%B8%D0%BD%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BA%D0%BE%D0%BD%D0%B5%D1%87%D0%BD%D1%8B%D0%B9_%D0%B0%D0%B2%D1%82%D0%BE%D0%BC%D0%B0%D1%82&diff=43825Двусторонний детерминированный конечный автомат2015-01-09T20:50:18Z<p>Kabanov: </p>
<hr />
<div>{{Определение<br />
|definition=<br />
'''Двусторонний детерминированный конечный автомат (2ДКА)''' (англ. ''Two-way deterministic finite automaton (2DFA)'') {{---}} набор из восьми элементов <tex>\langle \Sigma , Q, L, R, s, t, r, \delta \rangle</tex>, где <tex>\Sigma</tex> {{---}} алфавит (англ. ''alphabet''), <tex>Q</tex> {{---}} множество состояний (англ. ''finite set of states''), <tex>L \notin \Sigma</tex> {{---}} левый маркер конца строки, <tex>R \notin \Sigma</tex> {{---}} правый маркер конца строки, <tex>s \in Q</tex> — начальное (стартовое) состояние (англ. ''start state''), <tex>t \in Q</tex> {{---}} допускающее состояние (англ. ''accept state''), <tex>r \in Q</tex> — отвергающее состояние (англ. ''reject state''), <tex>\delta : Q \times \{\Sigma \cup \{L, R\}\} \to Q \times \{left, right\}</tex> {{---}} функция переходов (англ. ''transition function'').<br />
}}<br />
Также должны быть удовлетворены следующие условия:<br />
<br />
<tex>\forall q \in Q</tex><br />
* <tex>\delta(q, L) = (u, right)</tex> для некоторого <tex>u \in Q</tex>,<br />
* <tex>\delta(q, R) = (v, left)</tex> для некоторого <tex>v \in Q</tex>,<br />
и <tex>\forall b \in \Sigma \cup \{L\}</tex><br />
* <tex>\delta(t, b) = (t, right)</tex>,<br />
* <tex>\delta(r, b) = (r, right)</tex>,<br />
* <tex>\delta(t, R) = (t, left)</tex>,<br />
* <tex>\delta(r, R) = (r, left)</tex>.<br />
== Описание ==<br />
Зафиксируем <tex>x \in \Sigma^*</tex>, где <tex>x = a_1 a_2 a_3 \dots a_n</tex>. Пусть <tex>a_0 = L</tex> и <tex>a_{n+1}=R</tex>. Тогда <tex>a_0 a_1 a_2 \dots a_n a_{n+1} = L x R</tex>.<br />
<br />
Пусть дан 2ДКА <tex>A = \langle \Sigma , Q, L, R, s, t, r, \delta \rangle</tex> с <tex>n</tex> состояниями. Тогда можно построить ДКА, распознающий язык тот же язык, с <tex>O(e^n)</tex> состояниями.<br />
<br />
Для доказательства приведем ход построения требуемого автомата <tex>B = \langle \Sigma , Q, s \in Q, T \subset Q, \delta : Q \times \Sigma \to Q \rangle</tex>.<br />
<br />
== Регулярность языков ==<br />
Рассмотрим длинную входную строку <tex>w_1</tex> и разобьем на две подстроки <tex>w_1=xz</tex>. Так как у нас наш автомат может производить чтение в любом направлении, то граница <tex>x</tex> и <tex>z</tex> может быть пересечена несколько раз. Каждый раз, когда автомат пересекает границу справа налево (то есть из <tex>z</tex> в <tex>x</tex>), он выходит из состояния <tex>q</tex>. Когда пересечение происходит снова слева направо (если оно вообще происходит), то автомат выходит из состояния <tex>p</tex>. Теперь, если 2ДКА перейдет в <tex>x</tex> в состояние <tex>q</tex> заново, то он снова может появиться в состоянии <tex>p</tex>. Более того, состояние <tex>p</tex> зависит исключительно от <tex>q</tex> и <tex>x</tex>. Обозначим такое отношение через записать <tex>T_x(q) = p</tex>. Мы может записать все такие отношения в виде конечной таблицы <tex>T_x : Q \cup \{d\} \to Q \cup \{h\}</tex>, где <tex>Q</tex> {{---}} множество состояний 2ДКА, а <tex>d</tex> и <tex>h</tex> будут описаны ниже.<br />
<br />
На входной строке <tex>xz</tex> 2ДКА начнет чтение с левого маркера конца строки. В процессе работы автомата позиция чтения будет меняться. В конце концов это позиция пересечет слева направо границу между <tex>x</tex> и <tex>z</tex>. В первый раз, когда это произойдет в каком-нибудь состоянии <tex>T_x(d)</tex> (для этого мы и выделили <tex>d</tex>). Так же автомат может никогда не выйти из <tex>x</tex>. В таком случае мы запишем <tex>T_x(d) = h</tex>. Состояние <tex>T_x(d)</tex> дает немного информации о <tex>x</tex>, но только конечное количество, поскольку существует только конечное количество вариантов для <tex>T_x(d)</tex>. Отметим, что <tex>T_x(d)</tex> зависит только от <tex>x</tex> и не зависит от <tex>z</tex>. Если на вход подавалась строка <tex>xw</tex> вместо <tex>xz</tex>, то в таком случае при пересечении границы из <tex>x</tex> в <tex>w</tex> автомат также был бы в состоянии <tex>T_x(d)</tex>, потому что его значение до того момента определялось только <tex>x</tex> и до тех пор не все, что находится справа от границы никак не влияет.<br />
<br />
Если <tex>T_x(d) = h</tex>, то 2ДКА должен быть в бесконечном цикле внутри <tex>x</tex> и никогда не допустит или отвергнет входную строку.<br />
<br />
Предположим, что 2ДКА переходит из <tex>x</tex> в <tex>z</tex> и спустя время перейти обратно в состояние <tex>q</tex>. Если это происходит, то потом:<br />
* либо снова произойдет переход из <tex>x</tex> в некоторое состояние <tex>p</tex>. В таком случае мы определим <tex>T_x(q)=p</tex>.<br />
* либо никогда не перейдет. В таком случае <tex>T_x(q) = h</tex>.<br />
<br />
Ещё раз отметим, что <tex>T_x(q)</tex> зависит только от <tex>x</tex> и <tex>q</tex> и не зависит от <tex>z</tex>.<br />
<br />
== Пример ==<br />
Рассмотрим следующий язык <tex>L_n = (a+b)^∗a(a+b)^{n-1}a(a+b)^∗</tex> при <tex>\forall n > 0</tex>.<br />
<br />
Он может быть легко распознан с помощью следующего недетерменированного конечного автомата.<br />
<br />
Теперь построим на его основе ДКА. Мы можем построить автомат <tex>A_n</tex>, который запоминает последние <tex>n</tex> входных символов. Следовательно, когда мы находимся с состоянии, соответствующему подстроке <tex>\sigma_1 \sigma_2 \dots \sigma_n</tex>, и читаем очередной символ <tex>\gamma</tex>, то мы переходим в состояние, которому уже будет соответствовать подстрока <tex>\sigma_2 \sigma_3 \dots \sigma_n \gamma</tex>. Однако, в случае <tex>\sigma_1 = \gamma = a</tex> автомат переходит в допускающее состояние, где в цикле может переходить на любому символу. Следует отметить, что такая стратегия строит ДКА c <tex>2^n + 1</tex> состояниями. Ниже представлен автомат <tex>A_3</tex>, который распознает язык <tex>L_3</tex>.<br />
<br />
Покажем, что построенные таким образом автоматы будут минимальными.<br />
* Каждые две попарно различных строки <tex>x</tex> и <tex>y</tex> длины <tex>n</tex> различимы. Чтобы доказать это, достаточно рассмотреть строку <tex>b^{i-1}a</tex>, где <tex>i : 1 \leqslant i \leqslant n</tex> {{---}} самая левая позиция символа, в которой начинают различаться строки <tex>x</tex> и <tex>y</tex>, и проверить, что ровно одна строка <tex>xb^{i-1}a</tex> или <tex>yb^{i-1}a</tex> принадлежит <tex>L_n</tex>.<br />
* Каждая строка длины <tex>n</tex> не принадлежит <tex>L_n</tex> и, следовательно, различима от строки <tex>a^{n+1}</tex>, которая принадлежит <tex>L_n</tex>.<br />
* Таким образом, <tex>2^n + 1</tex> строк в <tex>\Sigma^n \cup \{a^{n+1}\}</tex> являются попарно различимыми для <tex>L_n</tex>. Как следствие, <tex>2^n + 1</tex> {{---}} минимальное количество состояний для ДКА, который способен распознать язык <tex>L_n</tex>.<br />
<br />
== См. также ==<br />
* [[Детерминированные конечные автоматы]]<br />
<br />
== Источники информации==<br />
<br />
[[Категория: Теория формальных языков]]<br />
[[Категория: Автоматы и регулярные языки]]</div>Kabanovhttp://neerc.ifmo.ru/wiki/index.php?title=%D0%94%D0%B2%D1%83%D1%81%D1%82%D0%BE%D1%80%D0%BE%D0%BD%D0%BD%D0%B8%D0%B9_%D0%B4%D0%B5%D1%82%D0%B5%D1%80%D0%BC%D0%B8%D0%BD%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BA%D0%BE%D0%BD%D0%B5%D1%87%D0%BD%D1%8B%D0%B9_%D0%B0%D0%B2%D1%82%D0%BE%D0%BC%D0%B0%D1%82&diff=43746Двусторонний детерминированный конечный автомат2015-01-09T17:00:10Z<p>Kabanov: /* Пример */</p>
<hr />
<div>{{Определение<br />
|definition=<br />
'''Двусторонний детерминированный конечный автомат (2ДКА)''' (англ. ''Two-way deterministic finite automaton (2DFA)'') {{---}} набор из восьми элементов <tex>\langle \Sigma , Q, L, R, s, t, r, \delta \rangle</tex>, где <tex>\Sigma</tex> {{---}} алфавит (англ. ''alphabet''), <tex>Q</tex> {{---}} множество состояний (англ. ''finite set of states''), <tex>L \notin \Sigma</tex> {{---}} левый маркер конца строки, <tex>R \notin \Sigma</tex> {{---}} правый маркер конца строки, <tex>s \in Q</tex> — начальное (стартовое) состояние (англ. ''start state''), <tex>t \in Q</tex> {{---}} допускающее состояние (англ. ''accept state''), <tex>r \in Q</tex> — отвергающее состояние (англ. ''reject state''), <tex>\delta : Q \times \{\Sigma \cup \{L, R\}\} \to Q \times \{left, right\}</tex> {{---}} функция переходов (англ. ''transition function'').<br />
}}<br />
Также должны быть удовлетворены следующие условия:<br />
<br />
<tex>\forall q \in Q</tex><br />
* <tex>\delta(q, L) = (u, right)</tex> для некоторого <tex>u \in Q</tex>,<br />
* <tex>\delta(q, R) = (v, left)</tex> для некоторого <tex>v \in Q</tex>,<br />
и <tex>\forall b \in \Sigma \cup \{L\}</tex><br />
* <tex>\delta(t, b) = (t, right)</tex>,<br />
* <tex>\delta(r, b) = (r, right)</tex>,<br />
* <tex>\delta(t, R) = (t, left)</tex>,<br />
* <tex>\delta(r, R) = (r, left)</tex>.<br />
== Описание ==<br />
Зафиксируем <tex>x \in \Sigma^*</tex>, где <tex>x = a_1 a_2 a_3 \dots a_n</tex>. Пусть <tex>a_0 = L</tex> и <tex>a_{n+1}=R</tex>. Тогда <tex>a_0 a_1 a_2 \dots a_n a_{n+1} = L x R</tex>.<br />
<br />
== Пример ==<br />
Рассмотрим следующий язык <tex>L_n = (a+b)^∗a(a+b)^{n-1}a(a+b)^∗</tex> при <tex>\forall n > 0</tex>.<br />
<br />
Он может быть легко распознан с помощью следующего недетерменированного конечного автомата.<br />
<br />
Теперь построим на его основе ДКА. Мы можем построить автомат <tex>A_n</tex>, который запоминает последние <tex>n</tex> входных символов. Следовательно, когда мы находимся с состоянии, соответствующему подстроке <tex>\sigma_1 \sigma_2 \dots \sigma_n</tex>, и читаем очередной символ <tex>\gamma</tex>, то мы переходим в состояние, которому уже будет соответствовать подстрока <tex>\sigma_2 \sigma_3 \dots \sigma_n \gamma</tex>. Однако, в случае <tex>\sigma_1 = \gamma = a</tex> автомат переходит в допускающее состояние, где в цикле может переходить на любому символу. Следует отметить, что такая стратегия строит ДКА c <tex>2^n + 1</tex> состояниями. Ниже представлен автомат <tex>A_3</tex>, который распознает язык <tex>L_3</tex>.<br />
<br />
Покажем, что построенные таким образом автоматы будут минимальными.<br />
* Каждые две попарно различных строки <tex>x</tex> и <tex>y</tex> длины <tex>n</tex> различимы. Чтобы доказать это, достаточно рассмотреть строку <tex>b^{i-1}a</tex>, где <tex>i : 1 \leqslant i \leqslant n</tex> {{---}} самая левая позиция символа, в которой начинают различаться строки <tex>x</tex> и <tex>y</tex>, и проверить, что ровно одна строка <tex>xb^{i-1}a</tex> или <tex>yb^{i-1}a</tex> принадлежит <tex>L_n</tex>.<br />
* Каждая строка длины <tex>n</tex> не принадлежит <tex>L_n</tex> и, следовательно, различима от строки <tex>a^{n+1}</tex>, которая принадлежит <tex>L_n</tex>.<br />
* Таким образом, <tex>2^n + 1</tex> строк в <tex>\Sigma^n \cup \{a^{n+1}\}</tex> являются попарно различимыми для <tex>L_n</tex>. Как следствие, <tex>2^n + 1</tex> {{---}} минимальное количество состояний для ДКА, который способен распознать язык <tex>L_n</tex>.<br />
<br />
== См. также ==<br />
* [[Детерминированные конечные автоматы]]<br />
<br />
== Источники информации==<br />
<br />
[[Категория: Теория формальных языков]]<br />
[[Категория: Автоматы и регулярные языки]]</div>Kabanovhttp://neerc.ifmo.ru/wiki/index.php?title=%D0%94%D0%B2%D1%83%D1%81%D1%82%D0%BE%D1%80%D0%BE%D0%BD%D0%BD%D0%B8%D0%B9_%D0%B4%D0%B5%D1%82%D0%B5%D1%80%D0%BC%D0%B8%D0%BD%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BA%D0%BE%D0%BD%D0%B5%D1%87%D0%BD%D1%8B%D0%B9_%D0%B0%D0%B2%D1%82%D0%BE%D0%BC%D0%B0%D1%82&diff=43736Двусторонний детерминированный конечный автомат2015-01-09T16:33:01Z<p>Kabanov: </p>
<hr />
<div>{{Определение<br />
|definition=<br />
'''Двусторонний детерминированный конечный автомат (2ДКА)''' (англ. ''Two-way deterministic finite automaton (2DFA)'') {{---}} набор из восьми элементов <tex>\langle \Sigma , Q, L, R, s, t, r, \delta \rangle</tex>, где <tex>\Sigma</tex> {{---}} алфавит (англ. ''alphabet''), <tex>Q</tex> {{---}} множество состояний (англ. ''finite set of states''), <tex>L \notin \Sigma</tex> {{---}} левый маркер конца строки, <tex>R \notin \Sigma</tex> {{---}} правый маркер конца строки, <tex>s \in Q</tex> — начальное (стартовое) состояние (англ. ''start state''), <tex>t \in Q</tex> {{---}} допускающее состояние (англ. ''accept state''), <tex>r \in Q</tex> — отвергающее состояние (англ. ''reject state''), <tex>\delta : Q \times \{\Sigma \cup \{L, R\}\} \to Q \times \{left, right\}</tex> {{---}} функция переходов (англ. ''transition function'').<br />
}}<br />
Также должны быть удовлетворены следующие условия:<br />
<br />
<tex>\forall q \in Q</tex><br />
* <tex>\delta(q, L) = (u, right)</tex> для некоторого <tex>u \in Q</tex>,<br />
* <tex>\delta(q, R) = (v, left)</tex> для некоторого <tex>v \in Q</tex>,<br />
и <tex>\forall b \in \Sigma \cup \{L\}</tex><br />
* <tex>\delta(t, b) = (t, right)</tex>,<br />
* <tex>\delta(r, b) = (r, right)</tex>,<br />
* <tex>\delta(t, R) = (t, left)</tex>,<br />
* <tex>\delta(r, R) = (r, left)</tex>.<br />
== Описание ==<br />
Зафиксируем <tex>x \in \Sigma^*</tex>, где <tex>x = a_1 a_2 a_3 \dots a_n</tex>. Пусть <tex>a_0 = L</tex> и <tex>a_{n+1}=R</tex>. Тогда <tex>a_0 a_1 a_2 \dots a_n a_{n+1} = L x R</tex>.<br />
<br />
== Пример ==<br />
Рассмотрим следующий язык <tex>L_n = (a+b)^∗a(a+b)^{n-1}a(a+b)^∗</tex> при <tex>\forall n > 0</tex>.<br />
<br />
Он может быть легко распознан с помощью следующего недетерменированного конечного автомата.<br />
<br />
Теперь построим на его основе ДКА. Мы можем построить автомат <tex>A_n</tex>, который запоминает последние <tex>n</tex> входных символов. Следовательно, когда мы находимся с состоянии, соответствующему подстроке <tex>\sigma_1 \sigma_2 \dots \sigma_n</tex>, и читаем очередной символ <tex>\gamma</tex>, то мы переходим в состояние, которому уже будет соответствовать подстрока <tex>\sigma_2 \sigma_3 \dots \sigma_n \gamma</tex>. Однако, в случае <tex>\sigma_1 = \gamma = a</tex> автомат переходит в допускающее состояние, где в цикле может переходить на любому символу. Следует отметить, что такая стратегия строит ДКА c <tex>2^n + 1</tex> состояниями. Ниже представлен автомат <tex>A_3</tex>, который распознает язык <tex>L_3</tex>.<br />
<br />
== См. также ==<br />
* [[Детерминированные конечные автоматы]]<br />
<br />
== Источники информации==<br />
<br />
[[Категория: Теория формальных языков]]<br />
[[Категория: Автоматы и регулярные языки]]</div>Kabanovhttp://neerc.ifmo.ru/wiki/index.php?title=%D0%94%D0%B2%D1%83%D1%81%D1%82%D0%BE%D1%80%D0%BE%D0%BD%D0%BD%D0%B8%D0%B9_%D0%B4%D0%B5%D1%82%D0%B5%D1%80%D0%BC%D0%B8%D0%BD%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BA%D0%BE%D0%BD%D0%B5%D1%87%D0%BD%D1%8B%D0%B9_%D0%B0%D0%B2%D1%82%D0%BE%D0%BC%D0%B0%D1%82&diff=43506Двусторонний детерминированный конечный автомат2015-01-06T18:31:59Z<p>Kabanov: </p>
<hr />
<div>{{Определение<br />
|definition=<br />
'''Двусторонний детерминированный конечный автомат (2ДКА)''' (англ. ''Two-way deterministic finite automaton (2DFA)'') {{---}} набор из восьми элементов <tex>\langle \Sigma , Q, L, R, s, t, r, \delta \rangle</tex>, где <tex>\Sigma</tex> {{---}} алфавит (англ. ''alphabet''), <tex>Q</tex> {{---}} множество состояний (англ. ''finite set of states''), <tex>L \notin \Sigma</tex> {{---}} левый маркер конца строки, <tex>R \notin \Sigma</tex> {{---}} правый маркер конца строки, <tex>s \in Q</tex> — начальное (стартовое) состояние (англ. ''start state''), <tex>t \in Q</tex> {{---}} допускающее состояние (англ. ''accept state''), <tex>r \in Q</tex> — отвергающее состояние (англ. ''reject state''), <tex>\delta : Q \times \{\Sigma \cup \{L, R\}\} \to Q \times \{left, right\}</tex> {{---}} функция переходов (англ. ''transition function'').<br />
}}<br />
Также должны быть удовлетворены следующие условия:<br />
<br />
<tex>\forall q \in Q</tex><br />
* <tex>\delta(q, L) = (u, right)</tex> для некоторого <tex>u \in Q</tex>,<br />
* <tex>\delta(q, R) = (v, left)</tex> для некоторого <tex>v \in Q</tex>,<br />
и <tex>\forall b \in \Sigma \cup \{L\}</tex><br />
* <tex>\delta(t, b) = (t, right)</tex>,<br />
* <tex>\delta(r, b) = (r, right)</tex>,<br />
* <tex>\delta(t, R) = (t, left)</tex>,<br />
* <tex>\delta(r, R) = (r, left)</tex>.<br />
<br />
<br />
== См. также ==<br />
* [[Детерминированные конечные автоматы]]<br />
<br />
== Источники информации==<br />
<br />
[[Категория: Теория формальных языков]]<br />
[[Категория: Автоматы и регулярные языки]]</div>Kabanovhttp://neerc.ifmo.ru/wiki/index.php?title=%D0%94%D0%B2%D1%83%D1%81%D1%82%D0%BE%D1%80%D0%BE%D0%BD%D0%BD%D0%B8%D0%B9_%D0%B4%D0%B5%D1%82%D0%B5%D1%80%D0%BC%D0%B8%D0%BD%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BA%D0%BE%D0%BD%D0%B5%D1%87%D0%BD%D1%8B%D0%B9_%D0%B0%D0%B2%D1%82%D0%BE%D0%BC%D0%B0%D1%82&diff=43491Двусторонний детерминированный конечный автомат2015-01-06T15:47:11Z<p>Kabanov: Новая страница: «{{Определение |definition= '''Двусторонний детерминированный конечный автомат (2ДКА)''' (англ. ''Two...»</p>
<hr />
<div>{{Определение<br />
|definition=<br />
'''Двусторонний детерминированный конечный автомат (2ДКА)''' (англ. ''Two-way deterministic finite automaton (2DFA)'') — набор из восьми элементов <tex>\langle \Sigma , Q, s \in Q, T \subset Q, \delta : Q \times \Sigma \to Q \rangle</tex>, где <tex>\Sigma</tex> — алфавит (англ. ''alphabet''), <tex>Q</tex> — множество состояний (англ. ''finite set of states''), <tex>s</tex> — начальное (стартовое) состояние (англ. ''start state''), <tex>T</tex> — множество допускающих состояний (англ. ''set of accept states''), <tex>\delta</tex> — функция переходов (англ. ''transition function'').<br />
}}<br />
<br />
== См. также ==<br />
* [[Детерминированные_конечные_автоматы]]<br />
<br />
== Источники информации==<br />
<br />
[[Категория: Теория формальных языков]]<br />
[[Категория: Автоматы и регулярные языки]]</div>Kabanovhttp://neerc.ifmo.ru/wiki/index.php?title=%D0%98%D0%B5%D1%80%D0%B0%D1%80%D1%85%D0%B8%D1%8F_%D0%A5%D0%BE%D0%BC%D1%81%D0%BA%D0%BE%D0%B3%D0%BE_%D1%84%D0%BE%D1%80%D0%BC%D0%B0%D0%BB%D1%8C%D0%BD%D1%8B%D1%85_%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B0%D1%82%D0%B8%D0%BA&diff=40864Иерархия Хомского формальных грамматик2014-11-17T18:15:04Z<p>Kabanov: </p>
<hr />
<div>{{Определение<br />
|definition=<br />
'''Иерархия Хомского''' (англ. ''Chomsky hierarchy'') {{---}} классификация [[формальные грамматики|формальных грамматик]] и [[формальные грамматики|задаваемых ими языков]], согласно которой они делятся на 4 класса по их условной сложности.<br />
}}<br />
== Класс 0 ==<br />
К нулевому классу относятся все [[Формальные_грамматики|формальные грамматики]]. Элементы этого класса называются '''неограниченными грамматиками''' (англ. ''unrestricted grammars''), поскольку на них не накладывается никаких ограничений. Они задают все языки, которые могут быть распознаны [[Машина_Тьюринга|машиной Тьюринга]]. Эти языки также известны как '''[[Перечислимые_языки|рекурсивно перечислимые]]''' (англ. ''recursively enumerable''). <br />
<br />
Правила можно записать в виде:<br />
<br />
<tex>\alpha \rightarrow \beta</tex>, где <tex>\alpha</tex> — любая непустая цепочка, содержащая хотя бы один нетерминальный символ, а <tex>\beta</tex> — любая цепочка символов из алфавита.<br />
<br />
Практического применения в силу своей сложности такие грамматики не имеют.<br />
<br />
===Пример===<br />
Продукции:<br />
<br />
<tex><br />
S \rightarrow aBcc \\<br />
B \rightarrow A \\<br />
BAA \rightarrow d \\<br />
Ac \rightarrow B \\<br />
A \rightarrow AAA\ |\ dB \\<br />
</tex><br />
<br />
Выведем в данной грамматике строку <tex>w = addd</tex>:<br />
<br />
<tex>\boldsymbol{S} \Rightarrow a\boldsymbol{B}cc \Rightarrow a\boldsymbol{Ac}c \Rightarrow a\boldsymbol{B}c \Rightarrow a\boldsymbol{Ac} \Rightarrow a\boldsymbol{B} \Rightarrow a\boldsymbol{A} \Rightarrow ad\boldsymbol{B} \Rightarrow ad\boldsymbol{A} \Rightarrow ad\boldsymbol{A}AA \Rightarrow add\boldsymbol{BAA} \Rightarrow addd</tex><br />
<br />
== Класс 1 ==<br />
Первый класс представлен '''неукорачивающими''' и '''контекстно-зависимыми''' грамматиками.<br />
{{Определение<br />
|id = Неукорачивающие грамматики<br />
|definition =<br />
'''Неукорачивающая грамматика''' (англ. ''noncontracting grammar'') {{---}} это формальная грамматика, всякое правило из <tex>P</tex> которой имеет вид <tex>\alpha\rightarrow\beta</tex>, где <tex>\alpha , \beta \in \{\Sigma\cup N\}^{+}</tex> и <tex>|\alpha| \leqslant |\beta|</tex> (возможно правило <tex>S \rightarrow \varepsilon</tex>, но тогда <tex>S</tex> не встречается в правых частях правил).<br />
}}<br />
{{Определение<br />
|definition =<br />
'''Контекстно-зависимая грамматика''' (англ. ''context-sensitive grammar'') {{---}} это формальная грамматика, всякое правило из <tex>P</tex> которой имеет вид <tex>\alpha A \beta\rightarrow\alpha\gamma\beta</tex>, где <tex>\alpha , \beta \in \{\Sigma\cup N\}^{*}</tex>, <tex>A \in N</tex> и <tex>\gamma \in \{\Sigma\cup N\}^{+}</tex> (возможно правило <tex>S \rightarrow \varepsilon</tex>, но тогда <tex>S</tex> не встречается в правых частях правил).<br />
}}<br />
Языки, заданные этими грамматиками, распознаются с помощью '''линейно ограниченного автомата''' (англ. ''linear bounded automaton'') (недетерминированная машина Тьюринга, чья лента ограничена константой, зависящей от длины входа.)<br />
<br />
[[Неукорачивающие и контекстно-зависимые грамматики, эквивалентность|Известно что]], неукорачивающие грамматики эквивалентны контекстно-зависимым.<br />
<br />
===Пример===<br />
<tex>L=\{w \in \Sigma^* | w = 0^n1^n2^n, n \geqslant 1\}</tex><br />
<br />
Продукции: <br />
<br />
<tex><br />
S \rightarrow 012 \\<br />
S \rightarrow 0AS2 \\<br />
A0 \rightarrow 0A \\ <br />
A1 \rightarrow 11 <br />
</tex><br />
<br />
== Класс 2 ==<br />
Второй класс составляют [[Контекстно-свободные грамматики, вывод, лево- и правосторонний вывод, дерево разбора|контекстно-свободные грамматики]], которые задают контекстно-свободные языки. Эти языки распознаются с помощью [[Автоматы_с_магазинной_памятью|автоматов с магазинной памятью]].<br />
{{Определение<br />
|definition =<br />
'''Контекстно-свободная грамматика''' (англ. ''context-free grammar'') {{---}} это формальная грамматика, всякое правило из <tex>P</tex> которой имеет вид <tex>A \rightarrow\beta</tex>, где <tex>A\in N </tex>, <tex>\beta \in \{\Sigma \cup N\}^{+}</tex>.<br />
}}<br />
То есть грамматика допускает появление в левой части правила только одного нетерминального символа.<br />
<br />
===Пример===<br />
<tex>L=\{w \in \Sigma^* | w = w^R\}</tex> (язык палиндромов).<br />
<br />
Продукции: <tex>S\rightarrow\alpha S\alpha\,|\,\alpha\,|\,\varepsilon, \alpha \in \Sigma</tex><br />
<br />
== Класс 3 ==<br />
К третьему типу относятся '''автоматные''' или '''регулярные грамматики''' (англ. ''regular grammars'') {{---}} самые простые из формальных грамматик, которые задают [[Регулярные_языки:_два_определения_и_их_эквивалентность|регулярные языки]]. Они являются контекстно-свободными, но с ограниченными возможностями.<br />
<br />
Все регулярные грамматики могут быть разделены на два эквивалентных класса следующего вида:<br />
{{Определение<br />
|definition =<br />
'''Леволинейная грамматика''' (англ. ''left-regular grammar'') {{---}} это формальная грамматика, всякое правило из <tex>P</tex> которой имеет вид <tex>A \rightarrow B\gamma</tex> или <tex>A \rightarrow \gamma</tex>, где <tex>\gamma \in \Sigma, A, B \in N</tex>.<br />
}}<br />
{{Определение<br />
|definition =<br />
'''Праволинейная грамматика''' (англ. ''right-regular grammar'') {{---}} это формальная грамматика, всякое правило из <tex>P</tex> которой имеет вид <tex>A \rightarrow \gamma B</tex>; или <tex>A \rightarrow \gamma</tex>, где <tex>\gamma \in \Sigma, A, B \in N</tex>.<br />
}}<br />
Оба вида задают одинаковые языки. При этом если правила леволинейной и праволинейной грамматик объединить, то язык будет уже не обязан быть регулярным.<br />
<br />
Также можно [[Правоконтекстные_грамматики,_эквивалентность_автоматам|показать]], что множество языков, задаваемых праволинейными грамматиками, совпадает со множеством языков, задаваемых [[Детерминированные конечные автоматы|конечными автоматами]].<br />
<br />
===Пример===<br />
<tex>L</tex> для регулярного выражения <tex>a^*bc^*</tex>.<br />
<br />
Продукции:<br />
<br />
<tex><br />
S \rightarrow aS\ |\ bA \\<br />
A \rightarrow \varepsilon\ |\ cA<br />
</tex><br />
<br />
== См. также ==<br />
* [[Разрешимые_(рекурсивные)_языки|Разрешимые (рекурсивные) языки]]<br />
* [[Возможность_порождения_формальной_грамматикой_произвольного_перечислимого_языка|Возможность порождения формальной грамматикой произвольного перечислимого языка]]<br />
<br />
==Источники информации==<br />
* ''А. Ахо, Дж. Ульман.'' Теория синтаксического анализа, перевода и компиляции. Синтаксический анализ. Том 2. Пер. с англ. — М.: Книга по Требованию, 2012. — ISBN 978-5-458-27407-4<br />
* [[wikipedia:Chomsky_hierarchy|Wikipedia {{---}} Chomsky hierarchy]]<br />
* [[wikipedia:ru:Иерархия_Хомского|Википедия {{---}} Иерархия Хомского]]<br />
<br />
[[Категория: Теория формальных языков]]<br />
[[Категория: Контекстно-свободные грамматики]]</div>Kabanov