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

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%A1%D0%BE%D0%BF%D1%80%D1%8F%D0%B6%D1%91%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BE%D0%BF%D0%B5%D1%80%D0%B0%D1%82%D0%BE%D1%80&amp;diff=48829</id>
		<title>Сопряжённый оператор</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%A1%D0%BE%D0%BF%D1%80%D1%8F%D0%B6%D1%91%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BE%D0%BF%D0%B5%D1%80%D0%B0%D1%82%D0%BE%D1%80&amp;diff=48829"/>
				<updated>2015-06-25T10:01:35Z</updated>
		
		<summary type="html">&lt;p&gt;194.85.160.130: /* Теорема 2 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{В разработке}}&lt;br /&gt;
&lt;br /&gt;
[[Спектр линейного оператора|&amp;lt;&amp;lt;]][[Компактный оператор |&amp;gt;&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
Все рассматриваемые далее пространства считаем Банаховыми.&lt;br /&gt;
&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
&amp;lt;tex&amp;gt; E^* &amp;lt;/tex&amp;gt; {{---}} множество линейных непрерывных функционалов над &amp;lt;tex&amp;gt; E &amp;lt;/tex&amp;gt;, его называют пространством, сопряженным к &amp;lt;tex&amp;gt; E &amp;lt;/tex&amp;gt;.&amp;lt;br&amp;gt;&lt;br /&gt;
Аналогично, &amp;lt;tex&amp;gt; E^{**} &amp;lt;/tex&amp;gt; {{---}} пространство, сопряженное к &amp;lt;tex&amp;gt; E^* &amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Естественное вложение ==&lt;br /&gt;
{{Утверждение&lt;br /&gt;
|statement=&lt;br /&gt;
Между &amp;lt;tex&amp;gt; E &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; E^{**} &amp;lt;/tex&amp;gt; существует так называемый '''естественный изоморфизм''', сохраняющий норму точки.&lt;br /&gt;
|proof=&lt;br /&gt;
Введем &amp;lt;tex&amp;gt; F_x &amp;lt;/tex&amp;gt; следующим образом: &amp;lt;tex&amp;gt;\forall x \in E : F_x (f) = f(x), f \in E^{*} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt; F_x : E^{*} \to \mathbb{R} &amp;lt;/tex&amp;gt; — функционал, заданный на &amp;lt;tex&amp;gt;E^{*}&amp;lt;/tex&amp;gt;, то есть &amp;lt;tex&amp;gt; F_x \in E^{**} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Тогда само &amp;lt;tex&amp;gt; F &amp;lt;/tex&amp;gt; отображает &amp;lt;tex&amp;gt; E &amp;lt;/tex&amp;gt; в &amp;lt;tex&amp;gt; E^{**} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt; F &amp;lt;/tex&amp;gt; линейно: &amp;lt;tex&amp;gt; F_{\alpha x_1 + \beta x_2} = \alpha F_{x_1} + \beta F_{x_2} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt; | F_x(f) | = |f(x)| \le \| f \| \| x \| &amp;lt;/tex&amp;gt;, откуда &amp;lt;tex&amp;gt; \| F_x \| \le \| x \| &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
С другой стороны, по следствию из теоремы Хана-Банаха, для любого &amp;lt;tex&amp;gt; x_0 \in E &amp;lt;/tex&amp;gt; существует &amp;lt;tex&amp;gt; f_0 \in E^* &amp;lt;/tex&amp;gt;, такое, что выполняются два условия:&lt;br /&gt;
# &amp;lt;tex&amp;gt; f_0(x_0) = \| x_0 \| &amp;lt;/tex&amp;gt;&lt;br /&gt;
# &amp;lt;tex&amp;gt; \| f_0 \| = 1 &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt; | F_{x_0} (f_0) | = f_0 (x_0) = \| x_0 \|, \| f_0 \| = 1 &amp;lt;/tex&amp;gt;, потому получаем, что &amp;lt;tex&amp;gt; \| F_{x_0} \| \ge \| x_0 \| \implies \| F_{x_0} \| = \| x_0 \| &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Значит, получившееся преобразование &amp;lt;tex&amp;gt; x \mapsto F_x &amp;lt;/tex&amp;gt; — изометрия, &amp;lt;tex&amp;gt; \| x \| = \| F_x \| &amp;lt;/tex&amp;gt;, получили '''естественное вложение''' &amp;lt;tex&amp;gt; E &amp;lt;/tex&amp;gt; в &amp;lt;tex&amp;gt; E^{**} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
&amp;lt;tex&amp;gt; E &amp;lt;/tex&amp;gt; называется '''рефлексивным''', если &amp;lt;tex&amp;gt; E &amp;lt;/tex&amp;gt; будет совпадать с &amp;lt;tex&amp;gt; E^{**} &amp;lt;/tex&amp;gt; при таком отображении.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Например, гильбертово пространство &amp;lt;tex&amp;gt; H &amp;lt;/tex&amp;gt; рефлексивно (следует из теоремы Рисса об общем виде линейного функционала).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt; C[0, 1] &amp;lt;/tex&amp;gt; не является рефлексивным.&lt;br /&gt;
&lt;br /&gt;
== Сопряженный оператор ==&lt;br /&gt;
&lt;br /&gt;
Пусть оператор &amp;lt;tex&amp;gt; A &amp;lt;/tex&amp;gt; действует из &amp;lt;tex&amp;gt; E &amp;lt;/tex&amp;gt; в &amp;lt;tex&amp;gt; F &amp;lt;/tex&amp;gt;, и функционал &amp;lt;tex&amp;gt; \varphi &amp;lt;/tex&amp;gt; принадлежит &amp;lt;tex&amp;gt; F^* &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Рассмотрим &amp;lt;tex&amp;gt; f(x) = \varphi (Ax), | f(x) | \le \| \varphi \| \| A \| \| x \| &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Получили новый функционал &amp;lt;tex&amp;gt; f &amp;lt;/tex&amp;gt;, принадлежащий &amp;lt;tex&amp;gt; E^* &amp;lt;/tex&amp;gt;. &amp;lt;tex&amp;gt; \varphi \mapsto \varphi A &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt; \varphi A = A^* (\varphi), A^* : F^* \to E^* &amp;lt;/tex&amp;gt;. &amp;lt;tex&amp;gt; A^* &amp;lt;/tex&amp;gt; {{---}} '''сопряженный оператор''' к &amp;lt;tex&amp;gt; A &amp;lt;/tex&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
{{Теорема&lt;br /&gt;
|statement=&lt;br /&gt;
Если &amp;lt;tex&amp;gt; A &amp;lt;/tex&amp;gt; {{---}} линейный ограниченный оператор, то &amp;lt;tex&amp;gt; \| A^* \| = \| A \| &amp;lt;/tex&amp;gt;.&lt;br /&gt;
|proof=&lt;br /&gt;
Возьмем &amp;lt;tex&amp;gt; x \in E, \varphi \in F^* &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt; | A^* (\varphi, x) | = | \varphi (Ax) | \le \| A \| \| \varphi \| \| x \| &amp;lt;/tex&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Получили, что &amp;lt;tex&amp;gt; \| A^* (\varphi) \| \le \| A \| \| \varphi \| &amp;lt;/tex&amp;gt;, откуда &amp;lt;tex&amp;gt; \| A^* \| \le \| A \| &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Для доказательства в обратную сторону используем [[Теорема Хана-Банаха#hbnorm|следствие из теоремы Хана-Банаха]]:&lt;br /&gt;
&lt;br /&gt;
По определению нормы оператора: &amp;lt;tex&amp;gt; \forall \varepsilon &amp;gt; 0 \, \exists x: \| x \| = 1 \implies \| A \| - \varepsilon &amp;lt; \| Ax \| &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt; Ax \in F &amp;lt;/tex&amp;gt;, по следствию из теоремы Хана-Банаха подберем &amp;lt;tex&amp;gt; \varphi_0 \in F^*, \| \varphi_0 \| = 1: \varphi_0 (Ax) = \| Ax \| &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt; | A^*(\varphi_0, x) | = | \varphi_0(Ax) | = \| Ax \| &amp;gt; \| A \| - \varepsilon &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt; | A^*(\varphi_0, x) | \le \| A^*(\varphi_0) \| \| x \| = \| A^*(\varphi_0) \| \le \| A^* \| \| \varphi_0 \| = \| A^* \| &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Соединяя эти два неравенства, получаем, что &amp;lt;tex&amp;gt; \forall \varepsilon &amp;gt; 0: \| A^* \| &amp;gt; \| A \| - \varepsilon &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Устремляя &amp;lt;tex&amp;gt; \varepsilon &amp;lt;/tex&amp;gt; к нулю, получаем, что &amp;lt;tex&amp;gt; \| A^* \| \ge \| A \| &amp;lt;/tex&amp;gt;, и, окончательно, &amp;lt;tex&amp;gt; \| A^* \| = \| A \| &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Примеры сопряженных операторов ==&lt;br /&gt;
&lt;br /&gt;
Возьмем любое гильбертово пространство &amp;lt;tex&amp;gt; H &amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt; A : H \to H &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt; \forall \varphi \in H^* &amp;lt;/tex&amp;gt; по теореме Рисса об общем виде линейного функционала в &amp;lt;tex&amp;gt; H &amp;lt;/tex&amp;gt; существует единственный&lt;br /&gt;
&amp;lt;tex&amp;gt; z : \varphi (y) = \langle y, z \rangle, \| \varphi \| = \| z \| &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Поскольку &amp;lt;tex&amp;gt; x \mapsto \varphi (Ax) &amp;lt;/tex&amp;gt; также является линейным функционалом &amp;lt;tex&amp;gt; H \to H &amp;lt;/tex&amp;gt;, то &amp;lt;tex&amp;gt; \varphi (Ax) = \langle Ax, z \rangle = \langle x, y \rangle &amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt; y &amp;lt;/tex&amp;gt; не зависит от &amp;lt;tex&amp;gt; x &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Имеем отображение &amp;lt;tex&amp;gt; z \mapsto y &amp;lt;/tex&amp;gt;, тогда &amp;lt;tex&amp;gt; y = A^*(z) &amp;lt;/tex&amp;gt;, и окончательно:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt; \langle Ax, z \rangle = \langle x, A^*z \rangle &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
В гильбертовом пространстве &amp;lt;tex&amp;gt; H &amp;lt;/tex&amp;gt; сопряженный оператор {{---}} тот оператор, который позволяет писать равенство выше.&lt;br /&gt;
&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
Оператор &amp;lt;tex&amp;gt; A &amp;lt;/tex&amp;gt; в гильбертовом пространстве называется '''самосопряженным''', если &amp;lt;tex&amp;gt; A = A^* &amp;lt;/tex&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
В случае &amp;lt;tex&amp;gt; \mathbb{R}^n &amp;lt;/tex&amp;gt; (частный случай &amp;lt;tex&amp;gt; H &amp;lt;/tex&amp;gt;) оператор &amp;lt;tex&amp;gt; A : \mathbb{R}^n \to \mathbb{R}^n &amp;lt;/tex&amp;gt; представляет собой матрицу размером &amp;lt;tex&amp;gt; n \times n &amp;lt;/tex&amp;gt;. Сопряженный к &amp;lt;tex&amp;gt; A &amp;lt;/tex&amp;gt; оператор получается транспонированием соответствующей матрицы: &amp;lt;tex&amp;gt; A^* = A^T &amp;lt;/tex&amp;gt;. Для симметричной матрицы &amp;lt;tex&amp;gt; A &amp;lt;/tex&amp;gt; получается &amp;lt;tex&amp;gt; A^* = A^T = A &amp;lt;/tex&amp;gt;, то есть, если &amp;lt;tex&amp;gt; A &amp;lt;/tex&amp;gt; {{---}} симметричная матрица, то &amp;lt;tex&amp;gt; A &amp;lt;/tex&amp;gt; {{---}} самосопряженный оператор.&lt;br /&gt;
&lt;br /&gt;
Рассмотрим теперь пространство &amp;lt;tex&amp;gt; E = L_p [0, 1] &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;tex&amp;gt; K(u, v) : [0, 1] \times [0, 1] \to \mathbb{R} &amp;lt;/tex&amp;gt; {{---}} непрерывная функция на &amp;lt;tex&amp;gt; [0, 1] \times [0, 1] &amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt; x \in E &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
'''Интегральный оператор''' &amp;lt;tex&amp;gt; A &amp;lt;/tex&amp;gt;, действующий из &amp;lt;tex&amp;gt; L_p [0, 1] &amp;lt;/tex&amp;gt; в &amp;lt;tex&amp;gt; L_p [0, 1] &amp;lt;/tex&amp;gt; определяется так: &amp;lt;tex&amp;gt; A(x, s) = (Ax)(s) = \int\limits_0^1 K(s, t) x(t) dt &amp;lt;/tex&amp;gt;. &amp;lt;tex&amp;gt; Ax \in E &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Построим сопряженный оператор:&lt;br /&gt;
&lt;br /&gt;
По теореме об общем виде линейного функционала в &amp;lt;tex&amp;gt; L_p &amp;lt;/tex&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt; \forall \varphi \in E^*, x \in E: \varphi(x) = \int\limits_0^1 y(t) x(t) dt, y \in L_q &amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt; \frac 1p + \frac 1q = 1 &amp;lt;/tex&amp;gt; (&amp;lt;tex&amp;gt; p &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; q &amp;lt;/tex&amp;gt; называются '''сопряженными показателями''').&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt; L_p^* = L_q &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt; A^*(\varphi, x) = \varphi (Ax) = \int\limits_0^1 y(s) (Ax)(s) ds = \int\limits_0^1 y(s) (\int\limits_0^1 K(s, t) x(t) dt) ds = &amp;lt;/tex&amp;gt; (по теореме Фубини поменяем порядок интегрирования) &amp;lt;tex&amp;gt; = \int\limits_0^1 ( \int\limits_0^1 K(s, t) y(s) ds) x(t) dt &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Получили, что &amp;lt;tex&amp;gt; A^*(\varphi, x) = \int\limits_0^1 ( \int\limits_0^1 K(s, t) y(s) ds) x(t) dt &amp;lt;/tex&amp;gt;. Обозначим &amp;lt;tex&amp;gt; z(t) = \int\limits_0^1 K(s, t) y(s) ds &amp;lt;/tex&amp;gt;, тогда &amp;lt;tex&amp;gt; A^* (\varphi) \equiv z &amp;lt;/tex&amp;gt;, аналогично &amp;lt;tex&amp;gt; \varphi \equiv y &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt; A^* &amp;lt;/tex&amp;gt; {{---}} интегральный оператор из &amp;lt;tex&amp;gt; L_q &amp;lt;/tex&amp;gt;, имеющий ядро &amp;lt;tex&amp;gt; K^*(s, t) = K(t, s) &amp;lt;/tex&amp;gt;. В частности, если ядро симметрично (&amp;lt;tex&amp;gt; K(s, t) = K(t, s) &amp;lt;/tex&amp;gt;) и &amp;lt;tex&amp;gt; p = q = 2 &amp;lt;/tex&amp;gt;, то &amp;lt;tex&amp;gt; A = A^* &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Ортогональное дополнение ==&lt;br /&gt;
&lt;br /&gt;
Важное значение имеет ортогональное дополнение (в любом нормированном пространстве):&lt;br /&gt;
&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
Пусть &amp;lt;tex&amp;gt; E &amp;lt;/tex&amp;gt; {{---}} НП, &amp;lt;tex&amp;gt; S \subset E^* &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt; S^{\bot} = \{ x \in E \mid \forall f \in S: f(x) = 0 \} &amp;lt;/tex&amp;gt; {{---}} '''ортогональное дополнение''' &amp;lt;tex&amp;gt; S &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Аналогично, если &amp;lt;tex&amp;gt; T \subset E &amp;lt;/tex&amp;gt;, то &amp;lt;tex&amp;gt; T^{\bot} = \{ f \in E^* \mid \forall x \in T: f(x) = 0 \} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{Утверждение&lt;br /&gt;
|statement= &amp;lt;tex&amp;gt; \{ 0 \} = (E^*)^{\bot}, \{ \mathbf{0} \} = E^{\bot} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
|proof=&lt;br /&gt;
&lt;br /&gt;
Оба включения &amp;lt;tex&amp;gt; \subset &amp;lt;/tex&amp;gt; очевидны по определению. В обратную сторону:&lt;br /&gt;
&lt;br /&gt;
# Пусть &amp;lt;tex&amp;gt; x \in (E^*)^{\bot} &amp;lt;/tex&amp;gt;, тогда &amp;lt;tex&amp;gt; \forall f \in E^*: f(x) = 0 &amp;lt;/tex&amp;gt;. Предположим, что &amp;lt;tex&amp;gt; x \neq 0 &amp;lt;/tex&amp;gt;, тогда по [[Теорема Хана-Банаха|следствию из теоремы Хана-Банаха]], для такого &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt;, найдется функционал &amp;lt;tex&amp;gt;f: f(x) = \| x \| \neq 0 &amp;lt;/tex&amp;gt;, получили противоречие, что &amp;lt;tex&amp;gt; x \in (E^*)^{\bot} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Пусть &amp;lt;tex&amp;gt; f \in E^\bot &amp;lt;/tex&amp;gt;, тогда &amp;lt;tex&amp;gt; \forall x \in E: f(x) = 0&amp;lt;/tex&amp;gt;. Тогда &amp;lt;tex&amp;gt;f&amp;lt;/tex&amp;gt; — нулевой функционал по определению.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Теоремы о множестве значений оператора ==&lt;br /&gt;
=== Теорема 1 ===&lt;br /&gt;
&lt;br /&gt;
{{Теорема&lt;br /&gt;
|statement= &amp;lt;tex&amp;gt; A \in \mathcal{L}(E,F) \implies \operatorname{Cl} R(A) = (\operatorname{Ker} A^*)^\perp &amp;lt;/tex&amp;gt;.&lt;br /&gt;
|proof = &lt;br /&gt;
&amp;lt;tex&amp;gt;\subset&amp;lt;/tex&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\forall \varphi \in \operatorname{Ker}A^*&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;A^* \varphi = \mathbf{0}&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;tex&amp;gt;y \in R(A) &amp;lt;/tex&amp;gt;, тогда &amp;lt;tex&amp;gt; y = Ax &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt; \varphi (y) = \varphi(A x) = A^*(\varphi, x) = 0 &amp;lt;/tex&amp;gt;, следовательно, &amp;lt;tex&amp;gt; R(A)\subset(\operatorname{Ker}A^*)^\perp&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Теперь, пусть &amp;lt;tex&amp;gt;y \in \operatorname{Cl} R(A)&amp;lt;/tex&amp;gt;, тогда &amp;lt;tex&amp;gt; y = \lim y_n, y_n \in R(A)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\varphi(y_n) = 0, \varphi(y_n) \xrightarrow[]{n \to \infty} \varphi(y) \implies \varphi(y) = 0&amp;lt;/tex&amp;gt;, и &amp;lt;tex&amp;gt;\operatorname{Cl}(R(A)) \subset (\operatorname{Ker}(A^*))^\perp&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\supset&amp;lt;/tex&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
Надо показать, что &amp;lt;tex&amp;gt;y \in (\operatorname{Ker}A^*)^\perp \implies y \in \operatorname{Cl} R(A)&amp;lt;/tex&amp;gt;. Пусть это не так: &amp;lt;tex&amp;gt; y \notin \operatorname{Cl} R(A)&amp;lt;/tex&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Рассмотрим &amp;lt;tex&amp;gt; F_1 = \left\{ z + ty \mid z \in \operatorname{Cl}(R(A)), y \notin \operatorname{Cl}(R(A)), t \in \mathbb{R} \right\} &amp;lt;/tex&amp;gt;. &amp;lt;tex&amp;gt;F_1&amp;lt;/tex&amp;gt; {{---}} линейное множество в силу линейности  &amp;lt;tex&amp;gt;\operatorname{Cl}(R(A))&amp;lt;/tex&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Покажем, что &amp;lt;tex&amp;gt;F_1&amp;lt;/tex&amp;gt; -- подпространство  &amp;lt;tex&amp;gt;F&amp;lt;/tex&amp;gt;. Для этого нам осталось проверить замкнутость &amp;lt;tex&amp;gt;F_1&amp;lt;/tex&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;tex&amp;gt;z_n+t_{n}y \to u  = z + ty&amp;lt;/tex&amp;gt;, хотим убедиться в том, что &amp;lt;tex&amp;gt;u \in F_1&amp;lt;/tex&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Если  &amp;lt;tex&amp;gt; |t_{n}| \le const &amp;lt;/tex&amp;gt;, то выберем &amp;lt;tex&amp;gt;t_{n_k}&amp;lt;/tex&amp;gt;, стремящееся к какому-то &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt;. Из &amp;lt;tex&amp;gt;z_n+t_{n}y \to u,  t_{n_k}y \to ty &amp;lt;/tex&amp;gt; получаем &amp;lt;tex&amp;gt; z_n \to z \in \operatorname{Cl}(R(A))&amp;lt;/tex&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Если допустить, что &amp;lt;tex&amp;gt;t_{n_k} \to \infty&amp;lt;/tex&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;z_{n_k}+t_{n_k}y \to u&amp;lt;/tex&amp;gt;. &amp;lt;tex&amp;gt;z_{n_k}/t_{n_k} + y \to 0 \implies z_{n_k}/t_{n_k} \to -y \implies -y \in \operatorname{Cl}(R(A)) \implies y \in \operatorname{Cl}(R(A))&amp;lt;/tex&amp;gt; {{---}} противоречие. &lt;br /&gt;
&lt;br /&gt;
Таким образом, &amp;lt;tex&amp;gt;\operatorname{Cl}(F_1) = F_1&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Построим на &amp;lt;tex&amp;gt;F_1&amp;lt;/tex&amp;gt; фунционал &amp;lt;tex&amp;gt;\varphi_0 : \varphi_0(z+ty) = t &amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt; \varphi_0(z) = 0&amp;lt;/tex&amp;gt;. Он, очевидно, непрерывен, а по теореме Хана-Банаха с сохранением непрерывности его можно продолжить на &amp;lt;tex&amp;gt;F: \widetilde{\varphi_0} \in F^*&amp;lt;/tex&amp;gt;, причем так, что &amp;lt;tex&amp;gt;\widetilde{\varphi_0}\mid _{F_1} = \varphi_0&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Рассмотрим значение &amp;lt;tex&amp;gt;\widetilde{\varphi_0}(y)&amp;lt;/tex&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
* С одной стороны, &amp;lt;tex&amp;gt;\widetilde{\varphi_0}(y) = \varphi_0(y) = \varphi_0(0 + 1 y) = 1&amp;lt;/tex&amp;gt;&lt;br /&gt;
* С другой стороны, &amp;lt;tex&amp;gt;y \in (\operatorname{Ker}A^*)^\perp&amp;lt;/tex&amp;gt;, а значит, на любом функционале из ядра &amp;lt;tex&amp;gt;A^*&amp;lt;/tex&amp;gt;, в том числе, и на &amp;lt;tex&amp;gt;\widetilde{\varphi_0}&amp;lt;/tex&amp;gt;, должно выполняться &amp;lt;tex&amp;gt;\widetilde{\varphi_0}(y) = 0&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Получили противоречие, следовательно, &amp;lt;tex&amp;gt; y \in \operatorname{Cl}(R(A))&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== Теорема 2 ===&lt;br /&gt;
&lt;br /&gt;
{{Теорема&lt;br /&gt;
|statement= &amp;lt;tex&amp;gt; A \in \mathcal{L}(E,F),~R(A) = \operatorname{Cl} R(A) \implies  R(A^*) = (\operatorname{Ker}A )^\perp &amp;lt;/tex&amp;gt;.&lt;br /&gt;
|proof = &lt;br /&gt;
1) &amp;lt;tex&amp;gt;f \in R(A^*) \implies f = \varphi A , \varphi \in F^*&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Рассмотрим &amp;lt;tex&amp;gt; x \in (\operatorname{Ker}A). &amp;lt;/tex&amp;gt; &lt;br /&gt;
&amp;lt;tex&amp;gt;f(x) = \varphi(Ax) = \varphi(0) = 0 \implies R(A^*) \subset (\operatorname{Ker}A )^\perp&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
2) Докажем теперь обратное включение:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;(\operatorname{Ker}A )^\perp&amp;lt;/tex&amp;gt; — набор таких &amp;lt;tex&amp;gt;f&amp;lt;/tex&amp;gt;, что если &amp;lt;tex&amp;gt;Ax=0&amp;lt;/tex&amp;gt;, то &amp;lt;tex&amp;gt;f(x)=0&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Надо показать, что &amp;lt;tex&amp;gt;f \in R(A^*)&amp;lt;/tex&amp;gt;, т.е. проверить, что  &amp;lt;tex&amp;gt;f = A^* \varphi = \varphi A&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Если найдем &amp;lt;tex&amp;gt;\varphi&amp;lt;/tex&amp;gt;, заданный на &amp;lt;tex&amp;gt;R(A)&amp;lt;/tex&amp;gt;, то сможем продолжить его на все &amp;lt;tex&amp;gt;F&amp;lt;/tex&amp;gt; по теореме Хана-Банаха.&lt;br /&gt;
&lt;br /&gt;
Рассмотрим произвольное &amp;lt;tex&amp;gt;y \in R(A)&amp;lt;/tex&amp;gt;, пусть &amp;lt;tex&amp;gt;y = Ax&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;y = Ax'&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Тогда &amp;lt;tex&amp;gt;A(x - x') = 0&amp;lt;/tex&amp;gt;, то есть &amp;lt;tex&amp;gt;x - x' \in \operatorname{Ker} A&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;f(x - x') = 0&amp;lt;/tex&amp;gt;, и &amp;lt;tex&amp;gt;f(x) = f(x')&amp;lt;/tex&amp;gt;, то есть, значение функционала не зависит от того, какой конкретно &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt; (при &amp;lt;tex&amp;gt;Ax = y&amp;lt;/tex&amp;gt;) был выбран. &lt;br /&gt;
&lt;br /&gt;
Тогда можно взять &amp;lt;tex&amp;gt;\varphi(y) = f(x)&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;y = Ax&amp;lt;/tex&amp;gt; — линейный функционал, &amp;lt;tex&amp;gt;f = \varphi A&amp;lt;/tex&amp;gt;. Осталось проверить ограниченность &amp;lt;tex&amp;gt;\varphi&amp;lt;/tex&amp;gt; на &amp;lt;tex&amp;gt;R(A)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Рассмотрим &amp;lt;tex&amp;gt;E/_{\operatorname{Ker} A}&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;\widetilde{A} : E/_{\operatorname{Ker} A} \to F&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;\widetilde{A}([x]) = Ax&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\widetilde{A} : E/_{\operatorname{Ker} A} \to R(A)&amp;lt;/tex&amp;gt; — биекция, &amp;lt;tex&amp;gt;R(A)&amp;lt;/tex&amp;gt; — замкнуто, &amp;lt;tex&amp;gt;F&amp;lt;/tex&amp;gt; — банахово, поэтому &amp;lt;tex&amp;gt;R(A)&amp;lt;/tex&amp;gt; — также банахово как подпространство в &amp;lt;tex&amp;gt;F&amp;lt;/tex&amp;gt;. Введем норму для &amp;lt;tex&amp;gt;[x] \in E/_{\operatorname{Ker} A}&amp;lt;/tex&amp;gt; как &amp;lt;tex&amp;gt;\|[x]\| = \inf\limits_{x\in [x]} \|x\|&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Покажем, что &amp;lt;tex&amp;gt;\widetilde{A}&amp;lt;/tex&amp;gt; — ограничен: &amp;lt;tex&amp;gt;\|\widetilde{A}\| = \sup\limits_{\|[x]\| = 1} \|\widetilde{A}[x]\|&amp;lt;/tex&amp;gt;. Для этого перейдем от классов эквивалентности к их представителям. Так как &amp;lt;tex&amp;gt;\|[x]\| = \inf\limits_{x \in [x]} \|x\| = 1&amp;lt;/tex&amp;gt;, найдется &amp;lt;tex&amp;gt;x \in [x]&amp;lt;/tex&amp;gt;, такой, что &amp;lt;tex&amp;gt;\|x\| \le 2&amp;lt;/tex&amp;gt; (по определению инфимума), возьмем его в качестве представителя (мы можем это сделать, так как значение &amp;lt;tex&amp;gt;Ax&amp;lt;/tex&amp;gt; одно и тоже для любого &amp;lt;tex&amp;gt;x\in[x]&amp;lt;/tex&amp;gt;). Тогда: &amp;lt;tex&amp;gt;\|\widetilde{A}\| = \sup\limits_{\|[x]\| = 1} \|\widetilde{A}[x]\| \le \sup\limits_{\|x\| \le 2} \|Ax\| \le \sup\limits_{\|y\| \le 1} \|A(2 y)\| \le 2 \sup\limits_{\|y\| \le 1} \|Ay\| = 2 \|A\|&amp;lt;/tex&amp;gt;, так как &amp;lt;tex&amp;gt;\|A\|&amp;lt;/tex&amp;gt; был ограничен, &amp;lt;tex&amp;gt;\widetilde{A}&amp;lt;/tex&amp;gt; тоже окажется ограниченным.&lt;br /&gt;
&lt;br /&gt;
Тогда по [[Теорема Банаха об обратном операторе#Теорема Банаха о гомеоморфизме|теореме Банаха об гомеоморфизме]] существует линейный ограниченный оператор &amp;lt;tex&amp;gt;\widetilde{A}^{-1}&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;\| \widetilde{A}^{-1}(y) \| \le m \|y\| &amp;lt; 2m \|y\|&amp;lt;/tex&amp;gt;. Замечание: строгое неравенство нам нужно для того, чтобы обеспечить существование такого &amp;lt;tex&amp;gt; x' \in A^{-1}(y) &amp;lt;/tex&amp;gt;, что &amp;lt;tex&amp;gt; \| x' \| &amp;lt; 2m\| y \| &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\widetilde{A}^{-1}(y) = \{ x: y = Ax \}&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\|\widetilde{A}^{-1}(y)\| = \inf\limits_{x\in \widetilde{A}^{-1}(y)} \|x\| &amp;lt; 2m \|y\| &amp;lt;/tex&amp;gt;, следовательно, существует &amp;lt;tex&amp;gt; x' = A^{-1}y, \|x'\| &amp;lt; 2m\|y\|&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt; \|\varphi(y)\| = \|f(x')\| \le \|f\|\|x'\| &amp;lt; (2m\|f\|)\|y\| &amp;lt;/tex&amp;gt;, то есть, получили ограниченность &amp;lt;tex&amp;gt; \varphi &amp;lt;/tex&amp;gt;, теорема доказана.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Эти две теоремы являются наиболее общей формой записи условий разрешимости операторных уравнений.&lt;br /&gt;
&lt;br /&gt;
Смысл: рассмотрим уравнение &amp;lt;tex&amp;gt;Ax = y&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;y&amp;lt;/tex&amp;gt; — дано. Для того, чтобы понять, разрешимо ли уравнение, нужно проверить, что &amp;lt;tex&amp;gt;y \in R(A)&amp;lt;/tex&amp;gt;. В общем случае, не существует способа это сделать, но можно ограничиться проверкой &amp;lt;tex&amp;gt;R(A) = \operatorname{Cl} R(A)&amp;lt;/tex&amp;gt;, и тогда &amp;lt;tex&amp;gt;R(A) = (\operatorname{Ker}A^*)^\bot&amp;lt;/tex&amp;gt;, сопряженный оператор можно построить, ядро поддается конструктивному описанию: &amp;lt;tex&amp;gt;y \in R(A) \iff y \perp \operatorname{Ker} A^*&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Например, &amp;lt;tex&amp;gt;A: \mathbb{R}^m \to \mathbb{R}^n&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;A^* = A^\top : \mathbb{R}^n \to \mathbb{R}^m&amp;lt;/tex&amp;gt;. &amp;lt;tex&amp;gt;R(A) = \operatorname{Cl} R(A)&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;Ax = y&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;y&amp;lt;/tex&amp;gt; — дано. Надо смотреть &amp;lt;tex&amp;gt;y \perp \operatorname{Ker} A^*&amp;lt;/tex&amp;gt;, то есть &amp;lt;tex&amp;gt;A^\top y = 0&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
В следующих параграфах мы введем класс бесконечномерных операторов, для которых &amp;lt;tex&amp;gt;R(A)&amp;lt;/tex&amp;gt; — замкнуто, в частности, в этот класс входят интегральные операторы.&lt;br /&gt;
&lt;br /&gt;
[[Категория: Функциональный анализ 3 курс]]&lt;/div&gt;</summary>
		<author><name>194.85.160.130</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D0%BE%D0%B8%D1%81%D0%BA%D0%BE%D0%B2%D1%8B%D0%B5_%D1%81%D1%82%D1%80%D1%83%D0%BA%D1%82%D1%83%D1%80%D1%8B_%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D1%85&amp;diff=46913</id>
		<title>Поисковые структуры данных</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D0%BE%D0%B8%D1%81%D0%BA%D0%BE%D0%B2%D1%8B%D0%B5_%D1%81%D1%82%D1%80%D1%83%D0%BA%D1%82%D1%83%D1%80%D1%8B_%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D1%85&amp;diff=46913"/>
				<updated>2015-05-25T12:26:47Z</updated>
		
		<summary type="html">&lt;p&gt;194.85.160.130: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Поисковая структура данных''' {{---}} любая структура данных реализующая эффективный поиск конкретных элементов множества, например, конкретной записи в базе данных.&lt;br /&gt;
&lt;br /&gt;
Простейший, наиболее общий, но менее эффективный поисковой структурой является простая неупорядоченный последовательная всех элементов. Расположив элементы в такой список, неизбежно возникнет ряд операций, которые потребуют линейного времени, в худшем случае, а также в средней случае. Используемые в реальной жизни поисковые структуры данных позволяют совершать операции более быстро, однако они ограничены запросами некоторого конкретного вида. Кроме того, поскольку стоимость построение таких структур пропорциональна &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt;, их построение окупится, даже если поступает лишь несколько запросов.&lt;br /&gt;
&lt;br /&gt;
=== Тип ===&lt;br /&gt;
&lt;br /&gt;
'''Статические поисковые структуры данных''' (англ. ''Online search structures'') предназначены для ответа на запросы на фиксированной базе данных.&lt;br /&gt;
&lt;br /&gt;
'''Динамические поисковые структуры''' (англ. ''Offline search structures'') также позволяют вставки, удаления или модификации элементов между последовательными запросами. В динамическом случае, необходимо также учитывать стоимость изменения структуры данных. Любую динамическую структуру данных можно сделать статической, если запретить вставку и удаление. Также если множество ключей известно, то можно его заранее упорядочить так, чтобы избежать худших случаев в поисках в структурах данных.&lt;br /&gt;
&lt;br /&gt;
=== Время работы ===&lt;br /&gt;
&lt;br /&gt;
Эту классификацию обычно считают самой важной. Оценивают худшее время алгоритма, среднее и лучшее для каждой операции. Лучшее время {{---}} минимальное время работы алгоритма на каком-либо наборе. Худшее время {{---}} наибольшее время.&lt;br /&gt;
&lt;br /&gt;
=== Используемая память ===&lt;br /&gt;
&lt;br /&gt;
Параметр структуры данных, показывающий, сколько памяти ей требуется. Обычно затраты составляют &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Сравнение структур данных ===&lt;br /&gt;
&lt;br /&gt;
Сравним эффективность поисковых структур данных для реализации интерфейса [[Упорядоченное множество|упорядоченного множества]]. Время работы методов &amp;lt;tex&amp;gt;Predecessor&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;Successor&amp;lt;/tex&amp;gt; совпадает с временем работы &amp;lt;tex&amp;gt;Search&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; {{---}} количество хранимых чисел, каждое из которых представляется с помощью &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt; битов.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! rowspan=&amp;quot;2&amp;quot; |&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; | Insert&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; | Delete&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; | Search&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; | Память&lt;br /&gt;
! rowspan=&amp;quot;2&amp;quot; | Описание&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background: #ddffdd;&amp;quot; | Среднее&lt;br /&gt;
! style=&amp;quot;background: #ffdddd;&amp;quot; | Худшее&lt;br /&gt;
! style=&amp;quot;background: #ddffdd;&amp;quot; | Среднее&lt;br /&gt;
! style=&amp;quot;background: #ffdddd;&amp;quot; | Худшее&lt;br /&gt;
! style=&amp;quot;background: #ddffdd;&amp;quot; | Среднее&lt;br /&gt;
! style=&amp;quot;background: #ffdddd;&amp;quot; | Худшее&lt;br /&gt;
! style=&amp;quot;background: #ddffdd;&amp;quot; | Среднее&lt;br /&gt;
! style=&amp;quot;background: #ffdddd;&amp;quot; | Худшее&lt;br /&gt;
|-&lt;br /&gt;
!&lt;br /&gt;
! colspan=&amp;quot;9&amp;quot; align=&amp;quot;center&amp;quot; |  Динамические структуры данных&lt;br /&gt;
|-&lt;br /&gt;
| Неотсортированный массив&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ddffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffdddd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ddffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffdddd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffdddd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Наивная реализация, использующая [[Динамический массив|динамический массив]]. Добавление происходит в конец массива, а для поиска элемента просто проходим по всему массиву.&lt;br /&gt;
|-&lt;br /&gt;
| Отсортированный массив&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffdddd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffdddd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | То же самое, но теперь массив отсортирован. Поиск ускоряется за счёт возможности применить [[Целочисленный двоичный поиск|двоичный поиск]]. Вставка замедляется из-за необходимости поддерживать инвариант отсортированности.&lt;br /&gt;
|-&lt;br /&gt;
| Неотсортированный [[Список|список]]&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ddffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ddffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffdddd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; rowspan=&amp;quot;2&amp;quot; | Аналогично массиву, но храним данные в [[Список|списке]]. Можно хранить дополнительную информацию о вершинах, что позволит ускорить время работы операции delete.&lt;br /&gt;
|-&lt;br /&gt;
| Отсортированный [[Список|список]]&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffdddd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ddffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffdddd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| [[Дерево поиска, наивная реализация]]&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffdddd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffdddd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffdddd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Бинарное дерево поиска  обладает следующим свойством:  если &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt; {{---}} узел бинарного дерева с ключом &amp;lt;tex&amp;gt;k&amp;lt;/tex&amp;gt;, то все узлы в левом поддереве должны иметь ключи, меньшие &amp;lt;tex&amp;gt;k&amp;lt;/tex&amp;gt;,  а в правом поддереве большие &amp;lt;tex&amp;gt;k&amp;lt;/tex&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
| [[Рандомизированное бинарное дерево поиска]]&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffdddd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffdddd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffdddd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Вариант [[Дерево поиска, наивная реализация|двоисного дерево поиска]] с добавлением инвариата &amp;quot;случайности&amp;quot;, что уменьнашает ожидаемую высоту дерева.&lt;br /&gt;
|-&lt;br /&gt;
|[[АВЛ-дерево]]&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Сбалансированное [[Дерево поиска, наивная реализация|двоичное дерево поиска]], в котором поддерживается следующее свойство: для каждой его вершины высота её двух поддеревьев различается не более чем на &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
|[[2-3 дерево]]&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Структура данных, представляющая собой сбалансированное дерево поиска, такое что из каждого узла может выходить две или три ветви и глубина всех листьев одинакова. Является частным случаем [[B-дерево#B.2B-.D0.B4.D0.B5.D1.80.D0.B5.D0.B2.D0.BE|B+ дерева]].&lt;br /&gt;
|-&lt;br /&gt;
|[[B-дерево]]&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Cильноветвящееся сбалансированное дерево поиска, позволяющее проводить поиск, добавление и удаление элементов за &amp;lt;tex&amp;gt;O(\log n)&amp;lt;/tex&amp;gt;. B-дерево с &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; узлами имеет высоту &amp;lt;tex&amp;gt;O(\log n)&amp;lt;/tex&amp;gt;. Количество детей узлов может быть от нескольких до тысяч (обычно степень ветвления B-дерева определяется характеристиками устройства (дисков), на котором производится работа с деревом). В-деревья также могут использоваться для реализации многих операций над динамическими множествами за время &amp;lt;tex&amp;gt;O(\log n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|[[Красно-черное дерево]]&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Сбалансированное [[Дерево поиска, наивная реализация|двоичное дерево поиска]], в котором баланс осуществляется на основе &amp;quot;цвета&amp;quot; узла дерева, который принимает только два значения: &amp;quot;красный&amp;quot; (англ. ''red'') и &amp;quot;чёрный&amp;quot; (англ. ''black'').  При этом все листья дерева являются фиктивными и не содержат данных, но относятся к дереву и являются чёрными.&lt;br /&gt;
|-&lt;br /&gt;
| [[Декартово дерево]]&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffdddd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffdddd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffdddd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Бинарное дерево, в узлах которого хранится пары &amp;lt;tex&amp;gt; (x,y) &amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt; {{---}} это ключ, а &amp;lt;tex&amp;gt;y&amp;lt;/tex&amp;gt; {{---}} это приоритет. Также оно является [[Дерево поиска, наивная реализация|двоичным деревом поиска]] по &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt; и [[Двоичная куча|пирамидой]] по &amp;lt;tex&amp;gt;y&amp;lt;/tex&amp;gt;. Предполагая, что все &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt; и все &amp;lt;tex&amp;gt;y&amp;lt;/tex&amp;gt; являются различными, получаем, что если некоторый элемент дерева содержит &amp;lt;tex&amp;gt;(x_0,y_0)&amp;lt;/tex&amp;gt;, то у всех элементов в левом поддереве &amp;lt;tex&amp;gt;x &amp;lt; x_0&amp;lt;/tex&amp;gt;, у всех элементов в правом поддереве &amp;lt;tex&amp;gt; x &amp;gt; x_0&amp;lt;/tex&amp;gt;, а также и в левом, и в правом поддереве имеем: &amp;lt;tex&amp;gt; y &amp;lt; y_0&amp;lt;/tex&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
|[[Splay-дерево]]&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffdddd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffdddd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffdddd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | [[Дерево поиска, наивная реализация|Двоичное дерево поиска]]. Оно позволяет находить быстрее те данные, которые использовались недавно, за счёт '''перемещения к корню''' (англ. ''Move to root''). Относится к разряду сливаемых деревьев. Сплей-дерево было придумано Робертом Тарьяном и Даниелем Слейтером в 1983 году.&lt;br /&gt;
|-&lt;br /&gt;
|[[Дерево ван Эмде Боаса]]&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log w)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log w)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log w)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffdddd;&amp;quot; | &amp;lt;tex&amp;gt;O(2^w)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Cтруктура данных, представляющая собой [[Дерево поиска, наивная реализация|дерево поиска]], позволяющее хранить целые неотрицательные числа в интервале &amp;lt;tex&amp;gt;[0;2^w)&amp;lt;/tex&amp;gt; и осуществлять над ними все соответствующие дереву поиска операции. Проще говоря, данная структура позволяет хранить &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;-битные числа.&lt;br /&gt;
Особенностью этой структуры является то, что все операции выполняются за &amp;lt;tex&amp;gt;O(\log w)&amp;lt;/tex&amp;gt;, что асимптотически лучше, чем &amp;lt;tex&amp;gt;O(\log n)&amp;lt;/tex&amp;gt; в большинстве других деревьев поиска, где &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; {{---}} количество элементов в дереве.&lt;br /&gt;
|-&lt;br /&gt;
| [[Список с пропусками]]&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffdddd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffdddd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffdddd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt; &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Вероятностная структура данных, основанная на нескольких отсортированных односвязных списках.&lt;br /&gt;
Отсортированный связный список является простейшей структурой с временем поиска &amp;lt;tex&amp;gt;\Theta(n)&amp;lt;/tex&amp;gt;. Добавление дополнительных уровней, обеспечивающих быстрый доступ через несколько элементов, помогает улучшить асимптотику до &amp;lt;tex&amp;gt;\Theta(\log(n))&amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|[[Fusion tree]]&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log_{w} n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log_{w} n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log_{w} n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Дерево поиска, позволяющее хранить &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;-битных чисел, используя &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt; памяти, и выполнять операции поиска за время &amp;lt;tex&amp;gt;O(\log_{w} n)&amp;lt;/tex&amp;gt;. Эта структура данных была впервые предложена в 1990 году М. Фредманом (M. Fredman) и Д. Уиллардом (D. Willard).&lt;br /&gt;
|-&lt;br /&gt;
| [[Сверхбыстрый цифровой бор|Цифровой бор]]&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffdddd;&amp;quot; | &amp;lt;tex&amp;gt;O(w)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffdddd;&amp;quot; | &amp;lt;tex&amp;gt;O(w)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffdddd;&amp;quot; | &amp;lt;tex&amp;gt;O(w)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffdddd;&amp;quot; | &amp;lt;tex&amp;gt;O(n \cdot w)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | [[Бор]], в котором в качестве строк используются двоичные записи чисел, включая ведущие нули. Таким образом он имеет глубину &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
| [[Сверхбыстрый цифровой бор|Быстрый цифровой бор]]&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffdddd;&amp;quot; | &amp;lt;tex&amp;gt;O(w)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffdddd;&amp;quot; | &amp;lt;tex&amp;gt;O(w)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log w)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffdddd;&amp;quot; | &amp;lt;tex&amp;gt;O(n \cdot w)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Улучшеная версия структуры цифрового бора.&lt;br /&gt;
|-&lt;br /&gt;
| [[Сверхбыстрый цифровой бор]]&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log w)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffdddd;&amp;quot; | &amp;lt;tex&amp;gt;O(w)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log w)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffdddd;&amp;quot; | &amp;lt;tex&amp;gt;O(w)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log w)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Улучшеная версия структуры быстрого цифрового бора.&lt;br /&gt;
|-&lt;br /&gt;
!&lt;br /&gt;
! colspan=&amp;quot;9&amp;quot; align=&amp;quot;center&amp;quot; | Статические структуры данных&lt;br /&gt;
|-&lt;br /&gt;
|[[Tango-дерево]]&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #e5e5e5;&amp;quot; | -&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #e5e5e5;&amp;quot; | -&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ddffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log \log n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | [[Дерево поиска, наивная реализация|Двоичное дерево поиска]], которое изобрели Эрик Д. Демейн, Дион Хармон, Джон Яконо и Михаи Патраску в 2004 году. Не поддерживает операции вставки и удаления, но перестраивается по ходу поисковых запросов, чтобы отвечать на них как можно оптимальней. Это лучшая известная реализация на данный момент.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==См. также==&lt;br /&gt;
&lt;br /&gt;
*[[:Сортировка|Сортировка]]&lt;br /&gt;
*[[:Поиск_подстроки_в_строке|Поиск подстроки в строке]]&lt;br /&gt;
*[[:Приоритетные_кучи|Приоритетные кучи]]&lt;br /&gt;
&lt;br /&gt;
== Источники информации ==&lt;br /&gt;
&lt;br /&gt;
* [[wikipedia:en:Search_data_structure|Wikidedia {{---}} Search data structure]]&lt;br /&gt;
* [http://habrahabr.ru/post/188010/ Habrahabr {{---}} Знай сложности алгоритмов ]&lt;br /&gt;
&lt;br /&gt;
[[Категория: Дискретная математика и алгоритмы]]&lt;br /&gt;
[[Категория: Деревья поиска]]&lt;br /&gt;
[[Категория: Структуры данных]]&lt;/div&gt;</summary>
		<author><name>194.85.160.130</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D0%BE%D0%B8%D1%81%D0%BA%D0%BE%D0%B2%D1%8B%D0%B5_%D1%81%D1%82%D1%80%D1%83%D0%BA%D1%82%D1%83%D1%80%D1%8B_%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D1%85&amp;diff=46912</id>
		<title>Поисковые структуры данных</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D0%BE%D0%B8%D1%81%D0%BA%D0%BE%D0%B2%D1%8B%D0%B5_%D1%81%D1%82%D1%80%D1%83%D0%BA%D1%82%D1%83%D1%80%D1%8B_%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D1%85&amp;diff=46912"/>
				<updated>2015-05-25T12:26:08Z</updated>
		
		<summary type="html">&lt;p&gt;194.85.160.130: /* Сравнение структур данных */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Поисковая структура данных''' {{---}} любая структура данных реализующая эффективный поиск конкретных элементов множества, например, конкретной записи в базе данных.&lt;br /&gt;
&lt;br /&gt;
Простейший, наиболее общий, но менее эффективный поисковой структурой является простая неупорядоченный последовательная всех элементов. Расположив элементы в такой список, неизбежно возникнет ряд операций, которые потребуют линейного времени, в худшем случае, а также в средней случае. Используемые в реальной жизни поисковые структуры данных позволяют совершать операции более быстро, однако они ограничены запросами некоторого конкретного вида. Кроме того, поскольку стоимость построение таких структур пропорциональна &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt;, их построение окупится, даже если поступает лишь несколько запросов.&lt;br /&gt;
&lt;br /&gt;
=== Тип ===&lt;br /&gt;
&lt;br /&gt;
'''Статические поисковые структуры данных''' (англ. ''Online search structures'') предназначены для ответа на запросы на фиксированной базе данных.&lt;br /&gt;
&lt;br /&gt;
'''Динамические поисковые структуры''' (англ. ''Offline search structures'') также позволяют вставки, удаления или модификации элементов между последовательными запросами. В динамическом случае, необходимо также учитывать стоимость изменения структуры данных. Любую динамическую структуру данных можно сделать статической, если запретить вставку и удаление. Также если множество ключей известно, то можно его заранее упорядочить так, чтобы избежать худших случаев в поисках в структурах данных.&lt;br /&gt;
&lt;br /&gt;
=== Время работы ===&lt;br /&gt;
&lt;br /&gt;
Эту классификацию обычно считают самой важной. Оценивают худшее время алгоритма, среднее и лучшее для каждой операции. Лучшее время {{---}} минимальное время работы алгоритма на каком-либо наборе. Худшее время {{---}} наибольшее время.&lt;br /&gt;
&lt;br /&gt;
=== Используемая память ===&lt;br /&gt;
&lt;br /&gt;
Параметр структуры данных, показывающий, сколько памяти ей требуется. Обычно затраты составляют &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Сравнение структур данных ===&lt;br /&gt;
&lt;br /&gt;
Сравним эффективность поисковых структур данных для реализации интерфейса [[Упорядоченное множество|упорядоченного множества]]. Время работы методов &amp;lt;tex&amp;gt;Predecessor&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;Successor&amp;lt;/tex&amp;gt; совпадает с временем работы &amp;lt;tex&amp;gt;Search&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; {{---}} количество хранимых чисел, каждое из которых представляется с помощью &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt; битов.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! rowspan=&amp;quot;2&amp;quot; |&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; | Insert&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; | Delete&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; | Search&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; | Память&lt;br /&gt;
! rowspan=&amp;quot;2&amp;quot; | Описание&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background: #ddffdd;&amp;quot; | Среднее&lt;br /&gt;
! style=&amp;quot;background: #ffdddd;&amp;quot; | Худшее&lt;br /&gt;
! style=&amp;quot;background: #ddffdd;&amp;quot; | Среднее&lt;br /&gt;
! style=&amp;quot;background: #ffdddd;&amp;quot; | Худшее&lt;br /&gt;
! style=&amp;quot;background: #ddffdd;&amp;quot; | Среднее&lt;br /&gt;
! style=&amp;quot;background: #ffdddd;&amp;quot; | Худшее&lt;br /&gt;
! style=&amp;quot;background: #ddffdd;&amp;quot; | Среднее&lt;br /&gt;
! style=&amp;quot;background: #ffdddd;&amp;quot; | Худшее&lt;br /&gt;
|-&lt;br /&gt;
!&lt;br /&gt;
! colspan=&amp;quot;9&amp;quot; align=&amp;quot;center&amp;quot; |  Динамические структуры данных&lt;br /&gt;
|-&lt;br /&gt;
| Неотсортированный массив&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ddffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffdddd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ddffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffdddd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffdddd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Наивная реализация, использующая [[Динамический массив|динамический массив]]. Добавление происходит в конец массива, а для поиска элемента просто проходим по всему массиву.&lt;br /&gt;
|-&lt;br /&gt;
| Отсортированный массив&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffdddd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffdddd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | То же самое, но теперь массив отсортирован. Поиск ускоряется за счёт возможности применить [[Целочисленный двоичный поиск|двоичный поиск]]. Вставка замедляется из-за необходимости поддерживать инвариант отсортированности.&lt;br /&gt;
|-&lt;br /&gt;
| Неотсортированный [[Список|список]]&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ddffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ddffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffdddd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; rowspan=&amp;quot;2&amp;quot; | Аналогично массиву, но храним данные в [[Список|списке]]. Можно хранить дополнительную информацию о вершинах, что позволит ускорить время работы операции delete.&lt;br /&gt;
|-&lt;br /&gt;
| Отсортированный [[Список|список]]&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffdddd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ddffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffdddd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| [[Дерево поиска, наивная реализация]]&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffdddd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffdddd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffdddd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Бинарное дерево поиска  обладает следующим свойством:  если &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt; {{---}} узел бинарного дерева с ключом &amp;lt;tex&amp;gt;k&amp;lt;/tex&amp;gt;, то все узлы в левом поддереве должны иметь ключи, меньшие &amp;lt;tex&amp;gt;k&amp;lt;/tex&amp;gt;,  а в правом поддереве большие &amp;lt;tex&amp;gt;k&amp;lt;/tex&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
| [[Рандомизированное бинарное дерево поиска]]&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffdddd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffdddd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffdddd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Вариант [[Дерево поиска, наивная реализация|двоисного дерево поиска]] с добавлением инвариата &amp;quot;случайности&amp;quot;, что уменьнашает ожидаемую высоту дерева.&lt;br /&gt;
|[[АВЛ-дерево]]&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Сбалансированное [[Дерево поиска, наивная реализация|двоичное дерево поиска]], в котором поддерживается следующее свойство: для каждой его вершины высота её двух поддеревьев различается не более чем на &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
|[[2-3 дерево]]&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Структура данных, представляющая собой сбалансированное дерево поиска, такое что из каждого узла может выходить две или три ветви и глубина всех листьев одинакова. Является частным случаем [[B-дерево#B.2B-.D0.B4.D0.B5.D1.80.D0.B5.D0.B2.D0.BE|B+ дерева]].&lt;br /&gt;
|-&lt;br /&gt;
|[[B-дерево]]&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Cильноветвящееся сбалансированное дерево поиска, позволяющее проводить поиск, добавление и удаление элементов за &amp;lt;tex&amp;gt;O(\log n)&amp;lt;/tex&amp;gt;. B-дерево с &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; узлами имеет высоту &amp;lt;tex&amp;gt;O(\log n)&amp;lt;/tex&amp;gt;. Количество детей узлов может быть от нескольких до тысяч (обычно степень ветвления B-дерева определяется характеристиками устройства (дисков), на котором производится работа с деревом). В-деревья также могут использоваться для реализации многих операций над динамическими множествами за время &amp;lt;tex&amp;gt;O(\log n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|[[Красно-черное дерево]]&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Сбалансированное [[Дерево поиска, наивная реализация|двоичное дерево поиска]], в котором баланс осуществляется на основе &amp;quot;цвета&amp;quot; узла дерева, который принимает только два значения: &amp;quot;красный&amp;quot; (англ. ''red'') и &amp;quot;чёрный&amp;quot; (англ. ''black'').  При этом все листья дерева являются фиктивными и не содержат данных, но относятся к дереву и являются чёрными.&lt;br /&gt;
|-&lt;br /&gt;
| [[Декартово дерево]]&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffdddd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffdddd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffdddd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Бинарное дерево, в узлах которого хранится пары &amp;lt;tex&amp;gt; (x,y) &amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt; {{---}} это ключ, а &amp;lt;tex&amp;gt;y&amp;lt;/tex&amp;gt; {{---}} это приоритет. Также оно является [[Дерево поиска, наивная реализация|двоичным деревом поиска]] по &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt; и [[Двоичная куча|пирамидой]] по &amp;lt;tex&amp;gt;y&amp;lt;/tex&amp;gt;. Предполагая, что все &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt; и все &amp;lt;tex&amp;gt;y&amp;lt;/tex&amp;gt; являются различными, получаем, что если некоторый элемент дерева содержит &amp;lt;tex&amp;gt;(x_0,y_0)&amp;lt;/tex&amp;gt;, то у всех элементов в левом поддереве &amp;lt;tex&amp;gt;x &amp;lt; x_0&amp;lt;/tex&amp;gt;, у всех элементов в правом поддереве &amp;lt;tex&amp;gt; x &amp;gt; x_0&amp;lt;/tex&amp;gt;, а также и в левом, и в правом поддереве имеем: &amp;lt;tex&amp;gt; y &amp;lt; y_0&amp;lt;/tex&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
|[[Splay-дерево]]&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffdddd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffdddd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffdddd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | [[Дерево поиска, наивная реализация|Двоичное дерево поиска]]. Оно позволяет находить быстрее те данные, которые использовались недавно, за счёт '''перемещения к корню''' (англ. ''Move to root''). Относится к разряду сливаемых деревьев. Сплей-дерево было придумано Робертом Тарьяном и Даниелем Слейтером в 1983 году.&lt;br /&gt;
|-&lt;br /&gt;
|[[Дерево ван Эмде Боаса]]&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log w)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log w)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log w)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffdddd;&amp;quot; | &amp;lt;tex&amp;gt;O(2^w)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Cтруктура данных, представляющая собой [[Дерево поиска, наивная реализация|дерево поиска]], позволяющее хранить целые неотрицательные числа в интервале &amp;lt;tex&amp;gt;[0;2^w)&amp;lt;/tex&amp;gt; и осуществлять над ними все соответствующие дереву поиска операции. Проще говоря, данная структура позволяет хранить &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;-битные числа.&lt;br /&gt;
Особенностью этой структуры является то, что все операции выполняются за &amp;lt;tex&amp;gt;O(\log w)&amp;lt;/tex&amp;gt;, что асимптотически лучше, чем &amp;lt;tex&amp;gt;O(\log n)&amp;lt;/tex&amp;gt; в большинстве других деревьев поиска, где &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; {{---}} количество элементов в дереве.&lt;br /&gt;
|-&lt;br /&gt;
| [[Список с пропусками]]&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffdddd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffdddd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffdddd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt; &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Вероятностная структура данных, основанная на нескольких отсортированных односвязных списках.&lt;br /&gt;
Отсортированный связный список является простейшей структурой с временем поиска &amp;lt;tex&amp;gt;\Theta(n)&amp;lt;/tex&amp;gt;. Добавление дополнительных уровней, обеспечивающих быстрый доступ через несколько элементов, помогает улучшить асимптотику до &amp;lt;tex&amp;gt;\Theta(\log(n))&amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|[[Fusion tree]]&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log_{w} n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log_{w} n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log_{w} n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Дерево поиска, позволяющее хранить &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;-битных чисел, используя &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt; памяти, и выполнять операции поиска за время &amp;lt;tex&amp;gt;O(\log_{w} n)&amp;lt;/tex&amp;gt;. Эта структура данных была впервые предложена в 1990 году М. Фредманом (M. Fredman) и Д. Уиллардом (D. Willard).&lt;br /&gt;
|-&lt;br /&gt;
| [[Сверхбыстрый цифровой бор|Цифровой бор]]&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffdddd;&amp;quot; | &amp;lt;tex&amp;gt;O(w)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffdddd;&amp;quot; | &amp;lt;tex&amp;gt;O(w)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffdddd;&amp;quot; | &amp;lt;tex&amp;gt;O(w)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffdddd;&amp;quot; | &amp;lt;tex&amp;gt;O(n \cdot w)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | [[Бор]], в котором в качестве строк используются двоичные записи чисел, включая ведущие нули. Таким образом он имеет глубину &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
| [[Сверхбыстрый цифровой бор|Быстрый цифровой бор]]&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffdddd;&amp;quot; | &amp;lt;tex&amp;gt;O(w)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffdddd;&amp;quot; | &amp;lt;tex&amp;gt;O(w)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log w)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffdddd;&amp;quot; | &amp;lt;tex&amp;gt;O(n \cdot w)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Улучшеная версия структуры цифрового бора.&lt;br /&gt;
|-&lt;br /&gt;
| [[Сверхбыстрый цифровой бор]]&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log w)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffdddd;&amp;quot; | &amp;lt;tex&amp;gt;O(w)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log w)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffdddd;&amp;quot; | &amp;lt;tex&amp;gt;O(w)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log w)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Улучшеная версия структуры быстрого цифрового бора.&lt;br /&gt;
|-&lt;br /&gt;
!&lt;br /&gt;
! colspan=&amp;quot;9&amp;quot; align=&amp;quot;center&amp;quot; | Статические структуры данных&lt;br /&gt;
|-&lt;br /&gt;
|[[Tango-дерево]]&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #e5e5e5;&amp;quot; | -&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #e5e5e5;&amp;quot; | -&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ddffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log \log n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | [[Дерево поиска, наивная реализация|Двоичное дерево поиска]], которое изобрели Эрик Д. Демейн, Дион Хармон, Джон Яконо и Михаи Патраску в 2004 году. Не поддерживает операции вставки и удаления, но перестраивается по ходу поисковых запросов, чтобы отвечать на них как можно оптимальней. Это лучшая известная реализация на данный момент.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==См. также==&lt;br /&gt;
&lt;br /&gt;
*[[:Сортировка|Сортировка]]&lt;br /&gt;
*[[:Поиск_подстроки_в_строке|Поиск подстроки в строке]]&lt;br /&gt;
*[[:Приоритетные_кучи|Приоритетные кучи]]&lt;br /&gt;
&lt;br /&gt;
== Источники информации ==&lt;br /&gt;
&lt;br /&gt;
* [[wikipedia:en:Search_data_structure|Wikidedia {{---}} Search data structure]]&lt;br /&gt;
* [http://habrahabr.ru/post/188010/ Habrahabr {{---}} Знай сложности алгоритмов ]&lt;br /&gt;
&lt;br /&gt;
[[Категория: Дискретная математика и алгоритмы]]&lt;br /&gt;
[[Категория: Деревья поиска]]&lt;br /&gt;
[[Категория: Структуры данных]]&lt;/div&gt;</summary>
		<author><name>194.85.160.130</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D0%BE%D0%B8%D1%81%D0%BA%D0%BE%D0%B2%D1%8B%D0%B5_%D1%81%D1%82%D1%80%D1%83%D0%BA%D1%82%D1%83%D1%80%D1%8B_%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D1%85&amp;diff=46911</id>
		<title>Поисковые структуры данных</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D0%BE%D0%B8%D1%81%D0%BA%D0%BE%D0%B2%D1%8B%D0%B5_%D1%81%D1%82%D1%80%D1%83%D0%BA%D1%82%D1%83%D1%80%D1%8B_%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D1%85&amp;diff=46911"/>
				<updated>2015-05-25T12:24:28Z</updated>
		
		<summary type="html">&lt;p&gt;194.85.160.130: /* Тип */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Поисковая структура данных''' {{---}} любая структура данных реализующая эффективный поиск конкретных элементов множества, например, конкретной записи в базе данных.&lt;br /&gt;
&lt;br /&gt;
Простейший, наиболее общий, но менее эффективный поисковой структурой является простая неупорядоченный последовательная всех элементов. Расположив элементы в такой список, неизбежно возникнет ряд операций, которые потребуют линейного времени, в худшем случае, а также в средней случае. Используемые в реальной жизни поисковые структуры данных позволяют совершать операции более быстро, однако они ограничены запросами некоторого конкретного вида. Кроме того, поскольку стоимость построение таких структур пропорциональна &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt;, их построение окупится, даже если поступает лишь несколько запросов.&lt;br /&gt;
&lt;br /&gt;
=== Тип ===&lt;br /&gt;
&lt;br /&gt;
'''Статические поисковые структуры данных''' (англ. ''Online search structures'') предназначены для ответа на запросы на фиксированной базе данных.&lt;br /&gt;
&lt;br /&gt;
'''Динамические поисковые структуры''' (англ. ''Offline search structures'') также позволяют вставки, удаления или модификации элементов между последовательными запросами. В динамическом случае, необходимо также учитывать стоимость изменения структуры данных. Любую динамическую структуру данных можно сделать статической, если запретить вставку и удаление. Также если множество ключей известно, то можно его заранее упорядочить так, чтобы избежать худших случаев в поисках в структурах данных.&lt;br /&gt;
&lt;br /&gt;
=== Время работы ===&lt;br /&gt;
&lt;br /&gt;
Эту классификацию обычно считают самой важной. Оценивают худшее время алгоритма, среднее и лучшее для каждой операции. Лучшее время {{---}} минимальное время работы алгоритма на каком-либо наборе. Худшее время {{---}} наибольшее время.&lt;br /&gt;
&lt;br /&gt;
=== Используемая память ===&lt;br /&gt;
&lt;br /&gt;
Параметр структуры данных, показывающий, сколько памяти ей требуется. Обычно затраты составляют &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Сравнение структур данных ===&lt;br /&gt;
&lt;br /&gt;
Сравним эффективность поисковых структур данных для реализации интерфейса [[Упорядоченное множество|упорядоченного множества]]. Время работы методов &amp;lt;tex&amp;gt;Predecessor&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;Successor&amp;lt;/tex&amp;gt; совпадает с временем работы &amp;lt;tex&amp;gt;Search&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; {{---}} количество хранимых чисел, каждое из которых представляется с помощью &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt; битов.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! rowspan=&amp;quot;2&amp;quot; |&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; | Insert&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; | Delete&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; | Search&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; | Память&lt;br /&gt;
! rowspan=&amp;quot;2&amp;quot; | Описание&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background: #ddffdd;&amp;quot; | Среднее&lt;br /&gt;
! style=&amp;quot;background: #ffdddd;&amp;quot; | Худшее&lt;br /&gt;
! style=&amp;quot;background: #ddffdd;&amp;quot; | Среднее&lt;br /&gt;
! style=&amp;quot;background: #ffdddd;&amp;quot; | Худшее&lt;br /&gt;
! style=&amp;quot;background: #ddffdd;&amp;quot; | Среднее&lt;br /&gt;
! style=&amp;quot;background: #ffdddd;&amp;quot; | Худшее&lt;br /&gt;
! style=&amp;quot;background: #ddffdd;&amp;quot; | Среднее&lt;br /&gt;
! style=&amp;quot;background: #ffdddd;&amp;quot; | Худшее&lt;br /&gt;
|-&lt;br /&gt;
!&lt;br /&gt;
! colspan=&amp;quot;9&amp;quot; align=&amp;quot;center&amp;quot; |  Динамические структуры данных&lt;br /&gt;
|-&lt;br /&gt;
| Неотсортированный массив&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ddffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffdddd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ddffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffdddd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffdddd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Наивная реализация, использующая [[Динамический массив|динамический массив]]. Добавление происходит в конец массива, а для поиска элемента просто проходим по всему массиву.&lt;br /&gt;
|-&lt;br /&gt;
| Отсортированный массив&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffdddd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffdddd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | То же самое, но теперь массив отсортирован. Поиск ускоряется за счёт возможности применить [[Целочисленный двоичный поиск|двоичный поиск]]. Вставка замедляется из-за необходимости поддерживать инвариант отсортированности.&lt;br /&gt;
|-&lt;br /&gt;
| Неотсортированный [[Список|список]]&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ddffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ddffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffdddd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; rowspan=&amp;quot;2&amp;quot; | Аналогично массиву, но храним данные в [[Список|списке]]. Можно хранить дополнительную информацию о вершинах, что позволит ускорить время работы операции delete.&lt;br /&gt;
|-&lt;br /&gt;
| Отсортированный [[Список|список]]&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffdddd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ddffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffdddd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| [[Дерево поиска, наивная реализация]]&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffdddd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffdddd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffdddd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Бинарное дерево поиска  обладает следующим свойством:  если &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt; {{---}} узел бинарного дерева с ключом &amp;lt;tex&amp;gt;k&amp;lt;/tex&amp;gt;, то все узлы в левом поддереве должны иметь ключи, меньшие &amp;lt;tex&amp;gt;k&amp;lt;/tex&amp;gt;,  а в правом поддереве большие &amp;lt;tex&amp;gt;k&amp;lt;/tex&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
| [[Рандомизированное бинарное дерево поиска]]&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffdddd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffdddd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffdddd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Вариант [[Дерево поиска, наивная реализация|двоисного дерево поиска]] с добавлением инвариата &amp;quot;случайности&amp;quot;. Что дает возможность того, что математическое ожидание глубины дерева будет небольшим.&lt;br /&gt;
|-&lt;br /&gt;
|[[АВЛ-дерево]]&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Сбалансированное [[Дерево поиска, наивная реализация|двоичное дерево поиска]], в котором поддерживается следующее свойство: для каждой его вершины высота её двух поддеревьев различается не более чем на &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
|[[2-3 дерево]]&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Структура данных, представляющая собой сбалансированное дерево поиска, такое что из каждого узла может выходить две или три ветви и глубина всех листьев одинакова. Является частным случаем [[B-дерево#B.2B-.D0.B4.D0.B5.D1.80.D0.B5.D0.B2.D0.BE|B+ дерева]].&lt;br /&gt;
|-&lt;br /&gt;
|[[B-дерево]]&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Cильноветвящееся сбалансированное дерево поиска, позволяющее проводить поиск, добавление и удаление элементов за &amp;lt;tex&amp;gt;O(\log n)&amp;lt;/tex&amp;gt;. B-дерево с &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; узлами имеет высоту &amp;lt;tex&amp;gt;O(\log n)&amp;lt;/tex&amp;gt;. Количество детей узлов может быть от нескольких до тысяч (обычно степень ветвления B-дерева определяется характеристиками устройства (дисков), на котором производится работа с деревом). В-деревья также могут использоваться для реализации многих операций над динамическими множествами за время &amp;lt;tex&amp;gt;O(\log n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|[[Красно-черное дерево]]&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Сбалансированное [[Дерево поиска, наивная реализация|двоичное дерево поиска]], в котором баланс осуществляется на основе &amp;quot;цвета&amp;quot; узла дерева, который принимает только два значения: &amp;quot;красный&amp;quot; (англ. ''red'') и &amp;quot;чёрный&amp;quot; (англ. ''black'').  При этом все листья дерева являются фиктивными и не содержат данных, но относятся к дереву и являются чёрными.&lt;br /&gt;
|-&lt;br /&gt;
| [[Декартово дерево]]&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffdddd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffdddd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffdddd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Бинарное дерево, в узлах которого хранится пары &amp;lt;tex&amp;gt; (x,y) &amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt; {{---}} это ключ, а &amp;lt;tex&amp;gt;y&amp;lt;/tex&amp;gt; {{---}} это приоритет. Также оно является [[Дерево поиска, наивная реализация|двоичным деревом поиска]] по &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt; и [[Двоичная куча|пирамидой]] по &amp;lt;tex&amp;gt;y&amp;lt;/tex&amp;gt;. Предполагая, что все &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt; и все &amp;lt;tex&amp;gt;y&amp;lt;/tex&amp;gt; являются различными, получаем, что если некоторый элемент дерева содержит &amp;lt;tex&amp;gt;(x_0,y_0)&amp;lt;/tex&amp;gt;, то у всех элементов в левом поддереве &amp;lt;tex&amp;gt;x &amp;lt; x_0&amp;lt;/tex&amp;gt;, у всех элементов в правом поддереве &amp;lt;tex&amp;gt; x &amp;gt; x_0&amp;lt;/tex&amp;gt;, а также и в левом, и в правом поддереве имеем: &amp;lt;tex&amp;gt; y &amp;lt; y_0&amp;lt;/tex&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
|[[Splay-дерево]]&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffdddd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffdddd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffdddd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | [[Дерево поиска, наивная реализация|Двоичное дерево поиска]]. Оно позволяет находить быстрее те данные, которые использовались недавно, за счёт '''перемещения к корню''' (англ. ''Move to root''). Относится к разряду сливаемых деревьев. Сплей-дерево было придумано Робертом Тарьяном и Даниелем Слейтером в 1983 году.&lt;br /&gt;
|-&lt;br /&gt;
|[[Дерево ван Эмде Боаса]]&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log w)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log w)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log w)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffdddd;&amp;quot; | &amp;lt;tex&amp;gt;O(2^w)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Cтруктура данных, представляющая собой [[Дерево поиска, наивная реализация|дерево поиска]], позволяющее хранить целые неотрицательные числа в интервале &amp;lt;tex&amp;gt;[0;2^w)&amp;lt;/tex&amp;gt; и осуществлять над ними все соответствующие дереву поиска операции. Проще говоря, данная структура позволяет хранить &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;-битные числа.&lt;br /&gt;
Особенностью этой структуры является то, что все операции выполняются за &amp;lt;tex&amp;gt;O(\log w)&amp;lt;/tex&amp;gt;, что асимптотически лучше, чем &amp;lt;tex&amp;gt;O(\log n)&amp;lt;/tex&amp;gt; в большинстве других деревьев поиска, где &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; {{---}} количество элементов в дереве.&lt;br /&gt;
|-&lt;br /&gt;
| [[Список с пропусками]]&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffdddd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffdddd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffdddd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt; &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Вероятностная структура данных, основанная на нескольких отсортированных односвязных списках.&lt;br /&gt;
Отсортированный связный список является простейшей структурой с временем поиска &amp;lt;tex&amp;gt;\Theta(n)&amp;lt;/tex&amp;gt;. Добавление дополнительных уровней, обеспечивающих быстрый доступ через несколько элементов, помогает улучшить асимптотику до &amp;lt;tex&amp;gt;\Theta(\log(n))&amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|[[Fusion tree]]&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log_{w} n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log_{w} n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log_{w} n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Дерево поиска, позволяющее хранить &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;-битных чисел, используя &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt; памяти, и выполнять операции поиска за время &amp;lt;tex&amp;gt;O(\log_{w} n)&amp;lt;/tex&amp;gt;. Эта структура данных была впервые предложена в 1990 году М. Фредманом (M. Fredman) и Д. Уиллардом (D. Willard).&lt;br /&gt;
|-&lt;br /&gt;
| [[Сверхбыстрый цифровой бор|Цифровой бор]]&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffdddd;&amp;quot; | &amp;lt;tex&amp;gt;O(w)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffdddd;&amp;quot; | &amp;lt;tex&amp;gt;O(w)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffdddd;&amp;quot; | &amp;lt;tex&amp;gt;O(w)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffdddd;&amp;quot; | &amp;lt;tex&amp;gt;O(n \cdot w)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | [[Бор]], в котором в качестве строк используются двоичные записи чисел, включая ведущие нули. Таким образом он имеет глубину &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
| [[Сверхбыстрый цифровой бор|Быстрый цифровой бор]]&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffdddd;&amp;quot; | &amp;lt;tex&amp;gt;O(w)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffdddd;&amp;quot; | &amp;lt;tex&amp;gt;O(w)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log w)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffdddd;&amp;quot; | &amp;lt;tex&amp;gt;O(n \cdot w)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Улучшеная версия структуры цифрового бора.&lt;br /&gt;
|-&lt;br /&gt;
| [[Сверхбыстрый цифровой бор]]&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log w)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffdddd;&amp;quot; | &amp;lt;tex&amp;gt;O(w)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log w)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffdddd;&amp;quot; | &amp;lt;tex&amp;gt;O(w)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log w)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Улучшеная версия структуры быстрого цифрового бора.&lt;br /&gt;
|-&lt;br /&gt;
!&lt;br /&gt;
! colspan=&amp;quot;9&amp;quot; align=&amp;quot;center&amp;quot; | Статические структуры данных&lt;br /&gt;
|-&lt;br /&gt;
|[[Tango-дерево]]&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #e5e5e5;&amp;quot; | -&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #e5e5e5;&amp;quot; | -&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ddffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(\log \log n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;background: #ffffdd;&amp;quot; | &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | [[Дерево поиска, наивная реализация|Двоичное дерево поиска]], которое изобрели Эрик Д. Демейн, Дион Хармон, Джон Яконо и Михаи Патраску в 2004 году. Не поддерживает операции вставки и удаления, но перестраивается по ходу поисковых запросов, чтобы отвечать на них как можно оптимальней. Это лучшая известная реализация на данный момент.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==См. также==&lt;br /&gt;
&lt;br /&gt;
*[[:Сортировка|Сортировка]]&lt;br /&gt;
*[[:Поиск_подстроки_в_строке|Поиск подстроки в строке]]&lt;br /&gt;
*[[:Приоритетные_кучи|Приоритетные кучи]]&lt;br /&gt;
&lt;br /&gt;
== Источники информации ==&lt;br /&gt;
&lt;br /&gt;
* [[wikipedia:en:Search_data_structure|Wikidedia {{---}} Search data structure]]&lt;br /&gt;
* [http://habrahabr.ru/post/188010/ Habrahabr {{---}} Знай сложности алгоритмов ]&lt;br /&gt;
&lt;br /&gt;
[[Категория: Дискретная математика и алгоритмы]]&lt;br /&gt;
[[Категория: Деревья поиска]]&lt;br /&gt;
[[Категория: Структуры данных]]&lt;/div&gt;</summary>
		<author><name>194.85.160.130</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D1%80%D0%B5%D0%B4%D0%B8%D0%BA%D1%82%D0%B8%D0%B2%D0%BD%D1%8B%D0%B9_%D1%81%D0%B8%D0%BD%D1%82%D0%B0%D0%BA%D1%81%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B8%D0%B9_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=46910</id>
		<title>Предиктивный синтаксический анализ</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D1%80%D0%B5%D0%B4%D0%B8%D0%BA%D1%82%D0%B8%D0%B2%D0%BD%D1%8B%D0%B9_%D1%81%D0%B8%D0%BD%D1%82%D0%B0%D0%BA%D1%81%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B8%D0%B9_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=46910"/>
				<updated>2015-05-25T12:23:34Z</updated>
		
		<summary type="html">&lt;p&gt;194.85.160.130: /* Общая схема построения рекурсивных парсеров с помощью FIRST и FOLLOW */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Для [[LL(k)-грамматики, множества FIRST и FOLLOW | LL(1)-грамматик]] возможна автоматическая генерация парсеров, если известны множества FIRST и FOLLOW. Существуют общедоступные генераторы: ANTLR&amp;lt;ref&amp;gt;[http://www.antlr.org/ ANTLR {{---}} Parser generator]&amp;lt;/ref&amp;gt;, Bison&amp;lt;ref&amp;gt;[http://www.gnu.org/software/bison/ Bison {{---}} GNU Project]&amp;lt;/ref&amp;gt;, Yacc&amp;lt;ref&amp;gt;[http://dinosaur.compilertools.net/ Lex &amp;amp; Yacc {{---}} A Lexical Analyzer Generator and Yet Another Compiler-Compiler]&amp;lt;/ref&amp;gt;, Happy&amp;lt;ref&amp;gt;[https://www.haskell.org/happy/ Happy {{---}} The Parser Generator for Haskell]&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Общая схема построения рекурсивных парсеров с помощью FIRST и FOLLOW ==&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;tex&amp;gt;\Gamma =\langle \Sigma, N, S, P \rangle&amp;lt;/tex&amp;gt; {{---}} LL(1)-грамматика. Построим для нее парсер.&lt;br /&gt;
&lt;br /&gt;
Для каждого нетерминала &amp;lt;tex&amp;gt;A : A \rightarrow \alpha_1 \mid \alpha_2 \mid \ldots \mid \alpha_k &amp;lt;/tex&amp;gt; создадим функцию &amp;lt;tex&amp;gt; \mathtt{A}() : \mathtt{Node} &amp;lt;/tex&amp;gt;, возвращающую фрагмент [[Контекстно-свободные грамматики, вывод, лево- и правосторонний вывод, дерево разбора#Дерево разбора | дерева разбора]], выведенный из нетерминала &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Здесь &amp;lt;tex&amp;gt;\mathtt{Node}&amp;lt;/tex&amp;gt; {{---}} структура следующего вида:&lt;br /&gt;
 '''struct''' Node&lt;br /&gt;
     children : '''list&amp;lt;Node&amp;gt;'''&lt;br /&gt;
     value : '''string'''          &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// имя нетерминала или текст терминала&amp;lt;/font&amp;gt;&lt;br /&gt;
     '''function''' addChild('''Node''') &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// функция, подвешивающая поддерево к данному узлу&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Каждый момент времени парсер работает с определённым '''токеном''' (англ. ''token'') входного слово &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt;. Токен {{---}} один или несколько нетерминалов, для удобства объединяемые по смыслу в одну логическую единицы. Примерами токенов могут выступать следующие лексические единицы:&lt;br /&gt;
* произвольный символ &amp;lt;tex&amp;gt; c &amp;lt;/tex&amp;gt;,&lt;br /&gt;
* целое слово, например &amp;lt;tex&amp;gt; public &amp;lt;/tex&amp;gt;,&lt;br /&gt;
* число со знаком, обозначаемое далее для краткости как &amp;lt;tex&amp;gt; n &amp;lt;/tex&amp;gt;.&lt;br /&gt;
В общем случае, токеном может являться любое слово, удовлетворяющее произвольному [[Регулярные языки: два определения и их эквивалентность | регулярному выражению]].&lt;br /&gt;
&lt;br /&gt;
[[Файл:string_token.png|300px]]&lt;br /&gt;
&lt;br /&gt;
Здесь &amp;lt;tex&amp;gt;\$&amp;lt;/tex&amp;gt; обозначает маркер конца строки.&lt;br /&gt;
&lt;br /&gt;
В псевдокоде используются следующие обозначения:&lt;br /&gt;
* &amp;lt;tex&amp;gt;\mathtt{curToken}&amp;lt;/tex&amp;gt; {{---}} текущий токен строки,&lt;br /&gt;
* &amp;lt;tex&amp;gt;\mathtt{nextToken()}&amp;lt;/tex&amp;gt; {{---}} функция, записывающая в &amp;lt;tex&amp;gt;\mathtt{curToken}&amp;lt;/tex&amp;gt; следующий за ним токен.&lt;br /&gt;
&lt;br /&gt;
Тогда шаблон функции рекурсивного парсера для нетерминала &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; имеет вид:&lt;br /&gt;
&lt;br /&gt;
 A() : '''Node'''&lt;br /&gt;
     Node res = Node(&amp;quot;A&amp;quot;)&lt;br /&gt;
     '''switch''' (curToken) : &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// принимаем решение в зависимости от текущего токена строки&amp;lt;/font&amp;gt;&lt;br /&gt;
         '''case''' &amp;lt;tex&amp;gt;\mathrm{FIRST}(\alpha_1) \cup (\mathrm{FOLLOW}(A)\ \mathrm{if}\ \varepsilon \in \mathrm{FIRST}(\alpha_1))&amp;lt;/tex&amp;gt; :&lt;br /&gt;
             &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// &amp;lt;tex&amp;gt;\alpha_1 = X_1X_2 \ldots X_{t}&amp;lt;/tex&amp;gt; &amp;lt;/font&amp;gt;&lt;br /&gt;
             '''for''' i = 1 .. t&lt;br /&gt;
                 '''if''' &amp;lt;tex&amp;gt; X_i &amp;lt;/tex&amp;gt; {{---}} нетерминал&lt;br /&gt;
                     consume(&amp;lt;tex&amp;gt;X_i&amp;lt;/tex&amp;gt;)&lt;br /&gt;
                     res.addChild(Node(&amp;quot;&amp;lt;tex&amp;gt;X_i&amp;lt;/tex&amp;gt;&amp;quot;)&lt;br /&gt;
                     nextToken()&lt;br /&gt;
                 '''else''' &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// &amp;lt;tex&amp;gt;X_i&amp;lt;/tex&amp;gt; {{---}} терминал, нужно вызвать соответствующую ему функцию рекурсивного парсера &amp;lt;/font&amp;gt;&lt;br /&gt;
                     Node t = &amp;lt;tex&amp;gt;X_i()&amp;lt;/tex&amp;gt;&lt;br /&gt;
                     res.addChild(t)&lt;br /&gt;
             '''break'''&lt;br /&gt;
         '''case''' &amp;lt;tex&amp;gt;\mathrm{FIRST}(\alpha_2) \cup (\mathrm{FOLLOW}(A)\ \mathrm{if}\ \varepsilon \in \mathrm{FIRST}(\alpha_2))&amp;lt;/tex&amp;gt; : &lt;br /&gt;
             &amp;lt;tex&amp;gt;\ldots&amp;lt;/tex&amp;gt;&lt;br /&gt;
             '''break'''&lt;br /&gt;
         &amp;lt;tex&amp;gt;\ldots&amp;lt;/tex&amp;gt;&lt;br /&gt;
         '''default''' :&lt;br /&gt;
             &amp;lt;font color=&amp;quot;red&amp;quot;&amp;gt;error&amp;lt;/font&amp;gt;(&amp;quot;unexpected char&amp;quot;)&lt;br /&gt;
     '''return''' res&lt;br /&gt;
&lt;br /&gt;
 '''function''' consume(c: '''char''') &lt;br /&gt;
     '''if''' curToken != c&lt;br /&gt;
         &amp;lt;font color=&amp;quot;red&amp;quot;&amp;gt;error&amp;lt;/font&amp;gt;(&amp;quot;expected &amp;quot; + c)&lt;br /&gt;
     nextToken()&lt;br /&gt;
&lt;br /&gt;
Такой парсер не только разбирает строку, но и находит ошибки в неудовлетворяющих грамматике выражениях.&lt;br /&gt;
&lt;br /&gt;
== Пример ==&lt;br /&gt;
Рассмотрим построение парсера на примере LL(1)-грамматики арифметических выражений, которая уже была разобрана [[Построение FIRST и FOLLOW#Пример | ранее]]:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt; &lt;br /&gt;
E \to TE' \\&lt;br /&gt;
E' \to +TE' \mid \varepsilon \\&lt;br /&gt;
T \to FT' \\&lt;br /&gt;
T' \to * FT' \mid \varepsilon \\&lt;br /&gt;
F \to n \mid (E)&lt;br /&gt;
&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Напомним, что множества &amp;lt;tex&amp;gt;\mathrm{FIRST}&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\mathrm{FOLLOW}&amp;lt;/tex&amp;gt; для неё выглядят так:&lt;br /&gt;
&lt;br /&gt;
{| style=&amp;quot;background-color:#CCC;margin:0.5px&amp;quot;&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| Правило&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| FIRST&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| FOLLOW&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;E&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ n,\ (\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ \$,\ )\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;E'&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ +,\ \varepsilon\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ \$,\ )\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ n,\ (\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ +,\ \$\ ,\ )\ \}&amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ *,\ \varepsilon\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ +,\ \$\ ,\ )\ \}&amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;F&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ n,\ (\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ *, \ +,\ \$\ ,\ )\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Псевдокоды ===&lt;br /&gt;
Построим функции обработки некоторых нетерминалов, используя описанный выше шаблон:&lt;br /&gt;
&lt;br /&gt;
 E() : '''Node'''&lt;br /&gt;
     Node res = Node(&amp;quot;E&amp;quot;)&lt;br /&gt;
     '''switch''' (curToken)&lt;br /&gt;
         '''case''' n, '('  :&lt;br /&gt;
             res.addChild(T())&lt;br /&gt;
             res.addChild(E'())&lt;br /&gt;
             '''break'''&lt;br /&gt;
         '''default''' :&lt;br /&gt;
             &amp;lt;font color=&amp;quot;red&amp;quot;&amp;gt;error&amp;lt;/font&amp;gt;(&amp;quot;unexpected char&amp;quot;)&lt;br /&gt;
     '''return''' res&lt;br /&gt;
&lt;br /&gt;
 E'() : '''Node'''&lt;br /&gt;
     Node res = Node(&amp;quot;E'&amp;quot;)&lt;br /&gt;
     '''switch''' (curToken) &lt;br /&gt;
         '''case''' '+' :&lt;br /&gt;
             consume('+')&lt;br /&gt;
             res.addChild(Node(&amp;quot;+&amp;quot;))&lt;br /&gt;
             res.addChild(T())&lt;br /&gt;
             res.addChild(E'())&lt;br /&gt;
             '''break'''&lt;br /&gt;
         '''case''' '$', ')' :&lt;br /&gt;
             '''break'''&lt;br /&gt;
         '''default''' :&lt;br /&gt;
             &amp;lt;font color=&amp;quot;red&amp;quot;&amp;gt;error&amp;lt;/font&amp;gt;(&amp;quot;unexpected char&amp;quot;)&lt;br /&gt;
      '''return''' res&lt;br /&gt;
&lt;br /&gt;
 F() : '''Node'''&lt;br /&gt;
     Node res = Node(&amp;quot;F&amp;quot;)&lt;br /&gt;
     '''switch''' (curToken)&lt;br /&gt;
         '''case''' n :&lt;br /&gt;
             consume(n)&lt;br /&gt;
             res.addChild(Node(curToken)) &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// &amp;lt;tex&amp;gt;\mathtt{curToken}&amp;lt;/tex&amp;gt; подпадает под шаблон &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt;, поэтому запишем его в &amp;lt;tex&amp;gt;\mathtt{value}&amp;lt;/tex&amp;gt; вершины&amp;lt;/font&amp;gt;&lt;br /&gt;
             '''break'''&lt;br /&gt;
         '''case''' '(' :&lt;br /&gt;
             consume('(')&lt;br /&gt;
             res.addChild(Node(&amp;quot;(&amp;quot;))&lt;br /&gt;
             res.addChild(E())&lt;br /&gt;
             consume(')')&lt;br /&gt;
             res.addChild(Node(&amp;quot;)&amp;quot;))&lt;br /&gt;
         '''default''' :&lt;br /&gt;
             &amp;lt;font color=&amp;quot;red&amp;quot;&amp;gt;error&amp;lt;/font&amp;gt;(&amp;quot;unexpected char&amp;quot;)&lt;br /&gt;
     '''return''' res&lt;br /&gt;
&lt;br /&gt;
Функции для &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt; строятся аналогично.&lt;br /&gt;
&lt;br /&gt;
=== Дерево разбора ===&lt;br /&gt;
&lt;br /&gt;
Рассмотрим дерево разбора для выражения &amp;lt;tex&amp;gt;(1 + 2) * 3&amp;lt;/tex&amp;gt; и несколько первых шагов алгоритма рекурсивного разбора. Сначала вызывается функция стартового нетерминала грамматики, то есть &amp;lt;tex&amp;gt;E&amp;lt;/tex&amp;gt;. Так как первым токеном является '(', то будет использовано первое правило разбора &amp;lt;tex&amp;gt;TE'&amp;lt;/tex&amp;gt;. Поэтому к вершине с меткой &amp;lt;tex&amp;gt;E&amp;lt;/tex&amp;gt; добавятся два ребёнка: &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;E'&amp;lt;/tex&amp;gt;. А рекурсивный разборщик перейдёт к нетерминалу &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt;. По-прежнему curToken равен '(', поэтому в &amp;lt;tex&amp;gt;F&amp;lt;/tex&amp;gt; сработает второй case, первым ребёнком добавится '(', curToken станет равен &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt;, а разборщик перейдёт к нетерминалу &amp;lt;tex&amp;gt;E&amp;lt;/tex&amp;gt;. После того как выражение после '(', которое выводится из &amp;lt;tex&amp;gt;E&amp;lt;/tex&amp;gt;, будет полностью разобрано, функция рекурсивного разбора для &amp;lt;tex&amp;gt;F&amp;lt;/tex&amp;gt; добавит ')' последним сыном к этому нетерминалу. &lt;br /&gt;
&lt;br /&gt;
Продолжая в том же духе, мы построим всё дерево разбора данного выражения.&lt;br /&gt;
&lt;br /&gt;
[[Файл:parse_ex1.png|400px|thumb|center|Дерево разбора выражения &amp;lt;tex&amp;gt;(1 + 2) * 3&amp;lt;/tex&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
== Нерекурсивный нисходящий парсер ==&lt;br /&gt;
&lt;br /&gt;
[[Файл:Parse_table.png|400px|right]]&lt;br /&gt;
&lt;br /&gt;
Рекурсивные разборщики можно генерировать автоматически, зная множества &amp;lt;tex&amp;gt;\mathrm{FIRST}&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\mathrm{FOLLOW}&amp;lt;/tex&amp;gt;, так как они имеют достаточно прозрачный шаблон построения. Альтернативным способом осуществления нисходящего синтаксического анализа является построение нерекурсивного нисходящего парсера. Его можно построить с помощью явного использования [[Стек | стека]] (вместо неявного при рекурсивных вызовах). Такой анализатор имитирует &lt;br /&gt;
[[Контекстно-свободные грамматики, вывод, лево- и правосторонний вывод, дерево разбора#Лево- и правосторонний вывод слова | левое порождение]].  &lt;br /&gt;
&lt;br /&gt;
Стек нерекурсивного предиктивного синтаксического анализатора содержит последовательность терминалов и нетерминалов и таблицу синтаксического анализа. На стеке располагается последовательность символов грамматики с маркером конца разбора &amp;lt;tex&amp;gt;\perp&amp;lt;/tex&amp;gt; на дне. В начале процесса анализа строки стек содержит стартовый нетерминал грамматики непосредственно над символом &amp;lt;tex&amp;gt;\perp&amp;lt;/tex&amp;gt;. Таблица синтаксического анализа представляет собой двухмерный массив &amp;lt;tex&amp;gt;\mathcal{M}[A, c]&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; {{---}} нетерминал или &amp;lt;tex&amp;gt;\perp&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt; c &amp;lt;/tex&amp;gt; {{---}} токен или символ конца строки &amp;lt;tex&amp;gt;\$&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Нерекурсивный синтаксический анализатор смотрит на текущий токен &amp;lt;tex&amp;gt; c &amp;lt;/tex&amp;gt; входного слова и на символ на вершине стека &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt;, а затем принимает решение в зависимости от одного из возникающих ниже случаев:&lt;br /&gt;
* если &amp;lt;tex&amp;gt;A\ =\ \perp\ \land\ c\ =\ \$&amp;lt;/tex&amp;gt;, то синтаксический анализатор прекращает работу, так как разбор строки завершён,&lt;br /&gt;
* eсли &amp;lt;tex&amp;gt;A = c&amp;lt;/tex&amp;gt;, то синтаксический анализатор снимает со стека токен &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; и перемещает указатель текущего токена ленты к следующему токену (то есть вызывает &amp;lt;tex&amp;gt;\mathtt{nextToken}&amp;lt;/tex&amp;gt;), таким образом, происходит выброс символа &amp;lt;tex&amp;gt; c &amp;lt;/tex&amp;gt; со стека,&lt;br /&gt;
* eсли &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; представляет собой нетерминал, то программа рассматривает запись &amp;lt;tex&amp;gt;\mathcal{M}[A,c]&amp;lt;/tex&amp;gt; таблицы разбора &amp;lt;tex&amp;gt;\mathcal{M}&amp;lt;/tex&amp;gt;. Эта запись представляет собой либо продукцию грамматики вида &amp;lt;tex&amp;gt;A \to \alpha_i,\ \alpha_i = X_1 X_2 \ldots X_t&amp;lt;/tex&amp;gt;, и тогда &amp;lt;tex&amp;gt;\mathcal{M}[A,c]&amp;lt;/tex&amp;gt; содержит номер &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; данной продукции, либо запись об ошибке, и тогда &amp;lt;tex&amp;gt;\mathcal{M}[A,c] = \varnothing&amp;lt;/tex&amp;gt;. Если &amp;lt;tex&amp;gt;\mathcal{M}[A,c] \neq \varnothing&amp;lt;/tex&amp;gt;, то синтаксический анализатор замещает на стеке нетерминал &amp;lt;tex&amp;gt; A &amp;lt;/tex&amp;gt; правой частью правила с номером &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt;, помещая символы правила на стек в обратном порядке,&lt;br /&gt;
* во всех остальных случаях парсер бросает сообщение об ошибке.&lt;br /&gt;
&lt;br /&gt;
Рассмотренные случаи отображены коротко на картинке справа, где блок &amp;lt;tex&amp;gt; N &amp;lt;/tex&amp;gt; отвечает нетерминалам грамматики. Из картинки видно, что вместо рассмотрения всех случаев в коде, достаточно просто создать таблицу &amp;lt;tex&amp;gt;\mathcal{M}&amp;lt;/tex&amp;gt; таким образом, чтобы она учитывала все случаи, что упростит код.&lt;br /&gt;
&lt;br /&gt;
=== Псевдокод ===&lt;br /&gt;
&lt;br /&gt;
Здесь по-прежнему &amp;lt;tex&amp;gt;\mathtt{curToken}&amp;lt;/tex&amp;gt; обозначает текущий токен строки, а &amp;lt;tex&amp;gt;\mathtt{nextToken}&amp;lt;/tex&amp;gt; передвигает указатель на следующий токен.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code style = &amp;quot;display: inline-block;&amp;quot;&amp;gt;&lt;br /&gt;
 '''function''' nonRecursiveParser():&lt;br /&gt;
     st : '''Stack'''&lt;br /&gt;
     st.push(&amp;lt;tex&amp;gt;\perp&amp;lt;/tex&amp;gt;)&lt;br /&gt;
     st.push(&amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;) &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// здесь &amp;lt;tex&amp;gt; S &amp;lt;/tex&amp;gt; {{---}} стартовый нетерминал грамматики&amp;lt;/font&amp;gt;&lt;br /&gt;
     '''while''' s.top() &amp;lt;tex&amp;gt;\neq\ \perp&amp;lt;/tex&amp;gt; &lt;br /&gt;
         A = st.top()&lt;br /&gt;
         '''if''' &amp;lt;tex&amp;gt;\mathcal{M}[A,\ curToken]\ ==\ \mathrm{ok}&amp;lt;/tex&amp;gt; &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// &amp;lt;tex&amp;gt; A\ ==\ \perp &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\mathtt{curToken}\ ==\ \$&amp;lt;/tex&amp;gt;&amp;lt;/font&amp;gt;&lt;br /&gt;
             '''break''' &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// разбор строки завершён&amp;lt;/font&amp;gt;&lt;br /&gt;
         '''else if''' &amp;lt;tex&amp;gt;\mathcal{M}[A,\ curToken]\ ==\ \nearrow&amp;lt;/tex&amp;gt; &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// выброс&amp;lt;/font&amp;gt;&lt;br /&gt;
             nextToken()&lt;br /&gt;
             st.pop()&lt;br /&gt;
             вывести в выходной поток нетерминал, отвечающий &amp;lt;tex&amp;gt;\mathtt{curToken}&amp;lt;/tex&amp;gt;&lt;br /&gt;
         '''else if''' &amp;lt;tex&amp;gt;\mathcal{M}[A,\ curToken]&amp;lt;/tex&amp;gt; {{---}} номер правила &amp;lt;tex&amp;gt;A \to \alpha_i,\ \alpha_i = X_1 X_2 \ldots X_t&amp;lt;/tex&amp;gt;&lt;br /&gt;
             st.pop()&lt;br /&gt;
             '''for''' k = t '''downto''' 1&lt;br /&gt;
                 st.push(&amp;lt;tex&amp;gt;X_k&amp;lt;/tex&amp;gt;)    &lt;br /&gt;
             вывести в выходной поток терминал, отвечающий &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt;&lt;br /&gt;
         '''else'''&lt;br /&gt;
             &amp;lt;font color=&amp;quot;red&amp;quot;&amp;gt;error&amp;lt;/font&amp;gt;(&amp;quot;unexpected symbol&amp;quot;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Пример ===&lt;br /&gt;
&lt;br /&gt;
Рассмотрим пример работы нерекурсивного парсера на всё той же грамматике арифметических выражений. Для начала пронумеруем все правила:&lt;br /&gt;
&lt;br /&gt;
{| style=&amp;quot;background-color:#CCC;margin:0.5px&amp;quot;&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| Номер&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| Правило&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px;text-align:center;&amp;quot;| &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 10px&amp;quot;| &amp;lt;tex&amp;gt;E \to TE'&amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px;text-align:center;&amp;quot;| &amp;lt;tex&amp;gt;2&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 10px&amp;quot;| &amp;lt;tex&amp;gt;E' \to +TE'&amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px;text-align:center;&amp;quot;| &amp;lt;tex&amp;gt;3&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 10px&amp;quot;| &amp;lt;tex&amp;gt;E' \to \varepsilon&amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px;text-align:center;&amp;quot;| &amp;lt;tex&amp;gt;4&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 10px&amp;quot;| &amp;lt;tex&amp;gt;T \to FT'&amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px;text-align:center;&amp;quot;| &amp;lt;tex&amp;gt;5&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 10px&amp;quot;| &amp;lt;tex&amp;gt;T' \to * FT'&amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px;text-align:center;&amp;quot;| &amp;lt;tex&amp;gt;6&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 10px&amp;quot;| &amp;lt;tex&amp;gt;T' \to \varepsilon&amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px;text-align:center;&amp;quot;| &amp;lt;tex&amp;gt;7&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 10px&amp;quot;| &amp;lt;tex&amp;gt;F \to n&amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px;text-align:center;&amp;quot;| &amp;lt;tex&amp;gt;8&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 10px&amp;quot;| &amp;lt;tex&amp;gt;F \to (E)&amp;lt;/tex&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Теперь можно построить часть таблицы &amp;lt;tex&amp;gt;\mathcal{M}&amp;lt;/tex&amp;gt;, содержащей строки, отвечающие нетерминалам. Её построение легко осуществить, если известны &amp;lt;tex&amp;gt;\mathrm{FIRST}&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\mathrm{FOLLOW}&amp;lt;/tex&amp;gt;. По сути по этим множествам можно понять, какое правило использовать для данного нетерминала при текущем токене.&lt;br /&gt;
&lt;br /&gt;
{| style=&amp;quot;background-color:#CCC;margin:0.5px&amp;quot;&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| &lt;br /&gt;
!style=&amp;quot;background-color:#EEE;text-align:center;&amp;quot;| &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt;&lt;br /&gt;
!style=&amp;quot;background-color:#EEE;text-align:center;&amp;quot;| &amp;lt;tex&amp;gt;(&amp;lt;/tex&amp;gt;&lt;br /&gt;
!style=&amp;quot;background-color:#EEE;text-align:center;&amp;quot;| &amp;lt;tex&amp;gt;)&amp;lt;/tex&amp;gt;&lt;br /&gt;
!style=&amp;quot;background-color:#EEE;text-align:center;&amp;quot;| &amp;lt;tex&amp;gt;+&amp;lt;/tex&amp;gt;&lt;br /&gt;
!style=&amp;quot;background-color:#EEE;text-align:center;&amp;quot;| &amp;lt;tex&amp;gt;*&amp;lt;/tex&amp;gt;&lt;br /&gt;
!style=&amp;quot;background-color:#EEE;text-align:center;&amp;quot;| &amp;lt;tex&amp;gt;\$&amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px;text-align:center;&amp;quot;| &amp;lt;tex&amp;gt;E&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px;text-align:center;&amp;quot;| &amp;lt;tex&amp;gt;1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px;text-align:center;&amp;quot;| &amp;lt;tex&amp;gt;1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px;text-align:center;&amp;quot;| &amp;lt;tex&amp;gt;E'&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px;text-align:center;&amp;quot;| &amp;lt;tex&amp;gt;3 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px;text-align:center;&amp;quot;| &amp;lt;tex&amp;gt;2 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px;text-align:center;&amp;quot;| &amp;lt;tex&amp;gt;3 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px;text-align:center;&amp;quot;| &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px;text-align:center;&amp;quot;| &amp;lt;tex&amp;gt;4 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px;text-align:center;&amp;quot;| &amp;lt;tex&amp;gt;4 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px;text-align:center;&amp;quot;| &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px;text-align:center;&amp;quot;| &amp;lt;tex&amp;gt;6 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px;text-align:center;&amp;quot;| &amp;lt;tex&amp;gt;6 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px;text-align:center;&amp;quot;| &amp;lt;tex&amp;gt;5 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px;text-align:center;&amp;quot;| &amp;lt;tex&amp;gt;6 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px;text-align:center;&amp;quot;| &amp;lt;tex&amp;gt;F&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px;text-align:center;&amp;quot;| &amp;lt;tex&amp;gt;7 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px;text-align:center;&amp;quot;| &amp;lt;tex&amp;gt;8 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px;text-align:center;&amp;quot;| &amp;lt;tex&amp;gt;\perp&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px;text-align:center;&amp;quot;| &amp;lt;tex&amp;gt;\mathrm{ok}&amp;lt;/tex&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
На картинке ниже показаны состояние стека на нескольких первых итерациях цикла и указатель на текущий токен.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Parse_stack.png|500px]]&lt;br /&gt;
&lt;br /&gt;
== Примечания ==&lt;br /&gt;
&amp;lt;references/&amp;gt;&lt;br /&gt;
== Источники информации ==&lt;br /&gt;
* Альфред Ахо, Рави Сети, Джеффри Ульман. Компиляторы. Принципы, технологии, инструменты. Издательство Вильямс. Второе издание. 2008. Стр. 288 {{---}} 294.&lt;br /&gt;
&lt;br /&gt;
[[Категория: Методы трансляции]]&lt;br /&gt;
[[Категория: Нисходящий разбор]]&lt;/div&gt;</summary>
		<author><name>194.85.160.130</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D1%80%D0%B5%D0%B4%D0%B8%D0%BA%D1%82%D0%B8%D0%B2%D0%BD%D1%8B%D0%B9_%D1%81%D0%B8%D0%BD%D1%82%D0%B0%D0%BA%D1%81%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B8%D0%B9_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=46909</id>
		<title>Предиктивный синтаксический анализ</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D1%80%D0%B5%D0%B4%D0%B8%D0%BA%D1%82%D0%B8%D0%B2%D0%BD%D1%8B%D0%B9_%D1%81%D0%B8%D0%BD%D1%82%D0%B0%D0%BA%D1%81%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B8%D0%B9_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=46909"/>
				<updated>2015-05-25T12:22:04Z</updated>
		
		<summary type="html">&lt;p&gt;194.85.160.130: готов&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Для [[LL(k)-грамматики, множества FIRST и FOLLOW | LL(1)-грамматик]] возможна автоматическая генерация парсеров, если известны множества FIRST и FOLLOW. Существуют общедоступные генераторы: ANTLR&amp;lt;ref&amp;gt;[http://www.antlr.org/ ANTLR {{---}} Parser generator]&amp;lt;/ref&amp;gt;, Bison&amp;lt;ref&amp;gt;[http://www.gnu.org/software/bison/ Bison {{---}} GNU Project]&amp;lt;/ref&amp;gt;, Yacc&amp;lt;ref&amp;gt;[http://dinosaur.compilertools.net/ Lex &amp;amp; Yacc {{---}} A Lexical Analyzer Generator and Yet Another Compiler-Compiler]&amp;lt;/ref&amp;gt;, Happy&amp;lt;ref&amp;gt;[https://www.haskell.org/happy/ Happy {{---}} The Parser Generator for Haskell]&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Общая схема построения рекурсивных парсеров с помощью FIRST и FOLLOW ==&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;tex&amp;gt;\Gamma =\langle \Sigma, N, S, P \rangle&amp;lt;/tex&amp;gt; {{---}} LL(1)-грамматика. Построим для нее парсер.&lt;br /&gt;
&lt;br /&gt;
Для каждого нетерминала &amp;lt;tex&amp;gt;A : A \rightarrow \alpha_1 \mid \alpha_2 \mid \ldots \mid \alpha_k &amp;lt;/tex&amp;gt; создадим функцию &amp;lt;tex&amp;gt; \mathtt{A}() : \mathtt{Node} &amp;lt;/tex&amp;gt;, возвращающую фрагмент дерева разбора, выведенный из нетерминала &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Здесь &amp;lt;tex&amp;gt;\mathtt{Node}&amp;lt;/tex&amp;gt; {{---}} структура следующего вида:&lt;br /&gt;
 '''struct''' Node&lt;br /&gt;
     children : '''list&amp;lt;Node&amp;gt;'''&lt;br /&gt;
     value : '''string'''          &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// имя нетерминала или текст терминала&amp;lt;/font&amp;gt;&lt;br /&gt;
     '''function''' addChild('''Node''') &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// функция, подвешивающая поддерево к данному узлу&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Каждый момент времени парсер работает с определённым '''токеном''' (англ. ''token'') входного слово &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt;. Токен {{---}} один или несколько нетерминалов, для удобства объединяемые по смыслу в одну логическую единицы. Примерами токенов могут выступать следующие лексические единицы:&lt;br /&gt;
* произвольный символ &amp;lt;tex&amp;gt; c &amp;lt;/tex&amp;gt;,&lt;br /&gt;
* целое слово, например &amp;lt;tex&amp;gt; public &amp;lt;/tex&amp;gt;,&lt;br /&gt;
* число со знаком, обозначаемое далее для краткости как &amp;lt;tex&amp;gt; n &amp;lt;/tex&amp;gt;.&lt;br /&gt;
В общем случае, токеном может являться любое слово, удовлетворяющее произвольному [[Регулярные языки: два определения и их эквивалентность | регулярному выражению]].&lt;br /&gt;
&lt;br /&gt;
[[Файл:string_token.png|300px]]&lt;br /&gt;
&lt;br /&gt;
Здесь &amp;lt;tex&amp;gt;\$&amp;lt;/tex&amp;gt; обозначает маркер конца строки.&lt;br /&gt;
&lt;br /&gt;
В псевдокоде используются следующие обозначения:&lt;br /&gt;
* &amp;lt;tex&amp;gt;\mathtt{curToken}&amp;lt;/tex&amp;gt; {{---}} текущий токен строки,&lt;br /&gt;
* &amp;lt;tex&amp;gt;\mathtt{nextToken()}&amp;lt;/tex&amp;gt; {{---}} функция, записывающая в &amp;lt;tex&amp;gt;\mathtt{curToken}&amp;lt;/tex&amp;gt; следующий за ним токен.&lt;br /&gt;
&lt;br /&gt;
Тогда шаблон функции рекурсивного парсера для нетерминала &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; имеет вид:&lt;br /&gt;
&lt;br /&gt;
 A() : '''Node'''&lt;br /&gt;
     Node res = Node(&amp;quot;A&amp;quot;)&lt;br /&gt;
     '''switch''' (curToken) : &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// принимаем решение в зависимости от текущего токена строки&amp;lt;/font&amp;gt;&lt;br /&gt;
         '''case''' &amp;lt;tex&amp;gt;\mathrm{FIRST}(\alpha_1) \cup (\mathrm{FOLLOW}(A)\ \mathrm{if}\ \varepsilon \in \mathrm{FIRST}(\alpha_1))&amp;lt;/tex&amp;gt; :&lt;br /&gt;
             &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// &amp;lt;tex&amp;gt;\alpha_1 = X_1X_2 \ldots X_{t}&amp;lt;/tex&amp;gt; &amp;lt;/font&amp;gt;&lt;br /&gt;
             '''for''' i = 1 .. t&lt;br /&gt;
                 '''if''' &amp;lt;tex&amp;gt; X_i &amp;lt;/tex&amp;gt; {{---}} нетерминал&lt;br /&gt;
                     consume(&amp;lt;tex&amp;gt;X_i&amp;lt;/tex&amp;gt;)&lt;br /&gt;
                     res.addChild(Node(&amp;quot;&amp;lt;tex&amp;gt;X_i&amp;lt;/tex&amp;gt;&amp;quot;)&lt;br /&gt;
                     nextToken()&lt;br /&gt;
                 '''else''' &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// &amp;lt;tex&amp;gt;X_i&amp;lt;/tex&amp;gt; {{---}} терминал, нужно вызвать соответствующую ему функцию рекурсивного парсера &amp;lt;/font&amp;gt;&lt;br /&gt;
                     Node t = &amp;lt;tex&amp;gt;X_i()&amp;lt;/tex&amp;gt;&lt;br /&gt;
                     res.addChild(t)&lt;br /&gt;
             '''break'''&lt;br /&gt;
         '''case''' &amp;lt;tex&amp;gt;\mathrm{FIRST}(\alpha_2) \cup (\mathrm{FOLLOW}(A)\ \mathrm{if}\ \varepsilon \in \mathrm{FIRST}(\alpha_2))&amp;lt;/tex&amp;gt; : &lt;br /&gt;
             &amp;lt;tex&amp;gt;\ldots&amp;lt;/tex&amp;gt;&lt;br /&gt;
             '''break'''&lt;br /&gt;
         &amp;lt;tex&amp;gt;\ldots&amp;lt;/tex&amp;gt;&lt;br /&gt;
         '''default''' :&lt;br /&gt;
             &amp;lt;font color=&amp;quot;red&amp;quot;&amp;gt;error&amp;lt;/font&amp;gt;(&amp;quot;unexpected char&amp;quot;)&lt;br /&gt;
     '''return''' res&lt;br /&gt;
&lt;br /&gt;
 '''function''' consume(c: '''char''') &lt;br /&gt;
     '''if''' curToken != c&lt;br /&gt;
         &amp;lt;font color=&amp;quot;red&amp;quot;&amp;gt;error&amp;lt;/font&amp;gt;(&amp;quot;expected &amp;quot; + c)&lt;br /&gt;
     nextToken()&lt;br /&gt;
&lt;br /&gt;
Такой парсер не только разбирает строку, но и находит ошибки в неудовлетворяющих грамматике выражениях.&lt;br /&gt;
&lt;br /&gt;
== Пример ==&lt;br /&gt;
Рассмотрим построение парсера на примере LL(1)-грамматики арифметических выражений, которая уже была разобрана [[Построение FIRST и FOLLOW#Пример | ранее]]:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt; &lt;br /&gt;
E \to TE' \\&lt;br /&gt;
E' \to +TE' \mid \varepsilon \\&lt;br /&gt;
T \to FT' \\&lt;br /&gt;
T' \to * FT' \mid \varepsilon \\&lt;br /&gt;
F \to n \mid (E)&lt;br /&gt;
&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Напомним, что множества &amp;lt;tex&amp;gt;\mathrm{FIRST}&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\mathrm{FOLLOW}&amp;lt;/tex&amp;gt; для неё выглядят так:&lt;br /&gt;
&lt;br /&gt;
{| style=&amp;quot;background-color:#CCC;margin:0.5px&amp;quot;&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| Правило&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| FIRST&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| FOLLOW&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;E&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ n,\ (\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ \$,\ )\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;E'&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ +,\ \varepsilon\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ \$,\ )\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ n,\ (\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ +,\ \$\ ,\ )\ \}&amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ *,\ \varepsilon\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ +,\ \$\ ,\ )\ \}&amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;F&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ n,\ (\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ *, \ +,\ \$\ ,\ )\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Псевдокоды ===&lt;br /&gt;
Построим функции обработки некоторых нетерминалов, используя описанный выше шаблон:&lt;br /&gt;
&lt;br /&gt;
 E() : '''Node'''&lt;br /&gt;
     Node res = Node(&amp;quot;E&amp;quot;)&lt;br /&gt;
     '''switch''' (curToken)&lt;br /&gt;
         '''case''' n, '('  :&lt;br /&gt;
             res.addChild(T())&lt;br /&gt;
             res.addChild(E'())&lt;br /&gt;
             '''break'''&lt;br /&gt;
         '''default''' :&lt;br /&gt;
             &amp;lt;font color=&amp;quot;red&amp;quot;&amp;gt;error&amp;lt;/font&amp;gt;(&amp;quot;unexpected char&amp;quot;)&lt;br /&gt;
     '''return''' res&lt;br /&gt;
&lt;br /&gt;
 E'() : '''Node'''&lt;br /&gt;
     Node res = Node(&amp;quot;E'&amp;quot;)&lt;br /&gt;
     '''switch''' (curToken) &lt;br /&gt;
         '''case''' '+' :&lt;br /&gt;
             consume('+')&lt;br /&gt;
             res.addChild(Node(&amp;quot;+&amp;quot;))&lt;br /&gt;
             res.addChild(T())&lt;br /&gt;
             res.addChild(E'())&lt;br /&gt;
             '''break'''&lt;br /&gt;
         '''case''' '$', ')' :&lt;br /&gt;
             '''break'''&lt;br /&gt;
         '''default''' :&lt;br /&gt;
             &amp;lt;font color=&amp;quot;red&amp;quot;&amp;gt;error&amp;lt;/font&amp;gt;(&amp;quot;unexpected char&amp;quot;)&lt;br /&gt;
      '''return''' res&lt;br /&gt;
&lt;br /&gt;
 F() : '''Node'''&lt;br /&gt;
     Node res = Node(&amp;quot;F&amp;quot;)&lt;br /&gt;
     '''switch''' (curToken)&lt;br /&gt;
         '''case''' n :&lt;br /&gt;
             consume(n)&lt;br /&gt;
             res.addChild(Node(curToken)) &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// &amp;lt;tex&amp;gt;\mathtt{curToken}&amp;lt;/tex&amp;gt; подпадает под шаблон &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt;, поэтому запишем его в &amp;lt;tex&amp;gt;\mathtt{value}&amp;lt;/tex&amp;gt; вершины&amp;lt;/font&amp;gt;&lt;br /&gt;
             '''break'''&lt;br /&gt;
         '''case''' '(' :&lt;br /&gt;
             consume('(')&lt;br /&gt;
             res.addChild(Node(&amp;quot;(&amp;quot;))&lt;br /&gt;
             res.addChild(E())&lt;br /&gt;
             consume(')')&lt;br /&gt;
             res.addChild(Node(&amp;quot;)&amp;quot;))&lt;br /&gt;
         '''default''' :&lt;br /&gt;
             &amp;lt;font color=&amp;quot;red&amp;quot;&amp;gt;error&amp;lt;/font&amp;gt;(&amp;quot;unexpected char&amp;quot;)&lt;br /&gt;
     '''return''' res&lt;br /&gt;
&lt;br /&gt;
Функции для &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt; строятся аналогично.&lt;br /&gt;
&lt;br /&gt;
=== Дерево разбора ===&lt;br /&gt;
&lt;br /&gt;
Рассмотрим дерево разбора для выражения &amp;lt;tex&amp;gt;(1 + 2) * 3&amp;lt;/tex&amp;gt; и несколько первых шагов алгоритма рекурсивного разбора. Сначала вызывается функция стартового нетерминала грамматики, то есть &amp;lt;tex&amp;gt;E&amp;lt;/tex&amp;gt;. Так как первым токеном является '(', то будет использовано первое правило разбора &amp;lt;tex&amp;gt;TE'&amp;lt;/tex&amp;gt;. Поэтому к вершине с меткой &amp;lt;tex&amp;gt;E&amp;lt;/tex&amp;gt; добавятся два ребёнка: &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;E'&amp;lt;/tex&amp;gt;. А рекурсивный разборщик перейдёт к нетерминалу &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt;. По-прежнему curToken равен '(', поэтому в &amp;lt;tex&amp;gt;F&amp;lt;/tex&amp;gt; сработает второй case, первым ребёнком добавится '(', curToken станет равен &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt;, а разборщик перейдёт к нетерминалу &amp;lt;tex&amp;gt;E&amp;lt;/tex&amp;gt;. После того как выражение после '(', которое выводится из &amp;lt;tex&amp;gt;E&amp;lt;/tex&amp;gt;, будет полностью разобрано, функция рекурсивного разбора для &amp;lt;tex&amp;gt;F&amp;lt;/tex&amp;gt; добавит ')' последним сыном к этому нетерминалу. &lt;br /&gt;
&lt;br /&gt;
Продолжая в том же духе, мы построим всё дерево разбора данного выражения.&lt;br /&gt;
&lt;br /&gt;
[[Файл:parse_ex1.png|400px|thumb|center|Дерево разбора выражения &amp;lt;tex&amp;gt;(1 + 2) * 3&amp;lt;/tex&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
== Нерекурсивный нисходящий парсер ==&lt;br /&gt;
&lt;br /&gt;
[[Файл:Parse_table.png|400px|right]]&lt;br /&gt;
&lt;br /&gt;
Рекурсивные разборщики можно генерировать автоматически, зная множества &amp;lt;tex&amp;gt;\mathrm{FIRST}&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\mathrm{FOLLOW}&amp;lt;/tex&amp;gt;, так как они имеют достаточно прозрачный шаблон построения. Альтернативным способом осуществления нисходящего синтаксического анализа является построение нерекурсивного нисходящего парсера. Его можно построить с помощью явного использования [[Стек | стека]] (вместо неявного при рекурсивных вызовах). Такой анализатор имитирует &lt;br /&gt;
[[Контекстно-свободные грамматики, вывод, лево- и правосторонний вывод, дерево разбора#Лево- и правосторонний вывод слова | левое порождение]].  &lt;br /&gt;
&lt;br /&gt;
Стек нерекурсивного предиктивного синтаксического анализатора содержит последовательность терминалов и нетерминалов и таблицу синтаксического анализа. На стеке располагается последовательность символов грамматики с маркером конца разбора &amp;lt;tex&amp;gt;\perp&amp;lt;/tex&amp;gt; на дне. В начале процесса анализа строки стек содержит стартовый нетерминал грамматики непосредственно над символом &amp;lt;tex&amp;gt;\perp&amp;lt;/tex&amp;gt;. Таблица синтаксического анализа представляет собой двухмерный массив &amp;lt;tex&amp;gt;\mathcal{M}[A, c]&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; {{---}} нетерминал или &amp;lt;tex&amp;gt;\perp&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt; c &amp;lt;/tex&amp;gt; {{---}} токен или символ конца строки &amp;lt;tex&amp;gt;\$&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Нерекурсивный синтаксический анализатор смотрит на текущий токен &amp;lt;tex&amp;gt; c &amp;lt;/tex&amp;gt; входного слова и на символ на вершине стека &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt;, а затем принимает решение в зависимости от одного из возникающих ниже случаев:&lt;br /&gt;
* если &amp;lt;tex&amp;gt;A\ =\ \perp\ \land\ c\ =\ \$&amp;lt;/tex&amp;gt;, то синтаксический анализатор прекращает работу, так как разбор строки завершён,&lt;br /&gt;
* eсли &amp;lt;tex&amp;gt;A = c&amp;lt;/tex&amp;gt;, то синтаксический анализатор снимает со стека токен &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; и перемещает указатель текущего токена ленты к следующему токену (то есть вызывает &amp;lt;tex&amp;gt;\mathtt{nextToken}&amp;lt;/tex&amp;gt;), таким образом, происходит выброс символа &amp;lt;tex&amp;gt; c &amp;lt;/tex&amp;gt; со стека,&lt;br /&gt;
* eсли &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; представляет собой нетерминал, то программа рассматривает запись &amp;lt;tex&amp;gt;\mathcal{M}[A,c]&amp;lt;/tex&amp;gt; таблицы разбора &amp;lt;tex&amp;gt;\mathcal{M}&amp;lt;/tex&amp;gt;. Эта запись представляет собой либо продукцию грамматики вида &amp;lt;tex&amp;gt;A \to \alpha_i,\ \alpha_i = X_1 X_2 \ldots X_t&amp;lt;/tex&amp;gt;, и тогда &amp;lt;tex&amp;gt;\mathcal{M}[A,c]&amp;lt;/tex&amp;gt; содержит номер &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; данной продукции, либо запись об ошибке, и тогда &amp;lt;tex&amp;gt;\mathcal{M}[A,c] = \varnothing&amp;lt;/tex&amp;gt;. Если &amp;lt;tex&amp;gt;\mathcal{M}[A,c] \neq \varnothing&amp;lt;/tex&amp;gt;, то синтаксический анализатор замещает на стеке нетерминал &amp;lt;tex&amp;gt; A &amp;lt;/tex&amp;gt; правой частью правила с номером &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt;, помещая символы правила на стек в обратном порядке,&lt;br /&gt;
* во всех остальных случаях парсер бросает сообщение об ошибке.&lt;br /&gt;
&lt;br /&gt;
Рассмотренные случаи отображены коротко на картинке справа, где блок &amp;lt;tex&amp;gt; N &amp;lt;/tex&amp;gt; отвечает нетерминалам грамматики. Из картинки видно, что вместо рассмотрения всех случаев в коде, достаточно просто создать таблицу &amp;lt;tex&amp;gt;\mathcal{M}&amp;lt;/tex&amp;gt; таким образом, чтобы она учитывала все случаи, что упростит код.&lt;br /&gt;
&lt;br /&gt;
=== Псевдокод ===&lt;br /&gt;
&lt;br /&gt;
Здесь по-прежнему &amp;lt;tex&amp;gt;\mathtt{curToken}&amp;lt;/tex&amp;gt; обозначает текущий токен строки, а &amp;lt;tex&amp;gt;\mathtt{nextToken}&amp;lt;/tex&amp;gt; передвигает указатель на следующий токен.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code style = &amp;quot;display: inline-block;&amp;quot;&amp;gt;&lt;br /&gt;
 '''function''' nonRecursiveParser():&lt;br /&gt;
     st : '''Stack'''&lt;br /&gt;
     st.push(&amp;lt;tex&amp;gt;\perp&amp;lt;/tex&amp;gt;)&lt;br /&gt;
     st.push(&amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;) &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// здесь &amp;lt;tex&amp;gt; S &amp;lt;/tex&amp;gt; {{---}} стартовый нетерминал грамматики&amp;lt;/font&amp;gt;&lt;br /&gt;
     '''while''' s.top() &amp;lt;tex&amp;gt;\neq\ \perp&amp;lt;/tex&amp;gt; &lt;br /&gt;
         A = st.top()&lt;br /&gt;
         '''if''' &amp;lt;tex&amp;gt;\mathcal{M}[A,\ curToken]\ ==\ \mathrm{ok}&amp;lt;/tex&amp;gt; &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// &amp;lt;tex&amp;gt; A\ ==\ \perp &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\mathtt{curToken}\ ==\ \$&amp;lt;/tex&amp;gt;&amp;lt;/font&amp;gt;&lt;br /&gt;
             '''break''' &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// разбор строки завершён&amp;lt;/font&amp;gt;&lt;br /&gt;
         '''else if''' &amp;lt;tex&amp;gt;\mathcal{M}[A,\ curToken]\ ==\ \nearrow&amp;lt;/tex&amp;gt; &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// выброс&amp;lt;/font&amp;gt;&lt;br /&gt;
             nextToken()&lt;br /&gt;
             st.pop()&lt;br /&gt;
             вывести в выходной поток нетерминал, отвечающий &amp;lt;tex&amp;gt;\mathtt{curToken}&amp;lt;/tex&amp;gt;&lt;br /&gt;
         '''else if''' &amp;lt;tex&amp;gt;\mathcal{M}[A,\ curToken]&amp;lt;/tex&amp;gt; {{---}} номер правила &amp;lt;tex&amp;gt;A \to \alpha_i,\ \alpha_i = X_1 X_2 \ldots X_t&amp;lt;/tex&amp;gt;&lt;br /&gt;
             st.pop()&lt;br /&gt;
             '''for''' k = t '''downto''' 1&lt;br /&gt;
                 st.push(&amp;lt;tex&amp;gt;X_k&amp;lt;/tex&amp;gt;)    &lt;br /&gt;
             вывести в выходной поток терминал, отвечающий &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt;&lt;br /&gt;
         '''else'''&lt;br /&gt;
             &amp;lt;font color=&amp;quot;red&amp;quot;&amp;gt;error&amp;lt;/font&amp;gt;(&amp;quot;unexpected symbol&amp;quot;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Пример ===&lt;br /&gt;
&lt;br /&gt;
Рассмотрим пример работы нерекурсивного парсера на всё той же грамматике арифметических выражений. Для начала пронумеруем все правила:&lt;br /&gt;
&lt;br /&gt;
{| style=&amp;quot;background-color:#CCC;margin:0.5px&amp;quot;&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| Номер&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| Правило&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px;text-align:center;&amp;quot;| &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 10px&amp;quot;| &amp;lt;tex&amp;gt;E \to TE'&amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px;text-align:center;&amp;quot;| &amp;lt;tex&amp;gt;2&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 10px&amp;quot;| &amp;lt;tex&amp;gt;E' \to +TE'&amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px;text-align:center;&amp;quot;| &amp;lt;tex&amp;gt;3&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 10px&amp;quot;| &amp;lt;tex&amp;gt;E' \to \varepsilon&amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px;text-align:center;&amp;quot;| &amp;lt;tex&amp;gt;4&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 10px&amp;quot;| &amp;lt;tex&amp;gt;T \to FT'&amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px;text-align:center;&amp;quot;| &amp;lt;tex&amp;gt;5&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 10px&amp;quot;| &amp;lt;tex&amp;gt;T' \to * FT'&amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px;text-align:center;&amp;quot;| &amp;lt;tex&amp;gt;6&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 10px&amp;quot;| &amp;lt;tex&amp;gt;T' \to \varepsilon&amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px;text-align:center;&amp;quot;| &amp;lt;tex&amp;gt;7&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 10px&amp;quot;| &amp;lt;tex&amp;gt;F \to n&amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px;text-align:center;&amp;quot;| &amp;lt;tex&amp;gt;8&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 10px&amp;quot;| &amp;lt;tex&amp;gt;F \to (E)&amp;lt;/tex&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Теперь можно построить часть таблицы &amp;lt;tex&amp;gt;\mathcal{M}&amp;lt;/tex&amp;gt;, содержащей строки, отвечающие нетерминалам. Её построение легко осуществить, если известны &amp;lt;tex&amp;gt;\mathrm{FIRST}&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\mathrm{FOLLOW}&amp;lt;/tex&amp;gt;. По сути по этим множествам можно понять, какое правило использовать для данного нетерминала при текущем токене.&lt;br /&gt;
&lt;br /&gt;
{| style=&amp;quot;background-color:#CCC;margin:0.5px&amp;quot;&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| &lt;br /&gt;
!style=&amp;quot;background-color:#EEE;text-align:center;&amp;quot;| &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt;&lt;br /&gt;
!style=&amp;quot;background-color:#EEE;text-align:center;&amp;quot;| &amp;lt;tex&amp;gt;(&amp;lt;/tex&amp;gt;&lt;br /&gt;
!style=&amp;quot;background-color:#EEE;text-align:center;&amp;quot;| &amp;lt;tex&amp;gt;)&amp;lt;/tex&amp;gt;&lt;br /&gt;
!style=&amp;quot;background-color:#EEE;text-align:center;&amp;quot;| &amp;lt;tex&amp;gt;+&amp;lt;/tex&amp;gt;&lt;br /&gt;
!style=&amp;quot;background-color:#EEE;text-align:center;&amp;quot;| &amp;lt;tex&amp;gt;*&amp;lt;/tex&amp;gt;&lt;br /&gt;
!style=&amp;quot;background-color:#EEE;text-align:center;&amp;quot;| &amp;lt;tex&amp;gt;\$&amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px;text-align:center;&amp;quot;| &amp;lt;tex&amp;gt;E&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px;text-align:center;&amp;quot;| &amp;lt;tex&amp;gt;1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px;text-align:center;&amp;quot;| &amp;lt;tex&amp;gt;1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px;text-align:center;&amp;quot;| &amp;lt;tex&amp;gt;E'&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px;text-align:center;&amp;quot;| &amp;lt;tex&amp;gt;3 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px;text-align:center;&amp;quot;| &amp;lt;tex&amp;gt;2 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px;text-align:center;&amp;quot;| &amp;lt;tex&amp;gt;3 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px;text-align:center;&amp;quot;| &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px;text-align:center;&amp;quot;| &amp;lt;tex&amp;gt;4 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px;text-align:center;&amp;quot;| &amp;lt;tex&amp;gt;4 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px;text-align:center;&amp;quot;| &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px;text-align:center;&amp;quot;| &amp;lt;tex&amp;gt;6 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px;text-align:center;&amp;quot;| &amp;lt;tex&amp;gt;6 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px;text-align:center;&amp;quot;| &amp;lt;tex&amp;gt;5 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px;text-align:center;&amp;quot;| &amp;lt;tex&amp;gt;6 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px;text-align:center;&amp;quot;| &amp;lt;tex&amp;gt;F&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px;text-align:center;&amp;quot;| &amp;lt;tex&amp;gt;7 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px;text-align:center;&amp;quot;| &amp;lt;tex&amp;gt;8 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px;text-align:center;&amp;quot;| &amp;lt;tex&amp;gt;\perp&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px;text-align:center;&amp;quot;| &amp;lt;tex&amp;gt;\mathrm{ok}&amp;lt;/tex&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
На картинке ниже показаны состояние стека на нескольких первых итерациях цикла и указатель на текущий токен.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Parse_stack.png|500px]]&lt;br /&gt;
&lt;br /&gt;
== Примечания ==&lt;br /&gt;
&amp;lt;references/&amp;gt;&lt;br /&gt;
== Источники информации ==&lt;br /&gt;
* Альфред Ахо, Рави Сети, Джеффри Ульман. Компиляторы. Принципы, технологии, инструменты. Издательство Вильямс. Второе издание. 2008. Стр. 288 {{---}} 294.&lt;br /&gt;
&lt;br /&gt;
[[Категория: Методы трансляции]]&lt;br /&gt;
[[Категория: Нисходящий разбор]]&lt;/div&gt;</summary>
		<author><name>194.85.160.130</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D1%80%D0%B5%D0%B4%D0%B8%D0%BA%D1%82%D0%B8%D0%B2%D0%BD%D1%8B%D0%B9_%D1%81%D0%B8%D0%BD%D1%82%D0%B0%D0%BA%D1%81%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B8%D0%B9_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=46908</id>
		<title>Предиктивный синтаксический анализ</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D1%80%D0%B5%D0%B4%D0%B8%D0%BA%D1%82%D0%B8%D0%B2%D0%BD%D1%8B%D0%B9_%D1%81%D0%B8%D0%BD%D1%82%D0%B0%D0%BA%D1%81%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B8%D0%B9_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=46908"/>
				<updated>2015-05-25T12:21:42Z</updated>
		
		<summary type="html">&lt;p&gt;194.85.160.130: /* Пример */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{В разработке}}&lt;br /&gt;
Для [[LL(k)-грамматики, множества FIRST и FOLLOW | LL(1)-грамматик]] возможна автоматическая генерация парсеров, если известны множества FIRST и FOLLOW. Существуют общедоступные генераторы: ANTLR&amp;lt;ref&amp;gt;[http://www.antlr.org/ ANTLR {{---}} Parser generator]&amp;lt;/ref&amp;gt;, Bison&amp;lt;ref&amp;gt;[http://www.gnu.org/software/bison/ Bison {{---}} GNU Project]&amp;lt;/ref&amp;gt;, Yacc&amp;lt;ref&amp;gt;[http://dinosaur.compilertools.net/ Lex &amp;amp; Yacc {{---}} A Lexical Analyzer Generator and Yet Another Compiler-Compiler]&amp;lt;/ref&amp;gt;, Happy&amp;lt;ref&amp;gt;[https://www.haskell.org/happy/ Happy {{---}} The Parser Generator for Haskell]&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Общая схема построения рекурсивных парсеров с помощью FIRST и FOLLOW ==&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;tex&amp;gt;\Gamma =\langle \Sigma, N, S, P \rangle&amp;lt;/tex&amp;gt; {{---}} LL(1)-грамматика. Построим для нее парсер.&lt;br /&gt;
&lt;br /&gt;
Для каждого нетерминала &amp;lt;tex&amp;gt;A : A \rightarrow \alpha_1 \mid \alpha_2 \mid \ldots \mid \alpha_k &amp;lt;/tex&amp;gt; создадим функцию &amp;lt;tex&amp;gt; \mathtt{A}() : \mathtt{Node} &amp;lt;/tex&amp;gt;, возвращающую фрагмент дерева разбора, выведенный из нетерминала &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Здесь &amp;lt;tex&amp;gt;\mathtt{Node}&amp;lt;/tex&amp;gt; {{---}} структура следующего вида:&lt;br /&gt;
 '''struct''' Node&lt;br /&gt;
     children : '''list&amp;lt;Node&amp;gt;'''&lt;br /&gt;
     value : '''string'''          &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// имя нетерминала или текст терминала&amp;lt;/font&amp;gt;&lt;br /&gt;
     '''function''' addChild('''Node''') &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// функция, подвешивающая поддерево к данному узлу&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Каждый момент времени парсер работает с определённым '''токеном''' (англ. ''token'') входного слово &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt;. Токен {{---}} один или несколько нетерминалов, для удобства объединяемые по смыслу в одну логическую единицы. Примерами токенов могут выступать следующие лексические единицы:&lt;br /&gt;
* произвольный символ &amp;lt;tex&amp;gt; c &amp;lt;/tex&amp;gt;,&lt;br /&gt;
* целое слово, например &amp;lt;tex&amp;gt; public &amp;lt;/tex&amp;gt;,&lt;br /&gt;
* число со знаком, обозначаемое далее для краткости как &amp;lt;tex&amp;gt; n &amp;lt;/tex&amp;gt;.&lt;br /&gt;
В общем случае, токеном может являться любое слово, удовлетворяющее произвольному [[Регулярные языки: два определения и их эквивалентность | регулярному выражению]].&lt;br /&gt;
&lt;br /&gt;
[[Файл:string_token.png|300px]]&lt;br /&gt;
&lt;br /&gt;
Здесь &amp;lt;tex&amp;gt;\$&amp;lt;/tex&amp;gt; обозначает маркер конца строки.&lt;br /&gt;
&lt;br /&gt;
В псевдокоде используются следующие обозначения:&lt;br /&gt;
* &amp;lt;tex&amp;gt;\mathtt{curToken}&amp;lt;/tex&amp;gt; {{---}} текущий токен строки,&lt;br /&gt;
* &amp;lt;tex&amp;gt;\mathtt{nextToken()}&amp;lt;/tex&amp;gt; {{---}} функция, записывающая в &amp;lt;tex&amp;gt;\mathtt{curToken}&amp;lt;/tex&amp;gt; следующий за ним токен.&lt;br /&gt;
&lt;br /&gt;
Тогда шаблон функции рекурсивного парсера для нетерминала &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; имеет вид:&lt;br /&gt;
&lt;br /&gt;
 A() : '''Node'''&lt;br /&gt;
     Node res = Node(&amp;quot;A&amp;quot;)&lt;br /&gt;
     '''switch''' (curToken) : &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// принимаем решение в зависимости от текущего токена строки&amp;lt;/font&amp;gt;&lt;br /&gt;
         '''case''' &amp;lt;tex&amp;gt;\mathrm{FIRST}(\alpha_1) \cup (\mathrm{FOLLOW}(A)\ \mathrm{if}\ \varepsilon \in \mathrm{FIRST}(\alpha_1))&amp;lt;/tex&amp;gt; :&lt;br /&gt;
             &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// &amp;lt;tex&amp;gt;\alpha_1 = X_1X_2 \ldots X_{t}&amp;lt;/tex&amp;gt; &amp;lt;/font&amp;gt;&lt;br /&gt;
             '''for''' i = 1 .. t&lt;br /&gt;
                 '''if''' &amp;lt;tex&amp;gt; X_i &amp;lt;/tex&amp;gt; {{---}} нетерминал&lt;br /&gt;
                     consume(&amp;lt;tex&amp;gt;X_i&amp;lt;/tex&amp;gt;)&lt;br /&gt;
                     res.addChild(Node(&amp;quot;&amp;lt;tex&amp;gt;X_i&amp;lt;/tex&amp;gt;&amp;quot;)&lt;br /&gt;
                     nextToken()&lt;br /&gt;
                 '''else''' &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// &amp;lt;tex&amp;gt;X_i&amp;lt;/tex&amp;gt; {{---}} терминал, нужно вызвать соответствующую ему функцию рекурсивного парсера &amp;lt;/font&amp;gt;&lt;br /&gt;
                     Node t = &amp;lt;tex&amp;gt;X_i()&amp;lt;/tex&amp;gt;&lt;br /&gt;
                     res.addChild(t)&lt;br /&gt;
             '''break'''&lt;br /&gt;
         '''case''' &amp;lt;tex&amp;gt;\mathrm{FIRST}(\alpha_2) \cup (\mathrm{FOLLOW}(A)\ \mathrm{if}\ \varepsilon \in \mathrm{FIRST}(\alpha_2))&amp;lt;/tex&amp;gt; : &lt;br /&gt;
             &amp;lt;tex&amp;gt;\ldots&amp;lt;/tex&amp;gt;&lt;br /&gt;
             '''break'''&lt;br /&gt;
         &amp;lt;tex&amp;gt;\ldots&amp;lt;/tex&amp;gt;&lt;br /&gt;
         '''default''' :&lt;br /&gt;
             &amp;lt;font color=&amp;quot;red&amp;quot;&amp;gt;error&amp;lt;/font&amp;gt;(&amp;quot;unexpected char&amp;quot;)&lt;br /&gt;
     '''return''' res&lt;br /&gt;
&lt;br /&gt;
 '''function''' consume(c: '''char''') &lt;br /&gt;
     '''if''' curToken != c&lt;br /&gt;
         &amp;lt;font color=&amp;quot;red&amp;quot;&amp;gt;error&amp;lt;/font&amp;gt;(&amp;quot;expected &amp;quot; + c)&lt;br /&gt;
     nextToken()&lt;br /&gt;
&lt;br /&gt;
Такой парсер не только разбирает строку, но и находит ошибки в неудовлетворяющих грамматике выражениях.&lt;br /&gt;
&lt;br /&gt;
== Пример ==&lt;br /&gt;
Рассмотрим построение парсера на примере LL(1)-грамматики арифметических выражений, которая уже была разобрана [[Построение FIRST и FOLLOW#Пример | ранее]]:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt; &lt;br /&gt;
E \to TE' \\&lt;br /&gt;
E' \to +TE' \mid \varepsilon \\&lt;br /&gt;
T \to FT' \\&lt;br /&gt;
T' \to * FT' \mid \varepsilon \\&lt;br /&gt;
F \to n \mid (E)&lt;br /&gt;
&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Напомним, что множества &amp;lt;tex&amp;gt;\mathrm{FIRST}&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\mathrm{FOLLOW}&amp;lt;/tex&amp;gt; для неё выглядят так:&lt;br /&gt;
&lt;br /&gt;
{| style=&amp;quot;background-color:#CCC;margin:0.5px&amp;quot;&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| Правило&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| FIRST&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| FOLLOW&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;E&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ n,\ (\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ \$,\ )\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;E'&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ +,\ \varepsilon\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ \$,\ )\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ n,\ (\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ +,\ \$\ ,\ )\ \}&amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ *,\ \varepsilon\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ +,\ \$\ ,\ )\ \}&amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;F&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ n,\ (\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ *, \ +,\ \$\ ,\ )\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Псевдокоды ===&lt;br /&gt;
Построим функции обработки некоторых нетерминалов, используя описанный выше шаблон:&lt;br /&gt;
&lt;br /&gt;
 E() : '''Node'''&lt;br /&gt;
     Node res = Node(&amp;quot;E&amp;quot;)&lt;br /&gt;
     '''switch''' (curToken)&lt;br /&gt;
         '''case''' n, '('  :&lt;br /&gt;
             res.addChild(T())&lt;br /&gt;
             res.addChild(E'())&lt;br /&gt;
             '''break'''&lt;br /&gt;
         '''default''' :&lt;br /&gt;
             &amp;lt;font color=&amp;quot;red&amp;quot;&amp;gt;error&amp;lt;/font&amp;gt;(&amp;quot;unexpected char&amp;quot;)&lt;br /&gt;
     '''return''' res&lt;br /&gt;
&lt;br /&gt;
 E'() : '''Node'''&lt;br /&gt;
     Node res = Node(&amp;quot;E'&amp;quot;)&lt;br /&gt;
     '''switch''' (curToken) &lt;br /&gt;
         '''case''' '+' :&lt;br /&gt;
             consume('+')&lt;br /&gt;
             res.addChild(Node(&amp;quot;+&amp;quot;))&lt;br /&gt;
             res.addChild(T())&lt;br /&gt;
             res.addChild(E'())&lt;br /&gt;
             '''break'''&lt;br /&gt;
         '''case''' '$', ')' :&lt;br /&gt;
             '''break'''&lt;br /&gt;
         '''default''' :&lt;br /&gt;
             &amp;lt;font color=&amp;quot;red&amp;quot;&amp;gt;error&amp;lt;/font&amp;gt;(&amp;quot;unexpected char&amp;quot;)&lt;br /&gt;
      '''return''' res&lt;br /&gt;
&lt;br /&gt;
 F() : '''Node'''&lt;br /&gt;
     Node res = Node(&amp;quot;F&amp;quot;)&lt;br /&gt;
     '''switch''' (curToken)&lt;br /&gt;
         '''case''' n :&lt;br /&gt;
             consume(n)&lt;br /&gt;
             res.addChild(Node(curToken)) &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// &amp;lt;tex&amp;gt;\mathtt{curToken}&amp;lt;/tex&amp;gt; подпадает под шаблон &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt;, поэтому запишем его в &amp;lt;tex&amp;gt;\mathtt{value}&amp;lt;/tex&amp;gt; вершины&amp;lt;/font&amp;gt;&lt;br /&gt;
             '''break'''&lt;br /&gt;
         '''case''' '(' :&lt;br /&gt;
             consume('(')&lt;br /&gt;
             res.addChild(Node(&amp;quot;(&amp;quot;))&lt;br /&gt;
             res.addChild(E())&lt;br /&gt;
             consume(')')&lt;br /&gt;
             res.addChild(Node(&amp;quot;)&amp;quot;))&lt;br /&gt;
         '''default''' :&lt;br /&gt;
             &amp;lt;font color=&amp;quot;red&amp;quot;&amp;gt;error&amp;lt;/font&amp;gt;(&amp;quot;unexpected char&amp;quot;)&lt;br /&gt;
     '''return''' res&lt;br /&gt;
&lt;br /&gt;
Функции для &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt; строятся аналогично.&lt;br /&gt;
&lt;br /&gt;
=== Дерево разбора ===&lt;br /&gt;
&lt;br /&gt;
Рассмотрим дерево разбора для выражения &amp;lt;tex&amp;gt;(1 + 2) * 3&amp;lt;/tex&amp;gt; и несколько первых шагов алгоритма рекурсивного разбора. Сначала вызывается функция стартового нетерминала грамматики, то есть &amp;lt;tex&amp;gt;E&amp;lt;/tex&amp;gt;. Так как первым токеном является '(', то будет использовано первое правило разбора &amp;lt;tex&amp;gt;TE'&amp;lt;/tex&amp;gt;. Поэтому к вершине с меткой &amp;lt;tex&amp;gt;E&amp;lt;/tex&amp;gt; добавятся два ребёнка: &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;E'&amp;lt;/tex&amp;gt;. А рекурсивный разборщик перейдёт к нетерминалу &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt;. По-прежнему curToken равен '(', поэтому в &amp;lt;tex&amp;gt;F&amp;lt;/tex&amp;gt; сработает второй case, первым ребёнком добавится '(', curToken станет равен &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt;, а разборщик перейдёт к нетерминалу &amp;lt;tex&amp;gt;E&amp;lt;/tex&amp;gt;. После того как выражение после '(', которое выводится из &amp;lt;tex&amp;gt;E&amp;lt;/tex&amp;gt;, будет полностью разобрано, функция рекурсивного разбора для &amp;lt;tex&amp;gt;F&amp;lt;/tex&amp;gt; добавит ')' последним сыном к этому нетерминалу. &lt;br /&gt;
&lt;br /&gt;
Продолжая в том же духе, мы построим всё дерево разбора данного выражения.&lt;br /&gt;
&lt;br /&gt;
[[Файл:parse_ex1.png|400px|thumb|center|Дерево разбора выражения &amp;lt;tex&amp;gt;(1 + 2) * 3&amp;lt;/tex&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
== Нерекурсивный нисходящий парсер ==&lt;br /&gt;
&lt;br /&gt;
[[Файл:Parse_table.png|400px|right]]&lt;br /&gt;
&lt;br /&gt;
Рекурсивные разборщики можно генерировать автоматически, зная множества &amp;lt;tex&amp;gt;\mathrm{FIRST}&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\mathrm{FOLLOW}&amp;lt;/tex&amp;gt;, так как они имеют достаточно прозрачный шаблон построения. Альтернативным способом осуществления нисходящего синтаксического анализа является построение нерекурсивного нисходящего парсера. Его можно построить с помощью явного использования [[Стек | стека]] (вместо неявного при рекурсивных вызовах). Такой анализатор имитирует &lt;br /&gt;
[[Контекстно-свободные грамматики, вывод, лево- и правосторонний вывод, дерево разбора#Лево- и правосторонний вывод слова | левое порождение]].  &lt;br /&gt;
&lt;br /&gt;
Стек нерекурсивного предиктивного синтаксического анализатора содержит последовательность терминалов и нетерминалов и таблицу синтаксического анализа. На стеке располагается последовательность символов грамматики с маркером конца разбора &amp;lt;tex&amp;gt;\perp&amp;lt;/tex&amp;gt; на дне. В начале процесса анализа строки стек содержит стартовый нетерминал грамматики непосредственно над символом &amp;lt;tex&amp;gt;\perp&amp;lt;/tex&amp;gt;. Таблица синтаксического анализа представляет собой двухмерный массив &amp;lt;tex&amp;gt;\mathcal{M}[A, c]&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; {{---}} нетерминал или &amp;lt;tex&amp;gt;\perp&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt; c &amp;lt;/tex&amp;gt; {{---}} токен или символ конца строки &amp;lt;tex&amp;gt;\$&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Нерекурсивный синтаксический анализатор смотрит на текущий токен &amp;lt;tex&amp;gt; c &amp;lt;/tex&amp;gt; входного слова и на символ на вершине стека &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt;, а затем принимает решение в зависимости от одного из возникающих ниже случаев:&lt;br /&gt;
* если &amp;lt;tex&amp;gt;A\ =\ \perp\ \land\ c\ =\ \$&amp;lt;/tex&amp;gt;, то синтаксический анализатор прекращает работу, так как разбор строки завершён,&lt;br /&gt;
* eсли &amp;lt;tex&amp;gt;A = c&amp;lt;/tex&amp;gt;, то синтаксический анализатор снимает со стека токен &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; и перемещает указатель текущего токена ленты к следующему токену (то есть вызывает &amp;lt;tex&amp;gt;\mathtt{nextToken}&amp;lt;/tex&amp;gt;), таким образом, происходит выброс символа &amp;lt;tex&amp;gt; c &amp;lt;/tex&amp;gt; со стека,&lt;br /&gt;
* eсли &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; представляет собой нетерминал, то программа рассматривает запись &amp;lt;tex&amp;gt;\mathcal{M}[A,c]&amp;lt;/tex&amp;gt; таблицы разбора &amp;lt;tex&amp;gt;\mathcal{M}&amp;lt;/tex&amp;gt;. Эта запись представляет собой либо продукцию грамматики вида &amp;lt;tex&amp;gt;A \to \alpha_i,\ \alpha_i = X_1 X_2 \ldots X_t&amp;lt;/tex&amp;gt;, и тогда &amp;lt;tex&amp;gt;\mathcal{M}[A,c]&amp;lt;/tex&amp;gt; содержит номер &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; данной продукции, либо запись об ошибке, и тогда &amp;lt;tex&amp;gt;\mathcal{M}[A,c] = \varnothing&amp;lt;/tex&amp;gt;. Если &amp;lt;tex&amp;gt;\mathcal{M}[A,c] \neq \varnothing&amp;lt;/tex&amp;gt;, то синтаксический анализатор замещает на стеке нетерминал &amp;lt;tex&amp;gt; A &amp;lt;/tex&amp;gt; правой частью правила с номером &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt;, помещая символы правила на стек в обратном порядке,&lt;br /&gt;
* во всех остальных случаях парсер бросает сообщение об ошибке.&lt;br /&gt;
&lt;br /&gt;
Рассмотренные случаи отображены коротко на картинке справа, где блок &amp;lt;tex&amp;gt; N &amp;lt;/tex&amp;gt; отвечает нетерминалам грамматики. Из картинки видно, что вместо рассмотрения всех случаев в коде, достаточно просто создать таблицу &amp;lt;tex&amp;gt;\mathcal{M}&amp;lt;/tex&amp;gt; таким образом, чтобы она учитывала все случаи, что упростит код.&lt;br /&gt;
&lt;br /&gt;
=== Псевдокод ===&lt;br /&gt;
&lt;br /&gt;
Здесь по-прежнему &amp;lt;tex&amp;gt;\mathtt{curToken}&amp;lt;/tex&amp;gt; обозначает текущий токен строки, а &amp;lt;tex&amp;gt;\mathtt{nextToken}&amp;lt;/tex&amp;gt; передвигает указатель на следующий токен.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code style = &amp;quot;display: inline-block;&amp;quot;&amp;gt;&lt;br /&gt;
 '''function''' nonRecursiveParser():&lt;br /&gt;
     st : '''Stack'''&lt;br /&gt;
     st.push(&amp;lt;tex&amp;gt;\perp&amp;lt;/tex&amp;gt;)&lt;br /&gt;
     st.push(&amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;) &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// здесь &amp;lt;tex&amp;gt; S &amp;lt;/tex&amp;gt; {{---}} стартовый нетерминал грамматики&amp;lt;/font&amp;gt;&lt;br /&gt;
     '''while''' s.top() &amp;lt;tex&amp;gt;\neq\ \perp&amp;lt;/tex&amp;gt; &lt;br /&gt;
         A = st.top()&lt;br /&gt;
         '''if''' &amp;lt;tex&amp;gt;\mathcal{M}[A,\ curToken]\ ==\ \mathrm{ok}&amp;lt;/tex&amp;gt; &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// &amp;lt;tex&amp;gt; A\ ==\ \perp &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\mathtt{curToken}\ ==\ \$&amp;lt;/tex&amp;gt;&amp;lt;/font&amp;gt;&lt;br /&gt;
             '''break''' &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// разбор строки завершён&amp;lt;/font&amp;gt;&lt;br /&gt;
         '''else if''' &amp;lt;tex&amp;gt;\mathcal{M}[A,\ curToken]\ ==\ \nearrow&amp;lt;/tex&amp;gt; &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// выброс&amp;lt;/font&amp;gt;&lt;br /&gt;
             nextToken()&lt;br /&gt;
             st.pop()&lt;br /&gt;
             вывести в выходной поток нетерминал, отвечающий &amp;lt;tex&amp;gt;\mathtt{curToken}&amp;lt;/tex&amp;gt;&lt;br /&gt;
         '''else if''' &amp;lt;tex&amp;gt;\mathcal{M}[A,\ curToken]&amp;lt;/tex&amp;gt; {{---}} номер правила &amp;lt;tex&amp;gt;A \to \alpha_i,\ \alpha_i = X_1 X_2 \ldots X_t&amp;lt;/tex&amp;gt;&lt;br /&gt;
             st.pop()&lt;br /&gt;
             '''for''' k = t '''downto''' 1&lt;br /&gt;
                 st.push(&amp;lt;tex&amp;gt;X_k&amp;lt;/tex&amp;gt;)    &lt;br /&gt;
             вывести в выходной поток терминал, отвечающий &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt;&lt;br /&gt;
         '''else'''&lt;br /&gt;
             &amp;lt;font color=&amp;quot;red&amp;quot;&amp;gt;error&amp;lt;/font&amp;gt;(&amp;quot;unexpected symbol&amp;quot;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Пример ===&lt;br /&gt;
&lt;br /&gt;
Рассмотрим пример работы нерекурсивного парсера на всё той же грамматике арифметических выражений. Для начала пронумеруем все правила:&lt;br /&gt;
&lt;br /&gt;
{| style=&amp;quot;background-color:#CCC;margin:0.5px&amp;quot;&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| Номер&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| Правило&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px;text-align:center;&amp;quot;| &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 10px&amp;quot;| &amp;lt;tex&amp;gt;E \to TE'&amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px;text-align:center;&amp;quot;| &amp;lt;tex&amp;gt;2&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 10px&amp;quot;| &amp;lt;tex&amp;gt;E' \to +TE'&amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px;text-align:center;&amp;quot;| &amp;lt;tex&amp;gt;3&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 10px&amp;quot;| &amp;lt;tex&amp;gt;E' \to \varepsilon&amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px;text-align:center;&amp;quot;| &amp;lt;tex&amp;gt;4&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 10px&amp;quot;| &amp;lt;tex&amp;gt;T \to FT'&amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px;text-align:center;&amp;quot;| &amp;lt;tex&amp;gt;5&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 10px&amp;quot;| &amp;lt;tex&amp;gt;T' \to * FT'&amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px;text-align:center;&amp;quot;| &amp;lt;tex&amp;gt;6&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 10px&amp;quot;| &amp;lt;tex&amp;gt;T' \to \varepsilon&amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px;text-align:center;&amp;quot;| &amp;lt;tex&amp;gt;7&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 10px&amp;quot;| &amp;lt;tex&amp;gt;F \to n&amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px;text-align:center;&amp;quot;| &amp;lt;tex&amp;gt;8&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 10px&amp;quot;| &amp;lt;tex&amp;gt;F \to (E)&amp;lt;/tex&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Теперь можно построить часть таблицы &amp;lt;tex&amp;gt;\mathcal{M}&amp;lt;/tex&amp;gt;, содержащей строки, отвечающие нетерминалам. Её построение легко осуществить, если известны &amp;lt;tex&amp;gt;\mathrm{FIRST}&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\mathrm{FOLLOW}&amp;lt;/tex&amp;gt;. По сути по этим множествам можно понять, какое правило использовать для данного нетерминала при текущем токене.&lt;br /&gt;
&lt;br /&gt;
{| style=&amp;quot;background-color:#CCC;margin:0.5px&amp;quot;&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| &lt;br /&gt;
!style=&amp;quot;background-color:#EEE;text-align:center;&amp;quot;| &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt;&lt;br /&gt;
!style=&amp;quot;background-color:#EEE;text-align:center;&amp;quot;| &amp;lt;tex&amp;gt;(&amp;lt;/tex&amp;gt;&lt;br /&gt;
!style=&amp;quot;background-color:#EEE;text-align:center;&amp;quot;| &amp;lt;tex&amp;gt;)&amp;lt;/tex&amp;gt;&lt;br /&gt;
!style=&amp;quot;background-color:#EEE;text-align:center;&amp;quot;| &amp;lt;tex&amp;gt;+&amp;lt;/tex&amp;gt;&lt;br /&gt;
!style=&amp;quot;background-color:#EEE;text-align:center;&amp;quot;| &amp;lt;tex&amp;gt;*&amp;lt;/tex&amp;gt;&lt;br /&gt;
!style=&amp;quot;background-color:#EEE;text-align:center;&amp;quot;| &amp;lt;tex&amp;gt;\$&amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px;text-align:center;&amp;quot;| &amp;lt;tex&amp;gt;E&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px;text-align:center;&amp;quot;| &amp;lt;tex&amp;gt;1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px;text-align:center;&amp;quot;| &amp;lt;tex&amp;gt;1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px;text-align:center;&amp;quot;| &amp;lt;tex&amp;gt;E'&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px;text-align:center;&amp;quot;| &amp;lt;tex&amp;gt;3 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px;text-align:center;&amp;quot;| &amp;lt;tex&amp;gt;2 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px;text-align:center;&amp;quot;| &amp;lt;tex&amp;gt;3 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px;text-align:center;&amp;quot;| &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px;text-align:center;&amp;quot;| &amp;lt;tex&amp;gt;4 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px;text-align:center;&amp;quot;| &amp;lt;tex&amp;gt;4 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px;text-align:center;&amp;quot;| &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px;text-align:center;&amp;quot;| &amp;lt;tex&amp;gt;6 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px;text-align:center;&amp;quot;| &amp;lt;tex&amp;gt;6 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px;text-align:center;&amp;quot;| &amp;lt;tex&amp;gt;5 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px;text-align:center;&amp;quot;| &amp;lt;tex&amp;gt;6 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px;text-align:center;&amp;quot;| &amp;lt;tex&amp;gt;F&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px;text-align:center;&amp;quot;| &amp;lt;tex&amp;gt;7 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px;text-align:center;&amp;quot;| &amp;lt;tex&amp;gt;8 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px;text-align:center;&amp;quot;| &amp;lt;tex&amp;gt;\perp&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px;text-align:center;&amp;quot;| &amp;lt;tex&amp;gt;\mathrm{ok}&amp;lt;/tex&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
На картинке ниже показаны состояние стека на нескольких первых итерациях цикла и указатель на текущий токен.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Parse_stack.png|500px]]&lt;br /&gt;
&lt;br /&gt;
== Примечания ==&lt;br /&gt;
&amp;lt;references/&amp;gt;&lt;br /&gt;
== Источники информации ==&lt;br /&gt;
* Альфред Ахо, Рави Сети, Джеффри Ульман. Компиляторы. Принципы, технологии, инструменты. Издательство Вильямс. Второе издание. 2008. Стр. 288 {{---}} 294.&lt;br /&gt;
&lt;br /&gt;
[[Категория: Методы трансляции]]&lt;br /&gt;
[[Категория: Нисходящий разбор]]&lt;/div&gt;</summary>
		<author><name>194.85.160.130</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D1%80%D0%B5%D0%B4%D0%B8%D0%BA%D1%82%D0%B8%D0%B2%D0%BD%D1%8B%D0%B9_%D1%81%D0%B8%D0%BD%D1%82%D0%B0%D0%BA%D1%81%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B8%D0%B9_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=46907</id>
		<title>Предиктивный синтаксический анализ</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D1%80%D0%B5%D0%B4%D0%B8%D0%BA%D1%82%D0%B8%D0%B2%D0%BD%D1%8B%D0%B9_%D1%81%D0%B8%D0%BD%D1%82%D0%B0%D0%BA%D1%81%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B8%D0%B9_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=46907"/>
				<updated>2015-05-25T12:06:29Z</updated>
		
		<summary type="html">&lt;p&gt;194.85.160.130: /* Общая схема построения рекурсивных парсеров с помощью FIRST и FOLLOW */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{В разработке}}&lt;br /&gt;
Для [[LL(k)-грамматики, множества FIRST и FOLLOW | LL(1)-грамматик]] возможна автоматическая генерация парсеров, если известны множества FIRST и FOLLOW. Существуют общедоступные генераторы: ANTLR&amp;lt;ref&amp;gt;[http://www.antlr.org/ ANTLR {{---}} Parser generator]&amp;lt;/ref&amp;gt;, Bison&amp;lt;ref&amp;gt;[http://www.gnu.org/software/bison/ Bison {{---}} GNU Project]&amp;lt;/ref&amp;gt;, Yacc&amp;lt;ref&amp;gt;[http://dinosaur.compilertools.net/ Lex &amp;amp; Yacc {{---}} A Lexical Analyzer Generator and Yet Another Compiler-Compiler]&amp;lt;/ref&amp;gt;, Happy&amp;lt;ref&amp;gt;[https://www.haskell.org/happy/ Happy {{---}} The Parser Generator for Haskell]&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Общая схема построения рекурсивных парсеров с помощью FIRST и FOLLOW ==&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;tex&amp;gt;\Gamma =\langle \Sigma, N, S, P \rangle&amp;lt;/tex&amp;gt; {{---}} LL(1)-грамматика. Построим для нее парсер.&lt;br /&gt;
&lt;br /&gt;
Для каждого нетерминала &amp;lt;tex&amp;gt;A : A \rightarrow \alpha_1 \mid \alpha_2 \mid \ldots \mid \alpha_k &amp;lt;/tex&amp;gt; создадим функцию &amp;lt;tex&amp;gt; \mathtt{A}() : \mathtt{Node} &amp;lt;/tex&amp;gt;, возвращающую фрагмент дерева разбора, выведенный из нетерминала &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Здесь &amp;lt;tex&amp;gt;\mathtt{Node}&amp;lt;/tex&amp;gt; {{---}} структура следующего вида:&lt;br /&gt;
 '''struct''' Node&lt;br /&gt;
     children : '''list&amp;lt;Node&amp;gt;'''&lt;br /&gt;
     value : '''string'''          &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// имя нетерминала или текст терминала&amp;lt;/font&amp;gt;&lt;br /&gt;
     '''function''' addChild('''Node''') &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// функция, подвешивающая поддерево к данному узлу&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Каждый момент времени парсер работает с определённым '''токеном''' (англ. ''token'') входного слово &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt;. Токен {{---}} один или несколько нетерминалов, для удобства объединяемые по смыслу в одну логическую единицы. Примерами токенов могут выступать следующие лексические единицы:&lt;br /&gt;
* произвольный символ &amp;lt;tex&amp;gt; c &amp;lt;/tex&amp;gt;,&lt;br /&gt;
* целое слово, например &amp;lt;tex&amp;gt; public &amp;lt;/tex&amp;gt;,&lt;br /&gt;
* число со знаком, обозначаемое далее для краткости как &amp;lt;tex&amp;gt; n &amp;lt;/tex&amp;gt;.&lt;br /&gt;
В общем случае, токеном может являться любое слово, удовлетворяющее произвольному [[Регулярные языки: два определения и их эквивалентность | регулярному выражению]].&lt;br /&gt;
&lt;br /&gt;
[[Файл:string_token.png|300px]]&lt;br /&gt;
&lt;br /&gt;
Здесь &amp;lt;tex&amp;gt;\$&amp;lt;/tex&amp;gt; обозначает маркер конца строки.&lt;br /&gt;
&lt;br /&gt;
В псевдокоде используются следующие обозначения:&lt;br /&gt;
* &amp;lt;tex&amp;gt;\mathtt{curToken}&amp;lt;/tex&amp;gt; {{---}} текущий токен строки,&lt;br /&gt;
* &amp;lt;tex&amp;gt;\mathtt{nextToken()}&amp;lt;/tex&amp;gt; {{---}} функция, записывающая в &amp;lt;tex&amp;gt;\mathtt{curToken}&amp;lt;/tex&amp;gt; следующий за ним токен.&lt;br /&gt;
&lt;br /&gt;
Тогда шаблон функции рекурсивного парсера для нетерминала &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; имеет вид:&lt;br /&gt;
&lt;br /&gt;
 A() : '''Node'''&lt;br /&gt;
     Node res = Node(&amp;quot;A&amp;quot;)&lt;br /&gt;
     '''switch''' (curToken) : &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// принимаем решение в зависимости от текущего токена строки&amp;lt;/font&amp;gt;&lt;br /&gt;
         '''case''' &amp;lt;tex&amp;gt;\mathrm{FIRST}(\alpha_1) \cup (\mathrm{FOLLOW}(A)\ \mathrm{if}\ \varepsilon \in \mathrm{FIRST}(\alpha_1))&amp;lt;/tex&amp;gt; :&lt;br /&gt;
             &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// &amp;lt;tex&amp;gt;\alpha_1 = X_1X_2 \ldots X_{t}&amp;lt;/tex&amp;gt; &amp;lt;/font&amp;gt;&lt;br /&gt;
             '''for''' i = 1 .. t&lt;br /&gt;
                 '''if''' &amp;lt;tex&amp;gt; X_i &amp;lt;/tex&amp;gt; {{---}} нетерминал&lt;br /&gt;
                     consume(&amp;lt;tex&amp;gt;X_i&amp;lt;/tex&amp;gt;)&lt;br /&gt;
                     res.addChild(Node(&amp;quot;&amp;lt;tex&amp;gt;X_i&amp;lt;/tex&amp;gt;&amp;quot;)&lt;br /&gt;
                     nextToken()&lt;br /&gt;
                 '''else''' &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// &amp;lt;tex&amp;gt;X_i&amp;lt;/tex&amp;gt; {{---}} терминал, нужно вызвать соответствующую ему функцию рекурсивного парсера &amp;lt;/font&amp;gt;&lt;br /&gt;
                     Node t = &amp;lt;tex&amp;gt;X_i()&amp;lt;/tex&amp;gt;&lt;br /&gt;
                     res.addChild(t)&lt;br /&gt;
             '''break'''&lt;br /&gt;
         '''case''' &amp;lt;tex&amp;gt;\mathrm{FIRST}(\alpha_2) \cup (\mathrm{FOLLOW}(A)\ \mathrm{if}\ \varepsilon \in \mathrm{FIRST}(\alpha_2))&amp;lt;/tex&amp;gt; : &lt;br /&gt;
             &amp;lt;tex&amp;gt;\ldots&amp;lt;/tex&amp;gt;&lt;br /&gt;
             '''break'''&lt;br /&gt;
         &amp;lt;tex&amp;gt;\ldots&amp;lt;/tex&amp;gt;&lt;br /&gt;
         '''default''' :&lt;br /&gt;
             &amp;lt;font color=&amp;quot;red&amp;quot;&amp;gt;error&amp;lt;/font&amp;gt;(&amp;quot;unexpected char&amp;quot;)&lt;br /&gt;
     '''return''' res&lt;br /&gt;
&lt;br /&gt;
 '''function''' consume(c: '''char''') &lt;br /&gt;
     '''if''' curToken != c&lt;br /&gt;
         &amp;lt;font color=&amp;quot;red&amp;quot;&amp;gt;error&amp;lt;/font&amp;gt;(&amp;quot;expected &amp;quot; + c)&lt;br /&gt;
     nextToken()&lt;br /&gt;
&lt;br /&gt;
Такой парсер не только разбирает строку, но и находит ошибки в неудовлетворяющих грамматике выражениях.&lt;br /&gt;
&lt;br /&gt;
== Пример ==&lt;br /&gt;
Рассмотрим построение парсера на примере LL(1)-грамматики арифметических выражений, которая уже была разобрана [[Построение FIRST и FOLLOW#Пример | ранее]]:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt; &lt;br /&gt;
E \to TE' \\&lt;br /&gt;
E' \to +TE' \mid \varepsilon \\&lt;br /&gt;
T \to FT' \\&lt;br /&gt;
T' \to * FT' \mid \varepsilon \\&lt;br /&gt;
F \to n \mid (E)&lt;br /&gt;
&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Напомним, что множества &amp;lt;tex&amp;gt;\mathrm{FIRST}&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\mathrm{FOLLOW}&amp;lt;/tex&amp;gt; для неё выглядят так:&lt;br /&gt;
&lt;br /&gt;
{| style=&amp;quot;background-color:#CCC;margin:0.5px&amp;quot;&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| Правило&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| FIRST&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| FOLLOW&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;E&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ n,\ (\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ \$,\ )\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;E'&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ +,\ \varepsilon\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ \$,\ )\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ n,\ (\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ +,\ \$\ ,\ )\ \}&amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ *,\ \varepsilon\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ +,\ \$\ ,\ )\ \}&amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;F&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ n,\ (\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ *, \ +,\ \$\ ,\ )\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Псевдокоды ===&lt;br /&gt;
Построим функции обработки некоторых нетерминалов, используя описанный выше шаблон:&lt;br /&gt;
&lt;br /&gt;
 E() : '''Node'''&lt;br /&gt;
     Node res = Node(&amp;quot;E&amp;quot;)&lt;br /&gt;
     '''switch''' (curToken)&lt;br /&gt;
         '''case''' n, '('  :&lt;br /&gt;
             res.addChild(T())&lt;br /&gt;
             res.addChild(E'())&lt;br /&gt;
             '''break'''&lt;br /&gt;
         '''default''' :&lt;br /&gt;
             &amp;lt;font color=&amp;quot;red&amp;quot;&amp;gt;error&amp;lt;/font&amp;gt;(&amp;quot;unexpected char&amp;quot;)&lt;br /&gt;
     '''return''' res&lt;br /&gt;
&lt;br /&gt;
 E'() : '''Node'''&lt;br /&gt;
     Node res = Node(&amp;quot;E'&amp;quot;)&lt;br /&gt;
     '''switch''' (curToken) &lt;br /&gt;
         '''case''' '+' :&lt;br /&gt;
             consume('+')&lt;br /&gt;
             res.addChild(Node(&amp;quot;+&amp;quot;))&lt;br /&gt;
             res.addChild(T())&lt;br /&gt;
             res.addChild(E'())&lt;br /&gt;
             '''break'''&lt;br /&gt;
         '''case''' '$', ')' :&lt;br /&gt;
             '''break'''&lt;br /&gt;
         '''default''' :&lt;br /&gt;
             &amp;lt;font color=&amp;quot;red&amp;quot;&amp;gt;error&amp;lt;/font&amp;gt;(&amp;quot;unexpected char&amp;quot;)&lt;br /&gt;
      '''return''' res&lt;br /&gt;
&lt;br /&gt;
 F() : '''Node'''&lt;br /&gt;
     Node res = Node(&amp;quot;F&amp;quot;)&lt;br /&gt;
     '''switch''' (curToken)&lt;br /&gt;
         '''case''' n :&lt;br /&gt;
             consume(n)&lt;br /&gt;
             res.addChild(Node(curToken)) &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// &amp;lt;tex&amp;gt;\mathtt{curToken}&amp;lt;/tex&amp;gt; подпадает под шаблон &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt;, поэтому запишем его в &amp;lt;tex&amp;gt;\mathtt{value}&amp;lt;/tex&amp;gt; вершины&amp;lt;/font&amp;gt;&lt;br /&gt;
             '''break'''&lt;br /&gt;
         '''case''' '(' :&lt;br /&gt;
             consume('(')&lt;br /&gt;
             res.addChild(Node(&amp;quot;(&amp;quot;))&lt;br /&gt;
             res.addChild(E())&lt;br /&gt;
             consume(')')&lt;br /&gt;
             res.addChild(Node(&amp;quot;)&amp;quot;))&lt;br /&gt;
         '''default''' :&lt;br /&gt;
             &amp;lt;font color=&amp;quot;red&amp;quot;&amp;gt;error&amp;lt;/font&amp;gt;(&amp;quot;unexpected char&amp;quot;)&lt;br /&gt;
     '''return''' res&lt;br /&gt;
&lt;br /&gt;
Функции для &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt; строятся аналогично.&lt;br /&gt;
&lt;br /&gt;
=== Дерево разбора ===&lt;br /&gt;
&lt;br /&gt;
Рассмотрим дерево разбора для выражения &amp;lt;tex&amp;gt;(1 + 2) * 3&amp;lt;/tex&amp;gt; и несколько первых шагов алгоритма рекурсивного разбора. Сначала вызывается функция стартового нетерминала грамматики, то есть &amp;lt;tex&amp;gt;E&amp;lt;/tex&amp;gt;. Так как первым токеном является '(', то будет использовано первое правило разбора &amp;lt;tex&amp;gt;TE'&amp;lt;/tex&amp;gt;. Поэтому к вершине с меткой &amp;lt;tex&amp;gt;E&amp;lt;/tex&amp;gt; добавятся два ребёнка: &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;E'&amp;lt;/tex&amp;gt;. А рекурсивный разборщик перейдёт к нетерминалу &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt;. По-прежнему curToken равен '(', поэтому в &amp;lt;tex&amp;gt;F&amp;lt;/tex&amp;gt; сработает второй case, первым ребёнком добавится '(', curToken станет равен &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt;, а разборщик перейдёт к нетерминалу &amp;lt;tex&amp;gt;E&amp;lt;/tex&amp;gt;. После того как выражение после '(', которое выводится из &amp;lt;tex&amp;gt;E&amp;lt;/tex&amp;gt;, будет полностью разобрано, функция рекурсивного разбора для &amp;lt;tex&amp;gt;F&amp;lt;/tex&amp;gt; добавит ')' последним сыном к этому нетерминалу. &lt;br /&gt;
&lt;br /&gt;
Продолжая в том же духе, мы построим всё дерево разбора данного выражения.&lt;br /&gt;
&lt;br /&gt;
[[Файл:parse_ex1.png|400px|thumb|center|Дерево разбора выражения &amp;lt;tex&amp;gt;(1 + 2) * 3&amp;lt;/tex&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
== Нерекурсивный нисходящий парсер ==&lt;br /&gt;
&lt;br /&gt;
[[Файл:Parse_table.png|400px|right]]&lt;br /&gt;
&lt;br /&gt;
Рекурсивные разборщики можно генерировать автоматически, зная множества &amp;lt;tex&amp;gt;\mathrm{FIRST}&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\mathrm{FOLLOW}&amp;lt;/tex&amp;gt;, так как они имеют достаточно прозрачный шаблон построения. Альтернативным способом осуществления нисходящего синтаксического анализа является построение нерекурсивного нисходящего парсера. Его можно построить с помощью явного использования [[Стек | стека]] (вместо неявного при рекурсивных вызовах). Такой анализатор имитирует &lt;br /&gt;
[[Контекстно-свободные грамматики, вывод, лево- и правосторонний вывод, дерево разбора#Лево- и правосторонний вывод слова | левое порождение]].  &lt;br /&gt;
&lt;br /&gt;
Стек нерекурсивного предиктивного синтаксического анализатора содержит последовательность терминалов и нетерминалов и таблицу синтаксического анализа. На стеке располагается последовательность символов грамматики с маркером конца разбора &amp;lt;tex&amp;gt;\perp&amp;lt;/tex&amp;gt; на дне. В начале процесса анализа строки стек содержит стартовый нетерминал грамматики непосредственно над символом &amp;lt;tex&amp;gt;\perp&amp;lt;/tex&amp;gt;. Таблица синтаксического анализа представляет собой двухмерный массив &amp;lt;tex&amp;gt;\mathcal{M}[A, c]&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; {{---}} нетерминал или &amp;lt;tex&amp;gt;\perp&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt; c &amp;lt;/tex&amp;gt; {{---}} токен или символ конца строки &amp;lt;tex&amp;gt;\$&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Нерекурсивный синтаксический анализатор смотрит на текущий токен &amp;lt;tex&amp;gt; c &amp;lt;/tex&amp;gt; входного слова и на символ на вершине стека &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt;, а затем принимает решение в зависимости от одного из возникающих ниже случаев:&lt;br /&gt;
* если &amp;lt;tex&amp;gt;A\ =\ \perp\ \land\ c\ =\ \$&amp;lt;/tex&amp;gt;, то синтаксический анализатор прекращает работу, так как разбор строки завершён,&lt;br /&gt;
* eсли &amp;lt;tex&amp;gt;A = c&amp;lt;/tex&amp;gt;, то синтаксический анализатор снимает со стека токен &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; и перемещает указатель текущего токена ленты к следующему токену (то есть вызывает &amp;lt;tex&amp;gt;\mathtt{nextToken}&amp;lt;/tex&amp;gt;), таким образом, происходит выброс символа &amp;lt;tex&amp;gt; c &amp;lt;/tex&amp;gt; со стека,&lt;br /&gt;
* eсли &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; представляет собой нетерминал, то программа рассматривает запись &amp;lt;tex&amp;gt;\mathcal{M}[A,c]&amp;lt;/tex&amp;gt; таблицы разбора &amp;lt;tex&amp;gt;\mathcal{M}&amp;lt;/tex&amp;gt;. Эта запись представляет собой либо продукцию грамматики вида &amp;lt;tex&amp;gt;A \to \alpha_i,\ \alpha_i = X_1 X_2 \ldots X_t&amp;lt;/tex&amp;gt;, и тогда &amp;lt;tex&amp;gt;\mathcal{M}[A,c]&amp;lt;/tex&amp;gt; содержит номер &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; данной продукции, либо запись об ошибке, и тогда &amp;lt;tex&amp;gt;\mathcal{M}[A,c] = \varnothing&amp;lt;/tex&amp;gt;. Если &amp;lt;tex&amp;gt;\mathcal{M}[A,c] \neq \varnothing&amp;lt;/tex&amp;gt;, то синтаксический анализатор замещает на стеке нетерминал &amp;lt;tex&amp;gt; A &amp;lt;/tex&amp;gt; правой частью правила с номером &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt;, помещая символы правила на стек в обратном порядке,&lt;br /&gt;
* во всех остальных случаях парсер бросает сообщение об ошибке.&lt;br /&gt;
&lt;br /&gt;
Рассмотренные случаи отображены коротко на картинке справа, где блок &amp;lt;tex&amp;gt; N &amp;lt;/tex&amp;gt; отвечает нетерминалам грамматики. Из картинки видно, что вместо рассмотрения всех случаев в коде, достаточно просто создать таблицу &amp;lt;tex&amp;gt;\mathcal{M}&amp;lt;/tex&amp;gt; таким образом, чтобы она учитывала все случаи, что упростит код.&lt;br /&gt;
&lt;br /&gt;
=== Псевдокод ===&lt;br /&gt;
&lt;br /&gt;
Здесь по-прежнему &amp;lt;tex&amp;gt;\mathtt{curToken}&amp;lt;/tex&amp;gt; обозначает текущий токен строки, а &amp;lt;tex&amp;gt;\mathtt{nextToken}&amp;lt;/tex&amp;gt; передвигает указатель на следующий токен.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code style = &amp;quot;display: inline-block;&amp;quot;&amp;gt;&lt;br /&gt;
 '''function''' nonRecursiveParser():&lt;br /&gt;
     st : '''Stack'''&lt;br /&gt;
     st.push(&amp;lt;tex&amp;gt;\perp&amp;lt;/tex&amp;gt;)&lt;br /&gt;
     st.push(&amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;) &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// здесь &amp;lt;tex&amp;gt; S &amp;lt;/tex&amp;gt; {{---}} стартовый нетерминал грамматики&amp;lt;/font&amp;gt;&lt;br /&gt;
     '''while''' s.top() &amp;lt;tex&amp;gt;\neq\ \perp&amp;lt;/tex&amp;gt; &lt;br /&gt;
         A = st.top()&lt;br /&gt;
         '''if''' &amp;lt;tex&amp;gt;\mathcal{M}[A,\ curToken]\ ==\ \mathrm{ok}&amp;lt;/tex&amp;gt; &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// &amp;lt;tex&amp;gt; A\ ==\ \perp &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\mathtt{curToken}\ ==\ \$&amp;lt;/tex&amp;gt;&amp;lt;/font&amp;gt;&lt;br /&gt;
             '''break''' &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// разбор строки завершён&amp;lt;/font&amp;gt;&lt;br /&gt;
         '''else if''' &amp;lt;tex&amp;gt;\mathcal{M}[A,\ curToken]\ ==\ \nearrow&amp;lt;/tex&amp;gt; &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// выброс&amp;lt;/font&amp;gt;&lt;br /&gt;
             nextToken()&lt;br /&gt;
             st.pop()&lt;br /&gt;
             вывести в выходной поток нетерминал, отвечающий &amp;lt;tex&amp;gt;\mathtt{curToken}&amp;lt;/tex&amp;gt;&lt;br /&gt;
         '''else if''' &amp;lt;tex&amp;gt;\mathcal{M}[A,\ curToken]&amp;lt;/tex&amp;gt; {{---}} номер правила &amp;lt;tex&amp;gt;A \to \alpha_i,\ \alpha_i = X_1 X_2 \ldots X_t&amp;lt;/tex&amp;gt;&lt;br /&gt;
             st.pop()&lt;br /&gt;
             '''for''' k = t '''downto''' 1&lt;br /&gt;
                 st.push(&amp;lt;tex&amp;gt;X_k&amp;lt;/tex&amp;gt;)    &lt;br /&gt;
             вывести в выходной поток терминал, отвечающий &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt;&lt;br /&gt;
         '''else'''&lt;br /&gt;
             &amp;lt;font color=&amp;quot;red&amp;quot;&amp;gt;error&amp;lt;/font&amp;gt;(&amp;quot;unexpected symbol&amp;quot;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Пример ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt; &lt;br /&gt;
(1) \  E \to TE' \\&lt;br /&gt;
(2) \ E' \to +TE' \\&lt;br /&gt;
(3) \ E' \to \varepsilon \\&lt;br /&gt;
(4) \ T \to FT' \\&lt;br /&gt;
(5) \ T' \to \times FT' \\&lt;br /&gt;
(6) \ T' \to \varepsilon \\&lt;br /&gt;
(7) \ F \to n \\&lt;br /&gt;
(8) \ F \to (E)&lt;br /&gt;
&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| style=&amp;quot;background-color:#CCC;margin:0.5px&amp;quot;&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| &lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| n&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| (&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| )&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| +&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| *&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| $&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;E&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;E'&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;3 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;2 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;3 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;4 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;4 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;6 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;6 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;5 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;6 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;F&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;7 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;8 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\perp&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
[[Файл:Parse_stack.png|500px]]&lt;br /&gt;
&lt;br /&gt;
== Примечания ==&lt;br /&gt;
&amp;lt;references/&amp;gt;&lt;br /&gt;
== Источники информации ==&lt;br /&gt;
* Альфред Ахо, Рави Сети, Джеффри Ульман. Компиляторы. Принципы, технологии, инструменты. Издательство Вильямс. Второе издание. 2008. Стр. 288 {{---}} 294.&lt;br /&gt;
&lt;br /&gt;
[[Категория: Методы трансляции]]&lt;br /&gt;
[[Категория: Нисходящий разбор]]&lt;/div&gt;</summary>
		<author><name>194.85.160.130</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D1%80%D0%B5%D0%B4%D0%B8%D0%BA%D1%82%D0%B8%D0%B2%D0%BD%D1%8B%D0%B9_%D1%81%D0%B8%D0%BD%D1%82%D0%B0%D0%BA%D1%81%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B8%D0%B9_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=46906</id>
		<title>Предиктивный синтаксический анализ</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D1%80%D0%B5%D0%B4%D0%B8%D0%BA%D1%82%D0%B8%D0%B2%D0%BD%D1%8B%D0%B9_%D1%81%D0%B8%D0%BD%D1%82%D0%B0%D0%BA%D1%81%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B8%D0%B9_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=46906"/>
				<updated>2015-05-25T11:59:47Z</updated>
		
		<summary type="html">&lt;p&gt;194.85.160.130: /* Нерекурсивный нисходящий парсер */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{В разработке}}&lt;br /&gt;
Для [[LL(k)-грамматики, множества FIRST и FOLLOW | LL(1)-грамматик]] возможна автоматическая генерация парсеров, если известны множества FIRST и FOLLOW. Существуют общедоступные генераторы: ANTLR&amp;lt;ref&amp;gt;[http://www.antlr.org/ ANTLR {{---}} Parser generator]&amp;lt;/ref&amp;gt;, Bison&amp;lt;ref&amp;gt;[http://www.gnu.org/software/bison/ Bison {{---}} GNU Project]&amp;lt;/ref&amp;gt;, Yacc&amp;lt;ref&amp;gt;[http://dinosaur.compilertools.net/ Lex &amp;amp; Yacc {{---}} A Lexical Analyzer Generator and Yet Another Compiler-Compiler]&amp;lt;/ref&amp;gt;, Happy&amp;lt;ref&amp;gt;[https://www.haskell.org/happy/ Happy {{---}} The Parser Generator for Haskell]&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Общая схема построения рекурсивных парсеров с помощью FIRST и FOLLOW ==&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;tex&amp;gt;\Gamma =\langle \Sigma, N, S, P \rangle&amp;lt;/tex&amp;gt; {{---}} LL(1)-грамматика. Построим для нее парсер.&lt;br /&gt;
&lt;br /&gt;
Для каждого нетерминала &amp;lt;tex&amp;gt;A : A \rightarrow \alpha_1 \mid \alpha_2 \mid \ldots \mid \alpha_k &amp;lt;/tex&amp;gt; создадим функцию &amp;lt;tex&amp;gt; \mathtt{A}() : \mathtt{Node} &amp;lt;/tex&amp;gt;, возвращающую фрагмент дерева разбора, выведенный из нетерминала &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Здесь &amp;lt;tex&amp;gt;\mathtt{Node}&amp;lt;/tex&amp;gt; {{---}} структура следующего вида:&lt;br /&gt;
 '''struct''' Node&lt;br /&gt;
     children : '''list&amp;lt;Node&amp;gt;'''&lt;br /&gt;
     value : '''string'''          &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// имя нетерминала или текст терминала&amp;lt;/font&amp;gt;&lt;br /&gt;
     '''function''' addChild('''Node''') &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// функция, подвешивающая поддерево к данному узлу&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Каждый момент времени парсер работает с определённым '''токеном''' (англ. ''token'') входного слово &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt;. Токен {{---}} один или несколько нетерминалов, для удобства объединяемые по смыслу в одну логическую единицы. Примерами токенов могут выступать следующие лексические единицы:&lt;br /&gt;
* произвольный символ &amp;lt;tex&amp;gt; c &amp;lt;/tex&amp;gt;,&lt;br /&gt;
* целое слово, например &amp;lt;tex&amp;gt; public &amp;lt;/tex&amp;gt;,&lt;br /&gt;
* число со знаком, обозначаемое далее для краткости как &amp;lt;tex&amp;gt; n &amp;lt;/tex&amp;gt;.&lt;br /&gt;
В общем случае, токеном может являться любое слово, удовлетворяющее произвольному [[Регулярные языки: два определения и их эквивалентность | регулярному выражению]].&lt;br /&gt;
&lt;br /&gt;
[[Файл:string_token.png|300px]]&lt;br /&gt;
&lt;br /&gt;
Здесь &amp;lt;tex&amp;gt;\$&amp;lt;/tex&amp;gt; обозначает маркер конца строки.&lt;br /&gt;
&lt;br /&gt;
В псевдокоде используются следующие обозначения:&lt;br /&gt;
* &amp;lt;tex&amp;gt;\mathtt{curToken}&amp;lt;/tex&amp;gt; {{---}} текущий токен строки,&lt;br /&gt;
* &amp;lt;tex&amp;gt;\mathtt{nextToken()}&amp;lt;/tex&amp;gt; {{---}} функция, записывающая в &amp;lt;tex&amp;gt;\mathtt{curToken}&amp;lt;/tex&amp;gt; следующий за ним токен.&lt;br /&gt;
&lt;br /&gt;
Тогда шаблон функции рекурсивного парсера для нетерминала &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; имеет вид:&lt;br /&gt;
&lt;br /&gt;
 A() : '''Node'''&lt;br /&gt;
     Node res = Node(&amp;quot;A&amp;quot;)&lt;br /&gt;
     '''switch''' (curToken) : &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// принимаем решение в зависимости от текущего токена строки&amp;lt;/font&amp;gt;&lt;br /&gt;
         '''case''' &amp;lt;tex&amp;gt;\mathrm{FIRST}(\alpha_1) \cup (\mathrm{FOLLOW}(A)\ \mathrm{if}\ \varepsilon \in \mathrm{FIRST}(\alpha_1))&amp;lt;/tex&amp;gt; :&lt;br /&gt;
             &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// &amp;lt;tex&amp;gt;\alpha_1 = X_1X_2 \ldots X_{t}&amp;lt;/tex&amp;gt; &amp;lt;/font&amp;gt;&lt;br /&gt;
             '''for''' i = 1 .. t&lt;br /&gt;
                 '''if''' &amp;lt;tex&amp;gt; X_i &amp;lt;/tex&amp;gt; {{---}} нетерминал&lt;br /&gt;
                     consume(&amp;lt;tex&amp;gt;X_i&amp;lt;/tex&amp;gt;)&lt;br /&gt;
                     res.addChild(Node(&amp;quot;&amp;lt;tex&amp;gt;X_i&amp;lt;/tex&amp;gt;&amp;quot;)&lt;br /&gt;
                     nextToken()&lt;br /&gt;
                 '''else''' &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// &amp;lt;tex&amp;gt;X_i&amp;lt;/tex&amp;gt; {{---}} терминал, нужно вызвать соответствующую ему функцию рекурсивного парсера &amp;lt;/font&amp;gt;&lt;br /&gt;
                     Node t = &amp;lt;tex&amp;gt;X_i()&amp;lt;/tex&amp;gt;&lt;br /&gt;
                     res.addChild(t)&lt;br /&gt;
             '''break'''&lt;br /&gt;
         '''case''' &amp;lt;tex&amp;gt;\mathrm{FIRST}(\alpha_2) \cup (\mathrm{FOLLOW}(A)\ \mathrm{if}\ \varepsilon \in \mathrm{FIRST}(\alpha_2))&amp;lt;/tex&amp;gt; : &lt;br /&gt;
             ...&lt;br /&gt;
             '''break'''&lt;br /&gt;
         ...&lt;br /&gt;
         '''default''' :&lt;br /&gt;
             &amp;lt;font color=&amp;quot;red&amp;quot;&amp;gt;error&amp;lt;/font&amp;gt;(&amp;quot;unexpected char&amp;quot;)&lt;br /&gt;
     '''return''' res&lt;br /&gt;
&lt;br /&gt;
 '''function''' consume(c: '''char''') &lt;br /&gt;
     '''if''' curToken != c&lt;br /&gt;
         &amp;lt;font color=&amp;quot;red&amp;quot;&amp;gt;error&amp;lt;/font&amp;gt;(&amp;quot;expected &amp;quot; + c)&lt;br /&gt;
     nextToken()&lt;br /&gt;
&lt;br /&gt;
Такой парсер не только разбирает строку, но и находит ошибки в неудовлетворяющих грамматике выражениях.&lt;br /&gt;
&lt;br /&gt;
== Пример ==&lt;br /&gt;
Рассмотрим построение парсера на примере LL(1)-грамматики арифметических выражений, которая уже была разобрана [[Построение FIRST и FOLLOW#Пример | ранее]]:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt; &lt;br /&gt;
E \to TE' \\&lt;br /&gt;
E' \to +TE' \mid \varepsilon \\&lt;br /&gt;
T \to FT' \\&lt;br /&gt;
T' \to * FT' \mid \varepsilon \\&lt;br /&gt;
F \to n \mid (E)&lt;br /&gt;
&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Напомним, что множества &amp;lt;tex&amp;gt;\mathrm{FIRST}&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\mathrm{FOLLOW}&amp;lt;/tex&amp;gt; для неё выглядят так:&lt;br /&gt;
&lt;br /&gt;
{| style=&amp;quot;background-color:#CCC;margin:0.5px&amp;quot;&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| Правило&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| FIRST&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| FOLLOW&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;E&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ n,\ (\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ \$,\ )\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;E'&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ +,\ \varepsilon\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ \$,\ )\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ n,\ (\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ +,\ \$\ ,\ )\ \}&amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ *,\ \varepsilon\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ +,\ \$\ ,\ )\ \}&amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;F&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ n,\ (\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ *, \ +,\ \$\ ,\ )\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Псевдокоды ===&lt;br /&gt;
Построим функции обработки некоторых нетерминалов, используя описанный выше шаблон:&lt;br /&gt;
&lt;br /&gt;
 E() : '''Node'''&lt;br /&gt;
     Node res = Node(&amp;quot;E&amp;quot;)&lt;br /&gt;
     '''switch''' (curToken)&lt;br /&gt;
         '''case''' n, '('  :&lt;br /&gt;
             res.addChild(T())&lt;br /&gt;
             res.addChild(E'())&lt;br /&gt;
             '''break'''&lt;br /&gt;
         '''default''' :&lt;br /&gt;
             &amp;lt;font color=&amp;quot;red&amp;quot;&amp;gt;error&amp;lt;/font&amp;gt;(&amp;quot;unexpected char&amp;quot;)&lt;br /&gt;
     '''return''' res&lt;br /&gt;
&lt;br /&gt;
 E'() : '''Node'''&lt;br /&gt;
     Node res = Node(&amp;quot;E'&amp;quot;)&lt;br /&gt;
     '''switch''' (curToken) &lt;br /&gt;
         '''case''' '+' :&lt;br /&gt;
             consume('+')&lt;br /&gt;
             res.addChild(Node(&amp;quot;+&amp;quot;))&lt;br /&gt;
             res.addChild(T())&lt;br /&gt;
             res.addChild(E'())&lt;br /&gt;
             '''break'''&lt;br /&gt;
         '''case''' '$', ')' :&lt;br /&gt;
             '''break'''&lt;br /&gt;
         '''default''' :&lt;br /&gt;
             &amp;lt;font color=&amp;quot;red&amp;quot;&amp;gt;error&amp;lt;/font&amp;gt;(&amp;quot;unexpected char&amp;quot;)&lt;br /&gt;
      '''return''' res&lt;br /&gt;
&lt;br /&gt;
 F() : '''Node'''&lt;br /&gt;
     Node res = Node(&amp;quot;F&amp;quot;)&lt;br /&gt;
     '''switch''' (curToken)&lt;br /&gt;
         '''case''' n :&lt;br /&gt;
             consume(n)&lt;br /&gt;
             res.addChild(Node(curToken)) &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// &amp;lt;tex&amp;gt;\mathtt{curToken}&amp;lt;/tex&amp;gt; подпадает под шаблон &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt;, поэтому запишем его в &amp;lt;tex&amp;gt;\mathtt{value}&amp;lt;/tex&amp;gt; вершины&amp;lt;/font&amp;gt;&lt;br /&gt;
             '''break'''&lt;br /&gt;
         '''case''' '(' :&lt;br /&gt;
             consume('(')&lt;br /&gt;
             res.addChild(Node(&amp;quot;(&amp;quot;))&lt;br /&gt;
             res.addChild(E())&lt;br /&gt;
             consume(')')&lt;br /&gt;
             res.addChild(Node(&amp;quot;)&amp;quot;))&lt;br /&gt;
         '''default''' :&lt;br /&gt;
             &amp;lt;font color=&amp;quot;red&amp;quot;&amp;gt;error&amp;lt;/font&amp;gt;(&amp;quot;unexpected char&amp;quot;)&lt;br /&gt;
     '''return''' res&lt;br /&gt;
&lt;br /&gt;
Функции для &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt; строятся аналогично.&lt;br /&gt;
&lt;br /&gt;
=== Дерево разбора ===&lt;br /&gt;
&lt;br /&gt;
Рассмотрим дерево разбора для выражения &amp;lt;tex&amp;gt;(1 + 2) * 3&amp;lt;/tex&amp;gt; и несколько первых шагов алгоритма рекурсивного разбора. Сначала вызывается функция стартового нетерминала грамматики, то есть &amp;lt;tex&amp;gt;E&amp;lt;/tex&amp;gt;. Так как первым токеном является '(', то будет использовано первое правило разбора &amp;lt;tex&amp;gt;TE'&amp;lt;/tex&amp;gt;. Поэтому к вершине с меткой &amp;lt;tex&amp;gt;E&amp;lt;/tex&amp;gt; добавятся два ребёнка: &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;E'&amp;lt;/tex&amp;gt;. А рекурсивный разборщик перейдёт к нетерминалу &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt;. По-прежнему curToken равен '(', поэтому в &amp;lt;tex&amp;gt;F&amp;lt;/tex&amp;gt; сработает второй case, первым ребёнком добавится '(', curToken станет равен &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt;, а разборщик перейдёт к нетерминалу &amp;lt;tex&amp;gt;E&amp;lt;/tex&amp;gt;. После того как выражение после '(', которое выводится из &amp;lt;tex&amp;gt;E&amp;lt;/tex&amp;gt;, будет полностью разобрано, функция рекурсивного разбора для &amp;lt;tex&amp;gt;F&amp;lt;/tex&amp;gt; добавит ')' последним сыном к этому нетерминалу. &lt;br /&gt;
&lt;br /&gt;
Продолжая в том же духе, мы построим всё дерево разбора данного выражения.&lt;br /&gt;
&lt;br /&gt;
[[Файл:parse_ex1.png|400px|thumb|center|Дерево разбора выражения &amp;lt;tex&amp;gt;(1 + 2) * 3&amp;lt;/tex&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
== Нерекурсивный нисходящий парсер ==&lt;br /&gt;
&lt;br /&gt;
[[Файл:Parse_table.png|400px|right]]&lt;br /&gt;
&lt;br /&gt;
Рекурсивные разборщики можно генерировать автоматически, зная множества &amp;lt;tex&amp;gt;\mathrm{FIRST}&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\mathrm{FOLLOW}&amp;lt;/tex&amp;gt;, так как они имеют достаточно прозрачный шаблон построения. Альтернативным способом осуществления нисходящего синтаксического анализа является построение нерекурсивного нисходящего парсера. Его можно построить с помощью явного использования [[Стек | стека]] (вместо неявного при рекурсивных вызовах). Такой анализатор имитирует &lt;br /&gt;
[[Контекстно-свободные грамматики, вывод, лево- и правосторонний вывод, дерево разбора#Лево- и правосторонний вывод слова | левое порождение]].  &lt;br /&gt;
&lt;br /&gt;
Стек нерекурсивного предиктивного синтаксического анализатора содержит последовательность терминалов и нетерминалов и таблицу синтаксического анализа. На стеке располагается последовательность символов грамматики с маркером конца разбора &amp;lt;tex&amp;gt;\perp&amp;lt;/tex&amp;gt; на дне. В начале процесса анализа строки стек содержит стартовый нетерминал грамматики непосредственно над символом &amp;lt;tex&amp;gt;\perp&amp;lt;/tex&amp;gt;. Таблица синтаксического анализа представляет собой двухмерный массив &amp;lt;tex&amp;gt;\mathcal{M}[A, c]&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; {{---}} нетерминал или &amp;lt;tex&amp;gt;\perp&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt; c &amp;lt;/tex&amp;gt; {{---}} токен или символ конца строки &amp;lt;tex&amp;gt;\$&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Нерекурсивный синтаксический анализатор смотрит на текущий токен &amp;lt;tex&amp;gt; c &amp;lt;/tex&amp;gt; входного слова и на символ на вершине стека &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt;, а затем принимает решение в зависимости от одного из возникающих ниже случаев:&lt;br /&gt;
* если &amp;lt;tex&amp;gt;A\ =\ \perp\ \land\ c\ =\ \$&amp;lt;/tex&amp;gt;, то синтаксический анализатор прекращает работу, так как разбор строки завершён,&lt;br /&gt;
* eсли &amp;lt;tex&amp;gt;A = c&amp;lt;/tex&amp;gt;, то синтаксический анализатор снимает со стека токен &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; и перемещает указатель текущего токена ленты к следующему токену (то есть вызывает &amp;lt;tex&amp;gt;\mathtt{nextToken}&amp;lt;/tex&amp;gt;), таким образом, происходит выброс символа &amp;lt;tex&amp;gt; c &amp;lt;/tex&amp;gt; со стека,&lt;br /&gt;
* eсли &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; представляет собой нетерминал, то программа рассматривает запись &amp;lt;tex&amp;gt;\mathcal{M}[A,c]&amp;lt;/tex&amp;gt; таблицы разбора &amp;lt;tex&amp;gt;\mathcal{M}&amp;lt;/tex&amp;gt;. Эта запись представляет собой либо продукцию грамматики вида &amp;lt;tex&amp;gt;A \to \alpha_i,\ \alpha_i = X_1 X_2 \ldots X_t&amp;lt;/tex&amp;gt;, и тогда &amp;lt;tex&amp;gt;\mathcal{M}[A,c]&amp;lt;/tex&amp;gt; содержит номер &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; данной продукции, либо запись об ошибке, и тогда &amp;lt;tex&amp;gt;\mathcal{M}[A,c] = \varnothing&amp;lt;/tex&amp;gt;. Если &amp;lt;tex&amp;gt;\mathcal{M}[A,c] \neq \varnothing&amp;lt;/tex&amp;gt;, то синтаксический анализатор замещает на стеке нетерминал &amp;lt;tex&amp;gt; A &amp;lt;/tex&amp;gt; правой частью правила с номером &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt;, помещая символы правила на стек в обратном порядке,&lt;br /&gt;
* во всех остальных случаях парсер бросает сообщение об ошибке.&lt;br /&gt;
&lt;br /&gt;
Рассмотренные случаи отображены коротко на картинке справа, где блок &amp;lt;tex&amp;gt; N &amp;lt;/tex&amp;gt; отвечает нетерминалам грамматики. Из картинки видно, что вместо рассмотрения всех случаев в коде, достаточно просто создать таблицу &amp;lt;tex&amp;gt;\mathcal{M}&amp;lt;/tex&amp;gt; таким образом, чтобы она учитывала все случаи, что упростит код.&lt;br /&gt;
&lt;br /&gt;
=== Псевдокод ===&lt;br /&gt;
&lt;br /&gt;
Здесь по-прежнему &amp;lt;tex&amp;gt;\mathtt{curToken}&amp;lt;/tex&amp;gt; обозначает текущий токен строки, а &amp;lt;tex&amp;gt;\mathtt{nextToken}&amp;lt;/tex&amp;gt; передвигает указатель на следующий токен.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code style = &amp;quot;display: inline-block;&amp;quot;&amp;gt;&lt;br /&gt;
 '''function''' nonRecursiveParser():&lt;br /&gt;
     st : '''Stack'''&lt;br /&gt;
     st.push(&amp;lt;tex&amp;gt;\perp&amp;lt;/tex&amp;gt;)&lt;br /&gt;
     st.push(&amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;) &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// здесь &amp;lt;tex&amp;gt; S &amp;lt;/tex&amp;gt; {{---}} стартовый нетерминал грамматики&amp;lt;/font&amp;gt;&lt;br /&gt;
     '''while''' s.top() &amp;lt;tex&amp;gt;\neq\ \perp&amp;lt;/tex&amp;gt; &lt;br /&gt;
         A = st.top()&lt;br /&gt;
         '''if''' &amp;lt;tex&amp;gt;\mathcal{M}[A,\ curToken]\ ==\ \mathrm{ok}&amp;lt;/tex&amp;gt; &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// &amp;lt;tex&amp;gt; A\ ==\ \perp &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\mathtt{curToken}\ ==\ \$&amp;lt;/tex&amp;gt;&amp;lt;/font&amp;gt;&lt;br /&gt;
             '''break''' &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// разбор строки завершён&amp;lt;/font&amp;gt;&lt;br /&gt;
         '''else if''' &amp;lt;tex&amp;gt;\mathcal{M}[A,\ curToken]\ ==\ \nearrow&amp;lt;/tex&amp;gt; &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// выброс&amp;lt;/font&amp;gt;&lt;br /&gt;
             nextToken()&lt;br /&gt;
             st.pop()&lt;br /&gt;
             вывести в выходной поток нетерминал, отвечающий &amp;lt;tex&amp;gt;\mathtt{curToken}&amp;lt;/tex&amp;gt;&lt;br /&gt;
         '''else if''' &amp;lt;tex&amp;gt;\mathcal{M}[A,\ curToken]&amp;lt;/tex&amp;gt; {{---}} номер правила &amp;lt;tex&amp;gt;A \to \alpha_i,\ \alpha_i = X_1 X_2 \ldots X_t&amp;lt;/tex&amp;gt;&lt;br /&gt;
             st.pop()&lt;br /&gt;
             '''for''' k = t '''downto''' 1&lt;br /&gt;
                 st.push(&amp;lt;tex&amp;gt;X_k&amp;lt;/tex&amp;gt;)    &lt;br /&gt;
             вывести в выходной поток терминал, отвечающий &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt;&lt;br /&gt;
         '''else'''&lt;br /&gt;
             &amp;lt;font color=&amp;quot;red&amp;quot;&amp;gt;error&amp;lt;/font&amp;gt;(&amp;quot;unexpected symbol&amp;quot;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Пример ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt; &lt;br /&gt;
(1) \  E \to TE' \\&lt;br /&gt;
(2) \ E' \to +TE' \\&lt;br /&gt;
(3) \ E' \to \varepsilon \\&lt;br /&gt;
(4) \ T \to FT' \\&lt;br /&gt;
(5) \ T' \to \times FT' \\&lt;br /&gt;
(6) \ T' \to \varepsilon \\&lt;br /&gt;
(7) \ F \to n \\&lt;br /&gt;
(8) \ F \to (E)&lt;br /&gt;
&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| style=&amp;quot;background-color:#CCC;margin:0.5px&amp;quot;&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| &lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| n&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| (&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| )&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| +&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| *&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| $&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;E&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;E'&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;3 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;2 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;3 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;4 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;4 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;6 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;6 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;5 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;6 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;F&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;7 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;8 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\perp&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
[[Файл:Parse_stack.png|500px]]&lt;br /&gt;
&lt;br /&gt;
== Примечания ==&lt;br /&gt;
&amp;lt;references/&amp;gt;&lt;br /&gt;
== Источники информации ==&lt;br /&gt;
* Альфред Ахо, Рави Сети, Джеффри Ульман. Компиляторы. Принципы, технологии, инструменты. Издательство Вильямс. Второе издание. 2008. Стр. 288 {{---}} 294.&lt;br /&gt;
&lt;br /&gt;
[[Категория: Методы трансляции]]&lt;br /&gt;
[[Категория: Нисходящий разбор]]&lt;/div&gt;</summary>
		<author><name>194.85.160.130</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D1%80%D0%B5%D0%B4%D0%B8%D0%BA%D1%82%D0%B8%D0%B2%D0%BD%D1%8B%D0%B9_%D1%81%D0%B8%D0%BD%D1%82%D0%B0%D0%BA%D1%81%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B8%D0%B9_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=46902</id>
		<title>Предиктивный синтаксический анализ</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D1%80%D0%B5%D0%B4%D0%B8%D0%BA%D1%82%D0%B8%D0%B2%D0%BD%D1%8B%D0%B9_%D1%81%D0%B8%D0%BD%D1%82%D0%B0%D0%BA%D1%81%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B8%D0%B9_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=46902"/>
				<updated>2015-05-25T11:09:51Z</updated>
		
		<summary type="html">&lt;p&gt;194.85.160.130: /* Общая схема построения рекурсивных парсеров с помощью FIRST и FOLLOW */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{В разработке}}&lt;br /&gt;
Для [[LL(k)-грамматики, множества FIRST и FOLLOW | LL(1)-грамматик]] возможна автоматическая генерация парсеров, если известны множества FIRST и FOLLOW. Существуют общедоступные генераторы: ANTLR&amp;lt;ref&amp;gt;[http://www.antlr.org/ ANTLR {{---}} Parser generator]&amp;lt;/ref&amp;gt;, Bison&amp;lt;ref&amp;gt;[http://www.gnu.org/software/bison/ Bison {{---}} GNU Project]&amp;lt;/ref&amp;gt;, Yacc&amp;lt;ref&amp;gt;[http://dinosaur.compilertools.net/ Lex &amp;amp; Yacc {{---}} A Lexical Analyzer Generator and Yet Another Compiler-Compiler]&amp;lt;/ref&amp;gt;, Happy&amp;lt;ref&amp;gt;[https://www.haskell.org/happy/ Happy {{---}} The Parser Generator for Haskell]&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Общая схема построения рекурсивных парсеров с помощью FIRST и FOLLOW ==&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;tex&amp;gt;\Gamma =\langle \Sigma, N, S, P \rangle&amp;lt;/tex&amp;gt; {{---}} LL(1)-грамматика. Построим для нее парсер.&lt;br /&gt;
&lt;br /&gt;
Для каждого нетерминала &amp;lt;tex&amp;gt;A : A \rightarrow \alpha_1 \mid \alpha_2 \mid \ldots \mid \alpha_k &amp;lt;/tex&amp;gt; создадим функцию &amp;lt;tex&amp;gt; \mathtt{A}() : \mathtt{Node} &amp;lt;/tex&amp;gt;, возвращающую фрагмент дерева разбора, выведенный из нетерминала &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Здесь &amp;lt;tex&amp;gt;\mathtt{Node}&amp;lt;/tex&amp;gt; {{---}} структура следующего вида:&lt;br /&gt;
 '''struct''' Node&lt;br /&gt;
     children : '''list&amp;lt;Node&amp;gt;'''&lt;br /&gt;
     value : '''string'''          &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// имя нетерминала или текст терминала&amp;lt;/font&amp;gt;&lt;br /&gt;
     '''function''' addChild('''Node''') &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// функция, подвешивающая поддерево к данному узлу&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Каждый момент времени парсер работает с определённым '''токеном''' (англ. ''token'') входного слово &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt;. Токен {{---}} один или несколько нетерминалов, для удобства объединяемые по смыслу в одну логическую единицы. Примерами токенов могут выступать следующие лексические единицы:&lt;br /&gt;
* произвольный символ &amp;lt;tex&amp;gt; c &amp;lt;/tex&amp;gt;,&lt;br /&gt;
* целое слово, например &amp;lt;tex&amp;gt; public &amp;lt;/tex&amp;gt;,&lt;br /&gt;
* число со знаком, обозначаемое далее для краткости как &amp;lt;tex&amp;gt; n &amp;lt;/tex&amp;gt;.&lt;br /&gt;
В общем случае, токеном может являться любое слово, удовлетворяющее произвольному [[Регулярные языки: два определения и их эквивалентность | регулярному выражению]].&lt;br /&gt;
&lt;br /&gt;
[[Файл:string_token.png|300px]]&lt;br /&gt;
&lt;br /&gt;
Здесь &amp;lt;tex&amp;gt;\$&amp;lt;/tex&amp;gt; обозначает маркер конца строки.&lt;br /&gt;
&lt;br /&gt;
В псевдокоде используются следующие обозначения:&lt;br /&gt;
* &amp;lt;tex&amp;gt;\mathtt{curToken}&amp;lt;/tex&amp;gt; {{---}} текущий токен строки,&lt;br /&gt;
* &amp;lt;tex&amp;gt;\mathtt{nextToken()}&amp;lt;/tex&amp;gt; {{---}} функция, записывающая в &amp;lt;tex&amp;gt;\mathtt{curToken}&amp;lt;/tex&amp;gt; следующий за ним токен.&lt;br /&gt;
&lt;br /&gt;
Тогда шаблон функции рекурсивного парсера для нетерминала &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; имеет вид:&lt;br /&gt;
&lt;br /&gt;
 A() : '''Node'''&lt;br /&gt;
     Node res = Node(&amp;quot;A&amp;quot;)&lt;br /&gt;
     '''switch''' (curToken) : &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// принимаем решение в зависимости от текущего токена строки&amp;lt;/font&amp;gt;&lt;br /&gt;
         '''case''' &amp;lt;tex&amp;gt;\mathrm{FIRST}(\alpha_1) \cup (\mathrm{FOLLOW}(A)\ \mathrm{if}\ \varepsilon \in \mathrm{FIRST}(\alpha_1))&amp;lt;/tex&amp;gt; :&lt;br /&gt;
             &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// &amp;lt;tex&amp;gt;\alpha_1 = X_1X_2 \ldots X_{t}&amp;lt;/tex&amp;gt; &amp;lt;/font&amp;gt;&lt;br /&gt;
             '''for''' i = 1 .. t&lt;br /&gt;
                 '''if''' &amp;lt;tex&amp;gt; X_i &amp;lt;/tex&amp;gt; {{---}} нетерминал&lt;br /&gt;
                     consume(&amp;lt;tex&amp;gt;X_i&amp;lt;/tex&amp;gt;)&lt;br /&gt;
                     res.addChild(Node(&amp;quot;&amp;lt;tex&amp;gt;X_i&amp;lt;/tex&amp;gt;&amp;quot;)&lt;br /&gt;
                     nextToken()&lt;br /&gt;
                 '''else''' &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// &amp;lt;tex&amp;gt;X_i&amp;lt;/tex&amp;gt; {{---}} терминал, нужно вызвать соответствующую ему функцию рекурсивного парсера &amp;lt;/font&amp;gt;&lt;br /&gt;
                     Node t = &amp;lt;tex&amp;gt;X_i()&amp;lt;/tex&amp;gt;&lt;br /&gt;
                     res.addChild(t)&lt;br /&gt;
             '''break'''&lt;br /&gt;
         '''case''' &amp;lt;tex&amp;gt;\mathrm{FIRST}(\alpha_2) \cup (\mathrm{FOLLOW}(A)\ \mathrm{if}\ \varepsilon \in \mathrm{FIRST}(\alpha_2))&amp;lt;/tex&amp;gt; : &lt;br /&gt;
             ...&lt;br /&gt;
             '''break'''&lt;br /&gt;
         ...&lt;br /&gt;
         '''default''' :&lt;br /&gt;
             &amp;lt;font color=&amp;quot;red&amp;quot;&amp;gt;error&amp;lt;/font&amp;gt;(&amp;quot;unexpected char&amp;quot;)&lt;br /&gt;
     '''return''' res&lt;br /&gt;
&lt;br /&gt;
 '''function''' consume(c: '''char''') &lt;br /&gt;
     '''if''' curToken != c&lt;br /&gt;
         &amp;lt;font color=&amp;quot;red&amp;quot;&amp;gt;error&amp;lt;/font&amp;gt;(&amp;quot;expected &amp;quot; + c)&lt;br /&gt;
     nextToken()&lt;br /&gt;
&lt;br /&gt;
Такой парсер не только разбирает строку, но и находит ошибки в неудовлетворяющих грамматике выражениях.&lt;br /&gt;
&lt;br /&gt;
== Пример ==&lt;br /&gt;
Рассмотрим построение парсера на примере LL(1)-грамматики арифметических выражений, которая уже была разобрана [[Построение FIRST и FOLLOW#Пример | ранее]]:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt; &lt;br /&gt;
E \to TE' \\&lt;br /&gt;
E' \to +TE' \mid \varepsilon \\&lt;br /&gt;
T \to FT' \\&lt;br /&gt;
T' \to * FT' \mid \varepsilon \\&lt;br /&gt;
F \to n \mid (E)&lt;br /&gt;
&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Напомним, что множества &amp;lt;tex&amp;gt;\mathrm{FIRST}&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\mathrm{FOLLOW}&amp;lt;/tex&amp;gt; для неё выглядят так:&lt;br /&gt;
&lt;br /&gt;
{| style=&amp;quot;background-color:#CCC;margin:0.5px&amp;quot;&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| Правило&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| FIRST&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| FOLLOW&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;E&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ n,\ (\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ \$,\ )\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;E'&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ +,\ \varepsilon\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ \$,\ )\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ n,\ (\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ +,\ \$\ ,\ )\ \}&amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ *,\ \varepsilon\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ +,\ \$\ ,\ )\ \}&amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;F&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ n,\ (\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ *, \ +,\ \$\ ,\ )\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Псевдокоды ===&lt;br /&gt;
Построим функции обработки некоторых нетерминалов, используя описанный выше шаблон:&lt;br /&gt;
&lt;br /&gt;
 E() : '''Node'''&lt;br /&gt;
     Node res = Node(&amp;quot;E&amp;quot;)&lt;br /&gt;
     '''switch''' (curToken)&lt;br /&gt;
         '''case''' n, '('  :&lt;br /&gt;
             res.addChild(T())&lt;br /&gt;
             res.addChild(E'())&lt;br /&gt;
             '''break'''&lt;br /&gt;
         '''default''' :&lt;br /&gt;
             &amp;lt;font color=&amp;quot;red&amp;quot;&amp;gt;error&amp;lt;/font&amp;gt;(&amp;quot;unexpected char&amp;quot;)&lt;br /&gt;
     '''return''' res&lt;br /&gt;
&lt;br /&gt;
 E'() : '''Node'''&lt;br /&gt;
     Node res = Node(&amp;quot;E'&amp;quot;)&lt;br /&gt;
     '''switch''' (curToken) &lt;br /&gt;
         '''case''' '+' :&lt;br /&gt;
             consume('+')&lt;br /&gt;
             res.addChild(Node(&amp;quot;+&amp;quot;))&lt;br /&gt;
             res.addChild(T())&lt;br /&gt;
             res.addChild(E'())&lt;br /&gt;
             '''break'''&lt;br /&gt;
         '''case''' '$', ')' :&lt;br /&gt;
             '''break'''&lt;br /&gt;
         '''default''' :&lt;br /&gt;
             &amp;lt;font color=&amp;quot;red&amp;quot;&amp;gt;error&amp;lt;/font&amp;gt;(&amp;quot;unexpected char&amp;quot;)&lt;br /&gt;
      '''return''' res&lt;br /&gt;
&lt;br /&gt;
 F() : '''Node'''&lt;br /&gt;
     Node res = Node(&amp;quot;F&amp;quot;)&lt;br /&gt;
     '''switch''' (curToken)&lt;br /&gt;
         '''case''' n :&lt;br /&gt;
             consume(n)&lt;br /&gt;
             res.addChild(Node(curToken)) &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// &amp;lt;tex&amp;gt;\mathtt{curToken}&amp;lt;/tex&amp;gt; подпадает под шаблон &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt;, поэтому запишем его в &amp;lt;tex&amp;gt;\mathtt{value}&amp;lt;/tex&amp;gt; вершины&amp;lt;/font&amp;gt;&lt;br /&gt;
             '''break'''&lt;br /&gt;
         '''case''' '(' :&lt;br /&gt;
             consume('(')&lt;br /&gt;
             res.addChild(Node(&amp;quot;(&amp;quot;))&lt;br /&gt;
             res.addChild(E())&lt;br /&gt;
             consume(')')&lt;br /&gt;
             res.addChild(Node(&amp;quot;)&amp;quot;))&lt;br /&gt;
         '''default''' :&lt;br /&gt;
             &amp;lt;font color=&amp;quot;red&amp;quot;&amp;gt;error&amp;lt;/font&amp;gt;(&amp;quot;unexpected char&amp;quot;)&lt;br /&gt;
     '''return''' res&lt;br /&gt;
&lt;br /&gt;
Функции для &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt; строятся аналогично.&lt;br /&gt;
&lt;br /&gt;
=== Дерево разбора ===&lt;br /&gt;
&lt;br /&gt;
Рассмотрим дерево разбора для выражения &amp;lt;tex&amp;gt;(1 + 2) * 3&amp;lt;/tex&amp;gt; и несколько первых шагов алгоритма рекурсивного разбора. Сначала вызывается функция стартового нетерминала грамматики, то есть &amp;lt;tex&amp;gt;E&amp;lt;/tex&amp;gt;. Так как первым токеном является '(', то будет использовано первое правило разбора &amp;lt;tex&amp;gt;TE'&amp;lt;/tex&amp;gt;. Поэтому к вершине с меткой &amp;lt;tex&amp;gt;E&amp;lt;/tex&amp;gt; добавятся два ребёнка: &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;E'&amp;lt;/tex&amp;gt;. А рекурсивный разборщик перейдёт к нетерминалу &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt;. По-прежнему curToken равен '(', поэтому в &amp;lt;tex&amp;gt;F&amp;lt;/tex&amp;gt; сработает второй case, первым ребёнком добавится '(', curToken станет равен &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt;, а разборщик перейдёт к нетерминалу &amp;lt;tex&amp;gt;E&amp;lt;/tex&amp;gt;. После того как выражение после '(', которое выводится из &amp;lt;tex&amp;gt;E&amp;lt;/tex&amp;gt;, будет полностью разобрано, функция рекурсивного разбора для &amp;lt;tex&amp;gt;F&amp;lt;/tex&amp;gt; добавит ')' последним сыном к этому нетерминалу. &lt;br /&gt;
&lt;br /&gt;
Продолжая в том же духе, мы построим всё дерево разбора данного выражения.&lt;br /&gt;
&lt;br /&gt;
[[Файл:parse_ex1.png|400px|thumb|center|Дерево разбора выражения &amp;lt;tex&amp;gt;(1 + 2) * 3&amp;lt;/tex&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
== Нерекурсивный нисходящий парсер ==&lt;br /&gt;
&lt;br /&gt;
[[Файл:Parse_table.png|400px|right]]&lt;br /&gt;
&lt;br /&gt;
Рекурсивные разборщики можно генерировать автоматически, зная множества FIRST и FOLLOW, так как они имеют достаточно прозрачный шаблон построения. Альтернативным способом осуществления нисходящего синтаксического анализа является построение нерекурсивного нисходящего парсера. Его можно построить с помощью явного использования стека (вместо неявного при рекурсивных вызовах). Такое анализатор имитирует левое порождение.  &lt;br /&gt;
&lt;br /&gt;
Нерекурсивный предиктивный синтаксический анализатор содержит дополнительно стек, содержащий последовательность терминалов и нетерминалов, и таблицу синтаксического анализа. На стеке располагается последовательность символов грамматики с маркером конца строки $ на дне. В начале процесса анализа строки стек содержит стартовый нетерминал грамматики непосредственно над символом $. Таблица синтаксического анализа представляет собой двухмерный массив М[X, а], где X — нетерминал, а — терминал или символ $.&lt;br /&gt;
&lt;br /&gt;
Нерекурсивный синтаксический анализатор смотрит на текущий токен строки a и на символ на вершине стека X, а затем принимает решение в зависимости от одного из возникающих ниже случаев:&lt;br /&gt;
* если Х=curToken=$, синтаксический анализатор прекращает работу, так как разбор строки завершён,&lt;br /&gt;
* eсли Х=curToken≠$, синтаксический анализатор снимает со стека X и перемещает указатель входного потока к следующему токену (то есть вызывает nextToken),&lt;br /&gt;
* eсли X представляет собой нетерминал, программа рассматривает запись M[Х,а] таблицы разбора М. Эта запись представляет собой либо X-продукцию грамматики, либо запись об ошибке. Если, например, М[Х,а] = {X → UVW}, синтаксический анализатор замещает X на вершине стека на WVU (с U на вершине стека). В кач-ве выхода синтаксический анализатор просто выводит использованную продукцию. Если M[Х,а] = error, синтаксический анализатор вызывает программу восстановления после ошибки.&lt;br /&gt;
&lt;br /&gt;
=== Псевдокод ===&lt;br /&gt;
&amp;lt;code style = &amp;quot;display: inline-block;&amp;quot;&amp;gt;&lt;br /&gt;
 function nonRecursiveParser(w : String):&lt;br /&gt;
     s : Stack&lt;br /&gt;
     s.push(bottom)&lt;br /&gt;
     s.push(Start)&lt;br /&gt;
     do &lt;br /&gt;
         X = s.top()&lt;br /&gt;
         if X == c&lt;br /&gt;
             c = nextToken()&lt;br /&gt;
             s.pop()&lt;br /&gt;
         else if X in Sigma&lt;br /&gt;
             error(&amp;quot;unexpected symbol&amp;quot;)&lt;br /&gt;
         else if M[X, c] == undefined // запись об ошибке&lt;br /&gt;
             error(&amp;quot;unexpected symbol&amp;quot;)&lt;br /&gt;
         else if M[X, c] == X -&amp;gt; Y_1 Y_2 ... Y_k&lt;br /&gt;
             s.pop()&lt;br /&gt;
             for i = k downto 1&lt;br /&gt;
                 s.push(Y_i)&lt;br /&gt;
     while s.top() != bottom&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Пример ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt; &lt;br /&gt;
(1) \  E \to TE' \\&lt;br /&gt;
(2) \ E' \to +TE' \\&lt;br /&gt;
(3) \ E' \to \varepsilon \\&lt;br /&gt;
(4) \ T \to FT' \\&lt;br /&gt;
(5) \ T' \to \times FT' \\&lt;br /&gt;
(6) \ T' \to \varepsilon \\&lt;br /&gt;
(7) \ F \to n \\&lt;br /&gt;
(8) \ F \to (E)&lt;br /&gt;
&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| style=&amp;quot;background-color:#CCC;margin:0.5px&amp;quot;&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| &lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| n&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| (&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| )&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| +&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| *&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| $&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;E&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;E'&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;3 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;2 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;3 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;4 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;4 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;6 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;6 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;5 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;6 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;F&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;7 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;8 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\perp&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
[[Файл:Parse_stack.png|500px]]&lt;br /&gt;
&lt;br /&gt;
== Примечания ==&lt;br /&gt;
&amp;lt;references/&amp;gt;&lt;br /&gt;
== Источники информации ==&lt;br /&gt;
* Альфред Ахо, Рави Сети, Джеффри Ульман. Компиляторы. Принципы, технологии, инструменты. Издательство Вильямс. Второе издание. 2008. Стр. 288 {{---}} 294.&lt;br /&gt;
&lt;br /&gt;
[[Категория: Методы трансляции]]&lt;br /&gt;
[[Категория: Нисходящий разбор]]&lt;/div&gt;</summary>
		<author><name>194.85.160.130</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D1%80%D0%B5%D0%B4%D0%B8%D0%BA%D1%82%D0%B8%D0%B2%D0%BD%D1%8B%D0%B9_%D1%81%D0%B8%D0%BD%D1%82%D0%B0%D0%BA%D1%81%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B8%D0%B9_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=46901</id>
		<title>Предиктивный синтаксический анализ</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D1%80%D0%B5%D0%B4%D0%B8%D0%BA%D1%82%D0%B8%D0%B2%D0%BD%D1%8B%D0%B9_%D1%81%D0%B8%D0%BD%D1%82%D0%B0%D0%BA%D1%81%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B8%D0%B9_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=46901"/>
				<updated>2015-05-25T11:08:59Z</updated>
		
		<summary type="html">&lt;p&gt;194.85.160.130: /* Нерекурсивный нисходящий парсер */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{В разработке}}&lt;br /&gt;
Для [[LL(k)-грамматики, множества FIRST и FOLLOW | LL(1)-грамматик]] возможна автоматическая генерация парсеров, если известны множества FIRST и FOLLOW. Существуют общедоступные генераторы: ANTLR&amp;lt;ref&amp;gt;[http://www.antlr.org/ ANTLR {{---}} Parser generator]&amp;lt;/ref&amp;gt;, Bison&amp;lt;ref&amp;gt;[http://www.gnu.org/software/bison/ Bison {{---}} GNU Project]&amp;lt;/ref&amp;gt;, Yacc&amp;lt;ref&amp;gt;[http://dinosaur.compilertools.net/ Lex &amp;amp; Yacc {{---}} A Lexical Analyzer Generator and Yet Another Compiler-Compiler]&amp;lt;/ref&amp;gt;, Happy&amp;lt;ref&amp;gt;[https://www.haskell.org/happy/ Happy {{---}} The Parser Generator for Haskell]&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Общая схема построения рекурсивных парсеров с помощью FIRST и FOLLOW ==&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;tex&amp;gt;\Gamma =\langle \Sigma, N, S, P \rangle&amp;lt;/tex&amp;gt; {{---}} LL(1)-грамматика. Построим для нее парсер.&lt;br /&gt;
&lt;br /&gt;
Для каждого нетерминала &amp;lt;tex&amp;gt;A : A \rightarrow \alpha_1 \mid \alpha_2 \mid \ldots \mid \alpha_k &amp;lt;/tex&amp;gt; создадим функцию &amp;lt;tex&amp;gt; \mathtt{A}() : \mathtt{Node} &amp;lt;/tex&amp;gt;, возвращающую фрагмент дерева разбора, выведенный из нетерминала &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Здесь &amp;lt;tex&amp;gt;\mathtt{Node}&amp;lt;/tex&amp;gt; {{---}} структура следующего вида:&lt;br /&gt;
 '''struct''' Node&lt;br /&gt;
     children : '''list&amp;lt;Node&amp;gt;'''&lt;br /&gt;
     value : '''string'''          &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// имя нетерминала или текст терминала&amp;lt;/font&amp;gt;&lt;br /&gt;
     '''function''' addChild('''Node''') &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// функция, подвешивающая поддерево к данному узлу&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Каждый момент времени парсер работает с определённым '''токеном''' (англ. ''token'') входного слово &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt;. Токен {{---}} один или несколько нетерминалов, для удобства объединяемые по смыслу в одну логическую единицы. Примерами токенов могут выступать следующие лексические единицы:&lt;br /&gt;
* произвольный символ &amp;lt;tex&amp;gt; c &amp;lt;/tex&amp;gt;,&lt;br /&gt;
* целое слово, например &amp;lt;tex&amp;gt; public &amp;lt;/tex&amp;gt;,&lt;br /&gt;
* число со знаком, обозначаемое далее для краткости как &amp;lt;tex&amp;gt; n &amp;lt;/tex&amp;gt;.&lt;br /&gt;
В общем случае, токеном может являться любое слово, удовлетворяющее произвольному [[Регулярные языки: два определения и их эквивалентность | регулярному выражению]].&lt;br /&gt;
&lt;br /&gt;
[[Файл:string_token.png|300px]]&lt;br /&gt;
&lt;br /&gt;
В псевдокоде используются следующие обозначения:&lt;br /&gt;
* &amp;lt;tex&amp;gt;\mathtt{curToken}&amp;lt;/tex&amp;gt; {{---}} текущий токен строки,&lt;br /&gt;
* &amp;lt;tex&amp;gt;\mathtt{nextToken()}&amp;lt;/tex&amp;gt; {{---}} функция, записывающая в &amp;lt;tex&amp;gt;\mathtt{curToken}&amp;lt;/tex&amp;gt; следующий за ним токен.&lt;br /&gt;
&lt;br /&gt;
Тогда шаблон функции рекурсивного парсера для нетерминала &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; имеет вид:&lt;br /&gt;
&lt;br /&gt;
 A() : '''Node'''&lt;br /&gt;
     Node res = Node(&amp;quot;A&amp;quot;)&lt;br /&gt;
     '''switch''' (curToken) : &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// принимаем решение в зависимости от текущего токена строки&amp;lt;/font&amp;gt;&lt;br /&gt;
         '''case''' &amp;lt;tex&amp;gt;\mathrm{FIRST}(\alpha_1) \cup (\mathrm{FOLLOW}(A)\ \mathrm{if}\ \varepsilon \in \mathrm{FIRST}(\alpha_1))&amp;lt;/tex&amp;gt; :&lt;br /&gt;
             &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// &amp;lt;tex&amp;gt;\alpha_1 = X_1X_2 \ldots X_{t}&amp;lt;/tex&amp;gt; &amp;lt;/font&amp;gt;&lt;br /&gt;
             '''for''' i = 1 .. t&lt;br /&gt;
                 '''if''' &amp;lt;tex&amp;gt; X_i &amp;lt;/tex&amp;gt; {{---}} нетерминал&lt;br /&gt;
                     consume(&amp;lt;tex&amp;gt;X_i&amp;lt;/tex&amp;gt;)&lt;br /&gt;
                     res.addChild(Node(&amp;quot;&amp;lt;tex&amp;gt;X_i&amp;lt;/tex&amp;gt;&amp;quot;)&lt;br /&gt;
                     nextToken()&lt;br /&gt;
                 '''else''' &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// &amp;lt;tex&amp;gt;X_i&amp;lt;/tex&amp;gt; {{---}} терминал, нужно вызвать соответствующую ему функцию рекурсивного парсера &amp;lt;/font&amp;gt;&lt;br /&gt;
                     Node t = &amp;lt;tex&amp;gt;X_i()&amp;lt;/tex&amp;gt;&lt;br /&gt;
                     res.addChild(t)&lt;br /&gt;
             '''break'''&lt;br /&gt;
         '''case''' &amp;lt;tex&amp;gt;\mathrm{FIRST}(\alpha_2) \cup (\mathrm{FOLLOW}(A)\ \mathrm{if}\ \varepsilon \in \mathrm{FIRST}(\alpha_2))&amp;lt;/tex&amp;gt; : &lt;br /&gt;
             ...&lt;br /&gt;
             '''break'''&lt;br /&gt;
         ...&lt;br /&gt;
         '''default''' :&lt;br /&gt;
             &amp;lt;font color=&amp;quot;red&amp;quot;&amp;gt;error&amp;lt;/font&amp;gt;(&amp;quot;unexpected char&amp;quot;)&lt;br /&gt;
     '''return''' res&lt;br /&gt;
&lt;br /&gt;
 '''function''' consume(c: '''char''') &lt;br /&gt;
     '''if''' curToken != c&lt;br /&gt;
         &amp;lt;font color=&amp;quot;red&amp;quot;&amp;gt;error&amp;lt;/font&amp;gt;(&amp;quot;expected &amp;quot; + c)&lt;br /&gt;
     nextToken()&lt;br /&gt;
&lt;br /&gt;
Такой парсер не только разбирает строку, но и находит ошибки в неудовлетворяющих грамматике выражениях.&lt;br /&gt;
&lt;br /&gt;
== Пример ==&lt;br /&gt;
Рассмотрим построение парсера на примере LL(1)-грамматики арифметических выражений, которая уже была разобрана [[Построение FIRST и FOLLOW#Пример | ранее]]:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt; &lt;br /&gt;
E \to TE' \\&lt;br /&gt;
E' \to +TE' \mid \varepsilon \\&lt;br /&gt;
T \to FT' \\&lt;br /&gt;
T' \to * FT' \mid \varepsilon \\&lt;br /&gt;
F \to n \mid (E)&lt;br /&gt;
&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Напомним, что множества &amp;lt;tex&amp;gt;\mathrm{FIRST}&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\mathrm{FOLLOW}&amp;lt;/tex&amp;gt; для неё выглядят так:&lt;br /&gt;
&lt;br /&gt;
{| style=&amp;quot;background-color:#CCC;margin:0.5px&amp;quot;&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| Правило&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| FIRST&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| FOLLOW&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;E&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ n,\ (\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ \$,\ )\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;E'&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ +,\ \varepsilon\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ \$,\ )\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ n,\ (\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ +,\ \$\ ,\ )\ \}&amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ *,\ \varepsilon\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ +,\ \$\ ,\ )\ \}&amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;F&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ n,\ (\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ *, \ +,\ \$\ ,\ )\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Псевдокоды ===&lt;br /&gt;
Построим функции обработки некоторых нетерминалов, используя описанный выше шаблон:&lt;br /&gt;
&lt;br /&gt;
 E() : '''Node'''&lt;br /&gt;
     Node res = Node(&amp;quot;E&amp;quot;)&lt;br /&gt;
     '''switch''' (curToken)&lt;br /&gt;
         '''case''' n, '('  :&lt;br /&gt;
             res.addChild(T())&lt;br /&gt;
             res.addChild(E'())&lt;br /&gt;
             '''break'''&lt;br /&gt;
         '''default''' :&lt;br /&gt;
             &amp;lt;font color=&amp;quot;red&amp;quot;&amp;gt;error&amp;lt;/font&amp;gt;(&amp;quot;unexpected char&amp;quot;)&lt;br /&gt;
     '''return''' res&lt;br /&gt;
&lt;br /&gt;
 E'() : '''Node'''&lt;br /&gt;
     Node res = Node(&amp;quot;E'&amp;quot;)&lt;br /&gt;
     '''switch''' (curToken) &lt;br /&gt;
         '''case''' '+' :&lt;br /&gt;
             consume('+')&lt;br /&gt;
             res.addChild(Node(&amp;quot;+&amp;quot;))&lt;br /&gt;
             res.addChild(T())&lt;br /&gt;
             res.addChild(E'())&lt;br /&gt;
             '''break'''&lt;br /&gt;
         '''case''' '$', ')' :&lt;br /&gt;
             '''break'''&lt;br /&gt;
         '''default''' :&lt;br /&gt;
             &amp;lt;font color=&amp;quot;red&amp;quot;&amp;gt;error&amp;lt;/font&amp;gt;(&amp;quot;unexpected char&amp;quot;)&lt;br /&gt;
      '''return''' res&lt;br /&gt;
&lt;br /&gt;
 F() : '''Node'''&lt;br /&gt;
     Node res = Node(&amp;quot;F&amp;quot;)&lt;br /&gt;
     '''switch''' (curToken)&lt;br /&gt;
         '''case''' n :&lt;br /&gt;
             consume(n)&lt;br /&gt;
             res.addChild(Node(curToken)) &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// &amp;lt;tex&amp;gt;\mathtt{curToken}&amp;lt;/tex&amp;gt; подпадает под шаблон &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt;, поэтому запишем его в &amp;lt;tex&amp;gt;\mathtt{value}&amp;lt;/tex&amp;gt; вершины&amp;lt;/font&amp;gt;&lt;br /&gt;
             '''break'''&lt;br /&gt;
         '''case''' '(' :&lt;br /&gt;
             consume('(')&lt;br /&gt;
             res.addChild(Node(&amp;quot;(&amp;quot;))&lt;br /&gt;
             res.addChild(E())&lt;br /&gt;
             consume(')')&lt;br /&gt;
             res.addChild(Node(&amp;quot;)&amp;quot;))&lt;br /&gt;
         '''default''' :&lt;br /&gt;
             &amp;lt;font color=&amp;quot;red&amp;quot;&amp;gt;error&amp;lt;/font&amp;gt;(&amp;quot;unexpected char&amp;quot;)&lt;br /&gt;
     '''return''' res&lt;br /&gt;
&lt;br /&gt;
Функции для &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt; строятся аналогично.&lt;br /&gt;
&lt;br /&gt;
=== Дерево разбора ===&lt;br /&gt;
&lt;br /&gt;
Рассмотрим дерево разбора для выражения &amp;lt;tex&amp;gt;(1 + 2) * 3&amp;lt;/tex&amp;gt; и несколько первых шагов алгоритма рекурсивного разбора. Сначала вызывается функция стартового нетерминала грамматики, то есть &amp;lt;tex&amp;gt;E&amp;lt;/tex&amp;gt;. Так как первым токеном является '(', то будет использовано первое правило разбора &amp;lt;tex&amp;gt;TE'&amp;lt;/tex&amp;gt;. Поэтому к вершине с меткой &amp;lt;tex&amp;gt;E&amp;lt;/tex&amp;gt; добавятся два ребёнка: &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;E'&amp;lt;/tex&amp;gt;. А рекурсивный разборщик перейдёт к нетерминалу &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt;. По-прежнему curToken равен '(', поэтому в &amp;lt;tex&amp;gt;F&amp;lt;/tex&amp;gt; сработает второй case, первым ребёнком добавится '(', curToken станет равен &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt;, а разборщик перейдёт к нетерминалу &amp;lt;tex&amp;gt;E&amp;lt;/tex&amp;gt;. После того как выражение после '(', которое выводится из &amp;lt;tex&amp;gt;E&amp;lt;/tex&amp;gt;, будет полностью разобрано, функция рекурсивного разбора для &amp;lt;tex&amp;gt;F&amp;lt;/tex&amp;gt; добавит ')' последним сыном к этому нетерминалу. &lt;br /&gt;
&lt;br /&gt;
Продолжая в том же духе, мы построим всё дерево разбора данного выражения.&lt;br /&gt;
&lt;br /&gt;
[[Файл:parse_ex1.png|400px|thumb|center|Дерево разбора выражения &amp;lt;tex&amp;gt;(1 + 2) * 3&amp;lt;/tex&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
== Нерекурсивный нисходящий парсер ==&lt;br /&gt;
&lt;br /&gt;
[[Файл:Parse_table.png|400px|right]]&lt;br /&gt;
&lt;br /&gt;
Рекурсивные разборщики можно генерировать автоматически, зная множества FIRST и FOLLOW, так как они имеют достаточно прозрачный шаблон построения. Альтернативным способом осуществления нисходящего синтаксического анализа является построение нерекурсивного нисходящего парсера. Его можно построить с помощью явного использования стека (вместо неявного при рекурсивных вызовах). Такое анализатор имитирует левое порождение.  &lt;br /&gt;
&lt;br /&gt;
Нерекурсивный предиктивный синтаксический анализатор содержит дополнительно стек, содержащий последовательность терминалов и нетерминалов, и таблицу синтаксического анализа. На стеке располагается последовательность символов грамматики с маркером конца строки $ на дне. В начале процесса анализа строки стек содержит стартовый нетерминал грамматики непосредственно над символом $. Таблица синтаксического анализа представляет собой двухмерный массив М[X, а], где X — нетерминал, а — терминал или символ $.&lt;br /&gt;
&lt;br /&gt;
Нерекурсивный синтаксический анализатор смотрит на текущий токен строки a и на символ на вершине стека X, а затем принимает решение в зависимости от одного из возникающих ниже случаев:&lt;br /&gt;
* если Х=curToken=$, синтаксический анализатор прекращает работу, так как разбор строки завершён,&lt;br /&gt;
* eсли Х=curToken≠$, синтаксический анализатор снимает со стека X и перемещает указатель входного потока к следующему токену (то есть вызывает nextToken),&lt;br /&gt;
* eсли X представляет собой нетерминал, программа рассматривает запись M[Х,а] таблицы разбора М. Эта запись представляет собой либо X-продукцию грамматики, либо запись об ошибке. Если, например, М[Х,а] = {X → UVW}, синтаксический анализатор замещает X на вершине стека на WVU (с U на вершине стека). В кач-ве выхода синтаксический анализатор просто выводит использованную продукцию. Если M[Х,а] = error, синтаксический анализатор вызывает программу восстановления после ошибки.&lt;br /&gt;
&lt;br /&gt;
=== Псевдокод ===&lt;br /&gt;
&amp;lt;code style = &amp;quot;display: inline-block;&amp;quot;&amp;gt;&lt;br /&gt;
 function nonRecursiveParser(w : String):&lt;br /&gt;
     s : Stack&lt;br /&gt;
     s.push(bottom)&lt;br /&gt;
     s.push(Start)&lt;br /&gt;
     do &lt;br /&gt;
         X = s.top()&lt;br /&gt;
         if X == c&lt;br /&gt;
             c = nextToken()&lt;br /&gt;
             s.pop()&lt;br /&gt;
         else if X in Sigma&lt;br /&gt;
             error(&amp;quot;unexpected symbol&amp;quot;)&lt;br /&gt;
         else if M[X, c] == undefined // запись об ошибке&lt;br /&gt;
             error(&amp;quot;unexpected symbol&amp;quot;)&lt;br /&gt;
         else if M[X, c] == X -&amp;gt; Y_1 Y_2 ... Y_k&lt;br /&gt;
             s.pop()&lt;br /&gt;
             for i = k downto 1&lt;br /&gt;
                 s.push(Y_i)&lt;br /&gt;
     while s.top() != bottom&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Пример ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt; &lt;br /&gt;
(1) \  E \to TE' \\&lt;br /&gt;
(2) \ E' \to +TE' \\&lt;br /&gt;
(3) \ E' \to \varepsilon \\&lt;br /&gt;
(4) \ T \to FT' \\&lt;br /&gt;
(5) \ T' \to \times FT' \\&lt;br /&gt;
(6) \ T' \to \varepsilon \\&lt;br /&gt;
(7) \ F \to n \\&lt;br /&gt;
(8) \ F \to (E)&lt;br /&gt;
&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| style=&amp;quot;background-color:#CCC;margin:0.5px&amp;quot;&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| &lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| n&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| (&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| )&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| +&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| *&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| $&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;E&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;E'&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;3 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;2 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;3 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;4 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;4 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;6 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;6 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;5 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;6 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;F&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;7 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;8 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\perp&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
[[Файл:Parse_stack.png|500px]]&lt;br /&gt;
&lt;br /&gt;
== Примечания ==&lt;br /&gt;
&amp;lt;references/&amp;gt;&lt;br /&gt;
== Источники информации ==&lt;br /&gt;
* Альфред Ахо, Рави Сети, Джеффри Ульман. Компиляторы. Принципы, технологии, инструменты. Издательство Вильямс. Второе издание. 2008. Стр. 288 {{---}} 294.&lt;br /&gt;
&lt;br /&gt;
[[Категория: Методы трансляции]]&lt;br /&gt;
[[Категория: Нисходящий разбор]]&lt;/div&gt;</summary>
		<author><name>194.85.160.130</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D1%80%D0%B5%D0%B4%D0%B8%D0%BA%D1%82%D0%B8%D0%B2%D0%BD%D1%8B%D0%B9_%D1%81%D0%B8%D0%BD%D1%82%D0%B0%D0%BA%D1%81%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B8%D0%B9_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=46900</id>
		<title>Предиктивный синтаксический анализ</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D1%80%D0%B5%D0%B4%D0%B8%D0%BA%D1%82%D0%B8%D0%B2%D0%BD%D1%8B%D0%B9_%D1%81%D0%B8%D0%BD%D1%82%D0%B0%D0%BA%D1%81%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B8%D0%B9_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=46900"/>
				<updated>2015-05-25T11:03:04Z</updated>
		
		<summary type="html">&lt;p&gt;194.85.160.130: /* Пример */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{В разработке}}&lt;br /&gt;
Для [[LL(k)-грамматики, множества FIRST и FOLLOW | LL(1)-грамматик]] возможна автоматическая генерация парсеров, если известны множества FIRST и FOLLOW. Существуют общедоступные генераторы: ANTLR&amp;lt;ref&amp;gt;[http://www.antlr.org/ ANTLR {{---}} Parser generator]&amp;lt;/ref&amp;gt;, Bison&amp;lt;ref&amp;gt;[http://www.gnu.org/software/bison/ Bison {{---}} GNU Project]&amp;lt;/ref&amp;gt;, Yacc&amp;lt;ref&amp;gt;[http://dinosaur.compilertools.net/ Lex &amp;amp; Yacc {{---}} A Lexical Analyzer Generator and Yet Another Compiler-Compiler]&amp;lt;/ref&amp;gt;, Happy&amp;lt;ref&amp;gt;[https://www.haskell.org/happy/ Happy {{---}} The Parser Generator for Haskell]&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Общая схема построения рекурсивных парсеров с помощью FIRST и FOLLOW ==&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;tex&amp;gt;\Gamma =\langle \Sigma, N, S, P \rangle&amp;lt;/tex&amp;gt; {{---}} LL(1)-грамматика. Построим для нее парсер.&lt;br /&gt;
&lt;br /&gt;
Для каждого нетерминала &amp;lt;tex&amp;gt;A : A \rightarrow \alpha_1 \mid \alpha_2 \mid \ldots \mid \alpha_k &amp;lt;/tex&amp;gt; создадим функцию &amp;lt;tex&amp;gt; \mathtt{A}() : \mathtt{Node} &amp;lt;/tex&amp;gt;, возвращающую фрагмент дерева разбора, выведенный из нетерминала &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Здесь &amp;lt;tex&amp;gt;\mathtt{Node}&amp;lt;/tex&amp;gt; {{---}} структура следующего вида:&lt;br /&gt;
 '''struct''' Node&lt;br /&gt;
     children : '''list&amp;lt;Node&amp;gt;'''&lt;br /&gt;
     value : '''string'''          &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// имя нетерминала или текст терминала&amp;lt;/font&amp;gt;&lt;br /&gt;
     '''function''' addChild('''Node''') &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// функция, подвешивающая поддерево к данному узлу&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Каждый момент времени парсер работает с определённым '''токеном''' (англ. ''token'') входного слово &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt;. Токен {{---}} один или несколько нетерминалов, для удобства объединяемые по смыслу в одну логическую единицы. Примерами токенов могут выступать следующие лексические единицы:&lt;br /&gt;
* произвольный символ &amp;lt;tex&amp;gt; c &amp;lt;/tex&amp;gt;,&lt;br /&gt;
* целое слово, например &amp;lt;tex&amp;gt; public &amp;lt;/tex&amp;gt;,&lt;br /&gt;
* число со знаком, обозначаемое далее для краткости как &amp;lt;tex&amp;gt; n &amp;lt;/tex&amp;gt;.&lt;br /&gt;
В общем случае, токеном может являться любое слово, удовлетворяющее произвольному [[Регулярные языки: два определения и их эквивалентность | регулярному выражению]].&lt;br /&gt;
&lt;br /&gt;
[[Файл:string_token.png|300px]]&lt;br /&gt;
&lt;br /&gt;
В псевдокоде используются следующие обозначения:&lt;br /&gt;
* &amp;lt;tex&amp;gt;\mathtt{curToken}&amp;lt;/tex&amp;gt; {{---}} текущий токен строки,&lt;br /&gt;
* &amp;lt;tex&amp;gt;\mathtt{nextToken()}&amp;lt;/tex&amp;gt; {{---}} функция, записывающая в &amp;lt;tex&amp;gt;\mathtt{curToken}&amp;lt;/tex&amp;gt; следующий за ним токен.&lt;br /&gt;
&lt;br /&gt;
Тогда шаблон функции рекурсивного парсера для нетерминала &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; имеет вид:&lt;br /&gt;
&lt;br /&gt;
 A() : '''Node'''&lt;br /&gt;
     Node res = Node(&amp;quot;A&amp;quot;)&lt;br /&gt;
     '''switch''' (curToken) : &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// принимаем решение в зависимости от текущего токена строки&amp;lt;/font&amp;gt;&lt;br /&gt;
         '''case''' &amp;lt;tex&amp;gt;\mathrm{FIRST}(\alpha_1) \cup (\mathrm{FOLLOW}(A)\ \mathrm{if}\ \varepsilon \in \mathrm{FIRST}(\alpha_1))&amp;lt;/tex&amp;gt; :&lt;br /&gt;
             &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// &amp;lt;tex&amp;gt;\alpha_1 = X_1X_2 \ldots X_{t}&amp;lt;/tex&amp;gt; &amp;lt;/font&amp;gt;&lt;br /&gt;
             '''for''' i = 1 .. t&lt;br /&gt;
                 '''if''' &amp;lt;tex&amp;gt; X_i &amp;lt;/tex&amp;gt; {{---}} нетерминал&lt;br /&gt;
                     consume(&amp;lt;tex&amp;gt;X_i&amp;lt;/tex&amp;gt;)&lt;br /&gt;
                     res.addChild(Node(&amp;quot;&amp;lt;tex&amp;gt;X_i&amp;lt;/tex&amp;gt;&amp;quot;)&lt;br /&gt;
                     nextToken()&lt;br /&gt;
                 '''else''' &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// &amp;lt;tex&amp;gt;X_i&amp;lt;/tex&amp;gt; {{---}} терминал, нужно вызвать соответствующую ему функцию рекурсивного парсера &amp;lt;/font&amp;gt;&lt;br /&gt;
                     Node t = &amp;lt;tex&amp;gt;X_i()&amp;lt;/tex&amp;gt;&lt;br /&gt;
                     res.addChild(t)&lt;br /&gt;
             '''break'''&lt;br /&gt;
         '''case''' &amp;lt;tex&amp;gt;\mathrm{FIRST}(\alpha_2) \cup (\mathrm{FOLLOW}(A)\ \mathrm{if}\ \varepsilon \in \mathrm{FIRST}(\alpha_2))&amp;lt;/tex&amp;gt; : &lt;br /&gt;
             ...&lt;br /&gt;
             '''break'''&lt;br /&gt;
         ...&lt;br /&gt;
         '''default''' :&lt;br /&gt;
             &amp;lt;font color=&amp;quot;red&amp;quot;&amp;gt;error&amp;lt;/font&amp;gt;(&amp;quot;unexpected char&amp;quot;)&lt;br /&gt;
     '''return''' res&lt;br /&gt;
&lt;br /&gt;
 '''function''' consume(c: '''char''') &lt;br /&gt;
     '''if''' curToken != c&lt;br /&gt;
         &amp;lt;font color=&amp;quot;red&amp;quot;&amp;gt;error&amp;lt;/font&amp;gt;(&amp;quot;expected &amp;quot; + c)&lt;br /&gt;
     nextToken()&lt;br /&gt;
&lt;br /&gt;
Такой парсер не только разбирает строку, но и находит ошибки в неудовлетворяющих грамматике выражениях.&lt;br /&gt;
&lt;br /&gt;
== Пример ==&lt;br /&gt;
Рассмотрим построение парсера на примере LL(1)-грамматики арифметических выражений, которая уже была разобрана [[Построение FIRST и FOLLOW#Пример | ранее]]:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt; &lt;br /&gt;
E \to TE' \\&lt;br /&gt;
E' \to +TE' \mid \varepsilon \\&lt;br /&gt;
T \to FT' \\&lt;br /&gt;
T' \to * FT' \mid \varepsilon \\&lt;br /&gt;
F \to n \mid (E)&lt;br /&gt;
&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Напомним, что множества &amp;lt;tex&amp;gt;\mathrm{FIRST}&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\mathrm{FOLLOW}&amp;lt;/tex&amp;gt; для неё выглядят так:&lt;br /&gt;
&lt;br /&gt;
{| style=&amp;quot;background-color:#CCC;margin:0.5px&amp;quot;&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| Правило&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| FIRST&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| FOLLOW&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;E&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ n,\ (\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ \$,\ )\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;E'&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ +,\ \varepsilon\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ \$,\ )\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ n,\ (\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ +,\ \$\ ,\ )\ \}&amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ *,\ \varepsilon\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ +,\ \$\ ,\ )\ \}&amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;F&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ n,\ (\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ *, \ +,\ \$\ ,\ )\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Псевдокоды ===&lt;br /&gt;
Построим функции обработки некоторых нетерминалов, используя описанный выше шаблон:&lt;br /&gt;
&lt;br /&gt;
 E() : '''Node'''&lt;br /&gt;
     Node res = Node(&amp;quot;E&amp;quot;)&lt;br /&gt;
     '''switch''' (curToken)&lt;br /&gt;
         '''case''' n, '('  :&lt;br /&gt;
             res.addChild(T())&lt;br /&gt;
             res.addChild(E'())&lt;br /&gt;
             '''break'''&lt;br /&gt;
         '''default''' :&lt;br /&gt;
             &amp;lt;font color=&amp;quot;red&amp;quot;&amp;gt;error&amp;lt;/font&amp;gt;(&amp;quot;unexpected char&amp;quot;)&lt;br /&gt;
     '''return''' res&lt;br /&gt;
&lt;br /&gt;
 E'() : '''Node'''&lt;br /&gt;
     Node res = Node(&amp;quot;E'&amp;quot;)&lt;br /&gt;
     '''switch''' (curToken) &lt;br /&gt;
         '''case''' '+' :&lt;br /&gt;
             consume('+')&lt;br /&gt;
             res.addChild(Node(&amp;quot;+&amp;quot;))&lt;br /&gt;
             res.addChild(T())&lt;br /&gt;
             res.addChild(E'())&lt;br /&gt;
             '''break'''&lt;br /&gt;
         '''case''' '$', ')' :&lt;br /&gt;
             '''break'''&lt;br /&gt;
         '''default''' :&lt;br /&gt;
             &amp;lt;font color=&amp;quot;red&amp;quot;&amp;gt;error&amp;lt;/font&amp;gt;(&amp;quot;unexpected char&amp;quot;)&lt;br /&gt;
      '''return''' res&lt;br /&gt;
&lt;br /&gt;
 F() : '''Node'''&lt;br /&gt;
     Node res = Node(&amp;quot;F&amp;quot;)&lt;br /&gt;
     '''switch''' (curToken)&lt;br /&gt;
         '''case''' n :&lt;br /&gt;
             consume(n)&lt;br /&gt;
             res.addChild(Node(curToken)) &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// &amp;lt;tex&amp;gt;\mathtt{curToken}&amp;lt;/tex&amp;gt; подпадает под шаблон &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt;, поэтому запишем его в &amp;lt;tex&amp;gt;\mathtt{value}&amp;lt;/tex&amp;gt; вершины&amp;lt;/font&amp;gt;&lt;br /&gt;
             '''break'''&lt;br /&gt;
         '''case''' '(' :&lt;br /&gt;
             consume('(')&lt;br /&gt;
             res.addChild(Node(&amp;quot;(&amp;quot;))&lt;br /&gt;
             res.addChild(E())&lt;br /&gt;
             consume(')')&lt;br /&gt;
             res.addChild(Node(&amp;quot;)&amp;quot;))&lt;br /&gt;
         '''default''' :&lt;br /&gt;
             &amp;lt;font color=&amp;quot;red&amp;quot;&amp;gt;error&amp;lt;/font&amp;gt;(&amp;quot;unexpected char&amp;quot;)&lt;br /&gt;
     '''return''' res&lt;br /&gt;
&lt;br /&gt;
Функции для &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt; строятся аналогично.&lt;br /&gt;
&lt;br /&gt;
=== Дерево разбора ===&lt;br /&gt;
&lt;br /&gt;
Рассмотрим дерево разбора для выражения &amp;lt;tex&amp;gt;(1 + 2) * 3&amp;lt;/tex&amp;gt; и несколько первых шагов алгоритма рекурсивного разбора. Сначала вызывается функция стартового нетерминала грамматики, то есть &amp;lt;tex&amp;gt;E&amp;lt;/tex&amp;gt;. Так как первым токеном является '(', то будет использовано первое правило разбора &amp;lt;tex&amp;gt;TE'&amp;lt;/tex&amp;gt;. Поэтому к вершине с меткой &amp;lt;tex&amp;gt;E&amp;lt;/tex&amp;gt; добавятся два ребёнка: &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;E'&amp;lt;/tex&amp;gt;. А рекурсивный разборщик перейдёт к нетерминалу &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt;. По-прежнему curToken равен '(', поэтому в &amp;lt;tex&amp;gt;F&amp;lt;/tex&amp;gt; сработает второй case, первым ребёнком добавится '(', curToken станет равен &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt;, а разборщик перейдёт к нетерминалу &amp;lt;tex&amp;gt;E&amp;lt;/tex&amp;gt;. После того как выражение после '(', которое выводится из &amp;lt;tex&amp;gt;E&amp;lt;/tex&amp;gt;, будет полностью разобрано, функция рекурсивного разбора для &amp;lt;tex&amp;gt;F&amp;lt;/tex&amp;gt; добавит ')' последним сыном к этому нетерминалу. &lt;br /&gt;
&lt;br /&gt;
Продолжая в том же духе, мы построим всё дерево разбора данного выражения.&lt;br /&gt;
&lt;br /&gt;
[[Файл:parse_ex1.png|400px|thumb|center|Дерево разбора выражения &amp;lt;tex&amp;gt;(1 + 2) * 3&amp;lt;/tex&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
== Нерекурсивный нисходящий парсер ==&lt;br /&gt;
&lt;br /&gt;
[[Файл:Parse_table.png|350px|right]]&lt;br /&gt;
&lt;br /&gt;
Рекурсивные разборщики можно генерировать автоматически, зная множества FIRST и FOLLOW, так как они имеют достаточно прозрачный шаблон построения. Альтернативным способом осуществления нисходящего синтаксического анализа является построение нерекурсивного нисходящего парсера. Его можно построить с помощью явного использования стека (вместо неявного при рекурсивных вызовах). Такое анализатор имитирует левое порождение.  &lt;br /&gt;
&lt;br /&gt;
Нерекурсивный предиктивный синтаксический анализатор содержит дополнительно стек, содержащий последовательность терминалов и нетерминалов, и таблицу синтаксического анализа. На стеке располагается последовательность символов грамматики с маркером конца строки $ на дне. В начале процесса анализа строки стек содержит стартовый нетерминал грамматики непосредственно над символом $. Таблица синтаксического анализа представляет собой двухмерный массив М[X, а], где X — нетерминал, а — терминал или символ $.&lt;br /&gt;
&lt;br /&gt;
Нерекурсивный синтаксический анализатор смотрит на текущий токен строки a и на символ на вершине стека X, а затем принимает решение в зависимости от одного из возникающих ниже случаев:&lt;br /&gt;
* если Х=curToken=$, синтаксический анализатор прекращает работу, так как разбор строки завершён,&lt;br /&gt;
* eсли Х=curToken≠$, синтаксический анализатор снимает со стека X и перемещает указатель входного потока к следующему токену (то есть вызывает nextToken),&lt;br /&gt;
* eсли X представляет собой нетерминал, программа рассматривает запись M[Х,а] таблицы разбора М. Эта запись представляет собой либо X-продукцию грамматики, либо запись об ошибке. Если, например, М[Х,а] = {X → UVW}, синтаксический анализатор замещает X на вершине стека на WVU (с U на вершине стека). В кач-ве выхода синтаксический анализатор просто выводит использованную продукцию. Если M[Х,а] = error, синтаксический анализатор вызывает программу восстановления после ошибки.&lt;br /&gt;
&lt;br /&gt;
=== Псевдокод ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
 function nonRecursiveParser(w : String):&lt;br /&gt;
     s : Stack&lt;br /&gt;
     s.push(bottom)&lt;br /&gt;
     s.push(Start)&lt;br /&gt;
     do &lt;br /&gt;
         X = s.top()&lt;br /&gt;
         if X == c&lt;br /&gt;
             c = nextToken()&lt;br /&gt;
             s.pop()&lt;br /&gt;
         else if X in Sigma&lt;br /&gt;
             error(&amp;quot;unexpected symbol&amp;quot;)&lt;br /&gt;
         else if M[X, c] == undefined // запись об ошибке&lt;br /&gt;
             error(&amp;quot;unexpected symbol&amp;quot;)&lt;br /&gt;
         else if M[X, c] == X -&amp;gt; Y_1 Y_2 ... Y_k&lt;br /&gt;
             s.pop()&lt;br /&gt;
             for i = k downto 1&lt;br /&gt;
                 s.push(Y_i)&lt;br /&gt;
     while s.top() != bottom&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Пример ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt; &lt;br /&gt;
(1) \  E \to TE' \\&lt;br /&gt;
(2) \ E' \to +TE' \\&lt;br /&gt;
(3) \ E' \to \varepsilon \\&lt;br /&gt;
(4) \ T \to FT' \\&lt;br /&gt;
(5) \ T' \to \times FT' \\&lt;br /&gt;
(6) \ T' \to \varepsilon \\&lt;br /&gt;
(7) \ F \to n \\&lt;br /&gt;
(8) \ F \to (E)&lt;br /&gt;
&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| style=&amp;quot;background-color:#CCC;margin:0.5px&amp;quot;&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| &lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| n&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| (&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| )&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| +&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| *&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| $&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;E&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;E'&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;3 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;2 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;3 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;4 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;4 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;6 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;6 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;5 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;6 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;F&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;7 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;8 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\perp&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
[[Файл:Parse_stack.png|500px]]&lt;br /&gt;
&lt;br /&gt;
== Примечания ==&lt;br /&gt;
&amp;lt;references/&amp;gt;&lt;br /&gt;
== Источники информации ==&lt;br /&gt;
* Альфред Ахо, Рави Сети, Джеффри Ульман. Компиляторы. Принципы, технологии, инструменты. Издательство Вильямс. Второе издание. 2008. Стр. 288 {{---}} 294.&lt;br /&gt;
&lt;br /&gt;
[[Категория: Методы трансляции]]&lt;br /&gt;
[[Категория: Нисходящий разбор]]&lt;/div&gt;</summary>
		<author><name>194.85.160.130</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D1%80%D0%B5%D0%B4%D0%B8%D0%BA%D1%82%D0%B8%D0%B2%D0%BD%D1%8B%D0%B9_%D1%81%D0%B8%D0%BD%D1%82%D0%B0%D0%BA%D1%81%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B8%D0%B9_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=46899</id>
		<title>Предиктивный синтаксический анализ</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D1%80%D0%B5%D0%B4%D0%B8%D0%BA%D1%82%D0%B8%D0%B2%D0%BD%D1%8B%D0%B9_%D1%81%D0%B8%D0%BD%D1%82%D0%B0%D0%BA%D1%81%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B8%D0%B9_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=46899"/>
				<updated>2015-05-25T10:55:47Z</updated>
		
		<summary type="html">&lt;p&gt;194.85.160.130: /* Общая схема построения рекурсивных парсеров с помощью FIRST и FOLLOW */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{В разработке}}&lt;br /&gt;
Для [[LL(k)-грамматики, множества FIRST и FOLLOW | LL(1)-грамматик]] возможна автоматическая генерация парсеров, если известны множества FIRST и FOLLOW. Существуют общедоступные генераторы: ANTLR&amp;lt;ref&amp;gt;[http://www.antlr.org/ ANTLR {{---}} Parser generator]&amp;lt;/ref&amp;gt;, Bison&amp;lt;ref&amp;gt;[http://www.gnu.org/software/bison/ Bison {{---}} GNU Project]&amp;lt;/ref&amp;gt;, Yacc&amp;lt;ref&amp;gt;[http://dinosaur.compilertools.net/ Lex &amp;amp; Yacc {{---}} A Lexical Analyzer Generator and Yet Another Compiler-Compiler]&amp;lt;/ref&amp;gt;, Happy&amp;lt;ref&amp;gt;[https://www.haskell.org/happy/ Happy {{---}} The Parser Generator for Haskell]&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Общая схема построения рекурсивных парсеров с помощью FIRST и FOLLOW ==&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;tex&amp;gt;\Gamma =\langle \Sigma, N, S, P \rangle&amp;lt;/tex&amp;gt; {{---}} LL(1)-грамматика. Построим для нее парсер.&lt;br /&gt;
&lt;br /&gt;
Для каждого нетерминала &amp;lt;tex&amp;gt;A : A \rightarrow \alpha_1 \mid \alpha_2 \mid \ldots \mid \alpha_k &amp;lt;/tex&amp;gt; создадим функцию &amp;lt;tex&amp;gt; \mathtt{A}() : \mathtt{Node} &amp;lt;/tex&amp;gt;, возвращающую фрагмент дерева разбора, выведенный из нетерминала &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Здесь &amp;lt;tex&amp;gt;\mathtt{Node}&amp;lt;/tex&amp;gt; {{---}} структура следующего вида:&lt;br /&gt;
 '''struct''' Node&lt;br /&gt;
     children : '''list&amp;lt;Node&amp;gt;'''&lt;br /&gt;
     value : '''string'''          &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// имя нетерминала или текст терминала&amp;lt;/font&amp;gt;&lt;br /&gt;
     '''function''' addChild('''Node''') &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// функция, подвешивающая поддерево к данному узлу&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Каждый момент времени парсер работает с определённым '''токеном''' (англ. ''token'') входного слово &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt;. Токен {{---}} один или несколько нетерминалов, для удобства объединяемые по смыслу в одну логическую единицы. Примерами токенов могут выступать следующие лексические единицы:&lt;br /&gt;
* произвольный символ &amp;lt;tex&amp;gt; c &amp;lt;/tex&amp;gt;,&lt;br /&gt;
* целое слово, например &amp;lt;tex&amp;gt; public &amp;lt;/tex&amp;gt;,&lt;br /&gt;
* число со знаком, обозначаемое далее для краткости как &amp;lt;tex&amp;gt; n &amp;lt;/tex&amp;gt;.&lt;br /&gt;
В общем случае, токеном может являться любое слово, удовлетворяющее произвольному [[Регулярные языки: два определения и их эквивалентность | регулярному выражению]].&lt;br /&gt;
&lt;br /&gt;
[[Файл:string_token.png|300px]]&lt;br /&gt;
&lt;br /&gt;
В псевдокоде используются следующие обозначения:&lt;br /&gt;
* &amp;lt;tex&amp;gt;\mathtt{curToken}&amp;lt;/tex&amp;gt; {{---}} текущий токен строки,&lt;br /&gt;
* &amp;lt;tex&amp;gt;\mathtt{nextToken()}&amp;lt;/tex&amp;gt; {{---}} функция, записывающая в &amp;lt;tex&amp;gt;\mathtt{curToken}&amp;lt;/tex&amp;gt; следующий за ним токен.&lt;br /&gt;
&lt;br /&gt;
Тогда шаблон функции рекурсивного парсера для нетерминала &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; имеет вид:&lt;br /&gt;
&lt;br /&gt;
 A() : '''Node'''&lt;br /&gt;
     Node res = Node(&amp;quot;A&amp;quot;)&lt;br /&gt;
     '''switch''' (curToken) : &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// принимаем решение в зависимости от текущего токена строки&amp;lt;/font&amp;gt;&lt;br /&gt;
         '''case''' &amp;lt;tex&amp;gt;\mathrm{FIRST}(\alpha_1) \cup (\mathrm{FOLLOW}(A)\ \mathrm{if}\ \varepsilon \in \mathrm{FIRST}(\alpha_1))&amp;lt;/tex&amp;gt; :&lt;br /&gt;
             &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// &amp;lt;tex&amp;gt;\alpha_1 = X_1X_2 \ldots X_{t}&amp;lt;/tex&amp;gt; &amp;lt;/font&amp;gt;&lt;br /&gt;
             '''for''' i = 1 .. t&lt;br /&gt;
                 '''if''' &amp;lt;tex&amp;gt; X_i &amp;lt;/tex&amp;gt; {{---}} нетерминал&lt;br /&gt;
                     consume(&amp;lt;tex&amp;gt;X_i&amp;lt;/tex&amp;gt;)&lt;br /&gt;
                     res.addChild(Node(&amp;quot;&amp;lt;tex&amp;gt;X_i&amp;lt;/tex&amp;gt;&amp;quot;)&lt;br /&gt;
                     nextToken()&lt;br /&gt;
                 '''else''' &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// &amp;lt;tex&amp;gt;X_i&amp;lt;/tex&amp;gt; {{---}} терминал, нужно вызвать соответствующую ему функцию рекурсивного парсера &amp;lt;/font&amp;gt;&lt;br /&gt;
                     Node t = &amp;lt;tex&amp;gt;X_i()&amp;lt;/tex&amp;gt;&lt;br /&gt;
                     res.addChild(t)&lt;br /&gt;
             '''break'''&lt;br /&gt;
         '''case''' &amp;lt;tex&amp;gt;\mathrm{FIRST}(\alpha_2) \cup (\mathrm{FOLLOW}(A)\ \mathrm{if}\ \varepsilon \in \mathrm{FIRST}(\alpha_2))&amp;lt;/tex&amp;gt; : &lt;br /&gt;
             ...&lt;br /&gt;
             '''break'''&lt;br /&gt;
         ...&lt;br /&gt;
         '''default''' :&lt;br /&gt;
             &amp;lt;font color=&amp;quot;red&amp;quot;&amp;gt;error&amp;lt;/font&amp;gt;(&amp;quot;unexpected char&amp;quot;)&lt;br /&gt;
     '''return''' res&lt;br /&gt;
&lt;br /&gt;
 '''function''' consume(c: '''char''') &lt;br /&gt;
     '''if''' curToken != c&lt;br /&gt;
         &amp;lt;font color=&amp;quot;red&amp;quot;&amp;gt;error&amp;lt;/font&amp;gt;(&amp;quot;expected &amp;quot; + c)&lt;br /&gt;
     nextToken()&lt;br /&gt;
&lt;br /&gt;
Такой парсер не только разбирает строку, но и находит ошибки в неудовлетворяющих грамматике выражениях.&lt;br /&gt;
&lt;br /&gt;
== Пример ==&lt;br /&gt;
Рассмотрим построение парсера на примере LL(1)-грамматики арифметических выражений, которая уже была разобрана [[Построение FIRST и FOLLOW#Пример | ранее]]:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt; &lt;br /&gt;
E \to TE' \\&lt;br /&gt;
E' \to +TE' \mid \varepsilon \\&lt;br /&gt;
T \to FT' \\&lt;br /&gt;
T' \to \times FT' \mid \varepsilon \\&lt;br /&gt;
F \to n \mid (E)&lt;br /&gt;
&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Напомним, что множества &amp;lt;tex&amp;gt;\mathrm{FIRST}&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\mathrm{FOLLOW}&amp;lt;/tex&amp;gt; для неё выглядят так:&lt;br /&gt;
&lt;br /&gt;
{| style=&amp;quot;background-color:#CCC;margin:0.5px&amp;quot;&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| Правило&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| FIRST&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| FOLLOW&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;E&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ n,\ (\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ \$,\ )\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;E'&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ +,\ \varepsilon\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ \$,\ )\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ n,\ (\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ +,\ \$\ ,\ )\ \}&amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ \times,\ \varepsilon\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ +,\ \$\ ,\ )\ \}&amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;F&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ n,\ (\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ \times, \ +,\ \$\ ,\ )\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Псевдокоды ===&lt;br /&gt;
Построим функции обработки некоторых нетерминалов, используя описанный выше шаблон:&lt;br /&gt;
&lt;br /&gt;
 E() : '''Node'''&lt;br /&gt;
     Node res = Node(&amp;quot;E&amp;quot;)&lt;br /&gt;
     '''switch''' (curToken)&lt;br /&gt;
         '''case''' &amp;lt;tex&amp;gt; n,\ ( &amp;lt;/tex&amp;gt; :&lt;br /&gt;
             res.addChild(T())&lt;br /&gt;
             res.addChild(E'())&lt;br /&gt;
             '''break'''&lt;br /&gt;
         '''default''' :&lt;br /&gt;
             error(&amp;quot;unexpected char&amp;quot;)&lt;br /&gt;
     '''return''' res&lt;br /&gt;
&lt;br /&gt;
 E'()&lt;br /&gt;
     res = Node(&amp;quot;E'&amp;quot;)&lt;br /&gt;
     switch(curToken) &lt;br /&gt;
         case '+' :&lt;br /&gt;
             consume('+')&lt;br /&gt;
             res.addChild(Node(&amp;quot;+&amp;quot;))&lt;br /&gt;
             res.addChild(T())&lt;br /&gt;
             res.addChild(E'())&lt;br /&gt;
             break&lt;br /&gt;
         case '$', ')' :&lt;br /&gt;
             break&lt;br /&gt;
         default :&lt;br /&gt;
             error(&amp;quot;unexpected char&amp;quot;)&lt;br /&gt;
      return res&lt;br /&gt;
&lt;br /&gt;
 F()&lt;br /&gt;
     res = Node(&amp;quot;F&amp;quot;)&lt;br /&gt;
     switch(curToken)&lt;br /&gt;
         case 'n' :&lt;br /&gt;
             consume('n')&lt;br /&gt;
             res.addChild(Node(&amp;quot;n&amp;quot;))&lt;br /&gt;
             break&lt;br /&gt;
         case '(' :&lt;br /&gt;
             consume('(')&lt;br /&gt;
             res.addChild(Node(&amp;quot;(&amp;quot;))&lt;br /&gt;
             res.addChild(E())&lt;br /&gt;
             consume(')')&lt;br /&gt;
             res.addChild(Node(&amp;quot;)&amp;quot;))&lt;br /&gt;
         default :&lt;br /&gt;
             error(&amp;quot;unexpected char&amp;quot;)&lt;br /&gt;
     return res&lt;br /&gt;
&lt;br /&gt;
Функции для &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt; строятся аналогично.&lt;br /&gt;
&lt;br /&gt;
=== Дерево разбора ===&lt;br /&gt;
&lt;br /&gt;
Рассмотрим дерево разбора для выражения (1 + 2) * 3 и несколько первых шагов алгоритма рекурсивного разбора. Сначала вызывается функция стартового нетерминала грамматики, то есть &amp;lt;tex&amp;gt;E&amp;lt;/tex&amp;gt;. Так как первым токеном является '(', то будет использовано первое правило разбора &amp;lt;tex&amp;gt;TE'&amp;lt;/tex&amp;gt;. Поэтому к вершине с меткой &amp;lt;tex&amp;gt;E&amp;lt;/tex&amp;gt; добавятся два ребёнка: &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;E'&amp;lt;/tex&amp;gt;. А рекурсивный разборщик перейдёт к нетерминалу &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt;. По-прежнему curToken равен '(', поэтому в &amp;lt;tex&amp;gt;F&amp;lt;/tex&amp;gt; сработает второй case, первым ребёнком добавится '(', curToken станет равен &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt;, а разборщик перейдёт к нетерминалу &amp;lt;tex&amp;gt;E&amp;lt;/tex&amp;gt;. После того как выражение после '(', которое выводится из &amp;lt;tex&amp;gt;E&amp;lt;/tex&amp;gt;, будет полностью разобрано, функция рекурсивного разбора для &amp;lt;tex&amp;gt;F&amp;lt;/tex&amp;gt; добавит ')' последним сыном к этому нетерминалу. &lt;br /&gt;
&lt;br /&gt;
Продолжая в том же духе, мы построим всё дерево разбора данного выражения.&lt;br /&gt;
&lt;br /&gt;
[[Файл:parse_ex1.png|400px|thumb|center|Дерево разбора выражения (1 + 2) * 3]]&lt;br /&gt;
&lt;br /&gt;
== Нерекурсивный нисходящий парсер ==&lt;br /&gt;
&lt;br /&gt;
[[Файл:Parse_table.png|350px|right]]&lt;br /&gt;
&lt;br /&gt;
Рекурсивные разборщики можно генерировать автоматически, зная множества FIRST и FOLLOW, так как они имеют достаточно прозрачный шаблон построения. Альтернативным способом осуществления нисходящего синтаксического анализа является построение нерекурсивного нисходящего парсера. Его можно построить с помощью явного использования стека (вместо неявного при рекурсивных вызовах). Такое анализатор имитирует левое порождение.  &lt;br /&gt;
&lt;br /&gt;
Нерекурсивный предиктивный синтаксический анализатор содержит дополнительно стек, содержащий последовательность терминалов и нетерминалов, и таблицу синтаксического анализа. На стеке располагается последовательность символов грамматики с маркером конца строки $ на дне. В начале процесса анализа строки стек содержит стартовый нетерминал грамматики непосредственно над символом $. Таблица синтаксического анализа представляет собой двухмерный массив М[X, а], где X — нетерминал, а — терминал или символ $.&lt;br /&gt;
&lt;br /&gt;
Нерекурсивный синтаксический анализатор смотрит на текущий токен строки a и на символ на вершине стека X, а затем принимает решение в зависимости от одного из возникающих ниже случаев:&lt;br /&gt;
* если Х=curToken=$, синтаксический анализатор прекращает работу, так как разбор строки завершён,&lt;br /&gt;
* eсли Х=curToken≠$, синтаксический анализатор снимает со стека X и перемещает указатель входного потока к следующему токену (то есть вызывает nextToken),&lt;br /&gt;
* eсли X представляет собой нетерминал, программа рассматривает запись M[Х,а] таблицы разбора М. Эта запись представляет собой либо X-продукцию грамматики, либо запись об ошибке. Если, например, М[Х,а] = {X → UVW}, синтаксический анализатор замещает X на вершине стека на WVU (с U на вершине стека). В кач-ве выхода синтаксический анализатор просто выводит использованную продукцию. Если M[Х,а] = error, синтаксический анализатор вызывает программу восстановления после ошибки.&lt;br /&gt;
&lt;br /&gt;
=== Псевдокод ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
 function nonRecursiveParser(w : String):&lt;br /&gt;
     s : Stack&lt;br /&gt;
     s.push(bottom)&lt;br /&gt;
     s.push(Start)&lt;br /&gt;
     do &lt;br /&gt;
         X = s.top()&lt;br /&gt;
         if X == c&lt;br /&gt;
             c = nextToken()&lt;br /&gt;
             s.pop()&lt;br /&gt;
         else if X in Sigma&lt;br /&gt;
             error(&amp;quot;unexpected symbol&amp;quot;)&lt;br /&gt;
         else if M[X, c] == undefined // запись об ошибке&lt;br /&gt;
             error(&amp;quot;unexpected symbol&amp;quot;)&lt;br /&gt;
         else if M[X, c] == X -&amp;gt; Y_1 Y_2 ... Y_k&lt;br /&gt;
             s.pop()&lt;br /&gt;
             for i = k downto 1&lt;br /&gt;
                 s.push(Y_i)&lt;br /&gt;
     while s.top() != bottom&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Пример ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt; &lt;br /&gt;
(1) \  E \to TE' \\&lt;br /&gt;
(2) \ E' \to +TE' \\&lt;br /&gt;
(3) \ E' \to \varepsilon \\&lt;br /&gt;
(4) \ T \to FT' \\&lt;br /&gt;
(5) \ T' \to \times FT' \\&lt;br /&gt;
(6) \ T' \to \varepsilon \\&lt;br /&gt;
(7) \ F \to n \\&lt;br /&gt;
(8) \ F \to (E)&lt;br /&gt;
&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| style=&amp;quot;background-color:#CCC;margin:0.5px&amp;quot;&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| &lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| n&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| (&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| )&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| +&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| *&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| $&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;E&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;E'&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;3 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;2 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;3 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;4 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;4 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;6 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;6 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;5 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;6 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;F&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;7 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;8 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\perp&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
[[Файл:Parse_stack.png|500px]]&lt;br /&gt;
&lt;br /&gt;
== Примечания ==&lt;br /&gt;
&amp;lt;references/&amp;gt;&lt;br /&gt;
== Источники информации ==&lt;br /&gt;
* Альфред Ахо, Рави Сети, Джеффри Ульман. Компиляторы. Принципы, технологии, инструменты. Издательство Вильямс. Второе издание. 2008. Стр. 288 {{---}} 294.&lt;br /&gt;
&lt;br /&gt;
[[Категория: Методы трансляции]]&lt;br /&gt;
[[Категория: Нисходящий разбор]]&lt;/div&gt;</summary>
		<author><name>194.85.160.130</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D1%80%D0%B5%D0%B4%D0%B8%D0%BA%D1%82%D0%B8%D0%B2%D0%BD%D1%8B%D0%B9_%D1%81%D0%B8%D0%BD%D1%82%D0%B0%D0%BA%D1%81%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B8%D0%B9_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=46898</id>
		<title>Предиктивный синтаксический анализ</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D1%80%D0%B5%D0%B4%D0%B8%D0%BA%D1%82%D0%B8%D0%B2%D0%BD%D1%8B%D0%B9_%D1%81%D0%B8%D0%BD%D1%82%D0%B0%D0%BA%D1%81%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B8%D0%B9_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=46898"/>
				<updated>2015-05-25T10:55:02Z</updated>
		
		<summary type="html">&lt;p&gt;194.85.160.130: /* Пример */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{В разработке}}&lt;br /&gt;
Для [[LL(k)-грамматики, множества FIRST и FOLLOW | LL(1)-грамматик]] возможна автоматическая генерация парсеров, если известны множества FIRST и FOLLOW. Существуют общедоступные генераторы: ANTLR&amp;lt;ref&amp;gt;[http://www.antlr.org/ ANTLR {{---}} Parser generator]&amp;lt;/ref&amp;gt;, Bison&amp;lt;ref&amp;gt;[http://www.gnu.org/software/bison/ Bison {{---}} GNU Project]&amp;lt;/ref&amp;gt;, Yacc&amp;lt;ref&amp;gt;[http://dinosaur.compilertools.net/ Lex &amp;amp; Yacc {{---}} A Lexical Analyzer Generator and Yet Another Compiler-Compiler]&amp;lt;/ref&amp;gt;, Happy&amp;lt;ref&amp;gt;[https://www.haskell.org/happy/ Happy {{---}} The Parser Generator for Haskell]&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Общая схема построения рекурсивных парсеров с помощью FIRST и FOLLOW ==&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;tex&amp;gt;\Gamma =\langle \Sigma, N, S, P \rangle&amp;lt;/tex&amp;gt; {{---}} LL(1)-грамматика. Построим для нее парсер.&lt;br /&gt;
&lt;br /&gt;
Для каждого нетерминала &amp;lt;tex&amp;gt;A : A \rightarrow \alpha_1 \mid \alpha_2 \mid \ldots \mid \alpha_k &amp;lt;/tex&amp;gt; создадим функцию &amp;lt;tex&amp;gt; \mathtt{A}() : \mathtt{Node} &amp;lt;/tex&amp;gt;, возвращающую фрагмент дерева разбора, выведенный из нетерминала &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Здесь &amp;lt;tex&amp;gt;\mathtt{Node}&amp;lt;/tex&amp;gt; {{---}} структура следующего вида:&lt;br /&gt;
 '''struct''' Node&lt;br /&gt;
     children : '''list&amp;lt;Node&amp;gt;'''&lt;br /&gt;
     value : '''string'''          &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// имя нетерминала или текст терминала&amp;lt;/font&amp;gt;&lt;br /&gt;
     '''function''' addChild('''Node''') &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// функция, подвешивающая поддерево к данному узлу&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Каждый момент времени парсер работает с определённым '''токеном''' (англ. ''token'') входного слово &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt;. Токен {{---}} один или несколько нетерминалов, для удобства объединяемые по смыслу в одну логическую единицы. Примерами токенов могут выступать следующие лексические единицы:&lt;br /&gt;
* произвольный символ &amp;lt;tex&amp;gt; c &amp;lt;/tex&amp;gt;,&lt;br /&gt;
* целое слово, например &amp;lt;tex&amp;gt; public &amp;lt;/tex&amp;gt;,&lt;br /&gt;
* число со знаком, обозначаемое далее для краткости как &amp;lt;tex&amp;gt; n &amp;lt;/tex&amp;gt;,&lt;br /&gt;
В общем случае, токеном может являться любое слово, удовлетворяющее произвольному [[Регулярные языки: два определения и их эквивалентность | регулярному выражению]].&lt;br /&gt;
&lt;br /&gt;
[[Файл:string_token.png|300px]]&lt;br /&gt;
&lt;br /&gt;
В псевдокоде используются следующие обозначения:&lt;br /&gt;
* &amp;lt;tex&amp;gt;\mathtt{curToken}&amp;lt;/tex&amp;gt; {{---}} текущий токен строки,&lt;br /&gt;
* &amp;lt;tex&amp;gt;\mathtt{nextToken()}&amp;lt;/tex&amp;gt; {{---}} функция, записывающая в &amp;lt;tex&amp;gt;\mathtt{curToken}&amp;lt;/tex&amp;gt; следующий за ним токен.&lt;br /&gt;
&lt;br /&gt;
Тогда шаблон функции рекурсивного парсера для нетерминала &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; имеет вид:&lt;br /&gt;
&lt;br /&gt;
 A() : '''Node'''&lt;br /&gt;
     Node res = Node(&amp;quot;A&amp;quot;)&lt;br /&gt;
     '''switch''' (curToken) : &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// принимаем решение в зависимости от текущего токена строки&amp;lt;/font&amp;gt;&lt;br /&gt;
         '''case''' &amp;lt;tex&amp;gt;\mathrm{FIRST}(\alpha_1) \cup (\mathrm{FOLLOW}(A)\ \mathrm{if}\ \varepsilon \in \mathrm{FIRST}(\alpha_1))&amp;lt;/tex&amp;gt; :&lt;br /&gt;
             &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// &amp;lt;tex&amp;gt;\alpha_1 = X_1X_2 \ldots X_{t}&amp;lt;/tex&amp;gt; &amp;lt;/font&amp;gt;&lt;br /&gt;
             '''for''' i = 1 .. t&lt;br /&gt;
                 '''if''' &amp;lt;tex&amp;gt; X_i &amp;lt;/tex&amp;gt; {{---}} нетерминал&lt;br /&gt;
                     consume(&amp;lt;tex&amp;gt;X_i&amp;lt;/tex&amp;gt;)&lt;br /&gt;
                     res.addChild(Node(&amp;quot;&amp;lt;tex&amp;gt;X_i&amp;lt;/tex&amp;gt;&amp;quot;)&lt;br /&gt;
                     nextToken()&lt;br /&gt;
                 '''else''' &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// &amp;lt;tex&amp;gt;X_i&amp;lt;/tex&amp;gt; {{---}} терминал, нужно вызвать соответствующую ему функцию рекурсивного парсера &amp;lt;/font&amp;gt;&lt;br /&gt;
                     Node t = &amp;lt;tex&amp;gt;X_i()&amp;lt;/tex&amp;gt;&lt;br /&gt;
                     res.addChild(t)&lt;br /&gt;
             '''break'''&lt;br /&gt;
         '''case''' &amp;lt;tex&amp;gt;\mathrm{FIRST}(\alpha_2) \cup (\mathrm{FOLLOW}(A)\ \mathrm{if}\ \varepsilon \in \mathrm{FIRST}(\alpha_2))&amp;lt;/tex&amp;gt; : &lt;br /&gt;
             ...&lt;br /&gt;
             '''break'''&lt;br /&gt;
         ...&lt;br /&gt;
         '''default''' :&lt;br /&gt;
             &amp;lt;font color=&amp;quot;red&amp;quot;&amp;gt;error&amp;lt;/font&amp;gt;(&amp;quot;unexpected char&amp;quot;)&lt;br /&gt;
     '''return''' res&lt;br /&gt;
&lt;br /&gt;
 '''function''' consume(c: '''char''') &lt;br /&gt;
     '''if''' curToken != c&lt;br /&gt;
         &amp;lt;font color=&amp;quot;red&amp;quot;&amp;gt;error&amp;lt;/font&amp;gt;(&amp;quot;expected &amp;quot; + c)&lt;br /&gt;
     nextToken()&lt;br /&gt;
&lt;br /&gt;
Такой парсер не только разбирает строку, но и находит ошибки в неудовлетворяющих грамматике выражениях.&lt;br /&gt;
&lt;br /&gt;
== Пример ==&lt;br /&gt;
Рассмотрим построение парсера на примере LL(1)-грамматики арифметических выражений, которая уже была разобрана [[Построение FIRST и FOLLOW#Пример | ранее]]:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt; &lt;br /&gt;
E \to TE' \\&lt;br /&gt;
E' \to +TE' \mid \varepsilon \\&lt;br /&gt;
T \to FT' \\&lt;br /&gt;
T' \to \times FT' \mid \varepsilon \\&lt;br /&gt;
F \to n \mid (E)&lt;br /&gt;
&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Напомним, что множества &amp;lt;tex&amp;gt;\mathrm{FIRST}&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\mathrm{FOLLOW}&amp;lt;/tex&amp;gt; для неё выглядят так:&lt;br /&gt;
&lt;br /&gt;
{| style=&amp;quot;background-color:#CCC;margin:0.5px&amp;quot;&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| Правило&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| FIRST&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| FOLLOW&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;E&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ n,\ (\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ \$,\ )\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;E'&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ +,\ \varepsilon\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ \$,\ )\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ n,\ (\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ +,\ \$\ ,\ )\ \}&amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ \times,\ \varepsilon\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ +,\ \$\ ,\ )\ \}&amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;F&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ n,\ (\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ \times, \ +,\ \$\ ,\ )\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Псевдокоды ===&lt;br /&gt;
Построим функции обработки некоторых нетерминалов, используя описанный выше шаблон:&lt;br /&gt;
&lt;br /&gt;
 E() : '''Node'''&lt;br /&gt;
     Node res = Node(&amp;quot;E&amp;quot;)&lt;br /&gt;
     '''switch''' (curToken)&lt;br /&gt;
         '''case''' &amp;lt;tex&amp;gt; n,\ ( &amp;lt;/tex&amp;gt; :&lt;br /&gt;
             res.addChild(T())&lt;br /&gt;
             res.addChild(E'())&lt;br /&gt;
             '''break'''&lt;br /&gt;
         '''default''' :&lt;br /&gt;
             error(&amp;quot;unexpected char&amp;quot;)&lt;br /&gt;
     '''return''' res&lt;br /&gt;
&lt;br /&gt;
 E'()&lt;br /&gt;
     res = Node(&amp;quot;E'&amp;quot;)&lt;br /&gt;
     switch(curToken) &lt;br /&gt;
         case '+' :&lt;br /&gt;
             consume('+')&lt;br /&gt;
             res.addChild(Node(&amp;quot;+&amp;quot;))&lt;br /&gt;
             res.addChild(T())&lt;br /&gt;
             res.addChild(E'())&lt;br /&gt;
             break&lt;br /&gt;
         case '$', ')' :&lt;br /&gt;
             break&lt;br /&gt;
         default :&lt;br /&gt;
             error(&amp;quot;unexpected char&amp;quot;)&lt;br /&gt;
      return res&lt;br /&gt;
&lt;br /&gt;
 F()&lt;br /&gt;
     res = Node(&amp;quot;F&amp;quot;)&lt;br /&gt;
     switch(curToken)&lt;br /&gt;
         case 'n' :&lt;br /&gt;
             consume('n')&lt;br /&gt;
             res.addChild(Node(&amp;quot;n&amp;quot;))&lt;br /&gt;
             break&lt;br /&gt;
         case '(' :&lt;br /&gt;
             consume('(')&lt;br /&gt;
             res.addChild(Node(&amp;quot;(&amp;quot;))&lt;br /&gt;
             res.addChild(E())&lt;br /&gt;
             consume(')')&lt;br /&gt;
             res.addChild(Node(&amp;quot;)&amp;quot;))&lt;br /&gt;
         default :&lt;br /&gt;
             error(&amp;quot;unexpected char&amp;quot;)&lt;br /&gt;
     return res&lt;br /&gt;
&lt;br /&gt;
Функции для &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt; строятся аналогично.&lt;br /&gt;
&lt;br /&gt;
=== Дерево разбора ===&lt;br /&gt;
&lt;br /&gt;
Рассмотрим дерево разбора для выражения (1 + 2) * 3 и несколько первых шагов алгоритма рекурсивного разбора. Сначала вызывается функция стартового нетерминала грамматики, то есть &amp;lt;tex&amp;gt;E&amp;lt;/tex&amp;gt;. Так как первым токеном является '(', то будет использовано первое правило разбора &amp;lt;tex&amp;gt;TE'&amp;lt;/tex&amp;gt;. Поэтому к вершине с меткой &amp;lt;tex&amp;gt;E&amp;lt;/tex&amp;gt; добавятся два ребёнка: &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;E'&amp;lt;/tex&amp;gt;. А рекурсивный разборщик перейдёт к нетерминалу &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt;. По-прежнему curToken равен '(', поэтому в &amp;lt;tex&amp;gt;F&amp;lt;/tex&amp;gt; сработает второй case, первым ребёнком добавится '(', curToken станет равен &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt;, а разборщик перейдёт к нетерминалу &amp;lt;tex&amp;gt;E&amp;lt;/tex&amp;gt;. После того как выражение после '(', которое выводится из &amp;lt;tex&amp;gt;E&amp;lt;/tex&amp;gt;, будет полностью разобрано, функция рекурсивного разбора для &amp;lt;tex&amp;gt;F&amp;lt;/tex&amp;gt; добавит ')' последним сыном к этому нетерминалу. &lt;br /&gt;
&lt;br /&gt;
Продолжая в том же духе, мы построим всё дерево разбора данного выражения.&lt;br /&gt;
&lt;br /&gt;
[[Файл:parse_ex1.png|400px|thumb|center|Дерево разбора выражения (1 + 2) * 3]]&lt;br /&gt;
&lt;br /&gt;
== Нерекурсивный нисходящий парсер ==&lt;br /&gt;
&lt;br /&gt;
[[Файл:Parse_table.png|350px|right]]&lt;br /&gt;
&lt;br /&gt;
Рекурсивные разборщики можно генерировать автоматически, зная множества FIRST и FOLLOW, так как они имеют достаточно прозрачный шаблон построения. Альтернативным способом осуществления нисходящего синтаксического анализа является построение нерекурсивного нисходящего парсера. Его можно построить с помощью явного использования стека (вместо неявного при рекурсивных вызовах). Такое анализатор имитирует левое порождение.  &lt;br /&gt;
&lt;br /&gt;
Нерекурсивный предиктивный синтаксический анализатор содержит дополнительно стек, содержащий последовательность терминалов и нетерминалов, и таблицу синтаксического анализа. На стеке располагается последовательность символов грамматики с маркером конца строки $ на дне. В начале процесса анализа строки стек содержит стартовый нетерминал грамматики непосредственно над символом $. Таблица синтаксического анализа представляет собой двухмерный массив М[X, а], где X — нетерминал, а — терминал или символ $.&lt;br /&gt;
&lt;br /&gt;
Нерекурсивный синтаксический анализатор смотрит на текущий токен строки a и на символ на вершине стека X, а затем принимает решение в зависимости от одного из возникающих ниже случаев:&lt;br /&gt;
* если Х=curToken=$, синтаксический анализатор прекращает работу, так как разбор строки завершён,&lt;br /&gt;
* eсли Х=curToken≠$, синтаксический анализатор снимает со стека X и перемещает указатель входного потока к следующему токену (то есть вызывает nextToken),&lt;br /&gt;
* eсли X представляет собой нетерминал, программа рассматривает запись M[Х,а] таблицы разбора М. Эта запись представляет собой либо X-продукцию грамматики, либо запись об ошибке. Если, например, М[Х,а] = {X → UVW}, синтаксический анализатор замещает X на вершине стека на WVU (с U на вершине стека). В кач-ве выхода синтаксический анализатор просто выводит использованную продукцию. Если M[Х,а] = error, синтаксический анализатор вызывает программу восстановления после ошибки.&lt;br /&gt;
&lt;br /&gt;
=== Псевдокод ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
 function nonRecursiveParser(w : String):&lt;br /&gt;
     s : Stack&lt;br /&gt;
     s.push(bottom)&lt;br /&gt;
     s.push(Start)&lt;br /&gt;
     do &lt;br /&gt;
         X = s.top()&lt;br /&gt;
         if X == c&lt;br /&gt;
             c = nextToken()&lt;br /&gt;
             s.pop()&lt;br /&gt;
         else if X in Sigma&lt;br /&gt;
             error(&amp;quot;unexpected symbol&amp;quot;)&lt;br /&gt;
         else if M[X, c] == undefined // запись об ошибке&lt;br /&gt;
             error(&amp;quot;unexpected symbol&amp;quot;)&lt;br /&gt;
         else if M[X, c] == X -&amp;gt; Y_1 Y_2 ... Y_k&lt;br /&gt;
             s.pop()&lt;br /&gt;
             for i = k downto 1&lt;br /&gt;
                 s.push(Y_i)&lt;br /&gt;
     while s.top() != bottom&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Пример ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt; &lt;br /&gt;
(1) \  E \to TE' \\&lt;br /&gt;
(2) \ E' \to +TE' \\&lt;br /&gt;
(3) \ E' \to \varepsilon \\&lt;br /&gt;
(4) \ T \to FT' \\&lt;br /&gt;
(5) \ T' \to \times FT' \\&lt;br /&gt;
(6) \ T' \to \varepsilon \\&lt;br /&gt;
(7) \ F \to n \\&lt;br /&gt;
(8) \ F \to (E)&lt;br /&gt;
&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| style=&amp;quot;background-color:#CCC;margin:0.5px&amp;quot;&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| &lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| n&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| (&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| )&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| +&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| *&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| $&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;E&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;E'&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;3 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;2 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;3 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;4 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;4 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;6 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;6 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;5 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;6 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;F&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;7 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;8 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\perp&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
[[Файл:Parse_stack.png|500px]]&lt;br /&gt;
&lt;br /&gt;
== Примечания ==&lt;br /&gt;
&amp;lt;references/&amp;gt;&lt;br /&gt;
== Источники информации ==&lt;br /&gt;
* Альфред Ахо, Рави Сети, Джеффри Ульман. Компиляторы. Принципы, технологии, инструменты. Издательство Вильямс. Второе издание. 2008. Стр. 288 {{---}} 294.&lt;br /&gt;
&lt;br /&gt;
[[Категория: Методы трансляции]]&lt;br /&gt;
[[Категория: Нисходящий разбор]]&lt;/div&gt;</summary>
		<author><name>194.85.160.130</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D1%80%D0%B5%D0%B4%D0%B8%D0%BA%D1%82%D0%B8%D0%B2%D0%BD%D1%8B%D0%B9_%D1%81%D0%B8%D0%BD%D1%82%D0%B0%D0%BA%D1%81%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B8%D0%B9_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=46897</id>
		<title>Предиктивный синтаксический анализ</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D1%80%D0%B5%D0%B4%D0%B8%D0%BA%D1%82%D0%B8%D0%B2%D0%BD%D1%8B%D0%B9_%D1%81%D0%B8%D0%BD%D1%82%D0%B0%D0%BA%D1%81%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B8%D0%B9_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=46897"/>
				<updated>2015-05-25T10:50:42Z</updated>
		
		<summary type="html">&lt;p&gt;194.85.160.130: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{В разработке}}&lt;br /&gt;
Для [[LL(k)-грамматики, множества FIRST и FOLLOW | LL(1)-грамматик]] возможна автоматическая генерация парсеров, если известны множества FIRST и FOLLOW. Существуют общедоступные генераторы: ANTLR&amp;lt;ref&amp;gt;[http://www.antlr.org/ ANTLR {{---}} Parser generator]&amp;lt;/ref&amp;gt;, Bison&amp;lt;ref&amp;gt;[http://www.gnu.org/software/bison/ Bison {{---}} GNU Project]&amp;lt;/ref&amp;gt;, Yacc&amp;lt;ref&amp;gt;[http://dinosaur.compilertools.net/ Lex &amp;amp; Yacc {{---}} A Lexical Analyzer Generator and Yet Another Compiler-Compiler]&amp;lt;/ref&amp;gt;, Happy&amp;lt;ref&amp;gt;[https://www.haskell.org/happy/ Happy {{---}} The Parser Generator for Haskell]&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Общая схема построения рекурсивных парсеров с помощью FIRST и FOLLOW ==&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;tex&amp;gt;\Gamma =\langle \Sigma, N, S, P \rangle&amp;lt;/tex&amp;gt; {{---}} LL(1)-грамматика. Построим для нее парсер.&lt;br /&gt;
&lt;br /&gt;
Для каждого нетерминала &amp;lt;tex&amp;gt;A : A \rightarrow \alpha_1 \mid \alpha_2 \mid \ldots \mid \alpha_k &amp;lt;/tex&amp;gt; создадим функцию &amp;lt;tex&amp;gt; \mathtt{A}() : \mathtt{Node} &amp;lt;/tex&amp;gt;, возвращающую фрагмент дерева разбора, выведенный из нетерминала &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Здесь &amp;lt;tex&amp;gt;\mathtt{Node}&amp;lt;/tex&amp;gt; {{---}} структура следующего вида:&lt;br /&gt;
 '''struct''' Node&lt;br /&gt;
     children : '''list&amp;lt;Node&amp;gt;'''&lt;br /&gt;
     value : '''string'''          &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// имя нетерминала или текст терминала&amp;lt;/font&amp;gt;&lt;br /&gt;
     '''function''' addChild('''Node''') &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// функция, подвешивающая поддерево к данному узлу&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Каждый момент времени парсер работает с определённым '''токеном''' (англ. ''token'') входного слово &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt;. Токен {{---}} один или несколько нетерминалов, для удобства объединяемые по смыслу в одну логическую единицы. Примерами токенов могут выступать следующие лексические единицы:&lt;br /&gt;
* произвольный символ &amp;lt;tex&amp;gt; c &amp;lt;/tex&amp;gt;,&lt;br /&gt;
* целое слово, например &amp;lt;tex&amp;gt; public &amp;lt;/tex&amp;gt;,&lt;br /&gt;
* число со знаком, обозначаемое далее для краткости как &amp;lt;tex&amp;gt; n &amp;lt;/tex&amp;gt;,&lt;br /&gt;
В общем случае, токеном может являться любое слово, удовлетворяющее произвольному [[Регулярные языки: два определения и их эквивалентность | регулярному выражению]].&lt;br /&gt;
&lt;br /&gt;
[[Файл:string_token.png|300px]]&lt;br /&gt;
&lt;br /&gt;
В псевдокоде используются следующие обозначения:&lt;br /&gt;
* &amp;lt;tex&amp;gt;\mathtt{curToken}&amp;lt;/tex&amp;gt; {{---}} текущий токен строки,&lt;br /&gt;
* &amp;lt;tex&amp;gt;\mathtt{nextToken()}&amp;lt;/tex&amp;gt; {{---}} функция, записывающая в &amp;lt;tex&amp;gt;\mathtt{curToken}&amp;lt;/tex&amp;gt; следующий за ним токен.&lt;br /&gt;
&lt;br /&gt;
Тогда шаблон функции рекурсивного парсера для нетерминала &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; имеет вид:&lt;br /&gt;
&lt;br /&gt;
 A() : '''Node'''&lt;br /&gt;
     Node res = Node(&amp;quot;A&amp;quot;)&lt;br /&gt;
     '''switch''' (curToken) : &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// принимаем решение в зависимости от текущего токена строки&amp;lt;/font&amp;gt;&lt;br /&gt;
         '''case''' &amp;lt;tex&amp;gt;\mathrm{FIRST}(\alpha_1) \cup (\mathrm{FOLLOW}(A)\ \mathrm{if}\ \varepsilon \in \mathrm{FIRST}(\alpha_1))&amp;lt;/tex&amp;gt; :&lt;br /&gt;
             &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// &amp;lt;tex&amp;gt;\alpha_1 = X_1X_2 \ldots X_{t}&amp;lt;/tex&amp;gt; &amp;lt;/font&amp;gt;&lt;br /&gt;
             '''for''' i = 1 .. t&lt;br /&gt;
                 '''if''' &amp;lt;tex&amp;gt; X_i &amp;lt;/tex&amp;gt; {{---}} нетерминал&lt;br /&gt;
                     consume(&amp;lt;tex&amp;gt;X_i&amp;lt;/tex&amp;gt;)&lt;br /&gt;
                     res.addChild(Node(&amp;quot;&amp;lt;tex&amp;gt;X_i&amp;lt;/tex&amp;gt;&amp;quot;)&lt;br /&gt;
                     nextToken()&lt;br /&gt;
                 '''else''' &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// &amp;lt;tex&amp;gt;X_i&amp;lt;/tex&amp;gt; {{---}} терминал, нужно вызвать соответствующую ему функцию рекурсивного парсера &amp;lt;/font&amp;gt;&lt;br /&gt;
                     Node t = &amp;lt;tex&amp;gt;X_i()&amp;lt;/tex&amp;gt;&lt;br /&gt;
                     res.addChild(t)&lt;br /&gt;
             '''break'''&lt;br /&gt;
         '''case''' &amp;lt;tex&amp;gt;\mathrm{FIRST}(\alpha_2) \cup (\mathrm{FOLLOW}(A)\ \mathrm{if}\ \varepsilon \in \mathrm{FIRST}(\alpha_2))&amp;lt;/tex&amp;gt; : &lt;br /&gt;
             ...&lt;br /&gt;
             '''break'''&lt;br /&gt;
         ...&lt;br /&gt;
         '''default''' :&lt;br /&gt;
             &amp;lt;font color=&amp;quot;red&amp;quot;&amp;gt;error&amp;lt;/font&amp;gt;(&amp;quot;unexpected char&amp;quot;)&lt;br /&gt;
     '''return''' res&lt;br /&gt;
&lt;br /&gt;
 '''function''' consume(c: '''char''') &lt;br /&gt;
     '''if''' curToken != c&lt;br /&gt;
         &amp;lt;font color=&amp;quot;red&amp;quot;&amp;gt;error&amp;lt;/font&amp;gt;(&amp;quot;expected &amp;quot; + c)&lt;br /&gt;
     nextToken()&lt;br /&gt;
&lt;br /&gt;
Такой парсер не только разбирает строку, но и находит ошибки в неудовлетворяющих грамматике выражениях.&lt;br /&gt;
&lt;br /&gt;
== Пример ==&lt;br /&gt;
Рассмотрим построение парсера на примере LL(1)-грамматики арифметических выражений.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt; &lt;br /&gt;
E \to TE' \\&lt;br /&gt;
E' \to +TE' \mid \varepsilon \\&lt;br /&gt;
T \to FT' \\&lt;br /&gt;
T' \to \times FT' \mid \varepsilon \\&lt;br /&gt;
F \to n \mid (E)&lt;br /&gt;
&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Построим для нее множества &amp;lt;tex&amp;gt;FIRST&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;FOLLOW&amp;lt;/tex&amp;gt; (их построение подробно разобрано [[Построение FIRST и FOLLOW#Пример | здесь]]).&lt;br /&gt;
&lt;br /&gt;
{| style=&amp;quot;background-color:#CCC;margin:0.5px&amp;quot;&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| Правило&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| FIRST&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| FOLLOW&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;E&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ n,\ (\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ \$,\ )\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;E'&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ +,\ \varepsilon\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ \$,\ )\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ n,\ (\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ +,\ \$\ ,\ )\ \}&amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ \times,\ \varepsilon\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ +,\ \$\ ,\ )\ \}&amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;F&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ n,\ (\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ \times, \ +,\ \$\ ,\ )\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Псевдокоды ===&lt;br /&gt;
Построим функции обработки некоторых нетерминалов.&lt;br /&gt;
&lt;br /&gt;
 E()&lt;br /&gt;
     res = Node(&amp;quot;E&amp;quot;)&lt;br /&gt;
     switch(curToken)&lt;br /&gt;
         case 'n', '(' :&lt;br /&gt;
             res.addChild(T())&lt;br /&gt;
             res.addChild(E'())&lt;br /&gt;
             break&lt;br /&gt;
         default :&lt;br /&gt;
             error(&amp;quot;unexpected char&amp;quot;)&lt;br /&gt;
     return res&lt;br /&gt;
&lt;br /&gt;
 E'()&lt;br /&gt;
     res = Node(&amp;quot;E'&amp;quot;)&lt;br /&gt;
     switch(curToken) &lt;br /&gt;
         case '+' :&lt;br /&gt;
             consume('+')&lt;br /&gt;
             res.addChild(Node(&amp;quot;+&amp;quot;))&lt;br /&gt;
             res.addChild(T())&lt;br /&gt;
             res.addChild(E'())&lt;br /&gt;
             break&lt;br /&gt;
         case '$', ')' :&lt;br /&gt;
             break&lt;br /&gt;
         default :&lt;br /&gt;
             error(&amp;quot;unexpected char&amp;quot;)&lt;br /&gt;
      return res&lt;br /&gt;
&lt;br /&gt;
 F()&lt;br /&gt;
     res = Node(&amp;quot;F&amp;quot;)&lt;br /&gt;
     switch(curToken)&lt;br /&gt;
         case 'n' :&lt;br /&gt;
             consume('n')&lt;br /&gt;
             res.addChild(Node(&amp;quot;n&amp;quot;))&lt;br /&gt;
             break&lt;br /&gt;
         case '(' :&lt;br /&gt;
             consume('(')&lt;br /&gt;
             res.addChild(Node(&amp;quot;(&amp;quot;))&lt;br /&gt;
             res.addChild(E())&lt;br /&gt;
             consume(')')&lt;br /&gt;
             res.addChild(Node(&amp;quot;)&amp;quot;))&lt;br /&gt;
         default :&lt;br /&gt;
             error(&amp;quot;unexpected char&amp;quot;)&lt;br /&gt;
     return res&lt;br /&gt;
&lt;br /&gt;
Функции для &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt; строятся аналогично.&lt;br /&gt;
&lt;br /&gt;
=== Дерево разбора ===&lt;br /&gt;
&lt;br /&gt;
Рассмотрим дерево разбора для выражения (1 + 2) * 3 и несколько первых шагов алгоритма рекурсивного разбора. Сначала вызывается функция стартового нетерминала грамматики, то есть &amp;lt;tex&amp;gt;E&amp;lt;/tex&amp;gt;. Так как первым токеном является '(', то будет использовано первое правило разбора &amp;lt;tex&amp;gt;TE'&amp;lt;/tex&amp;gt;. Поэтому к вершине с меткой &amp;lt;tex&amp;gt;E&amp;lt;/tex&amp;gt; добавятся два ребёнка: &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;E'&amp;lt;/tex&amp;gt;. А рекурсивный разборщик перейдёт к нетерминалу &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt;. По-прежнему curToken равен '(', поэтому в &amp;lt;tex&amp;gt;F&amp;lt;/tex&amp;gt; сработает второй case, первым ребёнком добавится '(', curToken станет равен &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt;, а разборщик перейдёт к нетерминалу &amp;lt;tex&amp;gt;E&amp;lt;/tex&amp;gt;. После того как выражение после '(', которое выводится из &amp;lt;tex&amp;gt;E&amp;lt;/tex&amp;gt;, будет полностью разобрано, функция рекурсивного разбора для &amp;lt;tex&amp;gt;F&amp;lt;/tex&amp;gt; добавит ')' последним сыном к этому нетерминалу. &lt;br /&gt;
&lt;br /&gt;
Продолжая в том же духе, мы построим всё дерево разбора данного выражения.&lt;br /&gt;
&lt;br /&gt;
[[Файл:parse_ex1.png|400px|thumb|center|Дерево разбора выражения (1 + 2) * 3]]&lt;br /&gt;
&lt;br /&gt;
== Нерекурсивный нисходящий парсер ==&lt;br /&gt;
&lt;br /&gt;
[[Файл:Parse_table.png|350px|right]]&lt;br /&gt;
&lt;br /&gt;
Рекурсивные разборщики можно генерировать автоматически, зная множества FIRST и FOLLOW, так как они имеют достаточно прозрачный шаблон построения. Альтернативным способом осуществления нисходящего синтаксического анализа является построение нерекурсивного нисходящего парсера. Его можно построить с помощью явного использования стека (вместо неявного при рекурсивных вызовах). Такое анализатор имитирует левое порождение.  &lt;br /&gt;
&lt;br /&gt;
Нерекурсивный предиктивный синтаксический анализатор содержит дополнительно стек, содержащий последовательность терминалов и нетерминалов, и таблицу синтаксического анализа. На стеке располагается последовательность символов грамматики с маркером конца строки $ на дне. В начале процесса анализа строки стек содержит стартовый нетерминал грамматики непосредственно над символом $. Таблица синтаксического анализа представляет собой двухмерный массив М[X, а], где X — нетерминал, а — терминал или символ $.&lt;br /&gt;
&lt;br /&gt;
Нерекурсивный синтаксический анализатор смотрит на текущий токен строки a и на символ на вершине стека X, а затем принимает решение в зависимости от одного из возникающих ниже случаев:&lt;br /&gt;
* если Х=curToken=$, синтаксический анализатор прекращает работу, так как разбор строки завершён,&lt;br /&gt;
* eсли Х=curToken≠$, синтаксический анализатор снимает со стека X и перемещает указатель входного потока к следующему токену (то есть вызывает nextToken),&lt;br /&gt;
* eсли X представляет собой нетерминал, программа рассматривает запись M[Х,а] таблицы разбора М. Эта запись представляет собой либо X-продукцию грамматики, либо запись об ошибке. Если, например, М[Х,а] = {X → UVW}, синтаксический анализатор замещает X на вершине стека на WVU (с U на вершине стека). В кач-ве выхода синтаксический анализатор просто выводит использованную продукцию. Если M[Х,а] = error, синтаксический анализатор вызывает программу восстановления после ошибки.&lt;br /&gt;
&lt;br /&gt;
=== Псевдокод ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
 function nonRecursiveParser(w : String):&lt;br /&gt;
     s : Stack&lt;br /&gt;
     s.push(bottom)&lt;br /&gt;
     s.push(Start)&lt;br /&gt;
     do &lt;br /&gt;
         X = s.top()&lt;br /&gt;
         if X == c&lt;br /&gt;
             c = nextToken()&lt;br /&gt;
             s.pop()&lt;br /&gt;
         else if X in Sigma&lt;br /&gt;
             error(&amp;quot;unexpected symbol&amp;quot;)&lt;br /&gt;
         else if M[X, c] == undefined // запись об ошибке&lt;br /&gt;
             error(&amp;quot;unexpected symbol&amp;quot;)&lt;br /&gt;
         else if M[X, c] == X -&amp;gt; Y_1 Y_2 ... Y_k&lt;br /&gt;
             s.pop()&lt;br /&gt;
             for i = k downto 1&lt;br /&gt;
                 s.push(Y_i)&lt;br /&gt;
     while s.top() != bottom&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Пример ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt; &lt;br /&gt;
(1) \  E \to TE' \\&lt;br /&gt;
(2) \ E' \to +TE' \\&lt;br /&gt;
(3) \ E' \to \varepsilon \\&lt;br /&gt;
(4) \ T \to FT' \\&lt;br /&gt;
(5) \ T' \to \times FT' \\&lt;br /&gt;
(6) \ T' \to \varepsilon \\&lt;br /&gt;
(7) \ F \to n \\&lt;br /&gt;
(8) \ F \to (E)&lt;br /&gt;
&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| style=&amp;quot;background-color:#CCC;margin:0.5px&amp;quot;&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| &lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| n&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| (&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| )&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| +&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| *&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| $&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;E&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;E'&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;3 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;2 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;3 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;4 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;4 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;6 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;6 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;5 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;6 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;F&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;7 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;8 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\perp&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
[[Файл:Parse_stack.png|500px]]&lt;br /&gt;
&lt;br /&gt;
== Примечания ==&lt;br /&gt;
&amp;lt;references/&amp;gt;&lt;br /&gt;
== Источники информации ==&lt;br /&gt;
* Альфред Ахо, Рави Сети, Джеффри Ульман. Компиляторы. Принципы, технологии, инструменты. Издательство Вильямс. Второе издание. 2008. Стр. 288 {{---}} 294.&lt;br /&gt;
&lt;br /&gt;
[[Категория: Методы трансляции]]&lt;br /&gt;
[[Категория: Нисходящий разбор]]&lt;/div&gt;</summary>
		<author><name>194.85.160.130</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D1%80%D0%B5%D0%B4%D0%B8%D0%BA%D1%82%D0%B8%D0%B2%D0%BD%D1%8B%D0%B9_%D1%81%D0%B8%D0%BD%D1%82%D0%B0%D0%BA%D1%81%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B8%D0%B9_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=46896</id>
		<title>Предиктивный синтаксический анализ</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D1%80%D0%B5%D0%B4%D0%B8%D0%BA%D1%82%D0%B8%D0%B2%D0%BD%D1%8B%D0%B9_%D1%81%D0%B8%D0%BD%D1%82%D0%B0%D0%BA%D1%81%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B8%D0%B9_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=46896"/>
				<updated>2015-05-25T10:49:10Z</updated>
		
		<summary type="html">&lt;p&gt;194.85.160.130: /* Общая схема построения рекурсивных парсеров с помощью FIRST и FOLLOW */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{В разработке}}&lt;br /&gt;
Для [[LL(k)-грамматики, множества FIRST и FOLLOW | LL(1)-грамматик]] возможна автоматическая генерация парсеров, если известны множества FIRST и FOLLOW. Существуют общедоступные генераторы: ANTLR&amp;lt;ref&amp;gt;[http://www.antlr.org/ ANTLR {{---}} Parser generator]&amp;lt;/ref&amp;gt;, Bison&amp;lt;ref&amp;gt;[http://www.gnu.org/software/bison/ Bison {{---}} GNU Project]&amp;lt;/ref&amp;gt;, Yacc&amp;lt;ref&amp;gt;[http://dinosaur.compilertools.net/ Lex &amp;amp; Yacc {{---}} A Lexical Analyzer Generator and Yet Another Compiler-Compiler]&amp;lt;/ref&amp;gt;, Happy&amp;lt;ref&amp;gt;[https://www.haskell.org/happy/ Happy {{---}} The Parser Generator for Haskell]&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Общая схема построения рекурсивных парсеров с помощью FIRST и FOLLOW ==&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;tex&amp;gt;\Gamma =\langle \Sigma, N, S, P \rangle&amp;lt;/tex&amp;gt; {{---}} LL(1)-грамматика. Построим для нее парсер.&lt;br /&gt;
&lt;br /&gt;
Для каждого нетерминала &amp;lt;tex&amp;gt;A : A \rightarrow \alpha_1 \mid \alpha_2 \mid \ldots \mid \alpha_k &amp;lt;/tex&amp;gt; создадим функцию &amp;lt;tex&amp;gt; \mathtt{A}() : \mathtt{Node} &amp;lt;/tex&amp;gt;, возвращающую фрагмент дерева разбора, выведенный из нетерминала &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Здесь &amp;lt;tex&amp;gt;\mathtt{Node}&amp;lt;/tex&amp;gt; {{---}} структура следующего вида:&lt;br /&gt;
 '''struct''' Node&lt;br /&gt;
     children : '''list&amp;lt;Node&amp;gt;'''&lt;br /&gt;
     value : '''string'''          &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// имя нетерминала или текст терминала&amp;lt;/font&amp;gt;&lt;br /&gt;
     '''function''' addChild('''Node''') &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// функция, подвешивающая поддерево к данному узлу&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Каждый момент времени парсер работает с определённым '''токеном''' (англ. ''token'') входного слово &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt;. Токен {{---}} один или несколько нетерминалов, для удобства объединяемые по смыслу в одну логическую единицы. Примерами токенов могут выступать следующие лексические единицы:&lt;br /&gt;
* произвольный символ &amp;lt;tex&amp;gt; c &amp;lt;/tex&amp;gt;,&lt;br /&gt;
* целое слово, например &amp;lt;tex&amp;gt; public &amp;lt;/tex&amp;gt;,&lt;br /&gt;
* число со знаком, обозначаемое далее для краткости как &amp;lt;tex&amp;gt; n &amp;lt;/tex&amp;gt;,&lt;br /&gt;
В общем случае, токеном может являться любое слово, удовлетворяющее произвольному [[Регулярные языки: два определения и их эквивалентность | регулярному выражению]].&lt;br /&gt;
&lt;br /&gt;
[[Файл:string_token.png|300px]]&lt;br /&gt;
&lt;br /&gt;
В псевдокоде используются следующие обозначения:&lt;br /&gt;
* &amp;lt;tex&amp;gt;\mathtt{curToken}&amp;lt;/tex&amp;gt; {{---}} текущий токен строки,&lt;br /&gt;
* &amp;lt;tex&amp;gt;\mathtt{nextToken()}&amp;lt;/tex&amp;gt; {{---}} функция, записывающая в &amp;lt;tex&amp;gt;\mathtt{curToken}&amp;lt;/tex&amp;gt; следующий за ним токен.&lt;br /&gt;
&lt;br /&gt;
Тогда шаблон функции рекурсивного парсера для нетерминала &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; имеет вид:&lt;br /&gt;
&lt;br /&gt;
 A() : '''Node'''&lt;br /&gt;
     Node res = Node(&amp;quot;A&amp;quot;)&lt;br /&gt;
     '''switch''' (curToken) : &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// принимаем решение в зависимости от текущего токена строки&amp;lt;/font&amp;gt;&lt;br /&gt;
         '''case''' &amp;lt;tex&amp;gt;\mathrm{FIRST}(\alpha_1) \cup (\mathrm{FOLLOW}(A)\ \mathrm{if}\ \varepsilon \in \mathrm{FIRST}(\alpha_1))&amp;lt;/tex&amp;gt; :&lt;br /&gt;
             &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// &amp;lt;tex&amp;gt;\alpha_1 = X_1X_2 \ldots X_{t}&amp;lt;/tex&amp;gt; &amp;lt;/font&amp;gt;&lt;br /&gt;
             '''for''' i = 1 .. t&lt;br /&gt;
                 '''if''' &amp;lt;tex&amp;gt; X_i &amp;lt;/tex&amp;gt; {{---}} нетерминал&lt;br /&gt;
                     consume(&amp;lt;tex&amp;gt;X_i&amp;lt;/tex&amp;gt;)&lt;br /&gt;
                     res.addChild(Node(&amp;quot;&amp;lt;tex&amp;gt;X_i&amp;lt;/tex&amp;gt;&amp;quot;)&lt;br /&gt;
                     nextToken()&lt;br /&gt;
                 '''else''' &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// &amp;lt;tex&amp;gt;X_i&amp;lt;/tex&amp;gt; {{---}} терминал, нужно вызвать  &amp;lt;/font&amp;gt;&lt;br /&gt;
                     Node t = &amp;lt;tex&amp;gt;X_i()&amp;lt;/tex&amp;gt;&lt;br /&gt;
                     res.addChild(t)&lt;br /&gt;
             '''break'''&lt;br /&gt;
         '''case''' &amp;lt;tex&amp;gt;\mathrm{FIRST}(\alpha_2) \cup (\mathrm{FOLLOW}(A)\ \mathrm{if}\ \varepsilon \in \mathrm{FIRST}(\alpha_2))&amp;lt;/tex&amp;gt; : &lt;br /&gt;
             ...&lt;br /&gt;
             '''break'''&lt;br /&gt;
         ...&lt;br /&gt;
         '''default''' :&lt;br /&gt;
             &amp;lt;font color=&amp;quot;red&amp;quot;&amp;gt;error&amp;lt;/font&amp;gt;(&amp;quot;unexpected char&amp;quot;)&lt;br /&gt;
     '''return''' res&lt;br /&gt;
&lt;br /&gt;
 '''function''' consume(c: '''char''') &lt;br /&gt;
     '''if''' curToken != c&lt;br /&gt;
         &amp;lt;font color=&amp;quot;red&amp;quot;&amp;gt;error&amp;lt;/font&amp;gt;(&amp;quot;expected &amp;quot; + c)&lt;br /&gt;
     nextToken()&lt;br /&gt;
&lt;br /&gt;
Такой парсер не только разбирает строку, но и находит ошибки в неудовлетворяющих грамматике выражениях.&lt;br /&gt;
&lt;br /&gt;
== Пример ==&lt;br /&gt;
Рассмотрим построение парсера на примере LL(1)-грамматики арифметических выражений.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt; &lt;br /&gt;
E \to TE' \\&lt;br /&gt;
E' \to +TE' \mid \varepsilon \\&lt;br /&gt;
T \to FT' \\&lt;br /&gt;
T' \to \times FT' \mid \varepsilon \\&lt;br /&gt;
F \to n \mid (E)&lt;br /&gt;
&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Построим для нее множества &amp;lt;tex&amp;gt;FIRST&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;FOLLOW&amp;lt;/tex&amp;gt; (их построение подробно разобрано [[Построение FIRST и FOLLOW#Пример | здесь]]).&lt;br /&gt;
&lt;br /&gt;
{| style=&amp;quot;background-color:#CCC;margin:0.5px&amp;quot;&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| Правило&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| FIRST&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| FOLLOW&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;E&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ n,\ (\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ \$,\ )\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;E'&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ +,\ \varepsilon\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ \$,\ )\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ n,\ (\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ +,\ \$\ ,\ )\ \}&amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ \times,\ \varepsilon\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ +,\ \$\ ,\ )\ \}&amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;F&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ n,\ (\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ \times, \ +,\ \$\ ,\ )\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Псевдокоды ===&lt;br /&gt;
Построим функции обработки некоторых нетерминалов.&lt;br /&gt;
&lt;br /&gt;
 E()&lt;br /&gt;
     res = Node(&amp;quot;E&amp;quot;)&lt;br /&gt;
     switch(curToken)&lt;br /&gt;
         case 'n', '(' :&lt;br /&gt;
             res.addChild(T())&lt;br /&gt;
             res.addChild(E'())&lt;br /&gt;
             break&lt;br /&gt;
         default :&lt;br /&gt;
             error(&amp;quot;unexpected char&amp;quot;)&lt;br /&gt;
     return res&lt;br /&gt;
&lt;br /&gt;
 E'()&lt;br /&gt;
     res = Node(&amp;quot;E'&amp;quot;)&lt;br /&gt;
     switch(curToken) &lt;br /&gt;
         case '+' :&lt;br /&gt;
             consume('+')&lt;br /&gt;
             res.addChild(Node(&amp;quot;+&amp;quot;))&lt;br /&gt;
             res.addChild(T())&lt;br /&gt;
             res.addChild(E'())&lt;br /&gt;
             break&lt;br /&gt;
         case '$', ')' :&lt;br /&gt;
             break&lt;br /&gt;
         default :&lt;br /&gt;
             error(&amp;quot;unexpected char&amp;quot;)&lt;br /&gt;
      return res&lt;br /&gt;
&lt;br /&gt;
 F()&lt;br /&gt;
     res = Node(&amp;quot;F&amp;quot;)&lt;br /&gt;
     switch(curToken)&lt;br /&gt;
         case 'n' :&lt;br /&gt;
             consume('n')&lt;br /&gt;
             res.addChild(Node(&amp;quot;n&amp;quot;))&lt;br /&gt;
             break&lt;br /&gt;
         case '(' :&lt;br /&gt;
             consume('(')&lt;br /&gt;
             res.addChild(Node(&amp;quot;(&amp;quot;))&lt;br /&gt;
             res.addChild(E())&lt;br /&gt;
             consume(')')&lt;br /&gt;
             res.addChild(Node(&amp;quot;)&amp;quot;))&lt;br /&gt;
         default :&lt;br /&gt;
             error(&amp;quot;unexpected char&amp;quot;)&lt;br /&gt;
     return res&lt;br /&gt;
&lt;br /&gt;
Функции для &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt; строятся аналогично.&lt;br /&gt;
&lt;br /&gt;
=== Дерево разбора ===&lt;br /&gt;
&lt;br /&gt;
Рассмотрим дерево разбора для выражения (1 + 2) * 3 и несколько первых шагов алгоритма рекурсивного разбора. Сначала вызывается функция стартового нетерминала грамматики, то есть &amp;lt;tex&amp;gt;E&amp;lt;/tex&amp;gt;. Так как первым токеном является '(', то будет использовано первое правило разбора &amp;lt;tex&amp;gt;TE'&amp;lt;/tex&amp;gt;. Поэтому к вершине с меткой &amp;lt;tex&amp;gt;E&amp;lt;/tex&amp;gt; добавятся два ребёнка: &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;E'&amp;lt;/tex&amp;gt;. А рекурсивный разборщик перейдёт к нетерминалу &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt;. По-прежнему curToken равен '(', поэтому в &amp;lt;tex&amp;gt;F&amp;lt;/tex&amp;gt; сработает второй case, первым ребёнком добавится '(', curToken станет равен &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt;, а разборщик перейдёт к нетерминалу &amp;lt;tex&amp;gt;E&amp;lt;/tex&amp;gt;. После того как выражение после '(', которое выводится из &amp;lt;tex&amp;gt;E&amp;lt;/tex&amp;gt;, будет полностью разобрано, функция рекурсивного разбора для &amp;lt;tex&amp;gt;F&amp;lt;/tex&amp;gt; добавит ')' последним сыном к этому нетерминалу. &lt;br /&gt;
&lt;br /&gt;
Продолжая в том же духе, мы построим всё дерево разбора данного выражения.&lt;br /&gt;
&lt;br /&gt;
[[Файл:parse_ex1.png|400px|thumb|center|Дерево разбора выражения (1 + 2) * 3]]&lt;br /&gt;
&lt;br /&gt;
== Нерекурсивный нисходящий парсер ==&lt;br /&gt;
&lt;br /&gt;
[[Файл:Parse_table.png|350px|right]]&lt;br /&gt;
&lt;br /&gt;
Рекурсивные разборщики можно генерировать автоматически, зная множества FIRST и FOLLOW, так как они имеют достаточно прозрачный шаблон построения. Альтернативным способом осуществления нисходящего синтаксического анализа является построение нерекурсивного нисходящего парсера. Его можно построить с помощью явного использования стека (вместо неявного при рекурсивных вызовах). Такое анализатор имитирует левое порождение.  &lt;br /&gt;
&lt;br /&gt;
Нерекурсивный предиктивный синтаксический анализатор содержит дополнительно стек, содержащий последовательность терминалов и нетерминалов, и таблицу синтаксического анализа. На стеке располагается последовательность символов грамматики с маркером конца строки $ на дне. В начале процесса анализа строки стек содержит стартовый нетерминал грамматики непосредственно над символом $. Таблица синтаксического анализа представляет собой двухмерный массив М[X, а], где X — нетерминал, а — терминал или символ $.&lt;br /&gt;
&lt;br /&gt;
Нерекурсивный синтаксический анализатор смотрит на текущий токен строки a и на символ на вершине стека X, а затем принимает решение в зависимости от одного из возникающих ниже случаев:&lt;br /&gt;
* если Х=curToken=$, синтаксический анализатор прекращает работу, так как разбор строки завершён,&lt;br /&gt;
* eсли Х=curToken≠$, синтаксический анализатор снимает со стека X и перемещает указатель входного потока к следующему токену (то есть вызывает nextToken),&lt;br /&gt;
* eсли X представляет собой нетерминал, программа рассматривает запись M[Х,а] таблицы разбора М. Эта запись представляет собой либо X-продукцию грамматики, либо запись об ошибке. Если, например, М[Х,а] = {X → UVW}, синтаксический анализатор замещает X на вершине стека на WVU (с U на вершине стека). В кач-ве выхода синтаксический анализатор просто выводит использованную продукцию. Если M[Х,а] = error, синтаксический анализатор вызывает программу восстановления после ошибки.&lt;br /&gt;
&lt;br /&gt;
=== Псевдокод ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
 function nonRecursiveParser(w : String):&lt;br /&gt;
     s : Stack&lt;br /&gt;
     s.push(bottom)&lt;br /&gt;
     s.push(Start)&lt;br /&gt;
     do &lt;br /&gt;
         X = s.top()&lt;br /&gt;
         if X == c&lt;br /&gt;
             c = nextToken()&lt;br /&gt;
             s.pop()&lt;br /&gt;
         else if X in Sigma&lt;br /&gt;
             error(&amp;quot;unexpected symbol&amp;quot;)&lt;br /&gt;
         else if M[X, c] == undefined // запись об ошибке&lt;br /&gt;
             error(&amp;quot;unexpected symbol&amp;quot;)&lt;br /&gt;
         else if M[X, c] == X -&amp;gt; Y_1 Y_2 ... Y_k&lt;br /&gt;
             s.pop()&lt;br /&gt;
             for i = k downto 1&lt;br /&gt;
                 s.push(Y_i)&lt;br /&gt;
     while s.top() != bottom&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Пример ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt; &lt;br /&gt;
(1) \  E \to TE' \\&lt;br /&gt;
(2) \ E' \to +TE' \\&lt;br /&gt;
(3) \ E' \to \varepsilon \\&lt;br /&gt;
(4) \ T \to FT' \\&lt;br /&gt;
(5) \ T' \to \times FT' \\&lt;br /&gt;
(6) \ T' \to \varepsilon \\&lt;br /&gt;
(7) \ F \to n \\&lt;br /&gt;
(8) \ F \to (E)&lt;br /&gt;
&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| style=&amp;quot;background-color:#CCC;margin:0.5px&amp;quot;&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| &lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| n&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| (&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| )&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| +&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| *&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| $&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;E&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;E'&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;3 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;2 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;3 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;4 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;4 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;6 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;6 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;5 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;6 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;F&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;7 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;8 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\perp&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
[[Файл:Parse_stack.png|500px]]&lt;br /&gt;
&lt;br /&gt;
== Примечания ==&lt;br /&gt;
&amp;lt;references/&amp;gt;&lt;br /&gt;
== Источники информации ==&lt;br /&gt;
* Альфред Ахо, Рави Сети, Джеффри Ульман. Компиляторы. Принципы, технологии, инструменты. Издательство Вильямс. Второе издание. 2008. Стр. 288 {{---}} 294.&lt;br /&gt;
&lt;br /&gt;
[[Категория: Методы трансляции]]&lt;br /&gt;
[[Категория: Нисходящий разбор]]&lt;/div&gt;</summary>
		<author><name>194.85.160.130</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D1%80%D0%B5%D0%B4%D0%B8%D0%BA%D1%82%D0%B8%D0%B2%D0%BD%D1%8B%D0%B9_%D1%81%D0%B8%D0%BD%D1%82%D0%B0%D0%BA%D1%81%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B8%D0%B9_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=46895</id>
		<title>Предиктивный синтаксический анализ</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D1%80%D0%B5%D0%B4%D0%B8%D0%BA%D1%82%D0%B8%D0%B2%D0%BD%D1%8B%D0%B9_%D1%81%D0%B8%D0%BD%D1%82%D0%B0%D0%BA%D1%81%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B8%D0%B9_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=46895"/>
				<updated>2015-05-25T10:40:06Z</updated>
		
		<summary type="html">&lt;p&gt;194.85.160.130: /* Общая схема построения рекурсивных парсеров с помощью FIRST и FOLLOW */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{В разработке}}&lt;br /&gt;
Для [[LL(k)-грамматики, множества FIRST и FOLLOW | LL(1)-грамматик]] возможна автоматическая генерация парсеров, если известны множества FIRST и FOLLOW. Существуют общедоступные генераторы: ANTLR&amp;lt;ref&amp;gt;[http://www.antlr.org/ ANTLR {{---}} Parser generator]&amp;lt;/ref&amp;gt;, Bison&amp;lt;ref&amp;gt;[http://www.gnu.org/software/bison/ Bison {{---}} GNU Project]&amp;lt;/ref&amp;gt;, Yacc&amp;lt;ref&amp;gt;[http://dinosaur.compilertools.net/ Lex &amp;amp; Yacc {{---}} A Lexical Analyzer Generator and Yet Another Compiler-Compiler]&amp;lt;/ref&amp;gt;, Happy&amp;lt;ref&amp;gt;[https://www.haskell.org/happy/ Happy {{---}} The Parser Generator for Haskell]&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Общая схема построения рекурсивных парсеров с помощью FIRST и FOLLOW ==&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;tex&amp;gt;\Gamma =\langle \Sigma, N, S, P \rangle&amp;lt;/tex&amp;gt; {{---}} LL(1)-грамматика. Построим для нее парсер.&lt;br /&gt;
&lt;br /&gt;
Для каждого нетерминала &amp;lt;tex&amp;gt;A : A \rightarrow \alpha_1 \mid \alpha_2 \mid \ldots \mid \alpha_k &amp;lt;/tex&amp;gt; создадим функцию &amp;lt;tex&amp;gt; \mathtt{A}() : \mathtt{Node} &amp;lt;/tex&amp;gt;, возвращающую фрагмент дерева разбора, выведенный из нетерминала &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Здесь &amp;lt;tex&amp;gt;\mathtt{Node}&amp;lt;/tex&amp;gt; {{---}} структура следующего вида:&lt;br /&gt;
 '''struct''' Node&lt;br /&gt;
     children : '''list&amp;lt;Node&amp;gt;'''&lt;br /&gt;
     value : '''string'''          &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// имя нетерминала или текст терминала&amp;lt;/font&amp;gt;&lt;br /&gt;
     '''function''' addChild('''Node''') &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// функция, подвешивающая поддерево к данному узлу&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Каждый момент времени парсер работает с определённым '''токеном''' (англ. ''token'') входного слово &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt;. Токен {{---}} один или несколько нетерминалов, для удобства объединяемые по смыслу в одну логическую единицы. Примерами токенов могут выступать следующие лексические единицы:&lt;br /&gt;
* произвольный символ &amp;lt;tex&amp;gt; c &amp;lt;/tex&amp;gt;,&lt;br /&gt;
* целое слово, например &amp;lt;tex&amp;gt; public &amp;lt;/tex&amp;gt;,&lt;br /&gt;
* число со знаком, обозначаемое далее для краткости как &amp;lt;tex&amp;gt; n &amp;lt;/tex&amp;gt;,&lt;br /&gt;
В общем случае, токеном может являться любое слово, удовлетворяющее произвольному [[Регулярные языки: два определения и их эквивалентность | регулярному выражению]].&lt;br /&gt;
&lt;br /&gt;
[[Файл:string_token.png|300px]]&lt;br /&gt;
&lt;br /&gt;
В псевдокоде используются следующие обозначения:&lt;br /&gt;
* &amp;lt;tex&amp;gt;\mathtt{curToken}&amp;lt;/tex&amp;gt; {{---}} текущий токен строки,&lt;br /&gt;
* &amp;lt;tex&amp;gt;\mathtt{nextToken()}&amp;lt;/tex&amp;gt; {{---}} функция, записывающая в &amp;lt;tex&amp;gt;\mathtt{curToken}&amp;lt;/tex&amp;gt; следующий за ним токен.&lt;br /&gt;
&lt;br /&gt;
Тогда шаблон функции рекурсивного парсера для нетерминала &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; имеет вид:&lt;br /&gt;
&lt;br /&gt;
 A() : '''Node'''&lt;br /&gt;
     Node res = Node(&amp;quot;A&amp;quot;)&lt;br /&gt;
     '''switch''' (curToken) : &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// принимаем решение в зависимости от текущего токена строки&amp;lt;/font&amp;gt;&lt;br /&gt;
         '''case''' &amp;lt;tex&amp;gt;\mathrm{FIRST}(\alpha_1) \cup (\mathrm{FOLLOW}(A)\ \mathrm{if}\ \varepsilon \in \mathrm{FIRST}(\alpha_1))&amp;lt;/tex&amp;gt; :&lt;br /&gt;
            &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// &amp;lt;tex&amp;gt;\alpha_1 = X_1X_2 \ldots X_{t}&amp;lt;/tex&amp;gt; &amp;lt;/font&amp;gt;&lt;br /&gt;
            '''for''' i = 1 .. t&lt;br /&gt;
                '''if''' &amp;lt;tex&amp;gt; X_i &amp;lt;/tex&amp;gt; {{---}} нетерминал&lt;br /&gt;
                    consume(&amp;lt;tex&amp;gt;X_i&amp;lt;/tex&amp;gt;)&lt;br /&gt;
                    res.addChild(Node(&amp;quot;&amp;lt;tex&amp;gt;X_i&amp;lt;/tex&amp;gt;&amp;quot;)&lt;br /&gt;
                    nextToken()&lt;br /&gt;
                '''else''' &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// &amp;lt;tex&amp;gt;X_i&amp;lt;/tex&amp;gt; {{---}} терминал, нужно вызвать  &amp;lt;/font&amp;gt;&lt;br /&gt;
                    Node t = &amp;lt;tex&amp;gt;X_i()&amp;lt;/tex&amp;gt;&lt;br /&gt;
                    res.addChild(t)&lt;br /&gt;
             '''break'''&lt;br /&gt;
         '''case''' &amp;lt;tex&amp;gt;\mathrm{FIRST}(\alpha_2) \cup (\mathrm{FOLLOW}(A)\ \mathrm{if}\ \varepsilon \in \mathrm{FIRST}(\alpha_2))&amp;lt;/tex&amp;gt; : &lt;br /&gt;
             ...&lt;br /&gt;
             '''break'''&lt;br /&gt;
         ...&lt;br /&gt;
         '''default''' :&lt;br /&gt;
             &amp;lt;font color=&amp;quot;red&amp;quot;&amp;gt;error&amp;lt;/font&amp;gt;(&amp;quot;unexpected char&amp;quot;)&lt;br /&gt;
     '''return''' res&lt;br /&gt;
&lt;br /&gt;
 '''function''' consume(c: '''char''') &lt;br /&gt;
     '''if''' curToken != c&lt;br /&gt;
         &amp;lt;font color=&amp;quot;red&amp;quot;&amp;gt;error&amp;lt;/font&amp;gt;(&amp;quot;expected &amp;quot; + c)&lt;br /&gt;
     nextToken()&lt;br /&gt;
&lt;br /&gt;
Такой парсер не только разбирает строку, но и находит ошибки в неудовлетворяющих грамматике выражениях.&lt;br /&gt;
&lt;br /&gt;
== Пример ==&lt;br /&gt;
Рассмотрим построение парсера на примере LL(1)-грамматики арифметических выражений.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt; &lt;br /&gt;
E \to TE' \\&lt;br /&gt;
E' \to +TE' \mid \varepsilon \\&lt;br /&gt;
T \to FT' \\&lt;br /&gt;
T' \to \times FT' \mid \varepsilon \\&lt;br /&gt;
F \to n \mid (E)&lt;br /&gt;
&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Построим для нее множества &amp;lt;tex&amp;gt;FIRST&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;FOLLOW&amp;lt;/tex&amp;gt; (их построение подробно разобрано [[Построение FIRST и FOLLOW#Пример | здесь]]).&lt;br /&gt;
&lt;br /&gt;
{| style=&amp;quot;background-color:#CCC;margin:0.5px&amp;quot;&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| Правило&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| FIRST&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| FOLLOW&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;E&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ n,\ (\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ \$,\ )\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;E'&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ +,\ \varepsilon\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ \$,\ )\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ n,\ (\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ +,\ \$\ ,\ )\ \}&amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ \times,\ \varepsilon\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ +,\ \$\ ,\ )\ \}&amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;F&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ n,\ (\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ \times, \ +,\ \$\ ,\ )\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Псевдокоды ===&lt;br /&gt;
Построим функции обработки некоторых нетерминалов.&lt;br /&gt;
&lt;br /&gt;
 E()&lt;br /&gt;
     res = Node(&amp;quot;E&amp;quot;)&lt;br /&gt;
     switch(curToken)&lt;br /&gt;
         case 'n', '(' :&lt;br /&gt;
             res.addChild(T())&lt;br /&gt;
             res.addChild(E'())&lt;br /&gt;
             break&lt;br /&gt;
         default :&lt;br /&gt;
             error(&amp;quot;unexpected char&amp;quot;)&lt;br /&gt;
     return res&lt;br /&gt;
&lt;br /&gt;
 E'()&lt;br /&gt;
     res = Node(&amp;quot;E'&amp;quot;)&lt;br /&gt;
     switch(curToken) &lt;br /&gt;
         case '+' :&lt;br /&gt;
             consume('+')&lt;br /&gt;
             res.addChild(Node(&amp;quot;+&amp;quot;))&lt;br /&gt;
             res.addChild(T())&lt;br /&gt;
             res.addChild(E'())&lt;br /&gt;
             break&lt;br /&gt;
         case '$', ')' :&lt;br /&gt;
             break&lt;br /&gt;
         default :&lt;br /&gt;
             error(&amp;quot;unexpected char&amp;quot;)&lt;br /&gt;
      return res&lt;br /&gt;
&lt;br /&gt;
 F()&lt;br /&gt;
     res = Node(&amp;quot;F&amp;quot;)&lt;br /&gt;
     switch(curToken)&lt;br /&gt;
         case 'n' :&lt;br /&gt;
             consume('n')&lt;br /&gt;
             res.addChild(Node(&amp;quot;n&amp;quot;))&lt;br /&gt;
             break&lt;br /&gt;
         case '(' :&lt;br /&gt;
             consume('(')&lt;br /&gt;
             res.addChild(Node(&amp;quot;(&amp;quot;))&lt;br /&gt;
             res.addChild(E())&lt;br /&gt;
             consume(')')&lt;br /&gt;
             res.addChild(Node(&amp;quot;)&amp;quot;))&lt;br /&gt;
         default :&lt;br /&gt;
             error(&amp;quot;unexpected char&amp;quot;)&lt;br /&gt;
     return res&lt;br /&gt;
&lt;br /&gt;
Функции для &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt; строятся аналогично.&lt;br /&gt;
&lt;br /&gt;
=== Дерево разбора ===&lt;br /&gt;
&lt;br /&gt;
Рассмотрим дерево разбора для выражения (1 + 2) * 3 и несколько первых шагов алгоритма рекурсивного разбора. Сначала вызывается функция стартового нетерминала грамматики, то есть &amp;lt;tex&amp;gt;E&amp;lt;/tex&amp;gt;. Так как первым токеном является '(', то будет использовано первое правило разбора &amp;lt;tex&amp;gt;TE'&amp;lt;/tex&amp;gt;. Поэтому к вершине с меткой &amp;lt;tex&amp;gt;E&amp;lt;/tex&amp;gt; добавятся два ребёнка: &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;E'&amp;lt;/tex&amp;gt;. А рекурсивный разборщик перейдёт к нетерминалу &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt;. По-прежнему curToken равен '(', поэтому в &amp;lt;tex&amp;gt;F&amp;lt;/tex&amp;gt; сработает второй case, первым ребёнком добавится '(', curToken станет равен &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt;, а разборщик перейдёт к нетерминалу &amp;lt;tex&amp;gt;E&amp;lt;/tex&amp;gt;. После того как выражение после '(', которое выводится из &amp;lt;tex&amp;gt;E&amp;lt;/tex&amp;gt;, будет полностью разобрано, функция рекурсивного разбора для &amp;lt;tex&amp;gt;F&amp;lt;/tex&amp;gt; добавит ')' последним сыном к этому нетерминалу. &lt;br /&gt;
&lt;br /&gt;
Продолжая в том же духе, мы построим всё дерево разбора данного выражения.&lt;br /&gt;
&lt;br /&gt;
[[Файл:parse_ex1.png|400px|thumb|center|Дерево разбора выражения (1 + 2) * 3]]&lt;br /&gt;
&lt;br /&gt;
== Нерекурсивный нисходящий парсер ==&lt;br /&gt;
&lt;br /&gt;
[[Файл:Parse_table.png|350px|right]]&lt;br /&gt;
&lt;br /&gt;
Рекурсивные разборщики можно генерировать автоматически, зная множества FIRST и FOLLOW, так как они имеют достаточно прозрачный шаблон построения. Альтернативным способом осуществления нисходящего синтаксического анализа является построение нерекурсивного нисходящего парсера. Его можно построить с помощью явного использования стека (вместо неявного при рекурсивных вызовах). Такое анализатор имитирует левое порождение.  &lt;br /&gt;
&lt;br /&gt;
Нерекурсивный предиктивный синтаксический анализатор содержит дополнительно стек, содержащий последовательность терминалов и нетерминалов, и таблицу синтаксического анализа. На стеке располагается последовательность символов грамматики с маркером конца строки $ на дне. В начале процесса анализа строки стек содержит стартовый нетерминал грамматики непосредственно над символом $. Таблица синтаксического анализа представляет собой двухмерный массив М[X, а], где X — нетерминал, а — терминал или символ $.&lt;br /&gt;
&lt;br /&gt;
Нерекурсивный синтаксический анализатор смотрит на текущий токен строки a и на символ на вершине стека X, а затем принимает решение в зависимости от одного из возникающих ниже случаев:&lt;br /&gt;
* если Х=curToken=$, синтаксический анализатор прекращает работу, так как разбор строки завершён,&lt;br /&gt;
* eсли Х=curToken≠$, синтаксический анализатор снимает со стека X и перемещает указатель входного потока к следующему токену (то есть вызывает nextToken),&lt;br /&gt;
* eсли X представляет собой нетерминал, программа рассматривает запись M[Х,а] таблицы разбора М. Эта запись представляет собой либо X-продукцию грамматики, либо запись об ошибке. Если, например, М[Х,а] = {X → UVW}, синтаксический анализатор замещает X на вершине стека на WVU (с U на вершине стека). В кач-ве выхода синтаксический анализатор просто выводит использованную продукцию. Если M[Х,а] = error, синтаксический анализатор вызывает программу восстановления после ошибки.&lt;br /&gt;
&lt;br /&gt;
=== Псевдокод ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
 function nonRecursiveParser(w : String):&lt;br /&gt;
     s : Stack&lt;br /&gt;
     s.push(bottom)&lt;br /&gt;
     s.push(Start)&lt;br /&gt;
     do &lt;br /&gt;
         X = s.top()&lt;br /&gt;
         if X == c&lt;br /&gt;
             c = nextToken()&lt;br /&gt;
             s.pop()&lt;br /&gt;
         else if X in Sigma&lt;br /&gt;
             error(&amp;quot;unexpected symbol&amp;quot;)&lt;br /&gt;
         else if M[X, c] == undefined // запись об ошибке&lt;br /&gt;
             error(&amp;quot;unexpected symbol&amp;quot;)&lt;br /&gt;
         else if M[X, c] == X -&amp;gt; Y_1 Y_2 ... Y_k&lt;br /&gt;
             s.pop()&lt;br /&gt;
             for i = k downto 1&lt;br /&gt;
                 s.push(Y_i)&lt;br /&gt;
     while s.top() != bottom&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Пример ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt; &lt;br /&gt;
(1) \  E \to TE' \\&lt;br /&gt;
(2) \ E' \to +TE' \\&lt;br /&gt;
(3) \ E' \to \varepsilon \\&lt;br /&gt;
(4) \ T \to FT' \\&lt;br /&gt;
(5) \ T' \to \times FT' \\&lt;br /&gt;
(6) \ T' \to \varepsilon \\&lt;br /&gt;
(7) \ F \to n \\&lt;br /&gt;
(8) \ F \to (E)&lt;br /&gt;
&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| style=&amp;quot;background-color:#CCC;margin:0.5px&amp;quot;&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| &lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| n&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| (&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| )&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| +&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| *&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| $&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;E&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;E'&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;3 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;2 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;3 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;4 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;4 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;6 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;6 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;5 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;6 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;F&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;7 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;8 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\perp&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
[[Файл:Parse_stack.png|500px]]&lt;br /&gt;
&lt;br /&gt;
== Примечания ==&lt;br /&gt;
&amp;lt;references/&amp;gt;&lt;br /&gt;
== Источники информации ==&lt;br /&gt;
* Альфред Ахо, Рави Сети, Джеффри Ульман. Компиляторы. Принципы, технологии, инструменты. Издательство Вильямс. Второе издание. 2008. Стр. 288 {{---}} 294.&lt;br /&gt;
&lt;br /&gt;
[[Категория: Методы трансляции]]&lt;br /&gt;
[[Категория: Нисходящий разбор]]&lt;/div&gt;</summary>
		<author><name>194.85.160.130</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D1%80%D0%B5%D0%B4%D0%B8%D0%BA%D1%82%D0%B8%D0%B2%D0%BD%D1%8B%D0%B9_%D1%81%D0%B8%D0%BD%D1%82%D0%B0%D0%BA%D1%81%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B8%D0%B9_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=46894</id>
		<title>Предиктивный синтаксический анализ</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D1%80%D0%B5%D0%B4%D0%B8%D0%BA%D1%82%D0%B8%D0%B2%D0%BD%D1%8B%D0%B9_%D1%81%D0%B8%D0%BD%D1%82%D0%B0%D0%BA%D1%81%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B8%D0%B9_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=46894"/>
				<updated>2015-05-25T09:44:34Z</updated>
		
		<summary type="html">&lt;p&gt;194.85.160.130: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{В разработке}}&lt;br /&gt;
Для [[LL(k)-грамматики, множества FIRST и FOLLOW | LL(1)-грамматик]] возможна автоматическая генерация парсеров, если известны множества FIRST и FOLLOW. Существуют общедоступные генераторы: ANTLR&amp;lt;ref&amp;gt;[http://www.antlr.org/ ANTLR {{---}} Parser generator]&amp;lt;/ref&amp;gt;, Bison&amp;lt;ref&amp;gt;[http://www.gnu.org/software/bison/ Bison {{---}} GNU Project]&amp;lt;/ref&amp;gt;, Yacc&amp;lt;ref&amp;gt;[http://dinosaur.compilertools.net/ Lex &amp;amp; Yacc {{---}} A Lexical Analyzer Generator and Yet Another Compiler-Compiler]&amp;lt;/ref&amp;gt;, Happy&amp;lt;ref&amp;gt;[https://www.haskell.org/happy/ Happy {{---}} The Parser Generator for Haskell]&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Общая схема построения рекурсивных парсеров с помощью FIRST и FOLLOW ==&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;tex&amp;gt;\Gamma&amp;lt;/tex&amp;gt; {{---}} LL(1)-грамматика. Построим для нее парсер.&lt;br /&gt;
&lt;br /&gt;
Для каждого нетерминала &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; : &amp;lt;tex&amp;gt;A \rightarrow \alpha_1 \mid \alpha_2 \mid ... \mid \alpha_k &amp;lt;/tex&amp;gt; создадим функцию A() : Node, возвращающую фрагмент дерева разбора, выведенный из нетерминала &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Здесь Node {{---}} структура вида:&lt;br /&gt;
 Node&lt;br /&gt;
     children : list&amp;lt;Node&amp;gt;&lt;br /&gt;
     value : string // имя нетерминала или текст терминала&lt;br /&gt;
     addChild(Node) // функция, подвешивающая поддерево к данному узлу&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:string_token.png|300px]]&lt;br /&gt;
&lt;br /&gt;
Токен {{---}} один или несколько нетерминалов, для удобства объединяемые по смыслу в одну логическую единицу.&lt;br /&gt;
&lt;br /&gt;
curToken {{---}} текущий токен строки.&lt;br /&gt;
&lt;br /&gt;
nextToken() {{---}} записывает в curToken следующий за ним токен.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 A() : Node&lt;br /&gt;
     res = Node(&amp;quot;A&amp;quot;)&lt;br /&gt;
     switch (curToken) :&lt;br /&gt;
          case &amp;lt;tex&amp;gt;FIRST(\alpha_1) \cup ((\varepsilon \in FIRST(\alpha_1))  ?  FOLLOW(A)  :  \varnothing)&amp;lt;/tex&amp;gt; :&lt;br /&gt;
             // &amp;lt;tex&amp;gt;\alpha_1 = x_1x_2..x_{t_1}&amp;lt;/tex&amp;gt;&lt;br /&gt;
             for &amp;lt;tex&amp;gt;x_1 .. x_{t_1}&amp;lt;/tex&amp;gt;&lt;br /&gt;
                 if &amp;lt;tex&amp;gt;x_1&amp;lt;/tex&amp;gt; is terminal&lt;br /&gt;
                     consume(&amp;lt;tex&amp;gt;x_1&amp;lt;/tex&amp;gt;)&lt;br /&gt;
                     res.addChild(new Node(&amp;quot;&amp;lt;tex&amp;gt;x_1&amp;lt;/tex&amp;gt;&amp;quot;)&lt;br /&gt;
                     nextToken()&lt;br /&gt;
                 else&lt;br /&gt;
                     Node t = &amp;lt;tex&amp;gt;X_1()&amp;lt;/tex&amp;gt;&lt;br /&gt;
                     res.addChild(t)&lt;br /&gt;
             break&lt;br /&gt;
         case &amp;lt;tex&amp;gt;FIRST(\alpha_2) \cup ((\varepsilon \in FIRST(\alpha_2))  ?  FOLLOW(A)  :  \varnothing)&amp;lt;/tex&amp;gt; : &lt;br /&gt;
             ...&lt;br /&gt;
             break&lt;br /&gt;
         ...&lt;br /&gt;
         default :&lt;br /&gt;
             error(&amp;quot;unexpected char&amp;quot;)&lt;br /&gt;
     return res&lt;br /&gt;
&lt;br /&gt;
 consume(char c) &lt;br /&gt;
     if (curToken != c)&lt;br /&gt;
         error(&amp;quot;expected&amp;quot; + c)&lt;br /&gt;
     nextToken()&lt;br /&gt;
&lt;br /&gt;
Такой парсер не только разбирает строку, но и находит ошибки в неудовлетворяющих грамматике выражениях.&lt;br /&gt;
&lt;br /&gt;
== Пример ==&lt;br /&gt;
Рассмотрим построение парсера на примере LL(1)-грамматики арифметических выражений.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt; &lt;br /&gt;
E \to TE' \\&lt;br /&gt;
E' \to +TE' \mid \varepsilon \\&lt;br /&gt;
T \to FT' \\&lt;br /&gt;
T' \to \times FT' \mid \varepsilon \\&lt;br /&gt;
F \to n \mid (E)&lt;br /&gt;
&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Построим для нее множества &amp;lt;tex&amp;gt;FIRST&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;FOLLOW&amp;lt;/tex&amp;gt; (их построение подробно разобрано [[Построение FIRST и FOLLOW#Пример | здесь]]).&lt;br /&gt;
&lt;br /&gt;
{| style=&amp;quot;background-color:#CCC;margin:0.5px&amp;quot;&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| Правило&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| FIRST&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| FOLLOW&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;E&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ n,\ (\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ \$,\ )\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;E'&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ +,\ \varepsilon\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ \$,\ )\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ n,\ (\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ +,\ \$\ ,\ )\ \}&amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ \times,\ \varepsilon\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ +,\ \$\ ,\ )\ \}&amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;F&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ n,\ (\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\{\ \times, \ +,\ \$\ ,\ )\ \} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Псевдокоды ===&lt;br /&gt;
Построим функции обработки некоторых нетерминалов.&lt;br /&gt;
&lt;br /&gt;
 E()&lt;br /&gt;
     res = Node(&amp;quot;E&amp;quot;)&lt;br /&gt;
     switch(curToken)&lt;br /&gt;
         case 'n', '(' :&lt;br /&gt;
             res.addChild(T())&lt;br /&gt;
             res.addChild(E'())&lt;br /&gt;
             break&lt;br /&gt;
         default :&lt;br /&gt;
             error(&amp;quot;unexpected char&amp;quot;)&lt;br /&gt;
     return res&lt;br /&gt;
&lt;br /&gt;
 E'()&lt;br /&gt;
     res = Node(&amp;quot;E'&amp;quot;)&lt;br /&gt;
     switch(curToken) &lt;br /&gt;
         case '+' :&lt;br /&gt;
             consume('+')&lt;br /&gt;
             res.addChild(Node(&amp;quot;+&amp;quot;))&lt;br /&gt;
             res.addChild(T())&lt;br /&gt;
             res.addChild(E'())&lt;br /&gt;
             break&lt;br /&gt;
         case '$', ')' :&lt;br /&gt;
             break&lt;br /&gt;
         default :&lt;br /&gt;
             error(&amp;quot;unexpected char&amp;quot;)&lt;br /&gt;
      return res&lt;br /&gt;
&lt;br /&gt;
 F()&lt;br /&gt;
     res = Node(&amp;quot;F&amp;quot;)&lt;br /&gt;
     switch(curToken)&lt;br /&gt;
         case 'n' :&lt;br /&gt;
             consume('n')&lt;br /&gt;
             res.addChild(Node(&amp;quot;n&amp;quot;))&lt;br /&gt;
             break&lt;br /&gt;
         case '(' :&lt;br /&gt;
             consume('(')&lt;br /&gt;
             res.addChild(Node(&amp;quot;(&amp;quot;))&lt;br /&gt;
             res.addChild(E())&lt;br /&gt;
             consume(')')&lt;br /&gt;
             res.addChild(Node(&amp;quot;)&amp;quot;))&lt;br /&gt;
         default :&lt;br /&gt;
             error(&amp;quot;unexpected char&amp;quot;)&lt;br /&gt;
     return res&lt;br /&gt;
&lt;br /&gt;
Функции для &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt; строятся аналогично.&lt;br /&gt;
&lt;br /&gt;
=== Дерево разбора ===&lt;br /&gt;
&lt;br /&gt;
Рассмотрим дерево разбора для выражения (1 + 2) * 3 и несколько первых шагов алгоритма рекурсивного разбора. Сначала вызывается функция стартового нетерминала грамматики, то есть &amp;lt;tex&amp;gt;E&amp;lt;/tex&amp;gt;. Так как первым токеном является '(', то будет использовано первое правило разбора &amp;lt;tex&amp;gt;TE'&amp;lt;/tex&amp;gt;. Поэтому к вершине с меткой &amp;lt;tex&amp;gt;E&amp;lt;/tex&amp;gt; добавятся два ребёнка: &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;E'&amp;lt;/tex&amp;gt;. А рекурсивный разборщик перейдёт к нетерминалу &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt;. По-прежнему curToken равен '(', поэтому в &amp;lt;tex&amp;gt;F&amp;lt;/tex&amp;gt; сработает второй case, первым ребёнком добавится '(', curToken станет равен &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt;, а разборщик перейдёт к нетерминалу &amp;lt;tex&amp;gt;E&amp;lt;/tex&amp;gt;. После того как выражение после '(', которое выводится из &amp;lt;tex&amp;gt;E&amp;lt;/tex&amp;gt;, будет полностью разобрано, функция рекурсивного разбора для &amp;lt;tex&amp;gt;F&amp;lt;/tex&amp;gt; добавит ')' последним сыном к этому нетерминалу. &lt;br /&gt;
&lt;br /&gt;
Продолжая в том же духе, мы построим всё дерево разбора данного выражения.&lt;br /&gt;
&lt;br /&gt;
[[Файл:parse_ex1.png|400px|thumb|center|Дерево разбора выражения (1 + 2) * 3]]&lt;br /&gt;
&lt;br /&gt;
== Нерекурсивный нисходящий парсер ==&lt;br /&gt;
&lt;br /&gt;
[[Файл:Parse_table.png|350px|right]]&lt;br /&gt;
&lt;br /&gt;
Рекурсивные разборщики можно генерировать автоматически, зная множества FIRST и FOLLOW, так как они имеют достаточно прозрачный шаблон построения. Альтернативным способом осуществления нисходящего синтаксического анализа является построение нерекурсивного нисходящего парсера. Его можно построить с помощью явного использования стека (вместо неявного при рекурсивных вызовах). Такое анализатор имитирует левое порождение.  &lt;br /&gt;
&lt;br /&gt;
Нерекурсивный предиктивный синтаксический анализатор содержит дополнительно стек, содержащий последовательность терминалов и нетерминалов, и таблицу синтаксического анализа. На стеке располагается последовательность символов грамматики с маркером конца строки $ на дне. В начале процесса анализа строки стек содержит стартовый нетерминал грамматики непосредственно над символом $. Таблица синтаксического анализа представляет собой двухмерный массив М[X, а], где X — нетерминал, а — терминал или символ $.&lt;br /&gt;
&lt;br /&gt;
Нерекурсивный синтаксический анализатор смотрит на текущий токен строки a и на символ на вершине стека X, а затем принимает решение в зависимости от одного из возникающих ниже случаев:&lt;br /&gt;
* если Х=curToken=$, синтаксический анализатор прекращает работу, так как разбор строки завершён,&lt;br /&gt;
* eсли Х=curToken≠$, синтаксический анализатор снимает со стека X и перемещает указатель входного потока к следующему токену (то есть вызывает nextToken),&lt;br /&gt;
* eсли X представляет собой нетерминал, программа рассматривает запись M[Х,а] таблицы разбора М. Эта запись представляет собой либо X-продукцию грамматики, либо запись об ошибке. Если, например, М[Х,а] = {X → UVW}, синтаксический анализатор замещает X на вершине стека на WVU (с U на вершине стека). В кач-ве выхода синтаксический анализатор просто выводит использованную продукцию. Если M[Х,а] = error, синтаксический анализатор вызывает программу восстановления после ошибки.&lt;br /&gt;
&lt;br /&gt;
=== Псевдокод ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
 function nonRecursiveParser(w : String):&lt;br /&gt;
     s : Stack&lt;br /&gt;
     s.push(bottom)&lt;br /&gt;
     s.push(Start)&lt;br /&gt;
     do &lt;br /&gt;
         X = s.top()&lt;br /&gt;
         if X == c&lt;br /&gt;
             c = nextToken()&lt;br /&gt;
             s.pop()&lt;br /&gt;
         else if X in Sigma&lt;br /&gt;
             error(&amp;quot;unexpected symbol&amp;quot;)&lt;br /&gt;
         else if M[X, c] == undefined // запись об ошибке&lt;br /&gt;
             error(&amp;quot;unexpected symbol&amp;quot;)&lt;br /&gt;
         else if M[X, c] == X -&amp;gt; Y_1 Y_2 ... Y_k&lt;br /&gt;
             s.pop()&lt;br /&gt;
             for i = k downto 1&lt;br /&gt;
                 s.push(Y_i)&lt;br /&gt;
     while s.top() != bottom&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Пример ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt; &lt;br /&gt;
(1) \  E \to TE' \\&lt;br /&gt;
(2) \ E' \to +TE' \\&lt;br /&gt;
(3) \ E' \to \varepsilon \\&lt;br /&gt;
(4) \ T \to FT' \\&lt;br /&gt;
(5) \ T' \to \times FT' \\&lt;br /&gt;
(6) \ T' \to \varepsilon \\&lt;br /&gt;
(7) \ F \to n \\&lt;br /&gt;
(8) \ F \to (E)&lt;br /&gt;
&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| style=&amp;quot;background-color:#CCC;margin:0.5px&amp;quot;&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| &lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| n&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| (&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| )&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| +&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| *&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| $&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;E&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;E'&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;3 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;2 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;3 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;4 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;4 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;6 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;6 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;5 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;6 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;F&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;7 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;8 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt;\perp&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
[[Файл:Parse_stack.png|500px]]&lt;br /&gt;
&lt;br /&gt;
== Примечания ==&lt;br /&gt;
&amp;lt;references/&amp;gt;&lt;br /&gt;
== Источники информации ==&lt;br /&gt;
* Альфред Ахо, Рави Сети, Джеффри Ульман. Компиляторы. Принципы, технологии, инструменты. Издательство Вильямс. Второе издание. 2008. Стр. 288 {{---}} 294.&lt;br /&gt;
&lt;br /&gt;
[[Категория: Методы трансляции]]&lt;br /&gt;
[[Категория: Нисходящий разбор]]&lt;/div&gt;</summary>
		<author><name>194.85.160.130</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%A1%D0%BF%D0%B8%D1%81%D0%BE%D0%BA_%D0%B7%D0%B0%D0%B4%D0%B0%D0%BD%D0%B8%D0%B9_%D0%BF%D0%BE_%D0%90%D0%A1%D0%94_%D1%81%D0%B5%D0%BC2&amp;diff=44948</id>
		<title>Список заданий по АСД сем2</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%A1%D0%BF%D0%B8%D1%81%D0%BE%D0%BA_%D0%B7%D0%B0%D0%B4%D0%B0%D0%BD%D0%B8%D0%B9_%D0%BF%D0%BE_%D0%90%D0%A1%D0%94_%D1%81%D0%B5%D0%BC2&amp;diff=44948"/>
				<updated>2015-03-05T10:15:22Z</updated>
		
		<summary type="html">&lt;p&gt;194.85.160.130: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;wikitex&amp;gt;&lt;br /&gt;
= Дискретная математика, алгоритмы и структуры данных, 4 семестр =&lt;br /&gt;
&lt;br /&gt;
# Бордером строки называется строка, которая является одновременно ее префиксом и суффиксом. Периодом строки $s$ называется число $p$, такое что для всех допустимых $i$ выполнено $s[i+p]=s[i]$. Докажите, что если у строки длины $n$ есть border длины $k$, то у нее есть период $n - k$.&lt;br /&gt;
# Докажите, что если у строки есть периоды $p$ и $q$, причем $p + q \le n$, то $gcd(p, q)$ также является периодом этой строки.&lt;br /&gt;
# Что будет, если в предыдущем задании убрать условие $p + q \le n$?&lt;br /&gt;
# Строки Фибоначчи. Определим $F_0 = \varepsilon$, $F_1 = b$, $F_2 = a$, $F_n = F_{n-1} F_{n-2}$. Докажите, что существует $k$ такое, что для $n \ge k$ выполнено $F_n^2$ - префикс $F_{n+2}$.&lt;br /&gt;
# Докажите, что существует $k$ такое, что если $n \ge k$, то строка $F_n[1...|F_n|-2]$ - палиндром.&lt;br /&gt;
# Определим строку Туе-Морса: $T_n = t_0t_1t_2...t_{2^n - 1}$, где $t_i = 0$, если двоичная запись числа $i$ содержит четное число единиц, и $t_i = 1$ в противном случае. Доказать, что не существует двух равных как строки подстрок строки $T_n$, имеющих пересекающиеся вхождения в $T_n$&lt;br /&gt;
# Докажите, что для любого $u \ne \varepsilon$ и любого $n$ строка $u^3$ - не подстрока $T_n$&lt;br /&gt;
# Разработать алгоритм восстановления строки по префикс-функции. ($O(n)$ или $O(n \log n)$, алфавит неограничен)&lt;br /&gt;
# Разработать алгоритм восстановления строки по z-функции. ($O(n)$ или $O(n \log n)$, алфавит неограничен)&lt;br /&gt;
# Разработать алгоритм восстановления строки по z-функции. ($O(n)$ или $O(n \log n)$, алфавит двоичный)&lt;br /&gt;
# Вычислить $z$-функцию по префикс функции. ($O(n)$ или $O(n \log n)$, алфавит неограничен, не прибегать к промежуточному представлению в виде строки)&lt;br /&gt;
# Вычислить префикс функцию по $z$-функции. ($O(n)$ или $O(n \log n)$, алфавит неограничен, не прибегать к промежуточному представлению в виде строки)&lt;br /&gt;
# Как найти строку длины $m$ в строке длины $n$ с использованием z-функции и O(m) дополнительной памяти?&lt;br /&gt;
# Задана строка. Пусть $p_1[i]$ - максимальная длина палиндрома нечетной длины с центром в позиции $i$. $p_0[i]$ - аналогично для четной длины. Модифицировать алгоритм поиска $z$-функции для построения $p_0$ и $p_1$.&lt;br /&gt;
# Дана строка $s$. Посчитать матрицу $A: ||a_{ij}|| = LCP(s[i .. n-1], s[j .. n-1])$; $i,j \ge 0$ за $O(|s|^2)$. (LCP - наибольший общий префикс двух строк)&lt;br /&gt;
# Докажите, что в конечном автомате для поиска подстроки в строке длины $n$ лишь $O(n)$ ребер ведут не в начальное состояние. Как это помогает сэкономить память?&lt;br /&gt;
# Алгоритм Саймона. Используя результат предыдущего задания, предложите алгоритм построения автомата за $O(n)$ (без множителя, зависящего от размера алфавита).&lt;br /&gt;
# Дана строка $s$. Посчитать число строк длины $L$, содержащих $s$ как подстроку. Время работы должно быть полиномом от длины $s$, и $L$.&lt;br /&gt;
# Дана строка $s$. Посчитать число строк длины $L$, содержащих $s$ как подстроку (по заданному модулю). Время работы должно быть полиномом от длины $s$, и $\log L$.&lt;br /&gt;
# Дана строка $s$. Посчитать число строк длины $l$, содержащих не менее $k$ вхождений $s$.&lt;br /&gt;
# Дана строка $s$. Посчитать число строк длины $l$, содержащих не менее $k$ непересекающихся вхождений $s$.&lt;br /&gt;
# Это и следующее задание доказывают линейность алгоритма Апостолико-Джанкарло. Будем обозначать закешированные значения наибольшего суффикса образца, который заканчивается в i-й позиции текста как suf[i]. Будем называть отрезок текста [i-suf[i]+1 - i] покрытым. Докажите, что любые два покрытых отрезка в процессе работы алгоритма либо вложены, либо не пересекаются.&lt;br /&gt;
# Используя результат предыдещего задания, докажите, что алгоритм Апостолико-Джанкарло работает линейное время.&lt;br /&gt;
# Докажите, что если строки s и t таковы, что st=ts, то найдется такая строка p, что $s=p^i$ и $t=p^j$ для некоторых i и j.&lt;br /&gt;
# Модифицировать алгоритм Ахо-Корасик так, чтобы не хранить все переходы, а только исходный бор и суффиксные ссылки, и время работы осталось прежним.&lt;br /&gt;
# Найти первые вхождения каждого из образцов в тексте за время O(длина текста + постр. автомата).&lt;br /&gt;
# Дано 2 бора A и B. Для всех вершин $u$ в $A$ найти самую глубокую вершину $v$ в $B$, соответствующую суффиксу $u$ (префикс-функция бора в боре). $O(|A| + |B|)$&lt;br /&gt;
# Дан набор образцов $\{p_i\}$. Определить, существует ли бесконечная вправо строка $t$, не содержащая $p_i$ как подстроки.&lt;br /&gt;
# Дан набор образцов $\{p_i\}$. Определить, существует ли бесконечная в две стороны строка $t$, не содержащая $p_i$ как подстроки.&lt;br /&gt;
# Дан набор образцов $\{p_i\}$. Посчитать число строк длины $l$, содержащих хотя бы одну из $p_i$ как подстроку. $O(\sum |p_i|\cdot l\cdot \sigma)$. ($\sigma$ - размер алфавита)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/wikitex&amp;gt;&lt;/div&gt;</summary>
		<author><name>194.85.160.130</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%9C%D0%B8%D0%BD%D0%B8%D0%BC%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D1%8F_%D0%94%D0%9A%D0%90,_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A5%D0%BE%D0%BF%D0%BA%D1%80%D0%BE%D1%84%D1%82%D0%B0_(%D1%81%D0%BB%D0%BE%D0%B6%D0%BD%D0%BE%D1%81%D1%82%D1%8C_O(n_log_n))&amp;diff=35990</id>
		<title>Минимизация ДКА, алгоритм Хопкрофта (сложность O(n log n))</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%9C%D0%B8%D0%BD%D0%B8%D0%BC%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D1%8F_%D0%94%D0%9A%D0%90,_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A5%D0%BE%D0%BF%D0%BA%D1%80%D0%BE%D1%84%D1%82%D0%B0_(%D1%81%D0%BB%D0%BE%D0%B6%D0%BD%D0%BE%D1%81%D1%82%D1%8C_O(n_log_n))&amp;diff=35990"/>
				<updated>2014-01-22T08:36:42Z</updated>
		
		<summary type="html">&lt;p&gt;194.85.160.130: /* Простой алгоритм */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Пусть дан автомат, распознающий определенный язык. Требуется найти [[ Эквивалентность_состояний_ДКА | эквивалентный автомат]] с наименьшим количеством состояний.&lt;br /&gt;
&lt;br /&gt;
== Минимизация ДКА ==&lt;br /&gt;
Если в ДКА существуют два [[ Эквивалентность_состояний_ДКА | эквивалентных состояния]], то при их объединении мы получим [[ Эквивалентность_состояний_ДКА | эквивалентный ДКА]], так как распознаваемый язык не изменится. Основная идея минимизации состоит в разбиении множества состояний на классы эквивалентности, полученные классы и будут состояниями минимизированного ДКА. &lt;br /&gt;
&lt;br /&gt;
== Простой алгоритм ==&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition =&lt;br /&gt;
Класс &amp;lt;tex&amp;gt;C&amp;lt;/tex&amp;gt; '''разбивает''' класс &amp;lt;tex&amp;gt;R&amp;lt;/tex&amp;gt; по символу &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; на &amp;lt;tex&amp;gt;R_1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;R_2&amp;lt;/tex&amp;gt;, если &lt;br /&gt;
# &amp;lt;tex&amp;gt;\forall r \in R_1 \,\,\, \delta(r, a) \in C&amp;lt;/tex&amp;gt; &lt;br /&gt;
# &amp;lt;tex&amp;gt;\forall r \in R_2 \,\,\, \delta(r, a) \notin C&amp;lt;/tex&amp;gt; &lt;br /&gt;
}} &lt;br /&gt;
Если класс &amp;lt;tex&amp;gt;R&amp;lt;/tex&amp;gt; может быть разбит по символу &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;, то он содержит хотя бы одну пару неэквивалентных состояний (так как существует строка которая их различает). Если класс нельзя разбить, то он состоит из эквивалентных состояний.&lt;br /&gt;
Поэтому самый простой алгоритм состоит в том, чтобы разбивать классы текущего разбиения до тех пор пока это возможно. &lt;br /&gt;
&lt;br /&gt;
Итеративно строим разбиение множества состояний следующим образом.&lt;br /&gt;
# Первоначальное разбиение множества состояний {{---}} класс допускающих состояний &amp;lt;tex&amp;gt;F&amp;lt;/tex&amp;gt; и класс недопускающих состояний &amp;lt;tex&amp;gt;Q \setminus F&amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Перебираются символы алфавита &amp;lt;tex&amp;gt;c \in \Sigma&amp;lt;/tex&amp;gt;, все пары &amp;lt;tex&amp;gt;(F, c)&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;(Q \setminus F, c)&amp;lt;/tex&amp;gt; помещаются в очередь.&lt;br /&gt;
# Из очереди извлекается пара &amp;lt;tex&amp;gt;(C, a)&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;C&amp;lt;/tex&amp;gt; далее именуется как мастер Сплиттер.&lt;br /&gt;
# Все классы текущего разбиения разбиваются на 2 подкласса (один из которых может быть пустым). Первый состоит из состояний, которые по символу &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; переходят в сплиттер, а второй из всех оставшихся. &lt;br /&gt;
# Те классы, которые разбились на два непустых подкласса, заменяются этими подклассами в разбиении, а также добавляются в очередь.&lt;br /&gt;
# Пока очередь не пуста, выполняем п.3 – п.5.&lt;br /&gt;
&lt;br /&gt;
===Псевдокод===&lt;br /&gt;
&amp;lt;tex&amp;gt;Q&amp;lt;/tex&amp;gt; {{---}} множество состояний ДКА.&lt;br /&gt;
&amp;lt;tex&amp;gt;F&amp;lt;/tex&amp;gt; {{---}} множество терминальных состояний.&lt;br /&gt;
&amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; {{---}} очередь пар &amp;lt;tex&amp;gt;(C, a)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&amp;lt;tex&amp;gt;P&amp;lt;/tex&amp;gt; {{---}} разбиение множества состояний ДКА.&lt;br /&gt;
&amp;lt;tex&amp;gt;R&amp;lt;/tex&amp;gt; {{---}} класс состояний ДКА.&lt;br /&gt;
  &amp;lt;tex&amp;gt;\mathtt{P} \leftarrow \{ \mathtt{F, Q} \setminus \mathtt{F} \}&amp;lt;/tex&amp;gt;&lt;br /&gt;
  &amp;lt;tex&amp;gt;\mathtt{S} \leftarrow \varnothing &amp;lt;/tex&amp;gt;&lt;br /&gt;
  '''for''' &amp;lt;tex&amp;gt;c \in \Sigma&amp;lt;/tex&amp;gt;&lt;br /&gt;
    '''insert''' &amp;lt;tex&amp;gt;(\mathtt{F}, c)&amp;lt;/tex&amp;gt; '''in''' &amp;lt;tex&amp;gt;\mathtt{S}&amp;lt;/tex&amp;gt;&lt;br /&gt;
    '''insert''' &amp;lt;tex&amp;gt;(\mathtt{Q} \setminus \mathtt{F}, c)&amp;lt;/tex&amp;gt; '''in''' &amp;lt;tex&amp;gt;\mathtt{S}&amp;lt;/tex&amp;gt;&lt;br /&gt;
  '''while''' &amp;lt;tex&amp;gt; \mathtt{S} \ne \varnothing &amp;lt;/tex&amp;gt;&lt;br /&gt;
    &amp;lt;tex&amp;gt;(C, a) \leftarrow&amp;lt;/tex&amp;gt; '''pop'''(&amp;lt;tex&amp;gt;\mathtt{S}&amp;lt;/tex&amp;gt;)&lt;br /&gt;
    '''for''' &amp;lt;tex&amp;gt;R&amp;lt;/tex&amp;gt; '''in''' &amp;lt;tex&amp;gt;\mathtt{P}&amp;lt;/tex&amp;gt; &lt;br /&gt;
      &amp;lt;tex&amp;gt;R_1 = R \cap \delta^{-1} (C, a) &amp;lt;/tex&amp;gt;&lt;br /&gt;
      &amp;lt;tex&amp;gt;R_2 = R \setminus R_1&amp;lt;/tex&amp;gt;&lt;br /&gt;
      '''if''' &amp;lt;tex&amp;gt; R_1 \ne \varnothing &amp;lt;/tex&amp;gt; '''and''' &amp;lt;tex&amp;gt; R_2 \ne \varnothing &amp;lt;/tex&amp;gt;&lt;br /&gt;
        '''replace''' &amp;lt;tex&amp;gt;R&amp;lt;/tex&amp;gt; '''in''' &amp;lt;tex&amp;gt;\mathtt{P}&amp;lt;/tex&amp;gt; '''with''' &amp;lt;tex&amp;gt;R_1&amp;lt;/tex&amp;gt; '''and''' &amp;lt;tex&amp;gt;R_2&amp;lt;/tex&amp;gt;&lt;br /&gt;
        '''for''' &amp;lt;tex&amp;gt; c \in \Sigma &amp;lt;/tex&amp;gt; &lt;br /&gt;
          '''insert''' &amp;lt;tex&amp;gt;(R_1, c)&amp;lt;/tex&amp;gt; '''in''' &amp;lt;tex&amp;gt;\mathtt{S}&amp;lt;/tex&amp;gt;&lt;br /&gt;
          '''insert''' &amp;lt;tex&amp;gt;(R_2, c)&amp;lt;/tex&amp;gt; '''in''' &amp;lt;tex&amp;gt;\mathtt{S}&amp;lt;/tex&amp;gt;&lt;br /&gt;
Когда очередь &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; станет пустой, будет получено разбиение на классы эквивалентности, так как больше ни один класс невозможно разбить.&lt;br /&gt;
&lt;br /&gt;
===Время работы===&lt;br /&gt;
Время работы алгоритма оценивается как &amp;lt;tex&amp;gt;O(|\Sigma| \cdot n^2)&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt; n &amp;lt;/tex&amp;gt; {{---}} количество состояний ДКА, а &amp;lt;tex&amp;gt; \Sigma &amp;lt;/tex&amp;gt;{{---}} алфавит. Это следует из того, что если пара &amp;lt;tex&amp;gt;(C, a)&amp;lt;/tex&amp;gt; попала в очередь, и класс &amp;lt;tex&amp;gt;C&amp;lt;/tex&amp;gt; использовался в качестве сплиттера, то при последующем разбиении этого класса в очередь добавляется два класса &amp;lt;tex&amp;gt;C_1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;C_2&amp;lt;/tex&amp;gt;, причем можно гарантировать лишь следующее уменьшение размера: &amp;lt;tex&amp;gt;|C| \ge |C_i| + 1&amp;lt;/tex&amp;gt;. Каждое состояние изначально принадлежит лишь одному классу в очереди, поэтому каждый переход в автомате будет просмотрен не более, чем &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt; раз. Учитывая, что ребер всего &amp;lt;tex&amp;gt;O(|\Sigma| \cdot n)&amp;lt;/tex&amp;gt;, получаем указанную оценку.&lt;br /&gt;
&lt;br /&gt;
== Алгоритм Хопкрофта==&lt;br /&gt;
&lt;br /&gt;
{{Лемма&lt;br /&gt;
|statement = Класс &amp;lt;tex&amp;gt;R = R_1 \cup R_2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;R_1 \cap R_2 = \varnothing&amp;lt;/tex&amp;gt;, тогда разбиение всех классов (текущее разбиение) по символу &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; любыми двумя классами из &amp;lt;tex&amp;gt;R, R_1, R_2&amp;lt;/tex&amp;gt; эквивалентно разбиению всех классов с помощью &amp;lt;tex&amp;gt;R, R_1, R_2&amp;lt;/tex&amp;gt; по символу &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;.&lt;br /&gt;
|proof = &lt;br /&gt;
Разобьем все классы с помощью &amp;lt;tex&amp;gt;R &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; R_1&amp;lt;/tex&amp;gt; по символу &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;, тогда для любого класса &amp;lt;tex&amp;gt;B&amp;lt;/tex&amp;gt; из текущего разбиения выполняется&lt;br /&gt;
:&amp;lt;tex&amp;gt;\forall r \in B \,\,\, \delta(r, a) \in R&amp;lt;/tex&amp;gt; and &amp;lt;tex&amp;gt; \delta(r, a) \in R_1&amp;lt;/tex&amp;gt; or &lt;br /&gt;
:&amp;lt;tex&amp;gt;\forall r \in B \,\,\, \delta(r, a) \in R&amp;lt;/tex&amp;gt; and &amp;lt;tex&amp;gt; \delta(r, a) \notin R_1&amp;lt;/tex&amp;gt; or &lt;br /&gt;
:&amp;lt;tex&amp;gt;\forall r \in B \,\,\, \delta(r, a) \notin R&amp;lt;/tex&amp;gt; and &amp;lt;tex&amp;gt; \delta(r, a) \notin R_1&amp;lt;/tex&amp;gt;  &lt;br /&gt;
А так как &amp;lt;tex&amp;gt;R = R_1 \cup R_2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;R_1 \cap R_2 = \varnothing&amp;lt;/tex&amp;gt; то выполняется&lt;br /&gt;
:&amp;lt;tex&amp;gt;\forall r \in B \,\,\, \delta(r, a) \in R_2 &amp;lt;/tex&amp;gt; or &lt;br /&gt;
:&amp;lt;tex&amp;gt; \forall r \in B \,\,\, \delta(r, a) \notin R_2&amp;lt;/tex&amp;gt; &lt;br /&gt;
Из этого следует, что разбиение всех классов с помощью &amp;lt;tex&amp;gt;R_2&amp;lt;/tex&amp;gt; никак не повлияет на текущее разбиение. &amp;lt;br/&amp;gt;&lt;br /&gt;
Аналогично доказывается и для разбиения с помощью &amp;lt;tex&amp;gt;R &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; R_2&amp;lt;/tex&amp;gt; по символу &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;. &amp;lt;br/&amp;gt;&lt;br /&gt;
Разобьем все классы с помощью &amp;lt;tex&amp;gt;R_1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; R_2&amp;lt;/tex&amp;gt; по символу &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;, тогда для любого класса &amp;lt;tex&amp;gt;B&amp;lt;/tex&amp;gt; из текущего разбиения выполняется&lt;br /&gt;
:&amp;lt;tex&amp;gt;\forall r \in B \,\,\, \delta(r, a) \in R_1&amp;lt;/tex&amp;gt; and &amp;lt;tex&amp;gt; \delta(r, a) \notin R_2&amp;lt;/tex&amp;gt; or &lt;br /&gt;
:&amp;lt;tex&amp;gt;\forall r \in B \,\,\, \delta(r, a) \notin R_1&amp;lt;/tex&amp;gt; and &amp;lt;tex&amp;gt; \delta(r, a) \in R_2&amp;lt;/tex&amp;gt; or &lt;br /&gt;
:&amp;lt;tex&amp;gt;\forall r \in B \,\,\, \delta(r, a) \notin R_1&amp;lt;/tex&amp;gt; and &amp;lt;tex&amp;gt; \delta(r, a) \notin R_2&amp;lt;/tex&amp;gt;  &lt;br /&gt;
А так как &amp;lt;tex&amp;gt;R = R_1 \cup R_2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;R_1 \cap R_2 = \varnothing&amp;lt;/tex&amp;gt; то выполняется&lt;br /&gt;
:&amp;lt;tex&amp;gt;\forall r \in B \,\,\, \delta(r, a) \in R &amp;lt;/tex&amp;gt; or &lt;br /&gt;
:&amp;lt;tex&amp;gt; \forall r \in B \,\,\, \delta(r, a) \notin R&amp;lt;/tex&amp;gt; &lt;br /&gt;
Из этого следует, что разбиение всех классов с помощью &amp;lt;tex&amp;gt;R&amp;lt;/tex&amp;gt; никак не повлияет на текущее разбиение.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Алгоритм Хопкрофта отличается от простого тем, что иначе добавляет классы в очередь.&lt;br /&gt;
Если класс &amp;lt;tex&amp;gt;R&amp;lt;/tex&amp;gt; уже есть в очереди, то согласно лемме можно просто заменить его на &amp;lt;tex&amp;gt;R_1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;R_2&amp;lt;/tex&amp;gt;. &lt;br /&gt;
Если класса &amp;lt;tex&amp;gt;R&amp;lt;/tex&amp;gt; нет в очереди, то согласно лемме в очередь можно добавить класс &amp;lt;tex&amp;gt;R&amp;lt;/tex&amp;gt; и любой из &amp;lt;tex&amp;gt;R_1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;R_2&amp;lt;/tex&amp;gt;, а так как для любого класса &amp;lt;tex&amp;gt;B&amp;lt;/tex&amp;gt; из текущего разбиения выполняется &lt;br /&gt;
:&amp;lt;tex&amp;gt;\forall r \in B \,\,\, \delta(r, a) \in R &amp;lt;/tex&amp;gt; or &lt;br /&gt;
:&amp;lt;tex&amp;gt; \forall r \in B \,\,\, \delta(r, a) \notin R&amp;lt;/tex&amp;gt; &lt;br /&gt;
то в очередь можно добавить только меньшее из &amp;lt;tex&amp;gt;R_1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;R_2&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
===Реализация===&lt;br /&gt;
&amp;lt;tex&amp;gt;Q&amp;lt;/tex&amp;gt; {{---}} множество состояний ДКА.&lt;br /&gt;
&amp;lt;tex&amp;gt;F&amp;lt;/tex&amp;gt; {{---}} множество терминальных состояний.&lt;br /&gt;
&amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; {{---}} очередь из пар &amp;lt;tex&amp;gt;(C, a)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&amp;lt;tex&amp;gt;P&amp;lt;/tex&amp;gt; {{---}} разбиение множества состояний ДКА.&lt;br /&gt;
&amp;lt;tex&amp;gt;R&amp;lt;/tex&amp;gt; {{---}} класс состояний ДКА.&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;tex&amp;gt;\mathtt{P} \leftarrow \{ \mathtt{F, Q} \setminus \mathtt{F} \}&amp;lt;/tex&amp;gt;&lt;br /&gt;
  &amp;lt;tex&amp;gt;\mathtt{S} \leftarrow \varnothing &amp;lt;/tex&amp;gt;&lt;br /&gt;
  '''for''' &amp;lt;tex&amp;gt;c \in \Sigma&amp;lt;/tex&amp;gt;&lt;br /&gt;
    '''insert''' &amp;lt;tex&amp;gt; (\mathtt{min} (\mathtt{F, Q} \setminus \mathtt{F}), c)&amp;lt;/tex&amp;gt; '''in''' &amp;lt;tex&amp;gt;\mathtt{S}&amp;lt;/tex&amp;gt;&lt;br /&gt;
  '''while''' &amp;lt;tex&amp;gt;\mathtt{S} \ne \varnothing&amp;lt;/tex&amp;gt;&lt;br /&gt;
    &amp;lt;tex&amp;gt;(C, a) \leftarrow&amp;lt;/tex&amp;gt; '''pop'''(&amp;lt;tex&amp;gt;\mathtt{S}&amp;lt;/tex&amp;gt;)&lt;br /&gt;
    &amp;lt;tex&amp;gt;T \leftarrow \{R \ | \ R \in \mathtt{P}, \ R&amp;lt;/tex&amp;gt; splits by &amp;lt;tex&amp;gt;(C, a) \}&amp;lt;/tex&amp;gt;&lt;br /&gt;
    '''for each''' &amp;lt;tex&amp;gt;R&amp;lt;/tex&amp;gt; '''in''' &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt;&lt;br /&gt;
      &amp;lt;tex&amp;gt; R_1, R_2 \leftarrow &amp;lt;/tex&amp;gt; '''split'''(&amp;lt;tex&amp;gt;R, C, a&amp;lt;/tex&amp;gt;)  &lt;br /&gt;
      '''replace''' &amp;lt;tex&amp;gt;R&amp;lt;/tex&amp;gt; '''in''' &amp;lt;tex&amp;gt;\mathtt{P}&amp;lt;/tex&amp;gt; '''with''' &amp;lt;tex&amp;gt;R_1&amp;lt;/tex&amp;gt; '''and''' &amp;lt;tex&amp;gt;R_2&amp;lt;/tex&amp;gt;&lt;br /&gt;
      '''for''' &amp;lt;tex&amp;gt;c \in \Sigma&amp;lt;/tex&amp;gt;&lt;br /&gt;
        '''if''' &amp;lt;tex&amp;gt;(R, c)&amp;lt;/tex&amp;gt; '''in''' &amp;lt;tex&amp;gt;\mathtt{S}&amp;lt;/tex&amp;gt;&lt;br /&gt;
          '''replace''' &amp;lt;tex&amp;gt; (R, c)&amp;lt;/tex&amp;gt; '''in''' &amp;lt;tex&amp;gt;\mathtt{S}&amp;lt;/tex&amp;gt; '''with''' &amp;lt;tex&amp;gt;(R_1, c)&amp;lt;/tex&amp;gt; '''and''' &amp;lt;tex&amp;gt;(R_2, c)&amp;lt;/tex&amp;gt;&lt;br /&gt;
        '''else'''&lt;br /&gt;
          '''insert''' &amp;lt;tex&amp;gt;(\mathtt{min}(R_1, R_2), c)&amp;lt;/tex&amp;gt; '''in''' &amp;lt;tex&amp;gt;\mathtt{S}&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
К сожалению, совсем не очевидно, как быстро находить множество &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt;. С другой стороны, понятно, что &amp;lt;tex&amp;gt;T \subset T'&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt; {{---}} это множество классов текущего разбиения, из состояний которых в автомате существует переход в состояния сплиттера &amp;lt;tex&amp;gt;C&amp;lt;/tex&amp;gt; по символу &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Модифицируем наш алгоритм: для каждой очередной пары &amp;lt;tex&amp;gt; (C, a) &amp;lt;/tex&amp;gt; будем находить &amp;lt;tex&amp;gt; T' &amp;lt;/tex&amp;gt;, и с каждым классом состояний из &amp;lt;tex&amp;gt; T' &amp;lt;/tex&amp;gt; будем производить те же действия, что и раньше.&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;tex&amp;gt;\mathtt{P} \leftarrow \{ \mathtt{F, Q} \setminus \mathtt{F} \}&amp;lt;/tex&amp;gt;&lt;br /&gt;
  &amp;lt;tex&amp;gt;\mathtt{S} \leftarrow \varnothing &amp;lt;/tex&amp;gt;&lt;br /&gt;
  '''for''' &amp;lt;tex&amp;gt;c \in \Sigma&amp;lt;/tex&amp;gt;&lt;br /&gt;
    '''insert''' &amp;lt;tex&amp;gt;(\mathtt{min} (\mathtt{F, Q} \setminus \mathtt{F}), c)&amp;lt;/tex&amp;gt; '''in''' &amp;lt;tex&amp;gt;\mathtt{S}&amp;lt;/tex&amp;gt;&lt;br /&gt;
  '''while''' &amp;lt;tex&amp;gt;\mathtt{S} \ne \varnothing&amp;lt;/tex&amp;gt;&lt;br /&gt;
    &amp;lt;tex&amp;gt;(C, a) \leftarrow&amp;lt;/tex&amp;gt; '''pop'''(&amp;lt;tex&amp;gt;\mathtt{S}&amp;lt;/tex&amp;gt;)&lt;br /&gt;
    &amp;lt;tex&amp;gt;\mathtt{Inverse} \leftarrow \{r \ | \ r \in \mathtt{Q}, \ \delta(r, a) \in C\}&amp;lt;/tex&amp;gt;&lt;br /&gt;
    &amp;lt;tex&amp;gt;T' \leftarrow \{R \ | \ R \in \mathtt{P}, \ R \cap \mathtt{Inverse} \neq \varnothing\}&amp;lt;/tex&amp;gt;&lt;br /&gt;
    '''for each''' &amp;lt;tex&amp;gt;R&amp;lt;/tex&amp;gt; '''in''' &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt;&lt;br /&gt;
      '''if''' &amp;lt;tex&amp;gt;R&amp;lt;/tex&amp;gt; splits by &amp;lt;tex&amp;gt;(C, a)&amp;lt;/tex&amp;gt;&lt;br /&gt;
        &amp;lt;tex&amp;gt; R_1, R_2 \leftarrow &amp;lt;/tex&amp;gt; '''split'''(&amp;lt;tex&amp;gt;R, C, a&amp;lt;/tex&amp;gt;)  &lt;br /&gt;
        '''replace''' &amp;lt;tex&amp;gt;R&amp;lt;/tex&amp;gt; '''in''' &amp;lt;tex&amp;gt;\mathtt{P}&amp;lt;/tex&amp;gt; '''with''' &amp;lt;tex&amp;gt;R_1&amp;lt;/tex&amp;gt; '''and''' &amp;lt;tex&amp;gt;R_2&amp;lt;/tex&amp;gt;&lt;br /&gt;
        '''for''' &amp;lt;tex&amp;gt;c \in \Sigma&amp;lt;/tex&amp;gt;&lt;br /&gt;
          '''if''' &amp;lt;tex&amp;gt;(R, c)&amp;lt;/tex&amp;gt; '''in''' &amp;lt;tex&amp;gt;\mathtt{S}&amp;lt;/tex&amp;gt;&lt;br /&gt;
            '''replace''' &amp;lt;tex&amp;gt;(R, c)&amp;lt;/tex&amp;gt; '''in''' &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; '''with''' &amp;lt;tex&amp;gt;(R_1, c)&amp;lt;/tex&amp;gt; '''and''' &amp;lt;tex&amp;gt;(R_2, c)&amp;lt;/tex&amp;gt;&lt;br /&gt;
          '''else'''&lt;br /&gt;
            '''insert''' &amp;lt;tex&amp;gt;(\mathtt{min}(R_1, R_2), c)&amp;lt;/tex&amp;gt; '''in''' &amp;lt;tex&amp;gt;\mathtt{S}&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Каждая итерация цикла &amp;lt;tex&amp;gt; while &amp;lt;/tex&amp;gt; не может быть выполнена быстрее, чем за &amp;lt;tex&amp;gt; O(|\mathtt{Inverse}|) &amp;lt;/tex&amp;gt; для текущей пары &amp;lt;tex&amp;gt; (C,a)&amp;lt;/tex&amp;gt;. Покажем, как достичь этой оценки.&lt;br /&gt;
&lt;br /&gt;
Разбиение &amp;lt;tex&amp;gt; P &amp;lt;/tex&amp;gt; можно поддерживать четырьмя массивами:&lt;br /&gt;
*&amp;lt;tex&amp;gt;\mathtt{Class}[r]&amp;lt;/tex&amp;gt; {{---}} номер класса, которому принадлежит состояние &amp;lt;tex&amp;gt;r&amp;lt;/tex&amp;gt;;&lt;br /&gt;
*&amp;lt;tex&amp;gt;\mathtt{Part}[i]&amp;lt;/tex&amp;gt; {{---}} указатель на голову [[Список#Двусвязный список|двусвязного списка]], содержащего состояния, принадлежащие классу &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt;;&lt;br /&gt;
*&amp;lt;tex&amp;gt;\mathtt{Card}[i]&amp;lt;/tex&amp;gt; {{---}} количество состояний в классе &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;;&lt;br /&gt;
*&amp;lt;tex&amp;gt;\mathtt{Place}[r]&amp;lt;/tex&amp;gt; {{---}} указатель на состояние &amp;lt;tex&amp;gt;r&amp;lt;/tex&amp;gt; в списке &amp;lt;tex&amp;gt;\mathtt{Part[Class}[r]]&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Так как мы храним указатель, где находится состояние в двусвязном списке, то операцию перемещения состояния из одного класса в другой можно выполнить за &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Чтобы эффективно находить множество &amp;lt;tex&amp;gt;\mathtt{Inverse}&amp;lt;/tex&amp;gt;, построим массив &amp;lt;tex&amp;gt;\mathtt{Inv}&amp;lt;/tex&amp;gt;, который для состояния &amp;lt;tex&amp;gt;r&amp;lt;/tex&amp;gt; и символа &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; в &amp;lt;tex&amp;gt;\mathtt{Inv}[r][a]&amp;lt;/tex&amp;gt; хранит множество состояний, из которых существует переход в &amp;lt;tex&amp;gt;r&amp;lt;/tex&amp;gt; по символу &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;. Так как наш алгоритм не меняет изначальный автомат, то массив &amp;lt;tex&amp;gt;\mathtt{Inv}&amp;lt;/tex&amp;gt; можно построить перед началом основной части алгоритма, что займет &amp;lt;tex&amp;gt;O(|\Sigma| |Q|)&amp;lt;/tex&amp;gt; времени.&lt;br /&gt;
&lt;br /&gt;
Теперь научимся за &amp;lt;tex&amp;gt;O(|\mathtt{Inverse}|)&amp;lt;/tex&amp;gt; обрабатывать множество &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt; и разбивать классы. Для этого нам понадобится следующая структура:&lt;br /&gt;
*&amp;lt;tex&amp;gt;\mathtt{Counter}&amp;lt;/tex&amp;gt; {{---}} количество классов;&lt;br /&gt;
*&amp;lt;tex&amp;gt;\mathtt{Involved}&amp;lt;/tex&amp;gt; {{---}} список из номеров классов, содержащихся во множестве &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt;;&lt;br /&gt;
*&amp;lt;tex&amp;gt;\mathtt{Size}&amp;lt;/tex&amp;gt; {{---}} целочисленный массив, где &amp;lt;tex&amp;gt;\mathtt{Size}[i]&amp;lt;/tex&amp;gt; хранит количество состояний из класса &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;, которые содержатся в &amp;lt;tex&amp;gt;\mathtt{Inverse}&amp;lt;/tex&amp;gt;;&lt;br /&gt;
*&amp;lt;tex&amp;gt;\mathtt{Twin}&amp;lt;/tex&amp;gt; {{---}} массив, хранящий в &amp;lt;tex&amp;gt;\mathtt{Twin}[i]&amp;lt;/tex&amp;gt; номер нового класса, образовавшегося при разбиении класса &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Сам же алгоритм обработки &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt; будет выглядеть так:&lt;br /&gt;
  &amp;lt;tex&amp;gt;\mathtt{Involved} \leftarrow \varnothing&amp;lt;/tex&amp;gt;&lt;br /&gt;
  '''for''' &amp;lt;tex&amp;gt;q \in C&amp;lt;/tex&amp;gt; '''and''' &amp;lt;tex&amp;gt;r \in \mathtt{Inv}[q][a]&amp;lt;/tex&amp;gt;&lt;br /&gt;
    &amp;lt;tex&amp;gt;i = \mathtt{Class}[q]&amp;lt;/tex&amp;gt;&lt;br /&gt;
    '''if''' &amp;lt;tex&amp;gt;\mathtt{Size}[i] == 0&amp;lt;/tex&amp;gt;&lt;br /&gt;
      '''insert''' &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; '''in''' &amp;lt;tex&amp;gt;\mathtt{Involved}&amp;lt;/tex&amp;gt;&lt;br /&gt;
    &amp;lt;tex&amp;gt;\mathtt{Size}[i]++&amp;lt;/tex&amp;gt;&lt;br /&gt;
  '''for''' &amp;lt;tex&amp;gt;q \in C&amp;lt;/tex&amp;gt; '''and''' &amp;lt;tex&amp;gt;r \in \mathtt{Inv}[q][a]&amp;lt;/tex&amp;gt;&lt;br /&gt;
    &amp;lt;tex&amp;gt;i = \mathtt{Class}[q]&amp;lt;/tex&amp;gt;&lt;br /&gt;
    '''if''' &amp;lt;tex&amp;gt;\mathtt{Size}[i] &amp;lt; \mathtt{Card}[i]&amp;lt;/tex&amp;gt;&lt;br /&gt;
      '''if''' &amp;lt;tex&amp;gt;\mathtt{Twin}[i] == 0&amp;lt;/tex&amp;gt;&lt;br /&gt;
        &amp;lt;tex&amp;gt;\mathtt{Counter}++&amp;lt;/tex&amp;gt;&lt;br /&gt;
        &amp;lt;tex&amp;gt;\mathtt{Twin[i]} = \mathtt{Counter}&amp;lt;/tex&amp;gt;&lt;br /&gt;
      '''move''' &amp;lt;tex&amp;gt;r&amp;lt;/tex&amp;gt; '''from''' &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; '''to''' &amp;lt;tex&amp;gt;\mathtt{Twin}[i]&amp;lt;/tex&amp;gt;&lt;br /&gt;
  '''for''' &amp;lt;tex&amp;gt; j \in \mathtt{Involved}&amp;lt;/tex&amp;gt;&lt;br /&gt;
    &amp;lt;tex&amp;gt;\mathtt{Size}[j] = 0&amp;lt;/tex&amp;gt;&lt;br /&gt;
    &amp;lt;tex&amp;gt;\mathtt{Twin}[j] = 0&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Для быстрой проверки, находится ли пара &amp;lt;tex&amp;gt;(C,a)&amp;lt;/tex&amp;gt; в очереди &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, будем использовать массив &amp;lt;tex&amp;gt;\mathtt{InQueue}&amp;lt;/tex&amp;gt; размера &amp;lt;tex&amp;gt;|Q| \times |\Sigma|&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;\mathtt{InQueue}[C][a] = true&amp;lt;/tex&amp;gt;, если пара &amp;lt;tex&amp;gt;(C,a)&amp;lt;/tex&amp;gt; содержится в очереди. &lt;br /&gt;
Так как при разбиении очередного класса &amp;lt;tex&amp;gt;R&amp;lt;/tex&amp;gt; на подклассы &amp;lt;tex&amp;gt;R_1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;R_2&amp;lt;/tex&amp;gt; мы в действительности создаем лишь один новый класс, то замена класса &amp;lt;tex&amp;gt;R&amp;lt;/tex&amp;gt; в очереди на подклассы, образовавшиеся при разбиении, сводится лишь к взаимодействию с массивом &amp;lt;tex&amp;gt;\mathtt{InQueue}&amp;lt;/tex&amp;gt;. В результате каждая операция с очередью требует &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt; времени.&lt;br /&gt;
&lt;br /&gt;
===Время работы===&lt;br /&gt;
&lt;br /&gt;
{{Лемма&lt;br /&gt;
|about = 1&lt;br /&gt;
|id = Лемма1&lt;br /&gt;
|statement =&lt;br /&gt;
Количество классов, созданных во время выполнения алгоритма, не превышает &amp;lt;tex&amp;gt;2 |Q| - 1&amp;lt;/tex&amp;gt;.&lt;br /&gt;
|proof =&lt;br /&gt;
Представим дерево, которое соответствует операциям разделения классов на подклассы. Корнем этого дерева является все множество состояний &amp;lt;tex&amp;gt;Q&amp;lt;/tex&amp;gt;. Листьями являются классы эквивалентности, оставшиеся после работы алгоритма. Так как дерево бинарное {{---}} каждый класс может породить лишь два новых, а количество листьев не может быть больше &amp;lt;tex&amp;gt;|Q|&amp;lt;/tex&amp;gt;, то количество узлов этого дерева не может быть больше &amp;lt;tex&amp;gt;2 |Q| - 1&amp;lt;/tex&amp;gt;, что доказывает утверждение леммы.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{Лемма&lt;br /&gt;
|about = 2&lt;br /&gt;
|id = Лемма2&lt;br /&gt;
|statement = &lt;br /&gt;
Количество итераций цикла &amp;lt;tex&amp;gt;while&amp;lt;/tex&amp;gt; не превышает &amp;lt;tex&amp;gt; 2 |\Sigma| |Q| &amp;lt;/tex&amp;gt;.&lt;br /&gt;
|proof =&lt;br /&gt;
Для доказательства этого утверждения достаточно показать, что количество пар &amp;lt;tex&amp;gt;(C,a)&amp;lt;/tex&amp;gt; добавленных в очередь &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; не превосходит &amp;lt;tex&amp;gt; 2 |\Sigma| |Q| &amp;lt;/tex&amp;gt;, так как на каждой итерации мы извлекаем одну пару из очереди.&lt;br /&gt;
&lt;br /&gt;
По [[#Лемма1 | лемме(1)]] количество классов не превосходит &amp;lt;tex&amp;gt;2 |Q| - 1&amp;lt;/tex&amp;gt;. Пусть &amp;lt;tex&amp;gt;C&amp;lt;/tex&amp;gt; элемент текущего разбиения. Тогда количество пар &amp;lt;tex&amp;gt;(C,a), \ a \in \Sigma&amp;lt;/tex&amp;gt; не может быть больше &amp;lt;tex&amp;gt;|\Sigma|&amp;lt;/tex&amp;gt;. Отсюда следует, что всего различных пар, которые можно добавить в очередь, не превосходит &amp;lt;tex&amp;gt; 2 |\Sigma| |Q| &amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{Лемма&lt;br /&gt;
|about = 3&lt;br /&gt;
|id = Лемма3&lt;br /&gt;
|statement = &lt;br /&gt;
Пусть &amp;lt;tex&amp;gt;a \in \Sigma&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;p \in Q&amp;lt;/tex&amp;gt;. Тогда количество пар &amp;lt;tex&amp;gt;(C,a)&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;p \in C&amp;lt;/tex&amp;gt;, которые мы удалим из очереди, не превосходит &amp;lt;tex&amp;gt;\log_2(|Q|)&amp;lt;/tex&amp;gt; для фиксированных &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;p&amp;lt;/tex&amp;gt;.&lt;br /&gt;
|proof =&lt;br /&gt;
Рассмотрим пару &amp;lt;tex&amp;gt;(C,a)&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;p \in C&amp;lt;/tex&amp;gt;, которую мы удаляем из очереди. И пусть &amp;lt;tex&amp;gt;(C',a)&amp;lt;/tex&amp;gt; следующая пара, где &amp;lt;tex&amp;gt;p \in C'&amp;lt;/tex&amp;gt; и которую мы удалим из очереди. Согласно нашему алгоритму класс &amp;lt;tex&amp;gt;C'&amp;lt;/tex&amp;gt; мог появиться в очереди только после операции &amp;lt;tex&amp;gt;\mathtt{replace}&amp;lt;/tex&amp;gt;. Но после первого же разбиения класса &amp;lt;tex&amp;gt;C&amp;lt;/tex&amp;gt; на подклассы мы добавим в очередь пару &amp;lt;tex&amp;gt;(C'', a)&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;C''&amp;lt;/tex&amp;gt; меньший из образовавшихся подклассов, то есть &amp;lt;tex&amp;gt;|C''| \leqslant |C| \ / \ 2&amp;lt;/tex&amp;gt;. Так же заметим, что &amp;lt;tex&amp;gt;C' \subseteq C''&amp;lt;/tex&amp;gt;, а следовательно &amp;lt;tex&amp;gt;|C'| \leqslant |C| \ / \ 2&amp;lt;/tex&amp;gt;. Но тогда таких пар не может быть больше, чем &amp;lt;tex&amp;gt;\log_2(|Q|)&amp;lt;/tex&amp;gt;. &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{Лемма&lt;br /&gt;
|about = 4&lt;br /&gt;
|id = Лемма4&lt;br /&gt;
|statement = &lt;br /&gt;
&amp;lt;tex&amp;gt;\sum |\mathtt{Inverse}|&amp;lt;/tex&amp;gt; по всем итерациям цикла &amp;lt;tex&amp;gt;while&amp;lt;/tex&amp;gt; не превосходит &amp;lt;tex&amp;gt;|\Sigma| |Q| \log_2(|Q|)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
|proof =&lt;br /&gt;
Пусть &amp;lt;tex&amp;gt;x, y \in Q&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;a \in \Sigma&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; \delta(x, a) = y&amp;lt;/tex&amp;gt;. Зафиксируем эту тройку. Заметим, что количество раз, которое &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt; встречается в &amp;lt;tex&amp;gt;\mathtt{Inverse}&amp;lt;/tex&amp;gt; при условии, что &amp;lt;tex&amp;gt; \delta(x, a) = y&amp;lt;/tex&amp;gt;, совпадает с числом удаленных из очереди пар &amp;lt;tex&amp;gt;(C, a)&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;y \in C&amp;lt;/tex&amp;gt;. Но по [[#Лемма3 | лемме(3)]] эта величина не превосходит &amp;lt;tex&amp;gt;\log_2(|Q|)&amp;lt;/tex&amp;gt;. Просуммировав по всем &amp;lt;tex&amp;gt; x \in Q &amp;lt;/tex&amp;gt; и по всем &amp;lt;tex&amp;gt; a \in \Sigma&amp;lt;/tex&amp;gt; мы получим утверждение леммы.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{Теорема&lt;br /&gt;
|statement =&lt;br /&gt;
Время работы алгоритма Хопкрофта равно &amp;lt;tex&amp;gt;O(|\Sigma| |Q| \log(|Q|)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
|proof =&lt;br /&gt;
Оценим, сколько времени занимает каждая часть алгоритма:&lt;br /&gt;
&lt;br /&gt;
*Построение массива &amp;lt;tex&amp;gt;\mathtt{Inv}&amp;lt;/tex&amp;gt; занимает &amp;lt;tex&amp;gt;O(|\Sigma| |Q|)&amp;lt;/tex&amp;gt; времени. &lt;br /&gt;
&lt;br /&gt;
*По [[#Лемма2 | второй лемме]] количество итераций цикла &amp;lt;tex&amp;gt;while&amp;lt;/tex&amp;gt; не превосходит &amp;lt;tex&amp;gt;O(|\Sigma| |Q|)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
*Операции с множеством &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt; и разбиение классов на подклассы требуют &amp;lt;tex&amp;gt;O(\sum(|\mathtt{Inverse}|))&amp;lt;/tex&amp;gt; времени. Но по [[#Лемма4 | лемме(4)]] &amp;lt;tex&amp;gt;\sum(|\mathtt{Inverse}|)&amp;lt;/tex&amp;gt; не превосходит &amp;lt;tex&amp;gt;|\Sigma| |Q| \log_2(|Q|)&amp;lt;/tex&amp;gt;, то есть данная часть алгоритма выполняется за &amp;lt;tex&amp;gt;O(|\Sigma| |Q| \log_2(|Q|))&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
*В [[#Лемма1 | лемме(1)]] мы показали, что в процессе работы алгоритма не может появится больше, чем &amp;lt;tex&amp;gt;2 |Q| - 1&amp;lt;/tex&amp;gt; классов, из чего следует, что количество операций &amp;lt;tex&amp;gt;\mathtt{replace}&amp;lt;/tex&amp;gt; равно &amp;lt;tex&amp;gt;O(|\Sigma| |Q|)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Итого, получается, что время работы алгоритма Хопкрофта не превышает &amp;lt;tex&amp;gt; O(|\Sigma| |Q|) + O(|\Sigma| |Q|) + O(|\Sigma| |Q| \log_2(|Q|)) + O(|\Sigma| |Q|) = O(|\Sigma| |Q| \log_2(|Q|))&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
* ''Хопкрофт Д., Мотвани Р., Ульман Д.'' Введение в теорию автоматов, языков и вычислений, 2-е изд. : Пер. с англ. — М.: Издательский дом «Вильямс», 2002. — С. 177 — ISBN 5-8459-0261-4 (рус.)&lt;br /&gt;
* ''D. Gries.'' Describing an algorithm by Hopcroft. Technical Report TR-72-151, Cornell University, December 1972.&lt;br /&gt;
* ''Hang Zhou.'' Implementation of Hopcroft's Algorithm, 19 December 2009.&lt;br /&gt;
&lt;br /&gt;
[[Категория: Теория формальных языков]]&lt;br /&gt;
[[Категория: Автоматы и регулярные языки]]&lt;/div&gt;</summary>
		<author><name>194.85.160.130</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%9B%D0%B5%D0%B2%D0%B8%D1%82%D0%B0&amp;diff=33845</id>
		<title>Алгоритм Левита</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%9B%D0%B5%D0%B2%D0%B8%D1%82%D0%B0&amp;diff=33845"/>
				<updated>2013-11-30T13:20:46Z</updated>
		
		<summary type="html">&lt;p&gt;194.85.160.130: /* Доказательство */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Алгоритм Левита''' (Levit algorithm) находит расстояние от заданной вершины &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; до всех остальных. Позволяет работать с ребрами отрицательного веса при отсутствии отрицательных циклов.&lt;br /&gt;
&lt;br /&gt;
== Алгоритм ==&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;tex&amp;gt;d_i&amp;lt;/tex&amp;gt; {{---}} текущая длина кратчайшего пути до вершины &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;. Изначально, все элементы &amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt;, кроме &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt;-го равны бесконечности; &amp;lt;tex&amp;gt;d[s] = 0&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Разделим вершины на три множества:&lt;br /&gt;
* &amp;lt;tex&amp;gt;M_0&amp;lt;/tex&amp;gt; {{---}} вершины, расстояние до которых уже вычислено (возможно, не окончательно)&lt;br /&gt;
* &amp;lt;tex&amp;gt;M_1&amp;lt;/tex&amp;gt; {{---}} вершины, расстояние до которых вычисляется. Это множество в свою очередь делится на два упорядоченных подмножества:&lt;br /&gt;
# &amp;lt;tex&amp;gt;M_1^{'}&amp;lt;/tex&amp;gt; {{---}} основная очередь&lt;br /&gt;
# &amp;lt;tex&amp;gt;M_1^{''}&amp;lt;/tex&amp;gt; {{---}} срочная очередь&lt;br /&gt;
* &amp;lt;tex&amp;gt;M_2&amp;lt;/tex&amp;gt; {{---}} вершины, расстояние до которых еще не вычислено&lt;br /&gt;
&lt;br /&gt;
Изначально все вершины, кроме &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; помещаются в множество &amp;lt;tex&amp;gt;M_2&amp;lt;/tex&amp;gt;. Вершина &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; помещается в множество &amp;lt;tex&amp;gt;M_1&amp;lt;/tex&amp;gt; (в любую из очередей).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Шаг алгоритма:''' выбирается вершина &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt; из &amp;lt;tex&amp;gt;M_1&amp;lt;/tex&amp;gt;. Если очередь &amp;lt;tex&amp;gt;M_1^{''}&amp;lt;/tex&amp;gt; не пуста, то вершина берется из нее, иначе из &amp;lt;tex&amp;gt;M_1^{'}&amp;lt;/tex&amp;gt;. Далее, для каждого ребра &amp;lt;tex&amp;gt;uv \in E&amp;lt;/tex&amp;gt;:&lt;br /&gt;
* если &amp;lt;tex&amp;gt;v \in M_2&amp;lt;/tex&amp;gt;, то &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; переводится в конец очереди &amp;lt;tex&amp;gt;M_1^{'}&amp;lt;/tex&amp;gt;. При этом &amp;lt;tex&amp;gt;d_v \gets d_u + w_{uv}&amp;lt;/tex&amp;gt; (производится релаксация ребра &amp;lt;tex&amp;gt;uv&amp;lt;/tex&amp;gt;)&lt;br /&gt;
* если &amp;lt;tex&amp;gt;v \in M_1&amp;lt;/tex&amp;gt;, то происходит релаксация ребра &amp;lt;tex&amp;gt;uv&amp;lt;/tex&amp;gt;&lt;br /&gt;
* если &amp;lt;tex&amp;gt;v \in M_0&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;d_v &amp;gt; d_u + w_{uv}&amp;lt;/tex&amp;gt;, то происходит релаксация ребра &amp;lt;tex&amp;gt;uv&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; помещается в &amp;lt;tex&amp;gt;M_1^{''}&amp;lt;/tex&amp;gt;&lt;br /&gt;
В конце шага помещаем вершину &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt; в множество &amp;lt;tex&amp;gt;M_0&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Алгоритм заканчивает работу, когда множество &amp;lt;tex&amp;gt;M_1&amp;lt;/tex&amp;gt; становится пустым.&lt;br /&gt;
&lt;br /&gt;
== Псевдокод ==&lt;br /&gt;
&lt;br /&gt;
 '''for''' u &amp;lt;tex&amp;gt;\in&amp;lt;/tex&amp;gt; V ''':'''&lt;br /&gt;
    &amp;lt;tex&amp;gt;d_v \gets&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;\infty&amp;lt;/tex&amp;gt;&lt;br /&gt;
 &amp;lt;tex&amp;gt;d_s \gets 0&amp;lt;/tex&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
 &amp;lt;tex&amp;gt;M_1^{''}&amp;lt;/tex&amp;gt;.add(s)&lt;br /&gt;
 '''for''' u &amp;lt;tex&amp;gt;\neq&amp;lt;/tex&amp;gt; s &amp;lt;tex&amp;gt;\in&amp;lt;/tex&amp;gt; V ''':'''&lt;br /&gt;
    &amp;lt;tex&amp;gt;M_2&amp;lt;/tex&amp;gt;.add(u)&lt;br /&gt;
 &lt;br /&gt;
 '''while''' &amp;lt;tex&amp;gt;M_1^{'} \neq \varnothing&amp;lt;/tex&amp;gt; '''and''' &amp;lt;tex&amp;gt;M_1^{''} \neq \varnothing&amp;lt;/tex&amp;gt; ''':'''&lt;br /&gt;
    '''if''' &amp;lt;tex&amp;gt;M_1^{''} \neq \varnothing&amp;lt;/tex&amp;gt; ''':'''&lt;br /&gt;
       u &amp;lt;tex&amp;gt;\gets&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;M_1^{''}&amp;lt;/tex&amp;gt;.pop()&lt;br /&gt;
    '''else :'''&lt;br /&gt;
       u &amp;lt;tex&amp;gt;\gets&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;M_1{'}&amp;lt;/tex&amp;gt;.pop()&lt;br /&gt;
 &lt;br /&gt;
    '''for''' uv &amp;lt;tex&amp;gt;\in&amp;lt;/tex&amp;gt; E ''':'''&lt;br /&gt;
       '''if''' v &amp;lt;tex&amp;gt;\in M_2&amp;lt;/tex&amp;gt; ''':'''&lt;br /&gt;
          &amp;lt;tex&amp;gt;M_1^{'}&amp;lt;/tex&amp;gt;.push(v)&lt;br /&gt;
          relax(uv, d)&lt;br /&gt;
       '''if''' v &amp;lt;tex&amp;gt;\in M_1&amp;lt;/tex&amp;gt; ''':'''&lt;br /&gt;
          relax(uv, d)&lt;br /&gt;
       '''if''' v &amp;lt;tex&amp;gt;\in M_0&amp;lt;/tex&amp;gt; '''and''' &amp;lt;tex&amp;gt;d_v &amp;gt; d_u + w_{uv}&amp;lt;/tex&amp;gt; ''':'''&lt;br /&gt;
          &amp;lt;tex&amp;gt;M_1^{''}&amp;lt;/tex&amp;gt;.push(v)&lt;br /&gt;
          relax(uv, d)&lt;br /&gt;
 &lt;br /&gt;
    &amp;lt;tex&amp;gt;M_0&amp;lt;/tex&amp;gt;.add(u)&lt;br /&gt;
&lt;br /&gt;
== Доказательство ==&lt;br /&gt;
&lt;br /&gt;
{{Лемма&lt;br /&gt;
|statement= Алгоритм отработает за конечное время&lt;br /&gt;
|proof= Не теряя общности, будем считать, что граф связен. Тогда в конце работы алгоритма все вершины окажутся в &amp;lt;tex&amp;gt;M_0&amp;lt;/tex&amp;gt;. PROFIT!!&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Сложность ==&lt;br /&gt;
&lt;br /&gt;
== Источники ==&lt;br /&gt;
* [http://ru.wikipedia.org/wiki/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%9B%D0%B5%D0%B2%D0%B8%D1%82%D0%B0 Алгоритм Левита - Википедия, свободная энциклопедия]&lt;br /&gt;
* [http://e-maxx.ru/algo/levit_algorithm Алгоритм Левита - MAXimal::algo]&lt;br /&gt;
* И. В. Романовский, Дискретный анализ, ISBN 5-7940-0138-0; 2008 г., стр. 228-234.&lt;/div&gt;</summary>
		<author><name>194.85.160.130</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%9B%D0%B5%D0%B2%D0%B8%D1%82%D0%B0&amp;diff=33844</id>
		<title>Алгоритм Левита</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%9B%D0%B5%D0%B2%D0%B8%D1%82%D0%B0&amp;diff=33844"/>
				<updated>2013-11-30T08:50:55Z</updated>
		
		<summary type="html">&lt;p&gt;194.85.160.130: /* См. также */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Алгоритм Левита''' (Levit algorithm) находит расстояние от заданной вершины &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; до всех остальных. Позволяет работать с ребрами отрицательного веса при отсутствии отрицательных циклов.&lt;br /&gt;
&lt;br /&gt;
== Алгоритм ==&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;tex&amp;gt;d_i&amp;lt;/tex&amp;gt; {{---}} текущая длина кратчайшего пути до вершины &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;. Изначально, все элементы &amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt;, кроме &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt;-го равны бесконечности; &amp;lt;tex&amp;gt;d[s] = 0&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Разделим вершины на три множества:&lt;br /&gt;
* &amp;lt;tex&amp;gt;M_0&amp;lt;/tex&amp;gt; {{---}} вершины, расстояние до которых уже вычислено (возможно, не окончательно)&lt;br /&gt;
* &amp;lt;tex&amp;gt;M_1&amp;lt;/tex&amp;gt; {{---}} вершины, расстояние до которых вычисляется. Это множество в свою очередь делится на два упорядоченных подмножества:&lt;br /&gt;
# &amp;lt;tex&amp;gt;M_1^{'}&amp;lt;/tex&amp;gt; {{---}} основная очередь&lt;br /&gt;
# &amp;lt;tex&amp;gt;M_1^{''}&amp;lt;/tex&amp;gt; {{---}} срочная очередь&lt;br /&gt;
* &amp;lt;tex&amp;gt;M_2&amp;lt;/tex&amp;gt; {{---}} вершины, расстояние до которых еще не вычислено&lt;br /&gt;
&lt;br /&gt;
Изначально все вершины, кроме &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; помещаются в множество &amp;lt;tex&amp;gt;M_2&amp;lt;/tex&amp;gt;. Вершина &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; помещается в множество &amp;lt;tex&amp;gt;M_1&amp;lt;/tex&amp;gt; (в любую из очередей).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Шаг алгоритма:''' выбирается вершина &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt; из &amp;lt;tex&amp;gt;M_1&amp;lt;/tex&amp;gt;. Если очередь &amp;lt;tex&amp;gt;M_1^{''}&amp;lt;/tex&amp;gt; не пуста, то вершина берется из нее, иначе из &amp;lt;tex&amp;gt;M_1^{'}&amp;lt;/tex&amp;gt;. Далее, для каждого ребра &amp;lt;tex&amp;gt;uv \in E&amp;lt;/tex&amp;gt;:&lt;br /&gt;
* если &amp;lt;tex&amp;gt;v \in M_2&amp;lt;/tex&amp;gt;, то &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; переводится в конец очереди &amp;lt;tex&amp;gt;M_1^{'}&amp;lt;/tex&amp;gt;. При этом &amp;lt;tex&amp;gt;d_v \gets d_u + w_{uv}&amp;lt;/tex&amp;gt; (производится релаксация ребра &amp;lt;tex&amp;gt;uv&amp;lt;/tex&amp;gt;)&lt;br /&gt;
* если &amp;lt;tex&amp;gt;v \in M_1&amp;lt;/tex&amp;gt;, то происходит релаксация ребра &amp;lt;tex&amp;gt;uv&amp;lt;/tex&amp;gt;&lt;br /&gt;
* если &amp;lt;tex&amp;gt;v \in M_0&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;d_v &amp;gt; d_u + w_{uv}&amp;lt;/tex&amp;gt;, то происходит релаксация ребра &amp;lt;tex&amp;gt;uv&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; помещается в &amp;lt;tex&amp;gt;M_1^{''}&amp;lt;/tex&amp;gt;&lt;br /&gt;
В конце шага помещаем вершину &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt; в множество &amp;lt;tex&amp;gt;M_0&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Алгоритм заканчивает работу, когда множество &amp;lt;tex&amp;gt;M_1&amp;lt;/tex&amp;gt; становится пустым.&lt;br /&gt;
&lt;br /&gt;
== Псевдокод ==&lt;br /&gt;
&lt;br /&gt;
 '''for''' u &amp;lt;tex&amp;gt;\in&amp;lt;/tex&amp;gt; V ''':'''&lt;br /&gt;
    &amp;lt;tex&amp;gt;d_v \gets&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;\infty&amp;lt;/tex&amp;gt;&lt;br /&gt;
 &amp;lt;tex&amp;gt;d_s \gets 0&amp;lt;/tex&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
 &amp;lt;tex&amp;gt;M_1^{''}&amp;lt;/tex&amp;gt;.add(s)&lt;br /&gt;
 '''for''' u &amp;lt;tex&amp;gt;\neq&amp;lt;/tex&amp;gt; s &amp;lt;tex&amp;gt;\in&amp;lt;/tex&amp;gt; V ''':'''&lt;br /&gt;
    &amp;lt;tex&amp;gt;M_2&amp;lt;/tex&amp;gt;.add(u)&lt;br /&gt;
 &lt;br /&gt;
 '''while''' &amp;lt;tex&amp;gt;M_1^{'} \neq \varnothing&amp;lt;/tex&amp;gt; '''and''' &amp;lt;tex&amp;gt;M_1^{''} \neq \varnothing&amp;lt;/tex&amp;gt; ''':'''&lt;br /&gt;
    '''if''' &amp;lt;tex&amp;gt;M_1^{''} \neq \varnothing&amp;lt;/tex&amp;gt; ''':'''&lt;br /&gt;
       u &amp;lt;tex&amp;gt;\gets&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;M_1^{''}&amp;lt;/tex&amp;gt;.pop()&lt;br /&gt;
    '''else :'''&lt;br /&gt;
       u &amp;lt;tex&amp;gt;\gets&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;M_1{'}&amp;lt;/tex&amp;gt;.pop()&lt;br /&gt;
 &lt;br /&gt;
    '''for''' uv &amp;lt;tex&amp;gt;\in&amp;lt;/tex&amp;gt; E ''':'''&lt;br /&gt;
       '''if''' v &amp;lt;tex&amp;gt;\in M_2&amp;lt;/tex&amp;gt; ''':'''&lt;br /&gt;
          &amp;lt;tex&amp;gt;M_1^{'}&amp;lt;/tex&amp;gt;.push(v)&lt;br /&gt;
          relax(uv, d)&lt;br /&gt;
       '''if''' v &amp;lt;tex&amp;gt;\in M_1&amp;lt;/tex&amp;gt; ''':'''&lt;br /&gt;
          relax(uv, d)&lt;br /&gt;
       '''if''' v &amp;lt;tex&amp;gt;\in M_0&amp;lt;/tex&amp;gt; '''and''' &amp;lt;tex&amp;gt;d_v &amp;gt; d_u + w_{uv}&amp;lt;/tex&amp;gt; ''':'''&lt;br /&gt;
          &amp;lt;tex&amp;gt;M_1^{''}&amp;lt;/tex&amp;gt;.push(v)&lt;br /&gt;
          relax(uv, d)&lt;br /&gt;
 &lt;br /&gt;
    &amp;lt;tex&amp;gt;M_0&amp;lt;/tex&amp;gt;.add(u)&lt;br /&gt;
&lt;br /&gt;
== Доказательство ==&lt;br /&gt;
&lt;br /&gt;
== Сложность ==&lt;br /&gt;
&lt;br /&gt;
== Источники ==&lt;br /&gt;
* [http://ru.wikipedia.org/wiki/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%9B%D0%B5%D0%B2%D0%B8%D1%82%D0%B0 Алгоритм Левита - Википедия, свободная энциклопедия]&lt;br /&gt;
* [http://e-maxx.ru/algo/levit_algorithm Алгоритм Левита - MAXimal::algo]&lt;br /&gt;
* И. В. Романовский, Дискретный анализ, ISBN 5-7940-0138-0; 2008 г., стр. 228-234.&lt;/div&gt;</summary>
		<author><name>194.85.160.130</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%9B%D0%B5%D0%B2%D0%B8%D1%82%D0%B0&amp;diff=33843</id>
		<title>Алгоритм Левита</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%9B%D0%B5%D0%B2%D0%B8%D1%82%D0%B0&amp;diff=33843"/>
				<updated>2013-11-30T08:50:41Z</updated>
		
		<summary type="html">&lt;p&gt;194.85.160.130: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Алгоритм Левита''' (Levit algorithm) находит расстояние от заданной вершины &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; до всех остальных. Позволяет работать с ребрами отрицательного веса при отсутствии отрицательных циклов.&lt;br /&gt;
&lt;br /&gt;
== Алгоритм ==&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;tex&amp;gt;d_i&amp;lt;/tex&amp;gt; {{---}} текущая длина кратчайшего пути до вершины &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;. Изначально, все элементы &amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt;, кроме &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt;-го равны бесконечности; &amp;lt;tex&amp;gt;d[s] = 0&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Разделим вершины на три множества:&lt;br /&gt;
* &amp;lt;tex&amp;gt;M_0&amp;lt;/tex&amp;gt; {{---}} вершины, расстояние до которых уже вычислено (возможно, не окончательно)&lt;br /&gt;
* &amp;lt;tex&amp;gt;M_1&amp;lt;/tex&amp;gt; {{---}} вершины, расстояние до которых вычисляется. Это множество в свою очередь делится на два упорядоченных подмножества:&lt;br /&gt;
# &amp;lt;tex&amp;gt;M_1^{'}&amp;lt;/tex&amp;gt; {{---}} основная очередь&lt;br /&gt;
# &amp;lt;tex&amp;gt;M_1^{''}&amp;lt;/tex&amp;gt; {{---}} срочная очередь&lt;br /&gt;
* &amp;lt;tex&amp;gt;M_2&amp;lt;/tex&amp;gt; {{---}} вершины, расстояние до которых еще не вычислено&lt;br /&gt;
&lt;br /&gt;
Изначально все вершины, кроме &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; помещаются в множество &amp;lt;tex&amp;gt;M_2&amp;lt;/tex&amp;gt;. Вершина &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; помещается в множество &amp;lt;tex&amp;gt;M_1&amp;lt;/tex&amp;gt; (в любую из очередей).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Шаг алгоритма:''' выбирается вершина &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt; из &amp;lt;tex&amp;gt;M_1&amp;lt;/tex&amp;gt;. Если очередь &amp;lt;tex&amp;gt;M_1^{''}&amp;lt;/tex&amp;gt; не пуста, то вершина берется из нее, иначе из &amp;lt;tex&amp;gt;M_1^{'}&amp;lt;/tex&amp;gt;. Далее, для каждого ребра &amp;lt;tex&amp;gt;uv \in E&amp;lt;/tex&amp;gt;:&lt;br /&gt;
* если &amp;lt;tex&amp;gt;v \in M_2&amp;lt;/tex&amp;gt;, то &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; переводится в конец очереди &amp;lt;tex&amp;gt;M_1^{'}&amp;lt;/tex&amp;gt;. При этом &amp;lt;tex&amp;gt;d_v \gets d_u + w_{uv}&amp;lt;/tex&amp;gt; (производится релаксация ребра &amp;lt;tex&amp;gt;uv&amp;lt;/tex&amp;gt;)&lt;br /&gt;
* если &amp;lt;tex&amp;gt;v \in M_1&amp;lt;/tex&amp;gt;, то происходит релаксация ребра &amp;lt;tex&amp;gt;uv&amp;lt;/tex&amp;gt;&lt;br /&gt;
* если &amp;lt;tex&amp;gt;v \in M_0&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;d_v &amp;gt; d_u + w_{uv}&amp;lt;/tex&amp;gt;, то происходит релаксация ребра &amp;lt;tex&amp;gt;uv&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; помещается в &amp;lt;tex&amp;gt;M_1^{''}&amp;lt;/tex&amp;gt;&lt;br /&gt;
В конце шага помещаем вершину &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt; в множество &amp;lt;tex&amp;gt;M_0&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Алгоритм заканчивает работу, когда множество &amp;lt;tex&amp;gt;M_1&amp;lt;/tex&amp;gt; становится пустым.&lt;br /&gt;
&lt;br /&gt;
== Псевдокод ==&lt;br /&gt;
&lt;br /&gt;
 '''for''' u &amp;lt;tex&amp;gt;\in&amp;lt;/tex&amp;gt; V ''':'''&lt;br /&gt;
    &amp;lt;tex&amp;gt;d_v \gets&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;\infty&amp;lt;/tex&amp;gt;&lt;br /&gt;
 &amp;lt;tex&amp;gt;d_s \gets 0&amp;lt;/tex&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
 &amp;lt;tex&amp;gt;M_1^{''}&amp;lt;/tex&amp;gt;.add(s)&lt;br /&gt;
 '''for''' u &amp;lt;tex&amp;gt;\neq&amp;lt;/tex&amp;gt; s &amp;lt;tex&amp;gt;\in&amp;lt;/tex&amp;gt; V ''':'''&lt;br /&gt;
    &amp;lt;tex&amp;gt;M_2&amp;lt;/tex&amp;gt;.add(u)&lt;br /&gt;
 &lt;br /&gt;
 '''while''' &amp;lt;tex&amp;gt;M_1^{'} \neq \varnothing&amp;lt;/tex&amp;gt; '''and''' &amp;lt;tex&amp;gt;M_1^{''} \neq \varnothing&amp;lt;/tex&amp;gt; ''':'''&lt;br /&gt;
    '''if''' &amp;lt;tex&amp;gt;M_1^{''} \neq \varnothing&amp;lt;/tex&amp;gt; ''':'''&lt;br /&gt;
       u &amp;lt;tex&amp;gt;\gets&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;M_1^{''}&amp;lt;/tex&amp;gt;.pop()&lt;br /&gt;
    '''else :'''&lt;br /&gt;
       u &amp;lt;tex&amp;gt;\gets&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;M_1{'}&amp;lt;/tex&amp;gt;.pop()&lt;br /&gt;
 &lt;br /&gt;
    '''for''' uv &amp;lt;tex&amp;gt;\in&amp;lt;/tex&amp;gt; E ''':'''&lt;br /&gt;
       '''if''' v &amp;lt;tex&amp;gt;\in M_2&amp;lt;/tex&amp;gt; ''':'''&lt;br /&gt;
          &amp;lt;tex&amp;gt;M_1^{'}&amp;lt;/tex&amp;gt;.push(v)&lt;br /&gt;
          relax(uv, d)&lt;br /&gt;
       '''if''' v &amp;lt;tex&amp;gt;\in M_1&amp;lt;/tex&amp;gt; ''':'''&lt;br /&gt;
          relax(uv, d)&lt;br /&gt;
       '''if''' v &amp;lt;tex&amp;gt;\in M_0&amp;lt;/tex&amp;gt; '''and''' &amp;lt;tex&amp;gt;d_v &amp;gt; d_u + w_{uv}&amp;lt;/tex&amp;gt; ''':'''&lt;br /&gt;
          &amp;lt;tex&amp;gt;M_1^{''}&amp;lt;/tex&amp;gt;.push(v)&lt;br /&gt;
          relax(uv, d)&lt;br /&gt;
 &lt;br /&gt;
    &amp;lt;tex&amp;gt;M_0&amp;lt;/tex&amp;gt;.add(u)&lt;br /&gt;
&lt;br /&gt;
== Доказательство ==&lt;br /&gt;
&lt;br /&gt;
== Сложность ==&lt;br /&gt;
&lt;br /&gt;
== См. также ==&lt;br /&gt;
&lt;br /&gt;
* [[Алгоритм A*]]&lt;br /&gt;
* [[Алгоритм Дейкстры]]&lt;br /&gt;
* [[Алгоритм Джонсона]]&lt;br /&gt;
* [[Алгоритм Флойда]]&lt;br /&gt;
* [[Алгоритм Форда-Беллмана]]&lt;br /&gt;
&lt;br /&gt;
== Источники ==&lt;br /&gt;
* [http://ru.wikipedia.org/wiki/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%9B%D0%B5%D0%B2%D0%B8%D1%82%D0%B0 Алгоритм Левита - Википедия, свободная энциклопедия]&lt;br /&gt;
* [http://e-maxx.ru/algo/levit_algorithm Алгоритм Левита - MAXimal::algo]&lt;br /&gt;
* И. В. Романовский, Дискретный анализ, ISBN 5-7940-0138-0; 2008 г., стр. 228-234.&lt;/div&gt;</summary>
		<author><name>194.85.160.130</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%9B%D0%B5%D0%B2%D0%B8%D1%82%D0%B0&amp;diff=33842</id>
		<title>Алгоритм Левита</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%9B%D0%B5%D0%B2%D0%B8%D1%82%D0%B0&amp;diff=33842"/>
				<updated>2013-11-30T08:50:16Z</updated>
		
		<summary type="html">&lt;p&gt;194.85.160.130: /* Сложность */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Алгоритм Левита''' (Levit algorithm) находит расстояние от заданной вершины &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; до всех остальных. Позволяет работать с ребрами отрицательного веса при отсутствии отрицательных циклов.&lt;br /&gt;
&lt;br /&gt;
== Алгоритм ==&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;tex&amp;gt;d_i&amp;lt;/tex&amp;gt; {{---}} текущая длина кратчайшего пути до вершины &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;. Изначально, все элементы &amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt;, кроме &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt;-го равны бесконечности; &amp;lt;tex&amp;gt;d[s] = 0&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Разделим вершины на три множества:&lt;br /&gt;
* &amp;lt;tex&amp;gt;M_0&amp;lt;/tex&amp;gt; {{---}} вершины, расстояние до которых уже вычислено (возможно, не окончательно)&lt;br /&gt;
* &amp;lt;tex&amp;gt;M_1&amp;lt;/tex&amp;gt; {{---}} вершины, расстояние до которых вычисляется. Это множество в свою очередь делится на два упорядоченных подмножества:&lt;br /&gt;
# &amp;lt;tex&amp;gt;M_1^{'}&amp;lt;/tex&amp;gt; {{---}} основная очередь&lt;br /&gt;
# &amp;lt;tex&amp;gt;M_1^{''}&amp;lt;/tex&amp;gt; {{---}} срочная очередь&lt;br /&gt;
* &amp;lt;tex&amp;gt;M_2&amp;lt;/tex&amp;gt; {{---}} вершины, расстояние до которых еще не вычислено&lt;br /&gt;
&lt;br /&gt;
Изначально все вершины, кроме &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; помещаются в множество &amp;lt;tex&amp;gt;M_2&amp;lt;/tex&amp;gt;. Вершина &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; помещается в множество &amp;lt;tex&amp;gt;M_1&amp;lt;/tex&amp;gt; (в любую из очередей).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Шаг алгоритма:''' выбирается вершина &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt; из &amp;lt;tex&amp;gt;M_1&amp;lt;/tex&amp;gt;. Если очередь &amp;lt;tex&amp;gt;M_1^{''}&amp;lt;/tex&amp;gt; не пуста, то вершина берется из нее, иначе из &amp;lt;tex&amp;gt;M_1^{'}&amp;lt;/tex&amp;gt;. Далее, для каждого ребра &amp;lt;tex&amp;gt;uv \in E&amp;lt;/tex&amp;gt;:&lt;br /&gt;
* если &amp;lt;tex&amp;gt;v \in M_2&amp;lt;/tex&amp;gt;, то &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; переводится в конец очереди &amp;lt;tex&amp;gt;M_1^{'}&amp;lt;/tex&amp;gt;. При этом &amp;lt;tex&amp;gt;d_v \gets d_u + w_{uv}&amp;lt;/tex&amp;gt; (производится релаксация ребра &amp;lt;tex&amp;gt;uv&amp;lt;/tex&amp;gt;)&lt;br /&gt;
* если &amp;lt;tex&amp;gt;v \in M_1&amp;lt;/tex&amp;gt;, то происходит релаксация ребра &amp;lt;tex&amp;gt;uv&amp;lt;/tex&amp;gt;&lt;br /&gt;
* если &amp;lt;tex&amp;gt;v \in M_0&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;d_v &amp;gt; d_u + w_{uv}&amp;lt;/tex&amp;gt;, то происходит релаксация ребра &amp;lt;tex&amp;gt;uv&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; помещается в &amp;lt;tex&amp;gt;M_1^{''}&amp;lt;/tex&amp;gt;&lt;br /&gt;
В конце шага помещаем вершину &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt; в множество &amp;lt;tex&amp;gt;M_0&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Алгоритм заканчивает работу, когда множество &amp;lt;tex&amp;gt;M_1&amp;lt;/tex&amp;gt; становится пустым.&lt;br /&gt;
&lt;br /&gt;
== Псевдокод ==&lt;br /&gt;
&lt;br /&gt;
 '''for''' u &amp;lt;tex&amp;gt;\in&amp;lt;/tex&amp;gt; V ''':'''&lt;br /&gt;
    &amp;lt;tex&amp;gt;d_v \gets&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;\infty&amp;lt;/tex&amp;gt;&lt;br /&gt;
 &amp;lt;tex&amp;gt;d_s \gets 0&amp;lt;/tex&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
 &amp;lt;tex&amp;gt;M_1^{''}&amp;lt;/tex&amp;gt;.add(s)&lt;br /&gt;
 '''for''' u &amp;lt;tex&amp;gt;\neq&amp;lt;/tex&amp;gt; s &amp;lt;tex&amp;gt;\in&amp;lt;/tex&amp;gt; V ''':'''&lt;br /&gt;
    &amp;lt;tex&amp;gt;M_2&amp;lt;/tex&amp;gt;.add(u)&lt;br /&gt;
 &lt;br /&gt;
 '''while''' &amp;lt;tex&amp;gt;M_1^{'} \neq \varnothing&amp;lt;/tex&amp;gt; '''and''' &amp;lt;tex&amp;gt;M_1^{''} \neq \varnothing&amp;lt;/tex&amp;gt; ''':'''&lt;br /&gt;
    '''if''' &amp;lt;tex&amp;gt;M_1^{''} \neq \varnothing&amp;lt;/tex&amp;gt; ''':'''&lt;br /&gt;
       u &amp;lt;tex&amp;gt;\gets&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;M_1^{''}&amp;lt;/tex&amp;gt;.pop()&lt;br /&gt;
    '''else :'''&lt;br /&gt;
       u &amp;lt;tex&amp;gt;\gets&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;M_1{'}&amp;lt;/tex&amp;gt;.pop()&lt;br /&gt;
 &lt;br /&gt;
    '''for''' uv &amp;lt;tex&amp;gt;\in&amp;lt;/tex&amp;gt; E ''':'''&lt;br /&gt;
       '''if''' v &amp;lt;tex&amp;gt;\in M_2&amp;lt;/tex&amp;gt; ''':'''&lt;br /&gt;
          &amp;lt;tex&amp;gt;M_1^{'}&amp;lt;/tex&amp;gt;.push(v)&lt;br /&gt;
          relax(uv, d)&lt;br /&gt;
       '''if''' v &amp;lt;tex&amp;gt;\in M_1&amp;lt;/tex&amp;gt; ''':'''&lt;br /&gt;
          relax(uv, d)&lt;br /&gt;
       '''if''' v &amp;lt;tex&amp;gt;\in M_0&amp;lt;/tex&amp;gt; '''and''' &amp;lt;tex&amp;gt;d_v &amp;gt; d_u + w_{uv}&amp;lt;/tex&amp;gt; ''':'''&lt;br /&gt;
          &amp;lt;tex&amp;gt;M_1^{''}&amp;lt;/tex&amp;gt;.push(v)&lt;br /&gt;
          relax(uv, d)&lt;br /&gt;
 &lt;br /&gt;
    &amp;lt;tex&amp;gt;M_0&amp;lt;/tex&amp;gt;.add(u)&lt;br /&gt;
&lt;br /&gt;
== Сложность ==&lt;br /&gt;
&lt;br /&gt;
== См. также ==&lt;br /&gt;
&lt;br /&gt;
* [[Алгоритм A*]]&lt;br /&gt;
* [[Алгоритм Дейкстры]]&lt;br /&gt;
* [[Алгоритм Джонсона]]&lt;br /&gt;
* [[Алгоритм Флойда]]&lt;br /&gt;
* [[Алгоритм Форда-Беллмана]]&lt;br /&gt;
&lt;br /&gt;
== Источники ==&lt;br /&gt;
* [http://ru.wikipedia.org/wiki/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%9B%D0%B5%D0%B2%D0%B8%D1%82%D0%B0 Алгоритм Левита - Википедия, свободная энциклопедия]&lt;br /&gt;
* [http://e-maxx.ru/algo/levit_algorithm Алгоритм Левита - MAXimal::algo]&lt;br /&gt;
* И. В. Романовский, Дискретный анализ, ISBN 5-7940-0138-0; 2008 г., стр. 228-234.&lt;/div&gt;</summary>
		<author><name>194.85.160.130</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%9B%D0%B5%D0%B2%D0%B8%D1%82%D0%B0&amp;diff=33841</id>
		<title>Алгоритм Левита</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%9B%D0%B5%D0%B2%D0%B8%D1%82%D0%B0&amp;diff=33841"/>
				<updated>2013-11-30T08:44:33Z</updated>
		
		<summary type="html">&lt;p&gt;194.85.160.130: /* Псевдокод */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Алгоритм Левита''' (Levit algorithm) находит расстояние от заданной вершины &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; до всех остальных. Позволяет работать с ребрами отрицательного веса при отсутствии отрицательных циклов.&lt;br /&gt;
&lt;br /&gt;
== Алгоритм ==&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;tex&amp;gt;d_i&amp;lt;/tex&amp;gt; {{---}} текущая длина кратчайшего пути до вершины &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;. Изначально, все элементы &amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt;, кроме &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt;-го равны бесконечности; &amp;lt;tex&amp;gt;d[s] = 0&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Разделим вершины на три множества:&lt;br /&gt;
* &amp;lt;tex&amp;gt;M_0&amp;lt;/tex&amp;gt; {{---}} вершины, расстояние до которых уже вычислено (возможно, не окончательно)&lt;br /&gt;
* &amp;lt;tex&amp;gt;M_1&amp;lt;/tex&amp;gt; {{---}} вершины, расстояние до которых вычисляется. Это множество в свою очередь делится на два упорядоченных подмножества:&lt;br /&gt;
# &amp;lt;tex&amp;gt;M_1^{'}&amp;lt;/tex&amp;gt; {{---}} основная очередь&lt;br /&gt;
# &amp;lt;tex&amp;gt;M_1^{''}&amp;lt;/tex&amp;gt; {{---}} срочная очередь&lt;br /&gt;
* &amp;lt;tex&amp;gt;M_2&amp;lt;/tex&amp;gt; {{---}} вершины, расстояние до которых еще не вычислено&lt;br /&gt;
&lt;br /&gt;
Изначально все вершины, кроме &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; помещаются в множество &amp;lt;tex&amp;gt;M_2&amp;lt;/tex&amp;gt;. Вершина &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; помещается в множество &amp;lt;tex&amp;gt;M_1&amp;lt;/tex&amp;gt; (в любую из очередей).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Шаг алгоритма:''' выбирается вершина &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt; из &amp;lt;tex&amp;gt;M_1&amp;lt;/tex&amp;gt;. Если очередь &amp;lt;tex&amp;gt;M_1^{''}&amp;lt;/tex&amp;gt; не пуста, то вершина берется из нее, иначе из &amp;lt;tex&amp;gt;M_1^{'}&amp;lt;/tex&amp;gt;. Далее, для каждого ребра &amp;lt;tex&amp;gt;uv \in E&amp;lt;/tex&amp;gt;:&lt;br /&gt;
* если &amp;lt;tex&amp;gt;v \in M_2&amp;lt;/tex&amp;gt;, то &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; переводится в конец очереди &amp;lt;tex&amp;gt;M_1^{'}&amp;lt;/tex&amp;gt;. При этом &amp;lt;tex&amp;gt;d_v \gets d_u + w_{uv}&amp;lt;/tex&amp;gt; (производится релаксация ребра &amp;lt;tex&amp;gt;uv&amp;lt;/tex&amp;gt;)&lt;br /&gt;
* если &amp;lt;tex&amp;gt;v \in M_1&amp;lt;/tex&amp;gt;, то происходит релаксация ребра &amp;lt;tex&amp;gt;uv&amp;lt;/tex&amp;gt;&lt;br /&gt;
* если &amp;lt;tex&amp;gt;v \in M_0&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;d_v &amp;gt; d_u + w_{uv}&amp;lt;/tex&amp;gt;, то происходит релаксация ребра &amp;lt;tex&amp;gt;uv&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; помещается в &amp;lt;tex&amp;gt;M_1^{''}&amp;lt;/tex&amp;gt;&lt;br /&gt;
В конце шага помещаем вершину &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt; в множество &amp;lt;tex&amp;gt;M_0&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Алгоритм заканчивает работу, когда множество &amp;lt;tex&amp;gt;M_1&amp;lt;/tex&amp;gt; становится пустым.&lt;br /&gt;
&lt;br /&gt;
== Псевдокод ==&lt;br /&gt;
&lt;br /&gt;
 '''for''' u &amp;lt;tex&amp;gt;\in&amp;lt;/tex&amp;gt; V ''':'''&lt;br /&gt;
    &amp;lt;tex&amp;gt;d_v \gets&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;\infty&amp;lt;/tex&amp;gt;&lt;br /&gt;
 &amp;lt;tex&amp;gt;d_s \gets 0&amp;lt;/tex&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
 &amp;lt;tex&amp;gt;M_1^{''}&amp;lt;/tex&amp;gt;.add(s)&lt;br /&gt;
 '''for''' u &amp;lt;tex&amp;gt;\neq&amp;lt;/tex&amp;gt; s &amp;lt;tex&amp;gt;\in&amp;lt;/tex&amp;gt; V ''':'''&lt;br /&gt;
    &amp;lt;tex&amp;gt;M_2&amp;lt;/tex&amp;gt;.add(u)&lt;br /&gt;
 &lt;br /&gt;
 '''while''' &amp;lt;tex&amp;gt;M_1^{'} \neq \varnothing&amp;lt;/tex&amp;gt; '''and''' &amp;lt;tex&amp;gt;M_1^{''} \neq \varnothing&amp;lt;/tex&amp;gt; ''':'''&lt;br /&gt;
    '''if''' &amp;lt;tex&amp;gt;M_1^{''} \neq \varnothing&amp;lt;/tex&amp;gt; ''':'''&lt;br /&gt;
       u &amp;lt;tex&amp;gt;\gets&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;M_1^{''}&amp;lt;/tex&amp;gt;.pop()&lt;br /&gt;
    '''else :'''&lt;br /&gt;
       u &amp;lt;tex&amp;gt;\gets&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;M_1{'}&amp;lt;/tex&amp;gt;.pop()&lt;br /&gt;
 &lt;br /&gt;
    '''for''' uv &amp;lt;tex&amp;gt;\in&amp;lt;/tex&amp;gt; E ''':'''&lt;br /&gt;
       '''if''' v &amp;lt;tex&amp;gt;\in M_2&amp;lt;/tex&amp;gt; ''':'''&lt;br /&gt;
          &amp;lt;tex&amp;gt;M_1^{'}&amp;lt;/tex&amp;gt;.push(v)&lt;br /&gt;
          relax(uv, d)&lt;br /&gt;
       '''if''' v &amp;lt;tex&amp;gt;\in M_1&amp;lt;/tex&amp;gt; ''':'''&lt;br /&gt;
          relax(uv, d)&lt;br /&gt;
       '''if''' v &amp;lt;tex&amp;gt;\in M_0&amp;lt;/tex&amp;gt; '''and''' &amp;lt;tex&amp;gt;d_v &amp;gt; d_u + w_{uv}&amp;lt;/tex&amp;gt; ''':'''&lt;br /&gt;
          &amp;lt;tex&amp;gt;M_1^{''}&amp;lt;/tex&amp;gt;.push(v)&lt;br /&gt;
          relax(uv, d)&lt;br /&gt;
 &lt;br /&gt;
    &amp;lt;tex&amp;gt;M_0&amp;lt;/tex&amp;gt;.add(u)&lt;br /&gt;
&lt;br /&gt;
== Сложность ==&lt;br /&gt;
&lt;br /&gt;
В худшем случае алгоритм Левита работает за &amp;lt;tex&amp;gt;\bf O(|V| \cdot 2^{|V|})&amp;lt;/tex&amp;gt;. Это происходит вследствие того, что некоторые вершины приходится обрабатывать повторно. Однако эксперементы показывают, что для реальных графов(например, карт дорог) данный алгоритм оказывается достаточно быстрым и '''эксперементальная''' оценка данного алгоритма составляет &amp;lt;tex&amp;gt;\bf O(|E| \cdot log |V|)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== См. также ==&lt;br /&gt;
&lt;br /&gt;
* [[Алгоритм A*]]&lt;br /&gt;
* [[Алгоритм Дейкстры]]&lt;br /&gt;
* [[Алгоритм Джонсона]]&lt;br /&gt;
* [[Алгоритм Флойда]]&lt;br /&gt;
* [[Алгоритм Форда-Беллмана]]&lt;br /&gt;
&lt;br /&gt;
== Источники ==&lt;br /&gt;
* [http://ru.wikipedia.org/wiki/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%9B%D0%B5%D0%B2%D0%B8%D1%82%D0%B0 Алгоритм Левита - Википедия, свободная энциклопедия]&lt;br /&gt;
* [http://e-maxx.ru/algo/levit_algorithm Алгоритм Левита - MAXimal::algo]&lt;br /&gt;
* И. В. Романовский, Дискретный анализ, ISBN 5-7940-0138-0; 2008 г., стр. 228-234.&lt;/div&gt;</summary>
		<author><name>194.85.160.130</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%9B%D0%B5%D0%B2%D0%B8%D1%82%D0%B0&amp;diff=33840</id>
		<title>Алгоритм Левита</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%9B%D0%B5%D0%B2%D0%B8%D1%82%D0%B0&amp;diff=33840"/>
				<updated>2013-11-30T08:39:33Z</updated>
		
		<summary type="html">&lt;p&gt;194.85.160.130: /* Алгоритм */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Алгоритм Левита''' (Levit algorithm) находит расстояние от заданной вершины &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; до всех остальных. Позволяет работать с ребрами отрицательного веса при отсутствии отрицательных циклов.&lt;br /&gt;
&lt;br /&gt;
== Алгоритм ==&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;tex&amp;gt;d_i&amp;lt;/tex&amp;gt; {{---}} текущая длина кратчайшего пути до вершины &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;. Изначально, все элементы &amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt;, кроме &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt;-го равны бесконечности; &amp;lt;tex&amp;gt;d[s] = 0&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Разделим вершины на три множества:&lt;br /&gt;
* &amp;lt;tex&amp;gt;M_0&amp;lt;/tex&amp;gt; {{---}} вершины, расстояние до которых уже вычислено (возможно, не окончательно)&lt;br /&gt;
* &amp;lt;tex&amp;gt;M_1&amp;lt;/tex&amp;gt; {{---}} вершины, расстояние до которых вычисляется. Это множество в свою очередь делится на два упорядоченных подмножества:&lt;br /&gt;
# &amp;lt;tex&amp;gt;M_1^{'}&amp;lt;/tex&amp;gt; {{---}} основная очередь&lt;br /&gt;
# &amp;lt;tex&amp;gt;M_1^{''}&amp;lt;/tex&amp;gt; {{---}} срочная очередь&lt;br /&gt;
* &amp;lt;tex&amp;gt;M_2&amp;lt;/tex&amp;gt; {{---}} вершины, расстояние до которых еще не вычислено&lt;br /&gt;
&lt;br /&gt;
Изначально все вершины, кроме &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; помещаются в множество &amp;lt;tex&amp;gt;M_2&amp;lt;/tex&amp;gt;. Вершина &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; помещается в множество &amp;lt;tex&amp;gt;M_1&amp;lt;/tex&amp;gt; (в любую из очередей).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Шаг алгоритма:''' выбирается вершина &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt; из &amp;lt;tex&amp;gt;M_1&amp;lt;/tex&amp;gt;. Если очередь &amp;lt;tex&amp;gt;M_1^{''}&amp;lt;/tex&amp;gt; не пуста, то вершина берется из нее, иначе из &amp;lt;tex&amp;gt;M_1^{'}&amp;lt;/tex&amp;gt;. Далее, для каждого ребра &amp;lt;tex&amp;gt;uv \in E&amp;lt;/tex&amp;gt;:&lt;br /&gt;
* если &amp;lt;tex&amp;gt;v \in M_2&amp;lt;/tex&amp;gt;, то &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; переводится в конец очереди &amp;lt;tex&amp;gt;M_1^{'}&amp;lt;/tex&amp;gt;. При этом &amp;lt;tex&amp;gt;d_v \gets d_u + w_{uv}&amp;lt;/tex&amp;gt; (производится релаксация ребра &amp;lt;tex&amp;gt;uv&amp;lt;/tex&amp;gt;)&lt;br /&gt;
* если &amp;lt;tex&amp;gt;v \in M_1&amp;lt;/tex&amp;gt;, то происходит релаксация ребра &amp;lt;tex&amp;gt;uv&amp;lt;/tex&amp;gt;&lt;br /&gt;
* если &amp;lt;tex&amp;gt;v \in M_0&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;d_v &amp;gt; d_u + w_{uv}&amp;lt;/tex&amp;gt;, то происходит релаксация ребра &amp;lt;tex&amp;gt;uv&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; помещается в &amp;lt;tex&amp;gt;M_1^{''}&amp;lt;/tex&amp;gt;&lt;br /&gt;
В конце шага помещаем вершину &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt; в множество &amp;lt;tex&amp;gt;M_0&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Алгоритм заканчивает работу, когда множество &amp;lt;tex&amp;gt;M_1&amp;lt;/tex&amp;gt; становится пустым.&lt;br /&gt;
&lt;br /&gt;
== Псевдокод ==&lt;br /&gt;
&lt;br /&gt;
 '''for''' u &amp;lt;tex&amp;gt;\in&amp;lt;/tex&amp;gt; V ''':'''&lt;br /&gt;
    &amp;lt;tex&amp;gt;d_v \gets&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;\infty&amp;lt;/tex&amp;gt;&lt;br /&gt;
 &amp;lt;tex&amp;gt;d_s \gets 0&amp;lt;/tex&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
 &amp;lt;tex&amp;gt;M_1^{''}&amp;lt;/tex&amp;gt;.add(s)&lt;br /&gt;
 '''for''' u &amp;lt;tex&amp;gt;\neq&amp;lt;/tex&amp;gt; s &amp;lt;tex&amp;gt;\in&amp;lt;/tex&amp;gt; V ''':'''&lt;br /&gt;
    &amp;lt;tex&amp;gt;M_2&amp;lt;/tex&amp;gt;.add(u)&lt;br /&gt;
 &lt;br /&gt;
 '''while''' &amp;lt;tex&amp;gt;M_1 \neq \varnothing&amp;lt;/tex&amp;gt; ''':'''&lt;br /&gt;
    '''if''' &amp;lt;tex&amp;gt;M_1^{''} \neq \varnothing&amp;lt;/tex&amp;gt; ''':'''&lt;br /&gt;
       u &amp;lt;tex&amp;gt;\gets&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;M_1^{''}&amp;lt;/tex&amp;gt;.pop()&lt;br /&gt;
    '''else :'''&lt;br /&gt;
       u &amp;lt;tex&amp;gt;\gets&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;M_1{'}&amp;lt;/tex&amp;gt;.pop()&lt;br /&gt;
 &lt;br /&gt;
    '''for''' uv &amp;lt;tex&amp;gt;\in&amp;lt;/tex&amp;gt; E ''':'''&lt;br /&gt;
       '''if''' v &amp;lt;tex&amp;gt;\in M_2&amp;lt;/tex&amp;gt; ''':'''&lt;br /&gt;
          &amp;lt;tex&amp;gt;M_1^{'}&amp;lt;/tex&amp;gt;.push(v)&lt;br /&gt;
          relax(uv)&lt;br /&gt;
       '''if''' v &amp;lt;tex&amp;gt;\in M_1&amp;lt;/tex&amp;gt; ''':'''&lt;br /&gt;
          relax(uv)&lt;br /&gt;
       '''if''' v &amp;lt;tex&amp;gt;\in M_0&amp;lt;/tex&amp;gt; '''and''' &amp;lt;tex&amp;gt;d_v &amp;gt; d_u + w_{uv}&amp;lt;/tex&amp;gt; ''':'''&lt;br /&gt;
          &amp;lt;tex&amp;gt;M_1^{''}&amp;lt;/tex&amp;gt;.push(v)&lt;br /&gt;
          relax(uv)&lt;br /&gt;
 &lt;br /&gt;
    &amp;lt;tex&amp;gt;M_0&amp;lt;/tex&amp;gt;.add(u)&lt;br /&gt;
&lt;br /&gt;
== Сложность ==&lt;br /&gt;
&lt;br /&gt;
В худшем случае алгоритм Левита работает за &amp;lt;tex&amp;gt;\bf O(|V| \cdot 2^{|V|})&amp;lt;/tex&amp;gt;. Это происходит вследствие того, что некоторые вершины приходится обрабатывать повторно. Однако эксперементы показывают, что для реальных графов(например, карт дорог) данный алгоритм оказывается достаточно быстрым и '''эксперементальная''' оценка данного алгоритма составляет &amp;lt;tex&amp;gt;\bf O(|E| \cdot log |V|)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== См. также ==&lt;br /&gt;
&lt;br /&gt;
* [[Алгоритм A*]]&lt;br /&gt;
* [[Алгоритм Дейкстры]]&lt;br /&gt;
* [[Алгоритм Джонсона]]&lt;br /&gt;
* [[Алгоритм Флойда]]&lt;br /&gt;
* [[Алгоритм Форда-Беллмана]]&lt;br /&gt;
&lt;br /&gt;
== Источники ==&lt;br /&gt;
* [http://ru.wikipedia.org/wiki/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%9B%D0%B5%D0%B2%D0%B8%D1%82%D0%B0 Алгоритм Левита - Википедия, свободная энциклопедия]&lt;br /&gt;
* [http://e-maxx.ru/algo/levit_algorithm Алгоритм Левита - MAXimal::algo]&lt;br /&gt;
* И. В. Романовский, Дискретный анализ, ISBN 5-7940-0138-0; 2008 г., стр. 228-234.&lt;/div&gt;</summary>
		<author><name>194.85.160.130</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%9B%D1%8F%D0%BC%D0%B1%D0%B4%D0%B0-%D0%B8%D1%81%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5&amp;diff=27298</id>
		<title>Лямбда-исчисление</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%9B%D1%8F%D0%BC%D0%B1%D0%B4%D0%B0-%D0%B8%D1%81%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5&amp;diff=27298"/>
				<updated>2012-12-06T17:41:54Z</updated>
		
		<summary type="html">&lt;p&gt;194.85.160.130: /* Логические значения */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{В разработке}}&lt;br /&gt;
&lt;br /&gt;
== Лямбда-исчисление==&lt;br /&gt;
&lt;br /&gt;
''Лямбда-исчисление'' {{---}} формальная система, придуманная в 1930-х годах &lt;br /&gt;
Алонзо Чёрчем. Лямбда-функция является, по сути, анонимной функцией.&lt;br /&gt;
Эта концепция показала себя удобной и сейчас активно используется во многих&lt;br /&gt;
языках программирования.&lt;br /&gt;
&lt;br /&gt;
Более формально, ''лямбда-функцию'' (или, ''лямбда-терм'') можно задать &lt;br /&gt;
следующей грамматикой:&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
&amp;lt;tex&amp;gt;&lt;br /&gt;
\begin{array}{r c l}&lt;br /&gt;
\langle Term \rangle &amp;amp; ::= &amp;amp; \langle Variable \rangle \\&lt;br /&gt;
                       &amp;amp; || &amp;amp; \langle Term \rangle \langle Term \rangle \\&lt;br /&gt;
                       &amp;amp; || &amp;amp; \lambda \langle Variable \rangle \to \langle Term \rangle\\&lt;br /&gt;
                       &amp;amp; || &amp;amp; ( \langle Term \rangle )\\&lt;br /&gt;
\langle Variable \rangle &amp;amp; ::= &amp;amp; \langle Char \rangle *\\&lt;br /&gt;
\end{array}&lt;br /&gt;
&amp;lt;/tex&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
В первом случае функция является просто переменной. &lt;br /&gt;
Во втором происходит ''аппликация'' (''применение'') одной функции к другой.&lt;br /&gt;
Это аналогично вычислению функции-левого операнда на аргументе-правом операнде.&lt;br /&gt;
В третьем {{---}} ''абстракция'' по переменной. В данном случае происходит &lt;br /&gt;
создание функции одного аргумента с заданными именем аргумента и телом функции.&lt;br /&gt;
&lt;br /&gt;
Рассмотрим, например, функцию &amp;lt;tex&amp;gt;id = \lambda x \to x&amp;lt;/tex&amp;gt;. Эта функция принимает аргумент и &lt;br /&gt;
возвращает его неизменённым. Например, &lt;br /&gt;
&amp;lt;tex&amp;gt;id 2 \equiv 2&amp;lt;/tex&amp;gt;. Аналогично, &amp;lt;tex&amp;gt;id y == y&amp;lt;/tex&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Ещё один пример функции: &amp;lt;tex&amp;gt;sum = \lambda x \to \lambda y \to x + y|. Эта функция двух аргументов,&lt;br /&gt;
которая возвращает их сумму. Правда, здесь мы немного вышли за написанную выше грамматику.&lt;br /&gt;
Ну да ладно. &amp;lt;tex&amp;gt;sum 2 3 == 5&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Приоритет операций===&lt;br /&gt;
* Применение левоассоциативно: &amp;lt;tex&amp;gt;x y z w == ((x y) z) w&amp;lt;/tex&amp;gt;&lt;br /&gt;
* Аппликация забирает себе всё, до чего дотянется: &amp;lt;tex&amp;gt;\lambda x \to \lambda y \to \lambda z \to z y x \equiv \lambda x \to (\lambda y \to (\lambda z \to ((z y) x)))&amp;lt;/tex&amp;gt;&lt;br /&gt;
* Скобки играют привычную роль группировки действий&lt;br /&gt;
&lt;br /&gt;
===Свободные и связанные переменные===&lt;br /&gt;
''Связанными'' переменными называются все переменные, по которым выше в &lt;br /&gt;
дереве разбора были абстракции. Все остальные переменные называются свободными.&lt;br /&gt;
&lt;br /&gt;
Например, в &amp;lt;tex&amp;gt;\lambda x \to \lambda y \to x&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt; связана, а &amp;lt;tex&amp;gt;y&amp;lt;/tex&amp;gt;{{---}} свободна. А в &amp;lt;tex&amp;gt;\lambda y \to x (\lambda x \to x)&amp;lt;/tex&amp;gt;&lt;br /&gt;
в своём первом вхождении переменная &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt; свободна, а во втором {{---}} связана.&lt;br /&gt;
&lt;br /&gt;
Рассмотрим функции &amp;lt;tex&amp;gt;\lambda y \to y&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\lambda x \to y&amp;lt;/tex&amp;gt;. В первой из них при взгляде на &amp;lt;tex&amp;gt;y&amp;lt;/tex&amp;gt;&lt;br /&gt;
понятно, что она имеет отношение к переменной, по которой производилась &lt;br /&gt;
абстракция. Если по одной и той же&lt;br /&gt;
переменной абстракция производилась более одного раза, то переменная связана&lt;br /&gt;
с самым поздним (самым нижним в дереве разбора) абстрагированием. Например, в&lt;br /&gt;
&amp;lt;tex&amp;gt;\lambda x \to \lambda x \to \lambda y \to \lambda x \to x&amp;lt;/tex&amp;gt;, переменная &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt; связана с самой правой абстракцией &lt;br /&gt;
по &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
===&amp;lt;tex&amp;gt;\alpha&amp;lt;/tex&amp;gt;-конверсия===&lt;br /&gt;
&lt;br /&gt;
Рассмотрим функции &amp;lt;tex&amp;gt;(\lambda x \to x) z&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;(\lambda y \to y) z&amp;lt;/tex&amp;gt;. Интуитивно понятно, что они &lt;br /&gt;
являются одинаковыми. &lt;br /&gt;
&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition=''&amp;lt;tex&amp;gt;\alpha&amp;lt;/tex&amp;gt;-конверсия'' {{---}} переименование связанной переменной. Выражение&lt;br /&gt;
&amp;lt;tex&amp;gt;\lambda x \to f&amp;lt;/tex&amp;gt; можно заменить на &amp;lt;tex&amp;gt;\lambda y \to f[x := y]&amp;lt;/tex&amp;gt;, если &amp;lt;tex&amp;gt;y&amp;lt;/tex&amp;gt; не входит свободно в &amp;lt;tex&amp;gt;f&amp;lt;/tex&amp;gt;,&lt;br /&gt;
где &amp;lt;tex&amp;gt;f[x:=y]&amp;lt;/tex&amp;gt; означает замену всех свободных вхождений &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt; в &amp;lt;tex&amp;gt;f&amp;lt;/tex&amp;gt; на &amp;lt;tex&amp;gt;y&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Функции, получающиеся одна из другой с помощью &amp;lt;tex&amp;gt;\alpha&amp;lt;/tex&amp;gt;-конверсий, называются &lt;br /&gt;
''&amp;lt;tex&amp;gt;\alpha&amp;lt;/tex&amp;gt;-эквивалентными'' и обозначаются &amp;lt;tex&amp;gt;f \equiv_\alpha g&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Функции &amp;lt;tex&amp;gt;\lambda x \to \lambda y \to x y z&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\lambda a \to \lambda x \to a x z&amp;lt;/tex&amp;gt; являются &amp;lt;tex&amp;gt;\alpha&amp;lt;/tex&amp;gt;-эквивалентными,&lt;br /&gt;
а &amp;lt;tex&amp;gt;\lambda x \to \lambda y \to y z&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\lambda y \to \lambda x \to y z&amp;lt;/tex&amp;gt; {{---}} нет.&lt;br /&gt;
&lt;br /&gt;
===&amp;lt;tex&amp;gt;\beta&amp;lt;/tex&amp;gt;-редукция===&lt;br /&gt;
&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
&amp;lt;tex&amp;gt;\beta&amp;lt;/tex&amp;gt;-редукция олицетворяет идею счёта значения функции. Выражение вида &lt;br /&gt;
&amp;lt;tex&amp;gt;(\lambda x \to f) y&amp;lt;/tex&amp;gt; можно заменить на &amp;lt;tex&amp;gt;f[x := y]&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;f[x:=y]&amp;lt;/tex&amp;gt;, как и ранее, означает&lt;br /&gt;
замену всех свободных вхождений &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt; в &amp;lt;tex&amp;gt;f&amp;lt;/tex&amp;gt; на &amp;lt;tex&amp;gt;y&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
Через &amp;lt;tex&amp;gt;f \to_\beta g&amp;lt;/tex&amp;gt; обозначают сведение &amp;lt;tex&amp;gt;f&amp;lt;/tex&amp;gt; к &amp;lt;tex&amp;gt;g&amp;lt;/tex&amp;gt; с помощью одной &amp;lt;tex&amp;gt;\beta&amp;lt;/tex&amp;gt;-редукции.&lt;br /&gt;
А через &amp;lt;tex&amp;gt;f \to_\beta^* g&amp;lt;/tex&amp;gt; {{---}} за ноль или более.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===&amp;lt;tex&amp;gt;\eta&amp;lt;/tex&amp;gt;-редукция===&lt;br /&gt;
Рассмотрим выражение вида &amp;lt;tex&amp;gt;\lambda x \to f x&amp;lt;/tex&amp;gt;. Если подставить в эту функцию значение &lt;br /&gt;
&amp;lt;tex&amp;gt;y&amp;lt;/tex&amp;gt;, то получим: &amp;lt;tex&amp;gt;(\lambda x \to f x) y \to_\beta f y&amp;lt;/tex&amp;gt;. Но если просто подставить&lt;br /&gt;
&amp;lt;tex&amp;gt;y&amp;lt;/tex&amp;gt; в &amp;lt;tex&amp;gt;f&amp;lt;/tex&amp;gt;, то получится то же самое. &lt;br /&gt;
&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
&amp;lt;tex&amp;gt;\eta&amp;lt;/tex&amp;gt;-редукция {{---}} преобразование &amp;lt;tex&amp;gt;\lambda x \to f x&amp;lt;/tex&amp;gt; в &amp;lt;tex&amp;gt;f&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
==Нотация Де Брюина==&lt;br /&gt;
Существует также альтернативное эквивалентное определение &amp;lt;tex&amp;gt;\lambda&amp;lt;/tex&amp;gt;-исчисления.&lt;br /&gt;
В оригинальном определении для обозначения переменных использовались имена,&lt;br /&gt;
и была проблема с тем, что не были запрещены одинаковые имена в разных&lt;br /&gt;
абстракциях. &lt;br /&gt;
&lt;br /&gt;
От этой проблемы можно избавиться следующим образом. Вместо имени переменной &lt;br /&gt;
будет храниться натуральное число {{---}} количество абстракций в дереве разбора,&lt;br /&gt;
на которое нужно подняться, чтобы найти ту лямбду, с которой данная переменная &lt;br /&gt;
связана. В данной нотации получаются несколько более простые определения &lt;br /&gt;
свободных переменных и &amp;lt;tex&amp;gt;\beta&amp;lt;/tex&amp;gt;-редукции. &lt;br /&gt;
&lt;br /&gt;
Переменная называется свободной, если ей соответствует число, которое больше&lt;br /&gt;
количества абстракций на пути до неё в дереве разбора.&lt;br /&gt;
&lt;br /&gt;
При &amp;lt;tex&amp;gt;\beta&amp;lt;/tex&amp;gt;-редукции же нужно будет ко всем свободным переменным заменяющего &lt;br /&gt;
дерева при каждой замене прибавить число, равное разницы уровней раньше и сейчас.&lt;br /&gt;
Это будет соответствовать тому, что эта переменная продолжит &amp;lt;&amp;lt;держаться&amp;gt;&amp;gt; за&lt;br /&gt;
ту же лямбду, что и раньше.&lt;br /&gt;
&lt;br /&gt;
==Нумералы Чёрча и программирование на &amp;lt;tex&amp;gt;\lambda&amp;lt;/tex&amp;gt;-исчислении==&lt;br /&gt;
&lt;br /&gt;
===Определение===&lt;br /&gt;
Введём на основе лямбда-исчисления аналог натуральных чисел, основанный на идее, &lt;br /&gt;
что натуральное число {{---}} это или ноль, или увеличенное на единицу натуральное &lt;br /&gt;
число.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;tex&amp;gt;\bar 0 = \lambda s \to \lambda z \to z&amp;lt;/tex&amp;gt;&lt;br /&gt;
* &amp;lt;tex&amp;gt;\bar 1 = \lambda s \to \lambda z \to s z&amp;lt;/tex&amp;gt;&lt;br /&gt;
* &amp;lt;tex&amp;gt;\bar 2 = \lambda s \to \lambda z \to s (s z)&amp;lt;/tex&amp;gt;&lt;br /&gt;
* &amp;lt;tex&amp;gt;\bar 3 = \lambda s \to \lambda z \to s (s (s z))&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Каждое число будет функцией двух аргументов: какой-то функции и начального значения.&lt;br /&gt;
Число &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; будет &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; раз применять функцию к начальному значению и возвращать &lt;br /&gt;
результат. Если такому &amp;lt;&amp;lt;числу&amp;gt;&amp;gt; дать на вход функцию &amp;lt;tex&amp;gt;(+1)&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;0&amp;lt;/tex&amp;gt; в качестве &lt;br /&gt;
начального значения, то на выходе как раз будет ожидаемое от функции число:&lt;br /&gt;
&amp;lt;tex&amp;gt;\bar 3 (+1) 0 \equiv 3&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
===+1===&lt;br /&gt;
Функция, прибавляющая 1 к числу, должна принимать первым аргументом число.&lt;br /&gt;
Но число {{---}} функция двух аргументов. Значит, эта функция должна принимать три&lt;br /&gt;
аргумента: &amp;lt;&amp;lt;число&amp;gt;&amp;gt; &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt;, которое хочется увеличить, функция, которую надо будет&lt;br /&gt;
&amp;lt;tex&amp;gt;n+1&amp;lt;/tex&amp;gt; раз применить, и начальное значение.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\operatorname{succ} = \lambda n \to \lambda s \to \lambda z \to s (n s z)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Здесь &amp;lt;tex&amp;gt;n s z&amp;lt;/tex&amp;gt; {{---}} &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; раз применённая к &amp;lt;tex&amp;gt;z&amp;lt;/tex&amp;gt; функция &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt;. Но нужно применить &amp;lt;tex&amp;gt;n+1&amp;lt;/tex&amp;gt; &lt;br /&gt;
раз. Отсюда &amp;lt;tex&amp;gt;s (n s z)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
===Сложение===&lt;br /&gt;
Сложение двух чисел похоже на прибавление единицы. Но только надо прибавить не единицу, а второе число.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\operatorname{plus} = \lambda n \to \lambda m \to \lambda s \to \lambda z \to n s (m s z)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; раз применить &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; к применённому &amp;lt;tex&amp;gt;m&amp;lt;/tex&amp;gt; раз &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; к &amp;lt;tex&amp;gt;z&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;(\operatorname{plus} \bar 3 \bar 3) (+1) 0 \equiv 6&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Умножение===&lt;br /&gt;
Умножение похоже на сложение, но прибавлять надо не единицу, а второе число.&lt;br /&gt;
Или, в терминах нумералов Чёрча, в качестве применяемой несколько раз&lt;br /&gt;
функции должна быть не &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt;, а функция, применяющая &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; раз &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\operatorname{mult} = \lambda n \to \lambda m \to \lambda s \to n (m s)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Здесь &amp;lt;tex&amp;gt;m s&amp;lt;/tex&amp;gt; {{---}} функция, которая &amp;lt;tex&amp;gt;m&amp;lt;/tex&amp;gt; раз применит &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; к тому, что дадут ей на &lt;br /&gt;
вход. С помощью &amp;lt;tex&amp;gt;\eta&amp;lt;/tex&amp;gt;-редукции можно немного сократить эту формулу&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\operatorname{mult} = \lambda n \to \lambda m \to \lambda s \to n (m s)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;(\operatorname{mult} \bar 3 \bar 3) (+1) 0 \equiv 9&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Возведение в степень===&lt;br /&gt;
It's a kind of magic&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\operatorname{power} = \lambda n \to \lambda m \to m n&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;(\operatorname{power} \bar 3 (\operatorname{succ} \bar 3)) (+1) 0 \equiv 81&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Логические значения===&lt;br /&gt;
&amp;lt;tex&amp;gt;\operatorname{true} = \lambda a \to \lambda b \to a&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\operatorname{false} = \lambda a \to \lambda b \to b&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Функции двух аргументов, возвращающие первый и второй, соответственное, аргументы.&lt;br /&gt;
Забавный факт: &amp;lt;tex&amp;gt;\operatorname{false} \equiv_\alpha \operatorname{zero}&amp;lt;/tex&amp;gt;. Эти функции сделаны такими для того, &lt;br /&gt;
чтобы красиво написать функцию &amp;lt;tex&amp;gt;\operatorname{if}&amp;lt;/tex&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\operatorname{if} = \lambda p \to \lambda t \to \lambda e \to p\ t\ e&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если ей в качестве первого аргумента дадут &amp;lt;tex&amp;gt;\operatorname{true}&amp;lt;/tex&amp;gt;, то вернётся &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt;, иначе {{---}} &amp;lt;tex&amp;gt;e&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Стандартные функции булевой логики:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\operatorname{and} = \lambda n \to \lambda m \to \operatorname{if} n\ m\ \operatorname{false}&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\operatorname{or} = \lambda n \to \lambda m \to \operatorname{if} n\ \operatorname{true} m&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\operatorname{not} = \lambda b \to \operatorname{if} b\ \operatorname{false} \operatorname{true}&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ещё одной важной функцией является функция проверки, является ли число нулём:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\operatorname{isZero} = \lambda n \to n\ (\lambda c \to \operatorname{false})\ \operatorname{true}&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Функция выглядит несколько странно. &amp;lt;tex&amp;gt;\lambda c -&amp;gt; \operatorname{false}&amp;lt;/tex&amp;gt; {{---}} функция, которая независимо&lt;br /&gt;
от того, что ей дали на вход, возвращает &amp;lt;tex&amp;gt;\operatorname{false}&amp;lt;/tex&amp;gt;. Тогда, если в качестве &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt;&lt;br /&gt;
будет дан ноль, то функция, по определению нуля, не выполнится ни разу, и будет&lt;br /&gt;
возвращено значение по умолчанию &amp;lt;tex&amp;gt;\operatorname{true}&amp;lt;/tex&amp;gt;. Иначе же функция будет запущено, и &lt;br /&gt;
вернётся &amp;lt;tex&amp;gt;\operatorname{false}&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
===Пара===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\operatorname{pair} = \lambda a \to \lambda b \to \lambda t \to t\ a\ b&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\operatorname{fst} = \lambda p \to p\ \operatorname{true}&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\operatorname{snd} = \lambda p \to p\ \operatorname{false}&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Функция &amp;lt;tex&amp;gt;\operatorname{pair}&amp;lt;/tex&amp;gt; принимает два значения и запаковывает их в пару так, чтобы к ним можно было обращаться по &amp;lt;tex&amp;gt;\operatorname{fst}&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\operatorname{snd}&amp;lt;/tex&amp;gt;. В &amp;lt;tex&amp;gt;\operatorname{fst}&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\operatorname{snd}&amp;lt;/tex&amp;gt; вместо &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; в &amp;lt;tex&amp;gt;\operatorname{pair}&amp;lt;/tex&amp;gt; будет подставлено &amp;lt;tex&amp;gt;\operatorname{true}&amp;lt;/tex&amp;gt; или &amp;lt;tex&amp;gt;\operatorname{false}&amp;lt;/tex&amp;gt;, возвращающие, соответственно, первый и второй аргументы, то есть &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; или &amp;lt;tex&amp;gt;b&amp;lt;/tex&amp;gt;, соответственно.&lt;br /&gt;
&lt;br /&gt;
===Вычитание===&lt;br /&gt;
В отличие от всех предыдущих функций, вычитание для натуральных чисел определено только в случае, если уменьшаемое больше вычитаемого. Положим в противном случае результат равным нулю. Пусть уже есть функция, которая вычитает из числа единицу. Тогда на её основе легко сделать, собственно, вычитание.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\operatorname{minus} = \lambda n \to \lambda m \to m\ \operatorname{pred} n&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Это то же самое, что &amp;lt;tex&amp;gt;m&amp;lt;/tex&amp;gt; раз вычесть единицу из &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Осталось, собственно, функция для вычитания единицы. Однако, это не так просто, как может показаться на первый взгляд. Проблема в том, что, имея функцию, которую нужно применить для того, чтобы продвинуться вперёд, продвинуться назад будет проблематично. Если попробовать воспользоваться идеей о том, чтобы, начав от нуля, идти вперёд, и пройти на один шаг меньше, то будет не очень понятно, как же остановиться ровно за один шаг до конца. Для реализации вычитания единицы сделаем следующее. &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; раз выполним следующее: имея пару &amp;lt;tex&amp;gt;\langle n-1, n-2\rangle&amp;lt;/tex&amp;gt; построим пару &amp;lt;tex&amp;gt;\langle n, n-1\rangle&amp;lt;/tex&amp;gt;. Тогда после &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; шагов во втором элементе пары будет записано число &amp;lt;tex&amp;gt;n-1&amp;lt;/tex&amp;gt;, которое и хочется получить. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\operatorname{pred} = \lambda n \to \lambda s \to \lambda z \to\ \operatorname{snd} (&lt;br /&gt;
   n\ (&lt;br /&gt;
          \lambda p \to \operatorname{pair}\ (s\ (\operatorname{fst} p))\ (\operatorname{fst} p)&lt;br /&gt;
     )\ (\operatorname{pair}\ z\ z)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если вы ничего не поняли, не огорчайтесь. Вычитание придумал Клини, когда ему вырывали зуб мудрости. А сейчас наркоз уже не тот.&lt;br /&gt;
&lt;br /&gt;
===Сравнение===&lt;br /&gt;
Так как вычитание определено таким способом, чтобы для случая, в котором уменьшаемое больше, чем вычитаемое, возвращать ноль, можно определить сравнение на больше-меньше через него. Равными же числа &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;b&amp;lt;/tex&amp;gt; считаются, если &amp;lt;tex&amp;gt;a - b = 0 \wedge b - a = 0&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\operatorname{le} = \lambda n \to \lambda m \to \operatorname{isZero}\ (\operatorname{minus}\ n\ m)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\operatorname{less} = \lambda n \to \lambda m \to \operatorname{le}\ n\ (\operatorname{pred} m)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\operatorname{eq} = \lambda n \to \lambda m \to \operatorname{and}\ (\operatorname{isZero}\ (\operatorname{minus}\ n\ m))\ &lt;br /&gt;
(\operatorname{isZero}\ (\operatorname{minus}\ m\ n))&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Комбинатор неподвижной точки===&lt;br /&gt;
Попробуем выразить в лямбда-исчислении какую-нибудь функцию, использующую рекурсию. Напрмер, факториал.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\operatorname{fact} = \lambda x \to \operatorname{if}\ (\operatorname{isZero}\ x)\ \bar 1\ (\operatorname{fact}\ (\operatorname{pred}\ x))&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Мы столкнулись с проблемой. В определении функции &amp;lt;tex&amp;gt;\operatorname{fact}&amp;lt;/tex&amp;gt; используется функция &amp;lt;tex&amp;gt;\operatorname{fact}&amp;lt;/tex&amp;gt;. При формальной замене, получим бесконечную функцию. Можно попытаться решить эту проблему следующим образом&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\operatorname{fact} = (\lambda f \to \lambda x \to \operatorname{if}\ (\operatorname{isZero}\ x)\ \bar 1\ (f\ (\operatorname{pred}\ x)))\ \operatorname{fact}&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition=''Неподвижной точкой'' лямбда-функции &amp;lt;tex&amp;gt;f&amp;lt;/tex&amp;gt; назовём такую функцию &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt;, что&lt;br /&gt;
&amp;lt;tex&amp;gt;f\ x \to_\beta x&amp;lt;/tex&amp;gt;. &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Лямбда исчисление обладаем замечательным свойством: у каждой функции есть неподвижная точка!&lt;br /&gt;
&lt;br /&gt;
Рассмотрим следующую функцию. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\operatorname{fix} = \lambda f \to (\lambda x \to f\ (x\ x))\ (\lambda x \to f\ (x\ x))&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Заметим, что &amp;lt;tex&amp;gt;\operatorname{fix} \to_\beta \lambda f \to f\ ((\lambda x \to f\ (x\ x))\ (\lambda x \to f\ (x\ x)))&amp;lt;/tex&amp;gt;.&lt;br /&gt;
Или, что то же самое, &lt;br /&gt;
&amp;lt;tex&amp;gt;\lambda f \to (\lambda x \to f\ (x\ x))\ (\lambda x \to f\ (x\ x)) \to_\beta&amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt;\lambda f \to f\ ((\lambda x \to f\ (x\ x))\ (\lambda x \to f\ (x\ x)))&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Рассмотрим функцию&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\operatorname{fact'} = \lambda f \to \lambda x \to \operatorname{if}\ (\operatorname{isZero}\ x)\ \bar 1\ (\operatorname{mult}\ x\ (f\ (\operatorname{pred}\ x)))&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Как было показано выше, &amp;lt;tex&amp;gt;\operatorname{fix} f \to_\beta f\ (\operatorname{fix} f)&amp;lt;/tex&amp;gt;, то есть, &amp;lt;tex&amp;gt;\operatorname{fix}\ \operatorname{fact'} \to_\beta \operatorname{fact}&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;\operatorname{fact}&amp;lt;/tex&amp;gt; {{---}} искомая функция, считающая факториал.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\operatorname{fact} = \operatorname{fix}\ \operatorname{fact'}&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Это даст функцию, которая посчитает факториал числа. Но делать она это будет мееедленно-меееедленно. Для того, чтобы посчитать &amp;lt;tex?5!&amp;lt;/tex&amp;gt; потребовалось сделать 66066 &amp;lt;tex&amp;gt;\beta&amp;lt;/tex&amp;gt;-редукций.&lt;br /&gt;
&lt;br /&gt;
Тут правда ничего не понятно? :'(&lt;br /&gt;
&lt;br /&gt;
===Деление===&lt;br /&gt;
Воспользовавшись идеей о том, что можно делать рекурсивные функции, сделаем функцию, которая будет искать частное двух чисел.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\operatorname{div'} = \lambda div \to \lambda n \to \lambda m \to \operatorname{if}\ (\operatorname{less}\ n\ m)\ \bar 0\ (\operatorname{succ}\ (div\ (\operatorname{minus}\ n\ m)\ m))&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\operatorname{div} = \operatorname{fix}\ \operatorname{div'}&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
И остатка от деления&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\operatorname{mod'} = \lambda mod \to \lambda n \to \lambda m \to \operatorname{if}\ (\operatorname{less}\ n\ m)\ n\ (mod\ (\operatorname{minus}\ n\ m)\ m)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\operatorname{mod} = \operatorname{fix}\ \operatorname{mod'}&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Проверка на простоту===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\operatorname{isPrimeHelp}&amp;lt;/tex&amp;gt; {{---}} принимает число, которое требуется проверить на простоту и то, на что его надо опытаться поделить, перебирая это от 2 до &amp;lt;tex&amp;gt;p-1&amp;lt;/tex&amp;gt;. Если на что-нибудь разделилось, то число {{---}} составное, иначе {{---}} простое.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\operatorname{isPrimeHelp'} =&amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt;\lambda f \to \lambda p \to \lambda i \to \operatorname{if}\ (\operatorname{le}\ p\ i)\ \operatorname{true}\ (\operatorname{if}\ (\operatorname{isZero}\ (\operatorname{mod}\ p\ i))\ \operatorname{false}\ (f\ p\ (\operatorname{succ}\ i)))&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\operatorname{isPrimeHelp} = \operatorname{fix}\ \operatorname{isPrimeHelp'}&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\operatorname{isPrime} = \lambda p \to \operatorname{isPrimeHelp}\ p\ \bar 2&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Следующее простое число. &amp;lt;tex&amp;gt;\operatorname{nextPrime'}&amp;lt;/tex&amp;gt; {{---}} следующее, больше либо равное заданного, &amp;lt;tex&amp;gt;\operatorname{nextPrime}&amp;lt;/tex&amp;gt; {{---}} следующее, большее заданного.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\operatorname{nextPrime''} = \lambda f \to \lambda p \to \operatorname{if}\ (\operatorname{isPrime}\ p)\ p\ (f\ (\operatorname{succ}\ p)) &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\operatorname{nextPrime'} = \operatorname{fix}\ \operatorname{nextPrime'}&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\operatorname{nextPrime} = \lambda p \to \operatorname{nextPrime'}\ (\operatorname{succ}\ p)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\operatorname{ithPrimeStep}&amp;lt;/tex&amp;gt; пропрыгает &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; простых чисел вперёд. &amp;lt;tex&amp;gt;\operatorname{ithPrime}&amp;lt;/tex&amp;gt; принимает число &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; и пропрыгивает столько простых чисел вперёд, начиная с двойки.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\operatorname{ithPrimeStep'} = \lambda f \to \lambda p \to \lambda i \to \operatorname{if}\ (\operatorname{isZero}\ i)\ p\ (f\  (\operatorname{nextPrime}\ p)\ (\operatorname{pred}\ i))&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\operatorname{ithPrimeStep} = \operatorname{fix}\ \operatorname{ithPrimeStep'}&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\operatorname{ithPrime} = \lambda i \to \operatorname{ithPrimeStep}\ \bar 2\ i&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...и всего через 314007 &amp;lt;tex&amp;gt;\beta&amp;lt;/tex&amp;gt;-редукций вы узнаете, что третье простое число {{---}} семь!&lt;br /&gt;
&lt;br /&gt;
\subsection{Списки}&lt;br /&gt;
&lt;br /&gt;
Для работы со списками чисел нам понадобятся следующие функции:&lt;br /&gt;
\begin{itemize}&lt;br /&gt;
    \item $empty$~--- возвращает пустой список&lt;br /&gt;
    \item $cons$~--- принимает первый элемент и оставшийся список, склеивает их&lt;br /&gt;
    \item $head$~--- вернуть голову списка&lt;br /&gt;
    \item $tail$~--- вернуть хвост списка&lt;br /&gt;
\end{itemize}&lt;br /&gt;
&lt;br /&gt;
Список будем хранить в следующем виде: $\langle len, p_1^{a_1}p_2^{a_2}\ldots p_{len}^{a_{len}} \rangle$. При этом, голова списка будет храниться как показатель степени при $p_{len}$.&lt;br /&gt;
&lt;br /&gt;
\begin{code}&lt;br /&gt;
empty = pair' `App` zero' `App` one'&lt;br /&gt;
cons = Lam &amp;quot;h&amp;quot; $ Lam &amp;quot;t&amp;quot; $ pair' `App` (succ' `App` (fst'' `App` Var &amp;quot;t&amp;quot;))&lt;br /&gt;
        `App` (mult' `App` (snd'' `App` Var &amp;quot;t&amp;quot;) `App` (power' &lt;br /&gt;
                `App` (ithPrime `App` (fst'' `App` Var &amp;quot;t&amp;quot;))&lt;br /&gt;
                `App` Var &amp;quot;h&amp;quot;&lt;br /&gt;
        ))&lt;br /&gt;
head = Lam &amp;quot;list&amp;quot; $ getExponent `App` (snd'' `App` Var &amp;quot;list&amp;quot;)&lt;br /&gt;
                                `App` (ithPrime `App` (pred' `App` (fst'' `App` Var &amp;quot;list&amp;quot;)))&lt;br /&gt;
&lt;br /&gt;
tail = Lam &amp;quot;list&amp;quot; $ pair' `App` (pred' `App` (fst'' `App` Var &amp;quot;list&amp;quot;))&lt;br /&gt;
                          `App` (eliminateMultiplier `App` (snd'' `App` Var &amp;quot;list&amp;quot;)&lt;br /&gt;
                                  `App` (ithPrime `App` (pred' `App` (fst'' `App` Var &amp;quot;list&amp;quot; )))&lt;br /&gt;
                                )&lt;br /&gt;
&lt;br /&gt;
eliminateMultiplier' = Lam &amp;quot;f&amp;quot; $ Lam &amp;quot;n&amp;quot; $ Lam &amp;quot;m&amp;quot; $ if'' `App` (isZero' `App` (mod' `App` Var &amp;quot;n&amp;quot; `App` Var &amp;quot;m&amp;quot;))&lt;br /&gt;
                        `App` (Var &amp;quot;f&amp;quot; `App` (div' `App` Var &amp;quot;n&amp;quot; `App` Var &amp;quot;m&amp;quot;) `App` Var &amp;quot;m&amp;quot;)&lt;br /&gt;
                        `App` Var &amp;quot;n&amp;quot;&lt;br /&gt;
eliminateMultiplier = fix' `App` eliminateMultiplier'&lt;br /&gt;
&lt;br /&gt;
getExponent' = Lam &amp;quot;f&amp;quot; $ Lam &amp;quot;n&amp;quot; $ Lam &amp;quot;m&amp;quot; $ if'' `App` (isZero' `App` (mod' `App` Var &amp;quot;n&amp;quot; `App` Var &amp;quot;m&amp;quot;))&lt;br /&gt;
                `App` (succ' `App`(Var &amp;quot;f&amp;quot; `App` (div' `App` Var &amp;quot;n&amp;quot; `App` Var &amp;quot;m&amp;quot;) `App` Var &amp;quot;m&amp;quot;))&lt;br /&gt;
                `App` zero'&lt;br /&gt;
getExponent = fix' `App` getExponent'&lt;br /&gt;
\end{code}&lt;br /&gt;
&lt;br /&gt;
На основе этого всего уже можно реализовать эмулятор машины тьюринга:&lt;br /&gt;
с помощью пар, списков чисел можно хранить состояния. С помощью рекурсии можно&lt;br /&gt;
обрабатывать переходы. Входная строка будет даваться, например, закодированной&lt;br /&gt;
аналогично списку: пара из длины и числа, характеризующего список степенями &lt;br /&gt;
простых. Я бы продолжил это писать, но уже на операции $head [1, 2]$ я не &lt;br /&gt;
дождался окончания выполнения. Скорость лямбда-исчисления как вычислителя&lt;br /&gt;
печальна.  &lt;br /&gt;
&lt;br /&gt;
\ignore{&lt;br /&gt;
\begin{code}&lt;br /&gt;
&lt;br /&gt;
four' = norm $ succ' `App` three'&lt;br /&gt;
five' = norm $ succ' `App` four'&lt;br /&gt;
&lt;br /&gt;
list2 = norm $ cons `App` one' `App` empty&lt;br /&gt;
list32 = cons `App` zero' `App` list2&lt;br /&gt;
&lt;br /&gt;
normFiveFact = normIO 0 $ fact' `App` five'&lt;br /&gt;
&lt;br /&gt;
-- fiftysix = mult twentyeight two&lt;br /&gt;
-- fiftyfive = pred fiftysix&lt;br /&gt;
-- six = pred seven&lt;br /&gt;
&lt;br /&gt;
--main = do print $ fiftysix (+1) 0&lt;br /&gt;
main = do&lt;br /&gt;
 --   f &amp;lt;- normIO 0 $ ithPrime `App` three'&lt;br /&gt;
 --   f &amp;lt;- normIO 0 $ getExponent `App` (norm $ plus' `App` four' `App` four') `App` two'&lt;br /&gt;
    f &amp;lt;- normIO 0 $ head `App` (tail `App` list32)&lt;br /&gt;
    print $ toInt f&lt;br /&gt;
\end{code}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
\end{document}&lt;br /&gt;
[http://rain.ifmo.ru/~komarov/Turing.lhs]&lt;/div&gt;</summary>
		<author><name>194.85.160.130</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%9B%D1%8F%D0%BC%D0%B1%D0%B4%D0%B0-%D0%B8%D1%81%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5&amp;diff=27295</id>
		<title>Лямбда-исчисление</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%9B%D1%8F%D0%BC%D0%B1%D0%B4%D0%B0-%D0%B8%D1%81%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5&amp;diff=27295"/>
				<updated>2012-12-06T16:14:27Z</updated>
		
		<summary type="html">&lt;p&gt;194.85.160.130: /* Логические значения */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{В разработке}}&lt;br /&gt;
&lt;br /&gt;
== Лямбда-исчисление==&lt;br /&gt;
&lt;br /&gt;
''Лямбда-исчисление'' {{---}} формальная система, придуманная в 1930-х годах &lt;br /&gt;
Алонзо Чёрчем. Лямбда-функция является, по сути, анонимной функцией.&lt;br /&gt;
Эта концепция показала себя удобной и сейчас активно используется во многих&lt;br /&gt;
языках программирования.&lt;br /&gt;
&lt;br /&gt;
Более формально, ''лямбда-функцию'' (или, ''лямбда-терм'') можно задать &lt;br /&gt;
следующей грамматикой:&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
&amp;lt;tex&amp;gt;&lt;br /&gt;
\begin{array}{r c l}&lt;br /&gt;
\langle Term \rangle &amp;amp; ::= &amp;amp; \langle Variable \rangle \\&lt;br /&gt;
                       &amp;amp; || &amp;amp; \langle Term \rangle \langle Term \rangle \\&lt;br /&gt;
                       &amp;amp; || &amp;amp; \lambda \langle Variable \rangle \to \langle Term \rangle\\&lt;br /&gt;
                       &amp;amp; || &amp;amp; ( \langle Term \rangle )\\&lt;br /&gt;
\langle Variable \rangle &amp;amp; ::= &amp;amp; \langle Char \rangle *\\&lt;br /&gt;
\end{array}&lt;br /&gt;
&amp;lt;/tex&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
В первом случае функция является просто переменной. &lt;br /&gt;
Во втором происходит ''аппликация'' (''применение'') одной функции к другой.&lt;br /&gt;
Это аналогично вычислению функции-левого операнда на аргументе-правом операнде.&lt;br /&gt;
В третьем {{---}} ''абстракция'' по переменной. В данном случае происходит &lt;br /&gt;
создание функции одного аргумента с заданными именем аргумента и телом функции.&lt;br /&gt;
&lt;br /&gt;
Рассмотрим, например, функцию &amp;lt;tex&amp;gt;id = \lambda x \to x&amp;lt;/tex&amp;gt;. Эта функция принимает аргумент и &lt;br /&gt;
возвращает его неизменённым. Например, &lt;br /&gt;
&amp;lt;tex&amp;gt;id 2 \equiv 2&amp;lt;/tex&amp;gt;. Аналогично, &amp;lt;tex&amp;gt;id y == y&amp;lt;/tex&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Ещё один пример функции: &amp;lt;tex&amp;gt;sum = \lambda x \to \lambda y \to x + y|. Эта функция двух аргументов,&lt;br /&gt;
которая возвращает их сумму. Правда, здесь мы немного вышли за написанную выше грамматику.&lt;br /&gt;
Ну да ладно. &amp;lt;tex&amp;gt;sum 2 3 == 5&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Приоритет операций===&lt;br /&gt;
* Применение левоассоциативно: &amp;lt;tex&amp;gt;x y z w == ((x y) z) w&amp;lt;/tex&amp;gt;&lt;br /&gt;
* Аппликация забирает себе всё, до чего дотянется: &amp;lt;tex&amp;gt;\lambda x \to \lambda y \to \lambda z \to z y x \equiv \lambda x \to (\lambda y \to (\lambda z \to ((z y) x)))&amp;lt;/tex&amp;gt;&lt;br /&gt;
* Скобки играют привычную роль группировки действий&lt;br /&gt;
&lt;br /&gt;
===Свободные и связанные переменные===&lt;br /&gt;
''Связанными'' переменными называются все переменные, по которым выше в &lt;br /&gt;
дереве разбора были абстракции. Все остальные переменные называются свободными.&lt;br /&gt;
&lt;br /&gt;
Например, в &amp;lt;tex&amp;gt;\lambda x \to \lambda y \to x&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt; связана, а &amp;lt;tex&amp;gt;y&amp;lt;/tex&amp;gt;{{---}} свободна. А в &amp;lt;tex&amp;gt;\lambda y \to x (\lambda x \to x)&amp;lt;/tex&amp;gt;&lt;br /&gt;
в своём первом вхождении переменная &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt; свободна, а во втором {{---}} связана.&lt;br /&gt;
&lt;br /&gt;
Рассмотрим функции &amp;lt;tex&amp;gt;\lambda y \to y&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\lambda x \to y&amp;lt;/tex&amp;gt;. В первой из них при взгляде на &amp;lt;tex&amp;gt;y&amp;lt;/tex&amp;gt;&lt;br /&gt;
понятно, что она имеет отношение к переменной, по которой производилась &lt;br /&gt;
абстракция. Если по одной и той же&lt;br /&gt;
переменной абстракция производилась более одного раза, то переменная связана&lt;br /&gt;
с самым поздним (самым нижним в дереве разбора) абстрагированием. Например, в&lt;br /&gt;
&amp;lt;tex&amp;gt;\lambda x \to \lambda x \to \lambda y \to \lambda x \to x&amp;lt;/tex&amp;gt;, переменная &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt; связана с самой правой абстракцией &lt;br /&gt;
по &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
===&amp;lt;tex&amp;gt;\alpha&amp;lt;/tex&amp;gt;-конверсия===&lt;br /&gt;
&lt;br /&gt;
Рассмотрим функции &amp;lt;tex&amp;gt;(\lambda x \to x) z&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;(\lambda y \to y) z&amp;lt;/tex&amp;gt;. Интуитивно понятно, что они &lt;br /&gt;
являются одинаковыми. &lt;br /&gt;
&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition=''&amp;lt;tex&amp;gt;\alpha&amp;lt;/tex&amp;gt;-конверсия'' {{---}} переименование связанной переменной. Выражение&lt;br /&gt;
&amp;lt;tex&amp;gt;\lambda x \to f&amp;lt;/tex&amp;gt; можно заменить на &amp;lt;tex&amp;gt;\lambda y \to f[x := y]&amp;lt;/tex&amp;gt;, если &amp;lt;tex&amp;gt;y&amp;lt;/tex&amp;gt; не входит свободно в &amp;lt;tex&amp;gt;f&amp;lt;/tex&amp;gt;,&lt;br /&gt;
где &amp;lt;tex&amp;gt;f[x:=y]&amp;lt;/tex&amp;gt; означает замену всех свободных вхождений &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt; в &amp;lt;tex&amp;gt;f&amp;lt;/tex&amp;gt; на &amp;lt;tex&amp;gt;y&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Функции, получающиеся одна из другой с помощью &amp;lt;tex&amp;gt;\alpha&amp;lt;/tex&amp;gt;-конверсий, называются &lt;br /&gt;
''&amp;lt;tex&amp;gt;\alpha&amp;lt;/tex&amp;gt;-эквивалентными'' и обозначаются &amp;lt;tex&amp;gt;f \equiv_\alpha g&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Функции &amp;lt;tex&amp;gt;\lambda x \to \lambda y \to x y z&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\lambda a \to \lambda x \to a x z&amp;lt;/tex&amp;gt; являются &amp;lt;tex&amp;gt;\alpha&amp;lt;/tex&amp;gt;-эквивалентными,&lt;br /&gt;
а &amp;lt;tex&amp;gt;\lambda x \to \lambda y \to y z&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\lambda y \to \lambda x \to y z&amp;lt;/tex&amp;gt; {{---}} нет.&lt;br /&gt;
&lt;br /&gt;
===&amp;lt;tex&amp;gt;\beta&amp;lt;/tex&amp;gt;-редукция===&lt;br /&gt;
&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
&amp;lt;tex&amp;gt;\beta&amp;lt;/tex&amp;gt;-редукция олицетворяет идею счёта значения функции. Выражение вида &lt;br /&gt;
&amp;lt;tex&amp;gt;(\lambda x \to f) y&amp;lt;/tex&amp;gt; можно заменить на &amp;lt;tex&amp;gt;f[x := y]&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;f[x:=y]&amp;lt;/tex&amp;gt;, как и ранее, означает&lt;br /&gt;
замену всех свободных вхождений &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt; в &amp;lt;tex&amp;gt;f&amp;lt;/tex&amp;gt; на &amp;lt;tex&amp;gt;y&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
Через &amp;lt;tex&amp;gt;f \to_\beta g&amp;lt;/tex&amp;gt; обозначают сведение &amp;lt;tex&amp;gt;f&amp;lt;/tex&amp;gt; к &amp;lt;tex&amp;gt;g&amp;lt;/tex&amp;gt; с помощью одной &amp;lt;tex&amp;gt;\beta&amp;lt;/tex&amp;gt;-редукции.&lt;br /&gt;
А через &amp;lt;tex&amp;gt;f \to_\beta^* g&amp;lt;/tex&amp;gt; {{---}} за ноль или более.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===&amp;lt;tex&amp;gt;\eta&amp;lt;/tex&amp;gt;-редукция===&lt;br /&gt;
Рассмотрим выражение вида &amp;lt;tex&amp;gt;\lambda x \to f x&amp;lt;/tex&amp;gt;. Если подставить в эту функцию значение &lt;br /&gt;
&amp;lt;tex&amp;gt;y&amp;lt;/tex&amp;gt;, то получим: &amp;lt;tex&amp;gt;(\lambda x \to f x) y \to_\beta f y&amp;lt;/tex&amp;gt;. Но если просто подставить&lt;br /&gt;
&amp;lt;tex&amp;gt;y&amp;lt;/tex&amp;gt; в &amp;lt;tex&amp;gt;f&amp;lt;/tex&amp;gt;, то получится то же самое. &lt;br /&gt;
&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
&amp;lt;tex&amp;gt;\eta&amp;lt;/tex&amp;gt;-редукция {{---}} преобразование &amp;lt;tex&amp;gt;\lambda x \to f x&amp;lt;/tex&amp;gt; в &amp;lt;tex&amp;gt;f&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
==Нотация Де Брюина==&lt;br /&gt;
Существует также альтернативное эквивалентное определение &amp;lt;tex&amp;gt;\lambda&amp;lt;/tex&amp;gt;-исчисления.&lt;br /&gt;
В оригинальном определении для обозначения переменных использовались имена,&lt;br /&gt;
и была проблема с тем, что не были запрещены одинаковые имена в разных&lt;br /&gt;
абстракциях. &lt;br /&gt;
&lt;br /&gt;
От этой проблемы можно избавиться следующим образом. Вместо имени переменной &lt;br /&gt;
будет храниться натуральное число {{---}} количество абстракций в дереве разбора,&lt;br /&gt;
на которое нужно подняться, чтобы найти ту лямбду, с которой данная переменная &lt;br /&gt;
связана. В данной нотации получаются несколько более простые определения &lt;br /&gt;
свободных переменных и &amp;lt;tex&amp;gt;\beta&amp;lt;/tex&amp;gt;-редукции. &lt;br /&gt;
&lt;br /&gt;
Переменная называется свободной, если ей соответствует число, которое больше&lt;br /&gt;
количества абстракций на пути до неё в дереве разбора.&lt;br /&gt;
&lt;br /&gt;
При &amp;lt;tex&amp;gt;\beta&amp;lt;/tex&amp;gt;-редукции же нужно будет ко всем свободным переменным заменяющего &lt;br /&gt;
дерева при каждой замене прибавить число, равное разницы уровней раньше и сейчас.&lt;br /&gt;
Это будет соответствовать тому, что эта переменная продолжит &amp;lt;&amp;lt;держаться&amp;gt;&amp;gt; за&lt;br /&gt;
ту же лямбду, что и раньше.&lt;br /&gt;
&lt;br /&gt;
==Нумералы Чёрча и программирование на &amp;lt;tex&amp;gt;\lambda&amp;lt;/tex&amp;gt;-исчислении==&lt;br /&gt;
&lt;br /&gt;
===Определение===&lt;br /&gt;
Введём на основе лямбда-исчисления аналог натуральных чисел, основанный на идее, &lt;br /&gt;
что натуральное число {{---}} это или ноль, или увеличенное на единицу натуральное &lt;br /&gt;
число.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;tex&amp;gt;\bar 0 = \lambda s \to \lambda z \to z&amp;lt;/tex&amp;gt;&lt;br /&gt;
* &amp;lt;tex&amp;gt;\bar 1 = \lambda s \to \lambda z \to s z&amp;lt;/tex&amp;gt;&lt;br /&gt;
* &amp;lt;tex&amp;gt;\bar 2 = \lambda s \to \lambda z \to s (s z)&amp;lt;/tex&amp;gt;&lt;br /&gt;
* &amp;lt;tex&amp;gt;\bar 3 = \lambda s \to \lambda z \to s (s (s z))&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Каждое число будет функцией двух аргументов: какой-то функции и начального значения.&lt;br /&gt;
Число &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; будет &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; раз применять функцию к начальному значению и возвращать &lt;br /&gt;
результат. Если такому &amp;lt;&amp;lt;числу&amp;gt;&amp;gt; дать на вход функцию &amp;lt;tex&amp;gt;(+1)&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;0&amp;lt;/tex&amp;gt; в качестве &lt;br /&gt;
начального значения, то на выходе как раз будет ожидаемое от функции число:&lt;br /&gt;
&amp;lt;tex&amp;gt;\bar 3 (+1) 0 \equiv 3&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
===+1===&lt;br /&gt;
Функция, прибавляющая 1 к числу, должна принимать первым аргументом число.&lt;br /&gt;
Но число {{---}} функция двух аргументов. Значит, эта функция должна принимать три&lt;br /&gt;
аргумента: &amp;lt;&amp;lt;число&amp;gt;&amp;gt; &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt;, которое хочется увеличить, функция, которую надо будет&lt;br /&gt;
&amp;lt;tex&amp;gt;n+1&amp;lt;/tex&amp;gt; раз применить, и начальное значение.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\operatorname{succ} = \lambda n \to \lambda s \to \lambda z \to s (n s z)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Здесь &amp;lt;tex&amp;gt;n s z&amp;lt;/tex&amp;gt; {{---}} &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; раз применённая к &amp;lt;tex&amp;gt;z&amp;lt;/tex&amp;gt; функция &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt;. Но нужно применить &amp;lt;tex&amp;gt;n+1&amp;lt;/tex&amp;gt; &lt;br /&gt;
раз. Отсюда &amp;lt;tex&amp;gt;s (n s z)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
===Сложение===&lt;br /&gt;
Сложение двух чисел похоже на прибавление единицы. Но только надо прибавить не единицу, а второе число.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\operatorname{plus} = \lambda n \to \lambda m \to \lambda s \to \lambda z \to n s (m s z)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; раз применить &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; к применённому &amp;lt;tex&amp;gt;m&amp;lt;/tex&amp;gt; раз &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; к &amp;lt;tex&amp;gt;z&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;(\operatorname{plus} \bar 3 \bar 3) (+1) 0 \equiv 6&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Умножение===&lt;br /&gt;
Умножение похоже на сложение, но прибавлять надо не единицу, а второе число.&lt;br /&gt;
Или, в терминах нумералов Чёрча, в качестве применяемой несколько раз&lt;br /&gt;
функции должна быть не &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt;, а функция, применяющая &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; раз &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\operatorname{mult} = \lambda n \to \lambda m \to \lambda s \to n (m s)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Здесь &amp;lt;tex&amp;gt;m s&amp;lt;/tex&amp;gt; {{---}} функция, которая &amp;lt;tex&amp;gt;m&amp;lt;/tex&amp;gt; раз применит &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; к тому, что дадут ей на &lt;br /&gt;
вход. С помощью &amp;lt;tex&amp;gt;\eta&amp;lt;/tex&amp;gt;-редукции можно немного сократить эту формулу&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\operatorname{mult} = \lambda n \to \lambda m \to \lambda s \to n (m s)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;(\operatorname{mult} \bar 3 \bar 3) (+1) 0 \equiv 9&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Возведение в степень===&lt;br /&gt;
It's a kind of magic&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\operatorname{power} = \lambda n \to \lambda m \to m n&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;(\operatorname{power} \bar 3 (\operatorname{succ} \bar 3)) (+1) 0 \equiv 81&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Логические значения===&lt;br /&gt;
&amp;lt;tex&amp;gt;\operatorname{true} = \lambda a \to \lambda b \to a&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\operatorname{false} = \lambda a \to \lambda b \to b&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Функции двух аргументов, возвращающие первый и второй, соответственное, аргументы.&lt;br /&gt;
Забавный факт: &amp;lt;tex&amp;gt;\operatorname{false} \equiv_\alpha \operatorname{zero}&amp;lt;/tex&amp;gt;. Эти функции сделаны такими для того, &lt;br /&gt;
чтобы красиво написать функцию &amp;lt;tex&amp;gt;\operatorname{if}&amp;lt;/tex&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\operatorname{if} = \lambda p \to \lambda t \to \lambda e \to p t e&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если ей в качестве первого аргумента дадут &amp;lt;tex&amp;gt;\operatorname{true}&amp;lt;/tex&amp;gt;, то вернётся &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt;, иначе {{---}} &amp;lt;tex&amp;gt;e&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Стандартные функции булевой логики:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\operatorname{and} = \lambda n \to \lambda m \to \operatorname{if} n m \operatorname{false}&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\operatorname{or} = \lambda n \to \lambda m \to \operatorname{if} n \operatorname{true} m&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\opeartorname{not} = \lambda b \to \opeartorname{if} b \operatorname{false} \operatorname{true}&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ещё одной важной функцией является функция проверки, является ли число нулём:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\operatorname{isZero} = \lambda n \to n (\lambda c \to \operatorname{false}) \operatorname{true}&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Функция выглядит несколько странно. &amp;lt;tex&amp;gt;\lambda c -&amp;gt; \operatorname{false}&amp;lt;/tex&amp;gt; {{---}} функция, которая независимо&lt;br /&gt;
от того, что ей дали на вход, возвращает &amp;lt;tex&amp;gt;\operatorname{false}&amp;lt;/tex&amp;gt;. Тогда, если в качестве &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt;&lt;br /&gt;
будет дан ноль, то функция, по определению нуля, не выполнится ни разу, и будет&lt;br /&gt;
возвращено значение по умолчанию &amp;lt;tex&amp;gt;\operatorname{true}&amp;lt;/tex&amp;gt;. Иначе же функция будет запущено, и &lt;br /&gt;
вернётся &amp;lt;tex&amp;gt;\operatorname{false}&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
\subsection{Пара}&lt;br /&gt;
&lt;br /&gt;
\begin{code}&lt;br /&gt;
pair' = Lam &amp;quot;a&amp;quot; $ Lam &amp;quot;b&amp;quot; $ Lam &amp;quot;t&amp;quot; $ Var &amp;quot;t&amp;quot; `App` Var &amp;quot;a&amp;quot; `App` Var &amp;quot;b&amp;quot;&lt;br /&gt;
fst'' = Lam &amp;quot;p&amp;quot; $ Var &amp;quot;p&amp;quot; `App` true'&lt;br /&gt;
snd'' = Lam &amp;quot;p&amp;quot; $ Var &amp;quot;p&amp;quot; `App` false'&lt;br /&gt;
\end{code}&lt;br /&gt;
&lt;br /&gt;
Функция $pair$ принимает два значения и запаковывает их в пару так, чтобы к&lt;br /&gt;
ним можно было обращаться по $fst$ и $snd$. В $fst$ и $snd$ вместо $t$ в $pair$&lt;br /&gt;
будет подставлено $true$ или $false$, возвращающие, соответственно, первый и&lt;br /&gt;
второй аргументы, то есть $a$ или $b$, соответственно.&lt;br /&gt;
&lt;br /&gt;
\subsection{Вычитание}&lt;br /&gt;
В отличие от всех предыдущих функций, вычитание для натуральных чисел определено&lt;br /&gt;
только в случае, если уменьшаемое больше вычитаемого. Положим в противном случае&lt;br /&gt;
результат равным нулю. Пусть уже есть функция, которая вычитает из числа единицу.&lt;br /&gt;
Тогда на её основе легко сделать, собственно, вычитание.&lt;br /&gt;
&lt;br /&gt;
\begin{code}&lt;br /&gt;
minus' = Lam &amp;quot;n&amp;quot; $ Lam &amp;quot;m&amp;quot; $ Var &amp;quot;m&amp;quot; `App` pred' `App` Var &amp;quot;n&amp;quot;&lt;br /&gt;
\end{code}&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;&amp;lt;$m$ раз вычесть единицу из $n$&amp;gt;&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Осталось, собственно, функция для вычитания единицы. Однако, это не так просто,&lt;br /&gt;
как может показаться на первый взгляд. Проблема в том, что, имея функцию, которую&lt;br /&gt;
нужно применить для того, чтобы продвинуться вперёд, продвинуться назад будет&lt;br /&gt;
проблематично. Если попробовать воспользоваться идеей о том, чтобы, начав от &lt;br /&gt;
нуля, идти вперёд, и пройти на один шаг меньше, то будет не очень понятно, как&lt;br /&gt;
же остановиться ровно за один шаг до конца. Для реализации вычитания единицы &lt;br /&gt;
сделаем следующее. $n$ раз выполним следующее: имея пару $(n-1, n-2)$ построим пару&lt;br /&gt;
$(n, n-1)$. Тогда после $n$ шагов во втором элементе пары будет записано число&lt;br /&gt;
$n-1$, которое и хочется получить.&lt;br /&gt;
% --pred = \n -&amp;gt; \s -&amp;gt; \z -&amp;gt; n (\g -&amp;gt; \h -&amp;gt; h (g s)) (\u -&amp;gt; z) (\u-&amp;gt;u)&lt;br /&gt;
% --pred = \n -&amp;gt; \s -&amp;gt; \z -&amp;gt; snd (n (\p -&amp;gt; (pair (s (fst p)) (fst p))) (pair z z))&lt;br /&gt;
% --pred = \n -&amp;gt; \s -&amp;gt; \z -&amp;gt; fst' (n (\p -&amp;gt; pair (s (fst p))(fst p)) (pair z z))&lt;br /&gt;
&lt;br /&gt;
\begin{code}&lt;br /&gt;
pred' = Lam &amp;quot;n&amp;quot; $ &lt;br /&gt;
        Lam &amp;quot;s&amp;quot; $ &lt;br /&gt;
        Lam &amp;quot;z&amp;quot; $ &lt;br /&gt;
            snd'' `App` (Var &amp;quot;n&amp;quot; &lt;br /&gt;
                            `App` ( &lt;br /&gt;
                                     Lam &amp;quot;p&amp;quot; $ pair' &lt;br /&gt;
                                        `App` (Var &amp;quot;s&amp;quot; `App` (fst'' `App` Var &amp;quot;p&amp;quot;))&lt;br /&gt;
                                        `App` (fst'' `App` Var &amp;quot;p&amp;quot;)&lt;br /&gt;
                                  )&lt;br /&gt;
                            `App` ( pair' `App` Var &amp;quot;z&amp;quot; `App` Var &amp;quot;z&amp;quot; )&lt;br /&gt;
                        )&lt;br /&gt;
\end{code}&lt;br /&gt;
&lt;br /&gt;
Если вы ничего не поняли, не огорчайтесь. Вычитание придумал Клини, когда &lt;br /&gt;
ему вырывали зуб мудрости. А сейчас наркоз уже не тот!&lt;br /&gt;
&lt;br /&gt;
\subsection{Сравнение}&lt;br /&gt;
Так как вычитание определено таким способом, чтобы для случая, в котором&lt;br /&gt;
уменьшаемое больше, чем вычитаемое, возвращать ноль, можно определить&lt;br /&gt;
сравнение на больше-меньше через него. Равными же числа $a$ и $b$ считаются, &lt;br /&gt;
если $a - b = 0 \wedge b - a = 0$.&lt;br /&gt;
&lt;br /&gt;
\begin{code}&lt;br /&gt;
le' = Lam &amp;quot;n&amp;quot; $ Lam &amp;quot;m&amp;quot; $ isZero' `App` (minus' `App` Var &amp;quot;n&amp;quot; `App` Var &amp;quot;m&amp;quot;)&lt;br /&gt;
less' = Lam &amp;quot;n&amp;quot; $ Lam &amp;quot;m&amp;quot; $ le' `App` Var &amp;quot;n&amp;quot; `App` (pred' `App` Var &amp;quot;m&amp;quot;)&lt;br /&gt;
eq' = Lam &amp;quot;n&amp;quot; $ Lam &amp;quot;m&amp;quot; $ and' &lt;br /&gt;
            `App` (isZero' `App` (minus' `App` Var &amp;quot;n&amp;quot; `App` Var &amp;quot;m&amp;quot;))&lt;br /&gt;
            `App` (isZero' `App` (minus' `App` Var &amp;quot;m&amp;quot; `App` Var &amp;quot;n&amp;quot;))&lt;br /&gt;
\end{code}&lt;br /&gt;
&lt;br /&gt;
\subsection{Комбинатор неподвижной точки}&lt;br /&gt;
Попробуем выразить в лямбда-исчислении какую-нибудь рекурсивную функцию.&lt;br /&gt;
Напрмер, факториал.&lt;br /&gt;
&lt;br /&gt;
\begin{spec}&lt;br /&gt;
fact = \x -&amp;gt; if (isZero x) one (fact (pred x))&lt;br /&gt;
\end{spec}&lt;br /&gt;
&lt;br /&gt;
Мы столкнулись с проблемой. В определении функции $fact$ используется функция &lt;br /&gt;
$fact$. При формальной замене, получим бесконечную функцию. Можно попытаться решить &lt;br /&gt;
эту проблему следующим образом&lt;br /&gt;
&lt;br /&gt;
\begin{spec}&lt;br /&gt;
fact = (\f -&amp;gt; \x -&amp;gt; if (isZero x) one (f (pred x))) fact&lt;br /&gt;
\end{spec}&lt;br /&gt;
&lt;br /&gt;
\emph{Неподвижной точкой} лямбда-функции $f$ назовём такую функцию $x$, что&lt;br /&gt;
$f x \to_\beta x$. Лямбда исчисление обладаем замечательным свойством: у каждой&lt;br /&gt;
функции есть неподвижная точка!&lt;br /&gt;
&lt;br /&gt;
Рассмотрим такую функцию. &lt;br /&gt;
&lt;br /&gt;
\begin{code}&lt;br /&gt;
fix' = Lam &amp;quot;f&amp;quot; $   (Lam &amp;quot;x&amp;quot; $ Var &amp;quot;f&amp;quot; `App` (Var &amp;quot;x&amp;quot; `App` Var &amp;quot;x&amp;quot;))&lt;br /&gt;
            `App`  (Lam &amp;quot;x&amp;quot; $ Var &amp;quot;f&amp;quot; `App` (Var &amp;quot;x&amp;quot; `App` Var &amp;quot;x&amp;quot;))&lt;br /&gt;
\end{code}&lt;br /&gt;
&lt;br /&gt;
Заметим, что $fix' \to_\beta \lambda f \to f ((\lambda x \to f (x x)) (\lambda x \to f (x x)))$.&lt;br /&gt;
Или, что то же самое, &lt;br /&gt;
$\lambda f \to (\lambda x \to f (x x)) (\lambda x \to f (x x)) \to_\beta&lt;br /&gt;
 \lambda f \to f ((\lambda x \to f (x x)) (\lambda x \to f (x x)))$&lt;br /&gt;
&lt;br /&gt;
Рассмотрим функцию&lt;br /&gt;
&lt;br /&gt;
\begin{code}&lt;br /&gt;
fact'' = Lam &amp;quot;f&amp;quot; $ Lam &amp;quot;x&amp;quot; $ if'' `App` (isZero' `App` Var &amp;quot;x&amp;quot;) &lt;br /&gt;
                                  `App` one'&lt;br /&gt;
                                  `App` (mult' `App` Var &amp;quot;x&amp;quot; `App` (Var &amp;quot;f&amp;quot; `App`&lt;br /&gt;
                                            (pred' `App` Var &amp;quot;x&amp;quot;)))&lt;br /&gt;
\end{code}&lt;br /&gt;
&lt;br /&gt;
Как было показано выше, $fix f \to_\beta f (fix f)$, то есть, &lt;br /&gt;
$fix fact'' \to_\beta fact'$, где $fact'$~--- искомая функция, считающая факториал.&lt;br /&gt;
&lt;br /&gt;
\begin{code}&lt;br /&gt;
fact' = fix' `App` fact''&lt;br /&gt;
\end{code}&lt;br /&gt;
&lt;br /&gt;
Это даст функцию, которая посчитает факториал числа. Но делать она это будет &lt;br /&gt;
мееедленно-меееедленно. Для того, чтобы посчитать $5!$ потребовалось сделать&lt;br /&gt;
66066 $\beta$-редукций.&lt;br /&gt;
&lt;br /&gt;
Тут правда ничего не понятно? :'(&lt;br /&gt;
&lt;br /&gt;
\subsection{Деление}&lt;br /&gt;
Воспользовавшись идеей о том, что можно делать рекурсивные функции, сделаем &lt;br /&gt;
функцию, которая будет искать частное двух чисел.&lt;br /&gt;
&lt;br /&gt;
\begin{code}&lt;br /&gt;
div'' = Lam &amp;quot;div&amp;quot; $ Lam &amp;quot;n&amp;quot; $ Lam &amp;quot;m&amp;quot; $ if'' `App` (less' `App` Var &amp;quot;n&amp;quot; `App` Var &amp;quot;m&amp;quot;)&lt;br /&gt;
         `App` zero'&lt;br /&gt;
         `App` (succ' `App` (Var &amp;quot;div&amp;quot; `App` (minus' `App` Var &amp;quot;n&amp;quot; `App` Var &amp;quot;m&amp;quot;) `App` Var &amp;quot;m&amp;quot;))&lt;br /&gt;
div' = fix' `App` div''&lt;br /&gt;
\end{code}&lt;br /&gt;
&lt;br /&gt;
И остатка от деления&lt;br /&gt;
&lt;br /&gt;
\begin{code}&lt;br /&gt;
mod'' = Lam &amp;quot;mod&amp;quot; $ Lam &amp;quot;n&amp;quot; $ Lam &amp;quot;m&amp;quot; $ if'' `App` (less' `App` Var &amp;quot;n&amp;quot; `App` Var &amp;quot;m&amp;quot;)&lt;br /&gt;
         `App` Var &amp;quot;n&amp;quot;&lt;br /&gt;
         `App` (Var &amp;quot;mod&amp;quot; `App` (minus' `App` Var &amp;quot;n&amp;quot; `App` Var &amp;quot;m&amp;quot;) `App` Var &amp;quot;m&amp;quot;)&lt;br /&gt;
mod' = fix' `App` mod'';&lt;br /&gt;
\end{code}&lt;br /&gt;
&lt;br /&gt;
\subsection{Проверка на простоту}&lt;br /&gt;
&lt;br /&gt;
$isPrimeHelp$~--- принимает число, которое требуется проверить на простоту и &lt;br /&gt;
то, на что его надо попытаться поделить, перебирая это от 2 до $p-1$. Если на &lt;br /&gt;
что-нибудь разделилось, то число~--- составное, иначе~--- простое.&lt;br /&gt;
&lt;br /&gt;
\begin{code}&lt;br /&gt;
isPrimeHelp' = Lam &amp;quot;f&amp;quot; $ Lam &amp;quot;p&amp;quot; $ Lam &amp;quot;i&amp;quot; $ if'' `App` (le' `App` Var &amp;quot;p&amp;quot; `App` Var &amp;quot;i&amp;quot;)&lt;br /&gt;
                `App` true'&lt;br /&gt;
                `App` ( if'' `App` (isZero' `App` (mod' `App` Var &amp;quot;p&amp;quot; `App` Var &amp;quot;i&amp;quot;))&lt;br /&gt;
                         `App` false'&lt;br /&gt;
                         `App` (Var &amp;quot;f&amp;quot; `App` Var &amp;quot;p&amp;quot; `App` (succ' `App` Var &amp;quot;i&amp;quot;))&lt;br /&gt;
                )&lt;br /&gt;
isPrimeHelp = fix' `App` isPrimeHelp'&lt;br /&gt;
isPrime = Lam &amp;quot;p&amp;quot; $ isPrimeHelp `App` Var &amp;quot;p&amp;quot; `App` two'&lt;br /&gt;
\end{code}&lt;br /&gt;
&lt;br /&gt;
Следующее простое число. $nextPrime'$~--- следующее, больше либо равное заданного,&lt;br /&gt;
$nextPrime$~--- следующее, большее заданного.&lt;br /&gt;
&lt;br /&gt;
\begin{code}&lt;br /&gt;
nextPrime'' = Lam &amp;quot;f&amp;quot; $ Lam &amp;quot;p&amp;quot; $ if'' `App` (isPrime `App` Var &amp;quot;p&amp;quot;)&lt;br /&gt;
                                   `App` Var &amp;quot;p&amp;quot;&lt;br /&gt;
                                   `App` (Var &amp;quot;f&amp;quot; `App` (succ' `App` Var &amp;quot;p&amp;quot;)) &lt;br /&gt;
nextPrime' = fix' `App` nextPrime''&lt;br /&gt;
nextPrime = Lam &amp;quot;p&amp;quot; $ nextPrime' `App` (succ' `App` Var &amp;quot;p&amp;quot;)&lt;br /&gt;
\end{code}&lt;br /&gt;
&lt;br /&gt;
$ithPrimeStep$ пропрыгает $i$ простых чисел вперёд. $ithPrime$ принимает число&lt;br /&gt;
$i$ и пропрыгивает столько простых чисел вперёд, начиная с двойки.&lt;br /&gt;
&lt;br /&gt;
\begin{code}&lt;br /&gt;
ithPrimeStep' = Lam &amp;quot;f&amp;quot; $ Lam &amp;quot;p&amp;quot; $ Lam &amp;quot;i&amp;quot; $ if'' `App` (isZero' `App` Var &amp;quot;i&amp;quot;)&lt;br /&gt;
                `App` Var &amp;quot;p&amp;quot;&lt;br /&gt;
                `App` (Var &amp;quot;f&amp;quot; `App` (nextPrime `App` Var &amp;quot;p&amp;quot;) `App` (pred' `App` Var &amp;quot;i&amp;quot;))&lt;br /&gt;
ithPrimeStep = fix' `App` ithPrimeStep'&lt;br /&gt;
ithPrime = Lam &amp;quot;i&amp;quot; $ ithPrimeStep `App` two' `App` Var &amp;quot;i&amp;quot;&lt;br /&gt;
\end{code}&lt;br /&gt;
&lt;br /&gt;
...и всего через 314007 $\beta$-редукций вы узнаете, что третье простое число~---&lt;br /&gt;
семь!&lt;br /&gt;
&lt;br /&gt;
\subsection{Списки}&lt;br /&gt;
&lt;br /&gt;
Для работы со списками чисел нам понадобятся следующие функции:&lt;br /&gt;
\begin{itemize}&lt;br /&gt;
    \item $empty$~--- возвращает пустой список&lt;br /&gt;
    \item $cons$~--- принимает первый элемент и оставшийся список, склеивает их&lt;br /&gt;
    \item $head$~--- вернуть голову списка&lt;br /&gt;
    \item $tail$~--- вернуть хвост списка&lt;br /&gt;
\end{itemize}&lt;br /&gt;
&lt;br /&gt;
Список будем хранить в следующем виде: $\langle len, p_1^{a_1}p_2^{a_2}\ldots p_{len}^{a_{len}} \rangle$. При этом, голова списка будет храниться как показатель степени при $p_{len}$.&lt;br /&gt;
&lt;br /&gt;
\begin{code}&lt;br /&gt;
empty = pair' `App` zero' `App` one'&lt;br /&gt;
cons = Lam &amp;quot;h&amp;quot; $ Lam &amp;quot;t&amp;quot; $ pair' `App` (succ' `App` (fst'' `App` Var &amp;quot;t&amp;quot;))&lt;br /&gt;
        `App` (mult' `App` (snd'' `App` Var &amp;quot;t&amp;quot;) `App` (power' &lt;br /&gt;
                `App` (ithPrime `App` (fst'' `App` Var &amp;quot;t&amp;quot;))&lt;br /&gt;
                `App` Var &amp;quot;h&amp;quot;&lt;br /&gt;
        ))&lt;br /&gt;
head = Lam &amp;quot;list&amp;quot; $ getExponent `App` (snd'' `App` Var &amp;quot;list&amp;quot;)&lt;br /&gt;
                                `App` (ithPrime `App` (pred' `App` (fst'' `App` Var &amp;quot;list&amp;quot;)))&lt;br /&gt;
&lt;br /&gt;
tail = Lam &amp;quot;list&amp;quot; $ pair' `App` (pred' `App` (fst'' `App` Var &amp;quot;list&amp;quot;))&lt;br /&gt;
                          `App` (eliminateMultiplier `App` (snd'' `App` Var &amp;quot;list&amp;quot;)&lt;br /&gt;
                                  `App` (ithPrime `App` (pred' `App` (fst'' `App` Var &amp;quot;list&amp;quot; )))&lt;br /&gt;
                                )&lt;br /&gt;
&lt;br /&gt;
eliminateMultiplier' = Lam &amp;quot;f&amp;quot; $ Lam &amp;quot;n&amp;quot; $ Lam &amp;quot;m&amp;quot; $ if'' `App` (isZero' `App` (mod' `App` Var &amp;quot;n&amp;quot; `App` Var &amp;quot;m&amp;quot;))&lt;br /&gt;
                        `App` (Var &amp;quot;f&amp;quot; `App` (div' `App` Var &amp;quot;n&amp;quot; `App` Var &amp;quot;m&amp;quot;) `App` Var &amp;quot;m&amp;quot;)&lt;br /&gt;
                        `App` Var &amp;quot;n&amp;quot;&lt;br /&gt;
eliminateMultiplier = fix' `App` eliminateMultiplier'&lt;br /&gt;
&lt;br /&gt;
getExponent' = Lam &amp;quot;f&amp;quot; $ Lam &amp;quot;n&amp;quot; $ Lam &amp;quot;m&amp;quot; $ if'' `App` (isZero' `App` (mod' `App` Var &amp;quot;n&amp;quot; `App` Var &amp;quot;m&amp;quot;))&lt;br /&gt;
                `App` (succ' `App`(Var &amp;quot;f&amp;quot; `App` (div' `App` Var &amp;quot;n&amp;quot; `App` Var &amp;quot;m&amp;quot;) `App` Var &amp;quot;m&amp;quot;))&lt;br /&gt;
                `App` zero'&lt;br /&gt;
getExponent = fix' `App` getExponent'&lt;br /&gt;
\end{code}&lt;br /&gt;
&lt;br /&gt;
На основе этого всего уже можно реализовать эмулятор машины тьюринга:&lt;br /&gt;
с помощью пар, списков чисел можно хранить состояния. С помощью рекурсии можно&lt;br /&gt;
обрабатывать переходы. Входная строка будет даваться, например, закодированной&lt;br /&gt;
аналогично списку: пара из длины и числа, характеризующего список степенями &lt;br /&gt;
простых. Я бы продолжил это писать, но уже на операции $head [1, 2]$ я не &lt;br /&gt;
дождался окончания выполнения. Скорость лямбда-исчисления как вычислителя&lt;br /&gt;
печальна.  &lt;br /&gt;
&lt;br /&gt;
\ignore{&lt;br /&gt;
\begin{code}&lt;br /&gt;
&lt;br /&gt;
four' = norm $ succ' `App` three'&lt;br /&gt;
five' = norm $ succ' `App` four'&lt;br /&gt;
&lt;br /&gt;
list2 = norm $ cons `App` one' `App` empty&lt;br /&gt;
list32 = cons `App` zero' `App` list2&lt;br /&gt;
&lt;br /&gt;
normFiveFact = normIO 0 $ fact' `App` five'&lt;br /&gt;
&lt;br /&gt;
-- fiftysix = mult twentyeight two&lt;br /&gt;
-- fiftyfive = pred fiftysix&lt;br /&gt;
-- six = pred seven&lt;br /&gt;
&lt;br /&gt;
--main = do print $ fiftysix (+1) 0&lt;br /&gt;
main = do&lt;br /&gt;
 --   f &amp;lt;- normIO 0 $ ithPrime `App` three'&lt;br /&gt;
 --   f &amp;lt;- normIO 0 $ getExponent `App` (norm $ plus' `App` four' `App` four') `App` two'&lt;br /&gt;
    f &amp;lt;- normIO 0 $ head `App` (tail `App` list32)&lt;br /&gt;
    print $ toInt f&lt;br /&gt;
\end{code}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
\end{document}&lt;br /&gt;
[http://rain.ifmo.ru/~komarov/Turing.lhs]&lt;/div&gt;</summary>
		<author><name>194.85.160.130</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%9B%D1%8F%D0%BC%D0%B1%D0%B4%D0%B0-%D0%B8%D1%81%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5&amp;diff=27294</id>
		<title>Лямбда-исчисление</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%9B%D1%8F%D0%BC%D0%B1%D0%B4%D0%B0-%D0%B8%D1%81%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5&amp;diff=27294"/>
				<updated>2012-12-06T16:13:16Z</updated>
		
		<summary type="html">&lt;p&gt;194.85.160.130: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{В разработке}}&lt;br /&gt;
&lt;br /&gt;
== Лямбда-исчисление==&lt;br /&gt;
&lt;br /&gt;
''Лямбда-исчисление'' {{---}} формальная система, придуманная в 1930-х годах &lt;br /&gt;
Алонзо Чёрчем. Лямбда-функция является, по сути, анонимной функцией.&lt;br /&gt;
Эта концепция показала себя удобной и сейчас активно используется во многих&lt;br /&gt;
языках программирования.&lt;br /&gt;
&lt;br /&gt;
Более формально, ''лямбда-функцию'' (или, ''лямбда-терм'') можно задать &lt;br /&gt;
следующей грамматикой:&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
&amp;lt;tex&amp;gt;&lt;br /&gt;
\begin{array}{r c l}&lt;br /&gt;
\langle Term \rangle &amp;amp; ::= &amp;amp; \langle Variable \rangle \\&lt;br /&gt;
                       &amp;amp; || &amp;amp; \langle Term \rangle \langle Term \rangle \\&lt;br /&gt;
                       &amp;amp; || &amp;amp; \lambda \langle Variable \rangle \to \langle Term \rangle\\&lt;br /&gt;
                       &amp;amp; || &amp;amp; ( \langle Term \rangle )\\&lt;br /&gt;
\langle Variable \rangle &amp;amp; ::= &amp;amp; \langle Char \rangle *\\&lt;br /&gt;
\end{array}&lt;br /&gt;
&amp;lt;/tex&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
В первом случае функция является просто переменной. &lt;br /&gt;
Во втором происходит ''аппликация'' (''применение'') одной функции к другой.&lt;br /&gt;
Это аналогично вычислению функции-левого операнда на аргументе-правом операнде.&lt;br /&gt;
В третьем {{---}} ''абстракция'' по переменной. В данном случае происходит &lt;br /&gt;
создание функции одного аргумента с заданными именем аргумента и телом функции.&lt;br /&gt;
&lt;br /&gt;
Рассмотрим, например, функцию &amp;lt;tex&amp;gt;id = \lambda x \to x&amp;lt;/tex&amp;gt;. Эта функция принимает аргумент и &lt;br /&gt;
возвращает его неизменённым. Например, &lt;br /&gt;
&amp;lt;tex&amp;gt;id 2 \equiv 2&amp;lt;/tex&amp;gt;. Аналогично, &amp;lt;tex&amp;gt;id y == y&amp;lt;/tex&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Ещё один пример функции: &amp;lt;tex&amp;gt;sum = \lambda x \to \lambda y \to x + y|. Эта функция двух аргументов,&lt;br /&gt;
которая возвращает их сумму. Правда, здесь мы немного вышли за написанную выше грамматику.&lt;br /&gt;
Ну да ладно. &amp;lt;tex&amp;gt;sum 2 3 == 5&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Приоритет операций===&lt;br /&gt;
* Применение левоассоциативно: &amp;lt;tex&amp;gt;x y z w == ((x y) z) w&amp;lt;/tex&amp;gt;&lt;br /&gt;
* Аппликация забирает себе всё, до чего дотянется: &amp;lt;tex&amp;gt;\lambda x \to \lambda y \to \lambda z \to z y x \equiv \lambda x \to (\lambda y \to (\lambda z \to ((z y) x)))&amp;lt;/tex&amp;gt;&lt;br /&gt;
* Скобки играют привычную роль группировки действий&lt;br /&gt;
&lt;br /&gt;
===Свободные и связанные переменные===&lt;br /&gt;
''Связанными'' переменными называются все переменные, по которым выше в &lt;br /&gt;
дереве разбора были абстракции. Все остальные переменные называются свободными.&lt;br /&gt;
&lt;br /&gt;
Например, в &amp;lt;tex&amp;gt;\lambda x \to \lambda y \to x&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt; связана, а &amp;lt;tex&amp;gt;y&amp;lt;/tex&amp;gt;{{---}} свободна. А в &amp;lt;tex&amp;gt;\lambda y \to x (\lambda x \to x)&amp;lt;/tex&amp;gt;&lt;br /&gt;
в своём первом вхождении переменная &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt; свободна, а во втором {{---}} связана.&lt;br /&gt;
&lt;br /&gt;
Рассмотрим функции &amp;lt;tex&amp;gt;\lambda y \to y&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\lambda x \to y&amp;lt;/tex&amp;gt;. В первой из них при взгляде на &amp;lt;tex&amp;gt;y&amp;lt;/tex&amp;gt;&lt;br /&gt;
понятно, что она имеет отношение к переменной, по которой производилась &lt;br /&gt;
абстракция. Если по одной и той же&lt;br /&gt;
переменной абстракция производилась более одного раза, то переменная связана&lt;br /&gt;
с самым поздним (самым нижним в дереве разбора) абстрагированием. Например, в&lt;br /&gt;
&amp;lt;tex&amp;gt;\lambda x \to \lambda x \to \lambda y \to \lambda x \to x&amp;lt;/tex&amp;gt;, переменная &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt; связана с самой правой абстракцией &lt;br /&gt;
по &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
===&amp;lt;tex&amp;gt;\alpha&amp;lt;/tex&amp;gt;-конверсия===&lt;br /&gt;
&lt;br /&gt;
Рассмотрим функции &amp;lt;tex&amp;gt;(\lambda x \to x) z&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;(\lambda y \to y) z&amp;lt;/tex&amp;gt;. Интуитивно понятно, что они &lt;br /&gt;
являются одинаковыми. &lt;br /&gt;
&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition=''&amp;lt;tex&amp;gt;\alpha&amp;lt;/tex&amp;gt;-конверсия'' {{---}} переименование связанной переменной. Выражение&lt;br /&gt;
&amp;lt;tex&amp;gt;\lambda x \to f&amp;lt;/tex&amp;gt; можно заменить на &amp;lt;tex&amp;gt;\lambda y \to f[x := y]&amp;lt;/tex&amp;gt;, если &amp;lt;tex&amp;gt;y&amp;lt;/tex&amp;gt; не входит свободно в &amp;lt;tex&amp;gt;f&amp;lt;/tex&amp;gt;,&lt;br /&gt;
где &amp;lt;tex&amp;gt;f[x:=y]&amp;lt;/tex&amp;gt; означает замену всех свободных вхождений &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt; в &amp;lt;tex&amp;gt;f&amp;lt;/tex&amp;gt; на &amp;lt;tex&amp;gt;y&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Функции, получающиеся одна из другой с помощью &amp;lt;tex&amp;gt;\alpha&amp;lt;/tex&amp;gt;-конверсий, называются &lt;br /&gt;
''&amp;lt;tex&amp;gt;\alpha&amp;lt;/tex&amp;gt;-эквивалентными'' и обозначаются &amp;lt;tex&amp;gt;f \equiv_\alpha g&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Функции &amp;lt;tex&amp;gt;\lambda x \to \lambda y \to x y z&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\lambda a \to \lambda x \to a x z&amp;lt;/tex&amp;gt; являются &amp;lt;tex&amp;gt;\alpha&amp;lt;/tex&amp;gt;-эквивалентными,&lt;br /&gt;
а &amp;lt;tex&amp;gt;\lambda x \to \lambda y \to y z&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\lambda y \to \lambda x \to y z&amp;lt;/tex&amp;gt; {{---}} нет.&lt;br /&gt;
&lt;br /&gt;
===&amp;lt;tex&amp;gt;\beta&amp;lt;/tex&amp;gt;-редукция===&lt;br /&gt;
&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
&amp;lt;tex&amp;gt;\beta&amp;lt;/tex&amp;gt;-редукция олицетворяет идею счёта значения функции. Выражение вида &lt;br /&gt;
&amp;lt;tex&amp;gt;(\lambda x \to f) y&amp;lt;/tex&amp;gt; можно заменить на &amp;lt;tex&amp;gt;f[x := y]&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;f[x:=y]&amp;lt;/tex&amp;gt;, как и ранее, означает&lt;br /&gt;
замену всех свободных вхождений &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt; в &amp;lt;tex&amp;gt;f&amp;lt;/tex&amp;gt; на &amp;lt;tex&amp;gt;y&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
Через &amp;lt;tex&amp;gt;f \to_\beta g&amp;lt;/tex&amp;gt; обозначают сведение &amp;lt;tex&amp;gt;f&amp;lt;/tex&amp;gt; к &amp;lt;tex&amp;gt;g&amp;lt;/tex&amp;gt; с помощью одной &amp;lt;tex&amp;gt;\beta&amp;lt;/tex&amp;gt;-редукции.&lt;br /&gt;
А через &amp;lt;tex&amp;gt;f \to_\beta^* g&amp;lt;/tex&amp;gt; {{---}} за ноль или более.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===&amp;lt;tex&amp;gt;\eta&amp;lt;/tex&amp;gt;-редукция===&lt;br /&gt;
Рассмотрим выражение вида &amp;lt;tex&amp;gt;\lambda x \to f x&amp;lt;/tex&amp;gt;. Если подставить в эту функцию значение &lt;br /&gt;
&amp;lt;tex&amp;gt;y&amp;lt;/tex&amp;gt;, то получим: &amp;lt;tex&amp;gt;(\lambda x \to f x) y \to_\beta f y&amp;lt;/tex&amp;gt;. Но если просто подставить&lt;br /&gt;
&amp;lt;tex&amp;gt;y&amp;lt;/tex&amp;gt; в &amp;lt;tex&amp;gt;f&amp;lt;/tex&amp;gt;, то получится то же самое. &lt;br /&gt;
&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
&amp;lt;tex&amp;gt;\eta&amp;lt;/tex&amp;gt;-редукция {{---}} преобразование &amp;lt;tex&amp;gt;\lambda x \to f x&amp;lt;/tex&amp;gt; в &amp;lt;tex&amp;gt;f&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
==Нотация Де Брюина==&lt;br /&gt;
Существует также альтернативное эквивалентное определение &amp;lt;tex&amp;gt;\lambda&amp;lt;/tex&amp;gt;-исчисления.&lt;br /&gt;
В оригинальном определении для обозначения переменных использовались имена,&lt;br /&gt;
и была проблема с тем, что не были запрещены одинаковые имена в разных&lt;br /&gt;
абстракциях. &lt;br /&gt;
&lt;br /&gt;
От этой проблемы можно избавиться следующим образом. Вместо имени переменной &lt;br /&gt;
будет храниться натуральное число {{---}} количество абстракций в дереве разбора,&lt;br /&gt;
на которое нужно подняться, чтобы найти ту лямбду, с которой данная переменная &lt;br /&gt;
связана. В данной нотации получаются несколько более простые определения &lt;br /&gt;
свободных переменных и &amp;lt;tex&amp;gt;\beta&amp;lt;/tex&amp;gt;-редукции. &lt;br /&gt;
&lt;br /&gt;
Переменная называется свободной, если ей соответствует число, которое больше&lt;br /&gt;
количества абстракций на пути до неё в дереве разбора.&lt;br /&gt;
&lt;br /&gt;
При &amp;lt;tex&amp;gt;\beta&amp;lt;/tex&amp;gt;-редукции же нужно будет ко всем свободным переменным заменяющего &lt;br /&gt;
дерева при каждой замене прибавить число, равное разницы уровней раньше и сейчас.&lt;br /&gt;
Это будет соответствовать тому, что эта переменная продолжит &amp;lt;&amp;lt;держаться&amp;gt;&amp;gt; за&lt;br /&gt;
ту же лямбду, что и раньше.&lt;br /&gt;
&lt;br /&gt;
==Нумералы Чёрча и программирование на &amp;lt;tex&amp;gt;\lambda&amp;lt;/tex&amp;gt;-исчислении==&lt;br /&gt;
&lt;br /&gt;
===Определение===&lt;br /&gt;
Введём на основе лямбда-исчисления аналог натуральных чисел, основанный на идее, &lt;br /&gt;
что натуральное число {{---}} это или ноль, или увеличенное на единицу натуральное &lt;br /&gt;
число.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;tex&amp;gt;\bar 0 = \lambda s \to \lambda z \to z&amp;lt;/tex&amp;gt;&lt;br /&gt;
* &amp;lt;tex&amp;gt;\bar 1 = \lambda s \to \lambda z \to s z&amp;lt;/tex&amp;gt;&lt;br /&gt;
* &amp;lt;tex&amp;gt;\bar 2 = \lambda s \to \lambda z \to s (s z)&amp;lt;/tex&amp;gt;&lt;br /&gt;
* &amp;lt;tex&amp;gt;\bar 3 = \lambda s \to \lambda z \to s (s (s z))&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Каждое число будет функцией двух аргументов: какой-то функции и начального значения.&lt;br /&gt;
Число &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; будет &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; раз применять функцию к начальному значению и возвращать &lt;br /&gt;
результат. Если такому &amp;lt;&amp;lt;числу&amp;gt;&amp;gt; дать на вход функцию &amp;lt;tex&amp;gt;(+1)&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;0&amp;lt;/tex&amp;gt; в качестве &lt;br /&gt;
начального значения, то на выходе как раз будет ожидаемое от функции число:&lt;br /&gt;
&amp;lt;tex&amp;gt;\bar 3 (+1) 0 \equiv 3&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
===+1===&lt;br /&gt;
Функция, прибавляющая 1 к числу, должна принимать первым аргументом число.&lt;br /&gt;
Но число {{---}} функция двух аргументов. Значит, эта функция должна принимать три&lt;br /&gt;
аргумента: &amp;lt;&amp;lt;число&amp;gt;&amp;gt; &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt;, которое хочется увеличить, функция, которую надо будет&lt;br /&gt;
&amp;lt;tex&amp;gt;n+1&amp;lt;/tex&amp;gt; раз применить, и начальное значение.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\operatorname{succ} = \lambda n \to \lambda s \to \lambda z \to s (n s z)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Здесь &amp;lt;tex&amp;gt;n s z&amp;lt;/tex&amp;gt; {{---}} &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; раз применённая к &amp;lt;tex&amp;gt;z&amp;lt;/tex&amp;gt; функция &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt;. Но нужно применить &amp;lt;tex&amp;gt;n+1&amp;lt;/tex&amp;gt; &lt;br /&gt;
раз. Отсюда &amp;lt;tex&amp;gt;s (n s z)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
===Сложение===&lt;br /&gt;
Сложение двух чисел похоже на прибавление единицы. Но только надо прибавить не единицу, а второе число.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\operatorname{plus} = \lambda n \to \lambda m \to \lambda s \to \lambda z \to n s (m s z)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; раз применить &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; к применённому &amp;lt;tex&amp;gt;m&amp;lt;/tex&amp;gt; раз &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; к &amp;lt;tex&amp;gt;z&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;(\operatorname{plus} \bar 3 \bar 3) (+1) 0 \equiv 6&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Умножение===&lt;br /&gt;
Умножение похоже на сложение, но прибавлять надо не единицу, а второе число.&lt;br /&gt;
Или, в терминах нумералов Чёрча, в качестве применяемой несколько раз&lt;br /&gt;
функции должна быть не &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt;, а функция, применяющая &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; раз &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\operatorname{mult} = \lambda n \to \lambda m \to \lambda s \to n (m s)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Здесь &amp;lt;tex&amp;gt;m s&amp;lt;/tex&amp;gt; {{---}} функция, которая &amp;lt;tex&amp;gt;m&amp;lt;/tex&amp;gt; раз применит &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; к тому, что дадут ей на &lt;br /&gt;
вход. С помощью &amp;lt;tex&amp;gt;\eta&amp;lt;/tex&amp;gt;-редукции можно немного сократить эту формулу&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\operatorname{mult} = \lambda n \to \lambda m \to \lambda s \to n (m s)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;(\operatorname{mult} \bar 3 \bar 3) (+1) 0 \equiv 9&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Возведение в степень===&lt;br /&gt;
It's a kind of magic&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\operatorname{power} = \lambda n \to \lambda m \to m n&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;(\operatorname{power} \bar 3 (\operatorname{succ} \bar 3)) (+1) 0 \equiv 81&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Логические значения===&lt;br /&gt;
&amp;lt;tex&amp;gt;\operatorname{true} = \lambda a \to \lambda b \to a&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\operatorname{false} = \lambda a \to \lambda b \to b&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Функции двух аргументов, возвращающие первый и второй, соответственное, аргументы.&lt;br /&gt;
Забавный факт: &amp;lt;tex&amp;gt;\operatorname{false} \equiv_\alpha \operatorname{zero}$. Эти функции сделаны такими для того, &lt;br /&gt;
чтобы красиво написать функцию &amp;lt;tex&amp;gt;\operatorname{if}&amp;lt;/tex&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\operatorname{if} = \lambda p \to \lambda t \to \lambda e \to p t e&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если ей в качестве первого аргумента дадут &amp;lt;tex&amp;gt;\operatorname{true}&amp;lt;/tex&amp;gt;, то вернётся &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt;, иначе {{---}} &amp;lt;tex&amp;gt;e&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Стандартные функции булевой логики:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\operatorname{and} = \lambda n \to \lambda m \to \operatorname{if} n m \operatorname{false}&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\operatorname{or} = \lambda n \to \lambda m \to \operatorname{if} n \operatorname{true} m&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\opeartorname{not} = \lambda b \to \opeartorname{if} b \operatorname{false} \operatorname{true}&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ещё одной важной функцией является функция проверки, является ли число нулём:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\operatorname{isZero} = \lambda n \to n (\lambda c \to \operatorname{false}) \operatorname{true}&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Функция выглядит несколько странно. &amp;lt;tex&amp;gt;\lambda c -&amp;gt; \operatorname{false}&amp;lt;/tex&amp;gt; {{---}} функция, которая независимо&lt;br /&gt;
от того, что ей дали на вход, возвращает &amp;lt;tex&amp;gt;\operatorname{false}&amp;lt;/tex&amp;gt;. Тогда, если в качестве &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt;&lt;br /&gt;
будет дан ноль, то функция, по определению нуля, не выполнится ни разу, и будет&lt;br /&gt;
возвращено значение по умолчанию &amp;lt;tex&amp;gt;\operatorname{true}&amp;lt;/tex&amp;gt;. Иначе же функция будет запущено, и &lt;br /&gt;
вернётся &amp;lt;tex&amp;gt;\operatorname{false}&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
\subsection{Пара}&lt;br /&gt;
&lt;br /&gt;
\begin{code}&lt;br /&gt;
pair' = Lam &amp;quot;a&amp;quot; $ Lam &amp;quot;b&amp;quot; $ Lam &amp;quot;t&amp;quot; $ Var &amp;quot;t&amp;quot; `App` Var &amp;quot;a&amp;quot; `App` Var &amp;quot;b&amp;quot;&lt;br /&gt;
fst'' = Lam &amp;quot;p&amp;quot; $ Var &amp;quot;p&amp;quot; `App` true'&lt;br /&gt;
snd'' = Lam &amp;quot;p&amp;quot; $ Var &amp;quot;p&amp;quot; `App` false'&lt;br /&gt;
\end{code}&lt;br /&gt;
&lt;br /&gt;
Функция $pair$ принимает два значения и запаковывает их в пару так, чтобы к&lt;br /&gt;
ним можно было обращаться по $fst$ и $snd$. В $fst$ и $snd$ вместо $t$ в $pair$&lt;br /&gt;
будет подставлено $true$ или $false$, возвращающие, соответственно, первый и&lt;br /&gt;
второй аргументы, то есть $a$ или $b$, соответственно.&lt;br /&gt;
&lt;br /&gt;
\subsection{Вычитание}&lt;br /&gt;
В отличие от всех предыдущих функций, вычитание для натуральных чисел определено&lt;br /&gt;
только в случае, если уменьшаемое больше вычитаемого. Положим в противном случае&lt;br /&gt;
результат равным нулю. Пусть уже есть функция, которая вычитает из числа единицу.&lt;br /&gt;
Тогда на её основе легко сделать, собственно, вычитание.&lt;br /&gt;
&lt;br /&gt;
\begin{code}&lt;br /&gt;
minus' = Lam &amp;quot;n&amp;quot; $ Lam &amp;quot;m&amp;quot; $ Var &amp;quot;m&amp;quot; `App` pred' `App` Var &amp;quot;n&amp;quot;&lt;br /&gt;
\end{code}&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;&amp;lt;$m$ раз вычесть единицу из $n$&amp;gt;&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Осталось, собственно, функция для вычитания единицы. Однако, это не так просто,&lt;br /&gt;
как может показаться на первый взгляд. Проблема в том, что, имея функцию, которую&lt;br /&gt;
нужно применить для того, чтобы продвинуться вперёд, продвинуться назад будет&lt;br /&gt;
проблематично. Если попробовать воспользоваться идеей о том, чтобы, начав от &lt;br /&gt;
нуля, идти вперёд, и пройти на один шаг меньше, то будет не очень понятно, как&lt;br /&gt;
же остановиться ровно за один шаг до конца. Для реализации вычитания единицы &lt;br /&gt;
сделаем следующее. $n$ раз выполним следующее: имея пару $(n-1, n-2)$ построим пару&lt;br /&gt;
$(n, n-1)$. Тогда после $n$ шагов во втором элементе пары будет записано число&lt;br /&gt;
$n-1$, которое и хочется получить.&lt;br /&gt;
% --pred = \n -&amp;gt; \s -&amp;gt; \z -&amp;gt; n (\g -&amp;gt; \h -&amp;gt; h (g s)) (\u -&amp;gt; z) (\u-&amp;gt;u)&lt;br /&gt;
% --pred = \n -&amp;gt; \s -&amp;gt; \z -&amp;gt; snd (n (\p -&amp;gt; (pair (s (fst p)) (fst p))) (pair z z))&lt;br /&gt;
% --pred = \n -&amp;gt; \s -&amp;gt; \z -&amp;gt; fst' (n (\p -&amp;gt; pair (s (fst p))(fst p)) (pair z z))&lt;br /&gt;
&lt;br /&gt;
\begin{code}&lt;br /&gt;
pred' = Lam &amp;quot;n&amp;quot; $ &lt;br /&gt;
        Lam &amp;quot;s&amp;quot; $ &lt;br /&gt;
        Lam &amp;quot;z&amp;quot; $ &lt;br /&gt;
            snd'' `App` (Var &amp;quot;n&amp;quot; &lt;br /&gt;
                            `App` ( &lt;br /&gt;
                                     Lam &amp;quot;p&amp;quot; $ pair' &lt;br /&gt;
                                        `App` (Var &amp;quot;s&amp;quot; `App` (fst'' `App` Var &amp;quot;p&amp;quot;))&lt;br /&gt;
                                        `App` (fst'' `App` Var &amp;quot;p&amp;quot;)&lt;br /&gt;
                                  )&lt;br /&gt;
                            `App` ( pair' `App` Var &amp;quot;z&amp;quot; `App` Var &amp;quot;z&amp;quot; )&lt;br /&gt;
                        )&lt;br /&gt;
\end{code}&lt;br /&gt;
&lt;br /&gt;
Если вы ничего не поняли, не огорчайтесь. Вычитание придумал Клини, когда &lt;br /&gt;
ему вырывали зуб мудрости. А сейчас наркоз уже не тот!&lt;br /&gt;
&lt;br /&gt;
\subsection{Сравнение}&lt;br /&gt;
Так как вычитание определено таким способом, чтобы для случая, в котором&lt;br /&gt;
уменьшаемое больше, чем вычитаемое, возвращать ноль, можно определить&lt;br /&gt;
сравнение на больше-меньше через него. Равными же числа $a$ и $b$ считаются, &lt;br /&gt;
если $a - b = 0 \wedge b - a = 0$.&lt;br /&gt;
&lt;br /&gt;
\begin{code}&lt;br /&gt;
le' = Lam &amp;quot;n&amp;quot; $ Lam &amp;quot;m&amp;quot; $ isZero' `App` (minus' `App` Var &amp;quot;n&amp;quot; `App` Var &amp;quot;m&amp;quot;)&lt;br /&gt;
less' = Lam &amp;quot;n&amp;quot; $ Lam &amp;quot;m&amp;quot; $ le' `App` Var &amp;quot;n&amp;quot; `App` (pred' `App` Var &amp;quot;m&amp;quot;)&lt;br /&gt;
eq' = Lam &amp;quot;n&amp;quot; $ Lam &amp;quot;m&amp;quot; $ and' &lt;br /&gt;
            `App` (isZero' `App` (minus' `App` Var &amp;quot;n&amp;quot; `App` Var &amp;quot;m&amp;quot;))&lt;br /&gt;
            `App` (isZero' `App` (minus' `App` Var &amp;quot;m&amp;quot; `App` Var &amp;quot;n&amp;quot;))&lt;br /&gt;
\end{code}&lt;br /&gt;
&lt;br /&gt;
\subsection{Комбинатор неподвижной точки}&lt;br /&gt;
Попробуем выразить в лямбда-исчислении какую-нибудь рекурсивную функцию.&lt;br /&gt;
Напрмер, факториал.&lt;br /&gt;
&lt;br /&gt;
\begin{spec}&lt;br /&gt;
fact = \x -&amp;gt; if (isZero x) one (fact (pred x))&lt;br /&gt;
\end{spec}&lt;br /&gt;
&lt;br /&gt;
Мы столкнулись с проблемой. В определении функции $fact$ используется функция &lt;br /&gt;
$fact$. При формальной замене, получим бесконечную функцию. Можно попытаться решить &lt;br /&gt;
эту проблему следующим образом&lt;br /&gt;
&lt;br /&gt;
\begin{spec}&lt;br /&gt;
fact = (\f -&amp;gt; \x -&amp;gt; if (isZero x) one (f (pred x))) fact&lt;br /&gt;
\end{spec}&lt;br /&gt;
&lt;br /&gt;
\emph{Неподвижной точкой} лямбда-функции $f$ назовём такую функцию $x$, что&lt;br /&gt;
$f x \to_\beta x$. Лямбда исчисление обладаем замечательным свойством: у каждой&lt;br /&gt;
функции есть неподвижная точка!&lt;br /&gt;
&lt;br /&gt;
Рассмотрим такую функцию. &lt;br /&gt;
&lt;br /&gt;
\begin{code}&lt;br /&gt;
fix' = Lam &amp;quot;f&amp;quot; $   (Lam &amp;quot;x&amp;quot; $ Var &amp;quot;f&amp;quot; `App` (Var &amp;quot;x&amp;quot; `App` Var &amp;quot;x&amp;quot;))&lt;br /&gt;
            `App`  (Lam &amp;quot;x&amp;quot; $ Var &amp;quot;f&amp;quot; `App` (Var &amp;quot;x&amp;quot; `App` Var &amp;quot;x&amp;quot;))&lt;br /&gt;
\end{code}&lt;br /&gt;
&lt;br /&gt;
Заметим, что $fix' \to_\beta \lambda f \to f ((\lambda x \to f (x x)) (\lambda x \to f (x x)))$.&lt;br /&gt;
Или, что то же самое, &lt;br /&gt;
$\lambda f \to (\lambda x \to f (x x)) (\lambda x \to f (x x)) \to_\beta&lt;br /&gt;
 \lambda f \to f ((\lambda x \to f (x x)) (\lambda x \to f (x x)))$&lt;br /&gt;
&lt;br /&gt;
Рассмотрим функцию&lt;br /&gt;
&lt;br /&gt;
\begin{code}&lt;br /&gt;
fact'' = Lam &amp;quot;f&amp;quot; $ Lam &amp;quot;x&amp;quot; $ if'' `App` (isZero' `App` Var &amp;quot;x&amp;quot;) &lt;br /&gt;
                                  `App` one'&lt;br /&gt;
                                  `App` (mult' `App` Var &amp;quot;x&amp;quot; `App` (Var &amp;quot;f&amp;quot; `App`&lt;br /&gt;
                                            (pred' `App` Var &amp;quot;x&amp;quot;)))&lt;br /&gt;
\end{code}&lt;br /&gt;
&lt;br /&gt;
Как было показано выше, $fix f \to_\beta f (fix f)$, то есть, &lt;br /&gt;
$fix fact'' \to_\beta fact'$, где $fact'$~--- искомая функция, считающая факториал.&lt;br /&gt;
&lt;br /&gt;
\begin{code}&lt;br /&gt;
fact' = fix' `App` fact''&lt;br /&gt;
\end{code}&lt;br /&gt;
&lt;br /&gt;
Это даст функцию, которая посчитает факториал числа. Но делать она это будет &lt;br /&gt;
мееедленно-меееедленно. Для того, чтобы посчитать $5!$ потребовалось сделать&lt;br /&gt;
66066 $\beta$-редукций.&lt;br /&gt;
&lt;br /&gt;
Тут правда ничего не понятно? :'(&lt;br /&gt;
&lt;br /&gt;
\subsection{Деление}&lt;br /&gt;
Воспользовавшись идеей о том, что можно делать рекурсивные функции, сделаем &lt;br /&gt;
функцию, которая будет искать частное двух чисел.&lt;br /&gt;
&lt;br /&gt;
\begin{code}&lt;br /&gt;
div'' = Lam &amp;quot;div&amp;quot; $ Lam &amp;quot;n&amp;quot; $ Lam &amp;quot;m&amp;quot; $ if'' `App` (less' `App` Var &amp;quot;n&amp;quot; `App` Var &amp;quot;m&amp;quot;)&lt;br /&gt;
         `App` zero'&lt;br /&gt;
         `App` (succ' `App` (Var &amp;quot;div&amp;quot; `App` (minus' `App` Var &amp;quot;n&amp;quot; `App` Var &amp;quot;m&amp;quot;) `App` Var &amp;quot;m&amp;quot;))&lt;br /&gt;
div' = fix' `App` div''&lt;br /&gt;
\end{code}&lt;br /&gt;
&lt;br /&gt;
И остатка от деления&lt;br /&gt;
&lt;br /&gt;
\begin{code}&lt;br /&gt;
mod'' = Lam &amp;quot;mod&amp;quot; $ Lam &amp;quot;n&amp;quot; $ Lam &amp;quot;m&amp;quot; $ if'' `App` (less' `App` Var &amp;quot;n&amp;quot; `App` Var &amp;quot;m&amp;quot;)&lt;br /&gt;
         `App` Var &amp;quot;n&amp;quot;&lt;br /&gt;
         `App` (Var &amp;quot;mod&amp;quot; `App` (minus' `App` Var &amp;quot;n&amp;quot; `App` Var &amp;quot;m&amp;quot;) `App` Var &amp;quot;m&amp;quot;)&lt;br /&gt;
mod' = fix' `App` mod'';&lt;br /&gt;
\end{code}&lt;br /&gt;
&lt;br /&gt;
\subsection{Проверка на простоту}&lt;br /&gt;
&lt;br /&gt;
$isPrimeHelp$~--- принимает число, которое требуется проверить на простоту и &lt;br /&gt;
то, на что его надо попытаться поделить, перебирая это от 2 до $p-1$. Если на &lt;br /&gt;
что-нибудь разделилось, то число~--- составное, иначе~--- простое.&lt;br /&gt;
&lt;br /&gt;
\begin{code}&lt;br /&gt;
isPrimeHelp' = Lam &amp;quot;f&amp;quot; $ Lam &amp;quot;p&amp;quot; $ Lam &amp;quot;i&amp;quot; $ if'' `App` (le' `App` Var &amp;quot;p&amp;quot; `App` Var &amp;quot;i&amp;quot;)&lt;br /&gt;
                `App` true'&lt;br /&gt;
                `App` ( if'' `App` (isZero' `App` (mod' `App` Var &amp;quot;p&amp;quot; `App` Var &amp;quot;i&amp;quot;))&lt;br /&gt;
                         `App` false'&lt;br /&gt;
                         `App` (Var &amp;quot;f&amp;quot; `App` Var &amp;quot;p&amp;quot; `App` (succ' `App` Var &amp;quot;i&amp;quot;))&lt;br /&gt;
                )&lt;br /&gt;
isPrimeHelp = fix' `App` isPrimeHelp'&lt;br /&gt;
isPrime = Lam &amp;quot;p&amp;quot; $ isPrimeHelp `App` Var &amp;quot;p&amp;quot; `App` two'&lt;br /&gt;
\end{code}&lt;br /&gt;
&lt;br /&gt;
Следующее простое число. $nextPrime'$~--- следующее, больше либо равное заданного,&lt;br /&gt;
$nextPrime$~--- следующее, большее заданного.&lt;br /&gt;
&lt;br /&gt;
\begin{code}&lt;br /&gt;
nextPrime'' = Lam &amp;quot;f&amp;quot; $ Lam &amp;quot;p&amp;quot; $ if'' `App` (isPrime `App` Var &amp;quot;p&amp;quot;)&lt;br /&gt;
                                   `App` Var &amp;quot;p&amp;quot;&lt;br /&gt;
                                   `App` (Var &amp;quot;f&amp;quot; `App` (succ' `App` Var &amp;quot;p&amp;quot;)) &lt;br /&gt;
nextPrime' = fix' `App` nextPrime''&lt;br /&gt;
nextPrime = Lam &amp;quot;p&amp;quot; $ nextPrime' `App` (succ' `App` Var &amp;quot;p&amp;quot;)&lt;br /&gt;
\end{code}&lt;br /&gt;
&lt;br /&gt;
$ithPrimeStep$ пропрыгает $i$ простых чисел вперёд. $ithPrime$ принимает число&lt;br /&gt;
$i$ и пропрыгивает столько простых чисел вперёд, начиная с двойки.&lt;br /&gt;
&lt;br /&gt;
\begin{code}&lt;br /&gt;
ithPrimeStep' = Lam &amp;quot;f&amp;quot; $ Lam &amp;quot;p&amp;quot; $ Lam &amp;quot;i&amp;quot; $ if'' `App` (isZero' `App` Var &amp;quot;i&amp;quot;)&lt;br /&gt;
                `App` Var &amp;quot;p&amp;quot;&lt;br /&gt;
                `App` (Var &amp;quot;f&amp;quot; `App` (nextPrime `App` Var &amp;quot;p&amp;quot;) `App` (pred' `App` Var &amp;quot;i&amp;quot;))&lt;br /&gt;
ithPrimeStep = fix' `App` ithPrimeStep'&lt;br /&gt;
ithPrime = Lam &amp;quot;i&amp;quot; $ ithPrimeStep `App` two' `App` Var &amp;quot;i&amp;quot;&lt;br /&gt;
\end{code}&lt;br /&gt;
&lt;br /&gt;
...и всего через 314007 $\beta$-редукций вы узнаете, что третье простое число~---&lt;br /&gt;
семь!&lt;br /&gt;
&lt;br /&gt;
\subsection{Списки}&lt;br /&gt;
&lt;br /&gt;
Для работы со списками чисел нам понадобятся следующие функции:&lt;br /&gt;
\begin{itemize}&lt;br /&gt;
    \item $empty$~--- возвращает пустой список&lt;br /&gt;
    \item $cons$~--- принимает первый элемент и оставшийся список, склеивает их&lt;br /&gt;
    \item $head$~--- вернуть голову списка&lt;br /&gt;
    \item $tail$~--- вернуть хвост списка&lt;br /&gt;
\end{itemize}&lt;br /&gt;
&lt;br /&gt;
Список будем хранить в следующем виде: $\langle len, p_1^{a_1}p_2^{a_2}\ldots p_{len}^{a_{len}} \rangle$. При этом, голова списка будет храниться как показатель степени при $p_{len}$.&lt;br /&gt;
&lt;br /&gt;
\begin{code}&lt;br /&gt;
empty = pair' `App` zero' `App` one'&lt;br /&gt;
cons = Lam &amp;quot;h&amp;quot; $ Lam &amp;quot;t&amp;quot; $ pair' `App` (succ' `App` (fst'' `App` Var &amp;quot;t&amp;quot;))&lt;br /&gt;
        `App` (mult' `App` (snd'' `App` Var &amp;quot;t&amp;quot;) `App` (power' &lt;br /&gt;
                `App` (ithPrime `App` (fst'' `App` Var &amp;quot;t&amp;quot;))&lt;br /&gt;
                `App` Var &amp;quot;h&amp;quot;&lt;br /&gt;
        ))&lt;br /&gt;
head = Lam &amp;quot;list&amp;quot; $ getExponent `App` (snd'' `App` Var &amp;quot;list&amp;quot;)&lt;br /&gt;
                                `App` (ithPrime `App` (pred' `App` (fst'' `App` Var &amp;quot;list&amp;quot;)))&lt;br /&gt;
&lt;br /&gt;
tail = Lam &amp;quot;list&amp;quot; $ pair' `App` (pred' `App` (fst'' `App` Var &amp;quot;list&amp;quot;))&lt;br /&gt;
                          `App` (eliminateMultiplier `App` (snd'' `App` Var &amp;quot;list&amp;quot;)&lt;br /&gt;
                                  `App` (ithPrime `App` (pred' `App` (fst'' `App` Var &amp;quot;list&amp;quot; )))&lt;br /&gt;
                                )&lt;br /&gt;
&lt;br /&gt;
eliminateMultiplier' = Lam &amp;quot;f&amp;quot; $ Lam &amp;quot;n&amp;quot; $ Lam &amp;quot;m&amp;quot; $ if'' `App` (isZero' `App` (mod' `App` Var &amp;quot;n&amp;quot; `App` Var &amp;quot;m&amp;quot;))&lt;br /&gt;
                        `App` (Var &amp;quot;f&amp;quot; `App` (div' `App` Var &amp;quot;n&amp;quot; `App` Var &amp;quot;m&amp;quot;) `App` Var &amp;quot;m&amp;quot;)&lt;br /&gt;
                        `App` Var &amp;quot;n&amp;quot;&lt;br /&gt;
eliminateMultiplier = fix' `App` eliminateMultiplier'&lt;br /&gt;
&lt;br /&gt;
getExponent' = Lam &amp;quot;f&amp;quot; $ Lam &amp;quot;n&amp;quot; $ Lam &amp;quot;m&amp;quot; $ if'' `App` (isZero' `App` (mod' `App` Var &amp;quot;n&amp;quot; `App` Var &amp;quot;m&amp;quot;))&lt;br /&gt;
                `App` (succ' `App`(Var &amp;quot;f&amp;quot; `App` (div' `App` Var &amp;quot;n&amp;quot; `App` Var &amp;quot;m&amp;quot;) `App` Var &amp;quot;m&amp;quot;))&lt;br /&gt;
                `App` zero'&lt;br /&gt;
getExponent = fix' `App` getExponent'&lt;br /&gt;
\end{code}&lt;br /&gt;
&lt;br /&gt;
На основе этого всего уже можно реализовать эмулятор машины тьюринга:&lt;br /&gt;
с помощью пар, списков чисел можно хранить состояния. С помощью рекурсии можно&lt;br /&gt;
обрабатывать переходы. Входная строка будет даваться, например, закодированной&lt;br /&gt;
аналогично списку: пара из длины и числа, характеризующего список степенями &lt;br /&gt;
простых. Я бы продолжил это писать, но уже на операции $head [1, 2]$ я не &lt;br /&gt;
дождался окончания выполнения. Скорость лямбда-исчисления как вычислителя&lt;br /&gt;
печальна.  &lt;br /&gt;
&lt;br /&gt;
\ignore{&lt;br /&gt;
\begin{code}&lt;br /&gt;
&lt;br /&gt;
four' = norm $ succ' `App` three'&lt;br /&gt;
five' = norm $ succ' `App` four'&lt;br /&gt;
&lt;br /&gt;
list2 = norm $ cons `App` one' `App` empty&lt;br /&gt;
list32 = cons `App` zero' `App` list2&lt;br /&gt;
&lt;br /&gt;
normFiveFact = normIO 0 $ fact' `App` five'&lt;br /&gt;
&lt;br /&gt;
-- fiftysix = mult twentyeight two&lt;br /&gt;
-- fiftyfive = pred fiftysix&lt;br /&gt;
-- six = pred seven&lt;br /&gt;
&lt;br /&gt;
--main = do print $ fiftysix (+1) 0&lt;br /&gt;
main = do&lt;br /&gt;
 --   f &amp;lt;- normIO 0 $ ithPrime `App` three'&lt;br /&gt;
 --   f &amp;lt;- normIO 0 $ getExponent `App` (norm $ plus' `App` four' `App` four') `App` two'&lt;br /&gt;
    f &amp;lt;- normIO 0 $ head `App` (tail `App` list32)&lt;br /&gt;
    print $ toInt f&lt;br /&gt;
\end{code}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
\end{document}&lt;br /&gt;
[http://rain.ifmo.ru/~komarov/Turing.lhs]&lt;/div&gt;</summary>
		<author><name>194.85.160.130</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D0%BE%D0%B8%D1%81%D0%BA_%D0%BD%D0%B0%D0%B8%D0%B1%D0%BE%D0%BB%D1%8C%D1%88%D0%B5%D0%B9_%D0%BE%D0%B1%D1%89%D0%B5%D0%B9_%D0%BF%D0%BE%D0%B4%D1%81%D1%82%D1%80%D0%BE%D0%BA%D0%B8_%D0%B4%D0%B2%D1%83%D1%85_%D1%81%D1%82%D1%80%D0%BE%D0%BA_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%85%D0%B5%D1%88%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F&amp;diff=27206</id>
		<title>Поиск наибольшей общей подстроки двух строк с использованием хеширования</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D0%BE%D0%B8%D1%81%D0%BA_%D0%BD%D0%B0%D0%B8%D0%B1%D0%BE%D0%BB%D1%8C%D1%88%D0%B5%D0%B9_%D0%BE%D0%B1%D1%89%D0%B5%D0%B9_%D0%BF%D0%BE%D0%B4%D1%81%D1%82%D1%80%D0%BE%D0%BA%D0%B8_%D0%B4%D0%B2%D1%83%D1%85_%D1%81%D1%82%D1%80%D0%BE%D0%BA_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%85%D0%B5%D1%88%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F&amp;diff=27206"/>
				<updated>2012-09-12T14:00:39Z</updated>
		
		<summary type="html">&lt;p&gt;194.85.160.130: /* Псевдокод */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Постановка задачи==&lt;br /&gt;
Имеются строки &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; такие, что элементы этих строк  &amp;lt;tex&amp;gt;-&amp;lt;/tex&amp;gt; символы из конечного алфавита &amp;lt;tex&amp;gt; \Sigma &amp;lt;/tex&amp;gt;. Говорят, что строка &amp;lt;tex&amp;gt;z[1 .. m]&amp;lt;/tex&amp;gt; является подстрокой строки &amp;lt;tex&amp;gt;s[1 .. n]&amp;lt;/tex&amp;gt;, если существует такой индекс &amp;lt;tex&amp;gt;k \in [0 .. n - m]&amp;lt;/tex&amp;gt;, что для любого &amp;lt;tex&amp;gt;i \in [1 .. m]&amp;lt;/tex&amp;gt; справедливо &amp;lt;tex&amp;gt;s[k + i] = z[i]&amp;lt;/tex&amp;gt;. Требуется найти такую строку &amp;lt;tex&amp;gt;z&amp;lt;/tex&amp;gt; максимальной длины, что &amp;lt;tex&amp;gt;z&amp;lt;/tex&amp;gt; является и подстрокой &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt;, и подстрокой &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Алгоритм==&lt;br /&gt;
Пусть длина наибольшей общей подстроки будет &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt;. Заметим, что у строк &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; обязательно найдется общая подстрока длины &amp;lt;tex&amp;gt;y \in [0 .. x]&amp;lt;/tex&amp;gt;, так как в качестве такой строки можно взять префикс наибольшей общей подстроки. Рассмотрим функцию &amp;lt;tex&amp;gt;f : [0 .. \min(|s|, |t|)] \rightarrow \{0, 1\}&amp;lt;/tex&amp;gt;, которая для &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; из области определения равна 1, если у строк &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; есть общая подстрока длины &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;, иначе она равна 0. Согласно замечанию, функция &amp;lt;tex&amp;gt;f&amp;lt;/tex&amp;gt; должна по мере возрастания &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; быть равной 1 до некоторого момента, а затем обращаться в 0. Собственно, максимальное значение, при котором функция принимает значение 1, является длиной наибольшей общей подстроки. Таким образом, требуется с помощью [[Целочисленный двоичный поиск|двоичного поиска]] найти это значение. В ходе работы придется проверять наличие общей подстроки заданной длины. Для этого будем использовать хеширование, чтобы улучшить асимптотику алгоритма. Алгоритм является эвристическим и может выдавать неверный ответ, так как совпадение хешей строк не гарантирует равенство строк. Поэтому нужно выполнить проверку нескольких случайных символов подстрок на совпадение, проиграв при этом по времени работы. Алгоритм работает следующим образом:&lt;br /&gt;
&lt;br /&gt;
1) У строки &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; хешируем подстроки заданной длины и полученные хеши записываем в Set.&lt;br /&gt;
&lt;br /&gt;
2) У строки &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; хешируем подстроки заданной длины и в случае совпадения хеша с элементом Set проверяем несколько случайных символов подстрок на совпадение.&lt;br /&gt;
&lt;br /&gt;
Хеширование будем производить так же, как и в [[Поиск подстроки в строке с использованием хеширования. Алгоритм Рабина-Карпа|алгоритме Рабина-Карпа]].&lt;br /&gt;
&lt;br /&gt;
== Псевдокод ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;f&amp;lt;/tex&amp;gt; — функция, описанная в алгоритме.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; — длина подстроки, найденная с помощью [[Целочисленный двоичный поиск|двоичного поиска]].&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;tex&amp;gt;f(i)&amp;lt;/tex&amp;gt;&lt;br /&gt;
  Записываем в &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; хеши подстрок строки &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; длины &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;;&lt;br /&gt;
  '''for''' &amp;lt;tex&amp;gt;j = 1...|t| - i + 1&amp;lt;/tex&amp;gt;&lt;br /&gt;
     Считаем хеш от подстроки &amp;lt;tex&amp;gt;t[j .. j + i - 1]&amp;lt;/tex&amp;gt;;&lt;br /&gt;
     '''if''' хеш содержится в &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;&lt;br /&gt;
        '''if''' совпали несколько случайных символов подстрок&lt;br /&gt;
           '''return''' 1;&lt;br /&gt;
        '''else'''&lt;br /&gt;
           '''continue''';&lt;br /&gt;
  '''return''' 0;&lt;br /&gt;
&lt;br /&gt;
==Время работы==&lt;br /&gt;
Проведем оценку асимптотики времени работы предложенного алгоритма. Посмотрим сколько нам потребуется действий на каждом шаге бинарного поиска. Во-первых, хеширование подстрок строки &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; и запись их в Set требует &amp;lt;tex&amp;gt;O(|s|)&amp;lt;/tex&amp;gt; шагов. Во-вторых, хеширование подстрок строки &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; и проверка их наличия в Set требует &amp;lt;tex&amp;gt;O(|t|)&amp;lt;/tex&amp;gt;. Проверка на совпадение нескольких символов подстрок требует константное время. Значит,на каждый шаг бинарного поиска требуется &amp;lt;tex&amp;gt;O(max(|s|, |t|))&amp;lt;/tex&amp;gt; действий. Заметим, что всего для завершения бинарного поиска потребуется &amp;lt;tex&amp;gt;O(\log(\min(|s|, |t|)))&amp;lt;/tex&amp;gt; шагов. Следовательно, суммарное время работы алгоритма будет &amp;lt;tex&amp;gt;O(\log(\min(|s|, |t|)) \cdot \max(|s|, |t|))&amp;lt;/tex&amp;gt; действий.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
* Кормен Т., Лейзерсон Ч., Ривест Р. Алгоритмы: построение и анализ. — 2-е изд. — М.: Издательский дом «Вильямс», 2007. — С. 1296.&lt;br /&gt;
&lt;br /&gt;
[[Категория:Алгоритмы и структуры данных]]&lt;br /&gt;
[[Категория:Поиск подстроки в строке]]&lt;/div&gt;</summary>
		<author><name>194.85.160.130</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D0%BE%D0%B8%D1%81%D0%BA_%D0%BD%D0%B0%D0%B8%D0%B1%D0%BE%D0%BB%D1%8C%D1%88%D0%B5%D0%B9_%D0%BE%D0%B1%D1%89%D0%B5%D0%B9_%D0%BF%D0%BE%D0%B4%D1%81%D1%82%D1%80%D0%BE%D0%BA%D0%B8_%D0%B4%D0%B2%D1%83%D1%85_%D1%81%D1%82%D1%80%D0%BE%D0%BA_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%85%D0%B5%D1%88%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F&amp;diff=27205</id>
		<title>Поиск наибольшей общей подстроки двух строк с использованием хеширования</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D0%BE%D0%B8%D1%81%D0%BA_%D0%BD%D0%B0%D0%B8%D0%B1%D0%BE%D0%BB%D1%8C%D1%88%D0%B5%D0%B9_%D0%BE%D0%B1%D1%89%D0%B5%D0%B9_%D0%BF%D0%BE%D0%B4%D1%81%D1%82%D1%80%D0%BE%D0%BA%D0%B8_%D0%B4%D0%B2%D1%83%D1%85_%D1%81%D1%82%D1%80%D0%BE%D0%BA_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%85%D0%B5%D1%88%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F&amp;diff=27205"/>
				<updated>2012-09-12T13:56:59Z</updated>
		
		<summary type="html">&lt;p&gt;194.85.160.130: /* Псевдокод */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Постановка задачи==&lt;br /&gt;
Имеются строки &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; такие, что элементы этих строк  &amp;lt;tex&amp;gt;-&amp;lt;/tex&amp;gt; символы из конечного алфавита &amp;lt;tex&amp;gt; \Sigma &amp;lt;/tex&amp;gt;. Говорят, что строка &amp;lt;tex&amp;gt;z[1 .. m]&amp;lt;/tex&amp;gt; является подстрокой строки &amp;lt;tex&amp;gt;s[1 .. n]&amp;lt;/tex&amp;gt;, если существует такой индекс &amp;lt;tex&amp;gt;k \in [0 .. n - m]&amp;lt;/tex&amp;gt;, что для любого &amp;lt;tex&amp;gt;i \in [1 .. m]&amp;lt;/tex&amp;gt; справедливо &amp;lt;tex&amp;gt;s[k + i] = z[i]&amp;lt;/tex&amp;gt;. Требуется найти такую строку &amp;lt;tex&amp;gt;z&amp;lt;/tex&amp;gt; максимальной длины, что &amp;lt;tex&amp;gt;z&amp;lt;/tex&amp;gt; является и подстрокой &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt;, и подстрокой &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Алгоритм==&lt;br /&gt;
Пусть длина наибольшей общей подстроки будет &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt;. Заметим, что у строк &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; обязательно найдется общая подстрока длины &amp;lt;tex&amp;gt;y \in [0 .. x]&amp;lt;/tex&amp;gt;, так как в качестве такой строки можно взять префикс наибольшей общей подстроки. Рассмотрим функцию &amp;lt;tex&amp;gt;f : [0 .. \min(|s|, |t|)] \rightarrow \{0, 1\}&amp;lt;/tex&amp;gt;, которая для &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; из области определения равна 1, если у строк &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; есть общая подстрока длины &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;, иначе она равна 0. Согласно замечанию, функция &amp;lt;tex&amp;gt;f&amp;lt;/tex&amp;gt; должна по мере возрастания &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; быть равной 1 до некоторого момента, а затем обращаться в 0. Собственно, максимальное значение, при котором функция принимает значение 1, является длиной наибольшей общей подстроки. Таким образом, требуется с помощью [[Целочисленный двоичный поиск|двоичного поиска]] найти это значение. В ходе работы придется проверять наличие общей подстроки заданной длины. Для этого будем использовать хеширование, чтобы улучшить асимптотику алгоритма. Алгоритм является эвристическим и может выдавать неверный ответ, так как совпадение хешей строк не гарантирует равенство строк. Поэтому нужно выполнить проверку нескольких случайных символов подстрок на совпадение, проиграв при этом по времени работы. Алгоритм работает следующим образом:&lt;br /&gt;
&lt;br /&gt;
1) У строки &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; хешируем подстроки заданной длины и полученные хеши записываем в Set.&lt;br /&gt;
&lt;br /&gt;
2) У строки &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; хешируем подстроки заданной длины и в случае совпадения хеша с элементом Set проверяем несколько случайных символов подстрок на совпадение.&lt;br /&gt;
&lt;br /&gt;
Хеширование будем производить так же, как и в [[Поиск подстроки в строке с использованием хеширования. Алгоритм Рабина-Карпа|алгоритме Рабина-Карпа]].&lt;br /&gt;
&lt;br /&gt;
== Псевдокод ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;f&amp;lt;/tex&amp;gt; — функция, описанная в алгоритме.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; — длина подстроки, найденная с помощью [[Целочисленный двоичный поиск|двоичного поиска]].&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;tex&amp;gt;f(i)&amp;lt;/tex&amp;gt;&lt;br /&gt;
  Записываем в &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; хеши подстрок строки &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; длины &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;;&lt;br /&gt;
  '''for''' &amp;lt;tex&amp;gt;j = 1...|t| - i + 1&amp;lt;/tex&amp;gt;&lt;br /&gt;
     Считаем хеш от подстроки &amp;lt;tex&amp;gt;t[j .. j + i - 1]&amp;lt;/tex&amp;gt;;&lt;br /&gt;
     '''if''' хеш содержится в &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;&lt;br /&gt;
       '''if''' совпали несколько случайных символов подстрок&lt;br /&gt;
          '''return''' 1;&lt;br /&gt;
       '''else'''&lt;br /&gt;
          '''continue''';&lt;br /&gt;
   '''return''' 0;&lt;br /&gt;
&lt;br /&gt;
==Время работы==&lt;br /&gt;
Проведем оценку асимптотики времени работы предложенного алгоритма. Посмотрим сколько нам потребуется действий на каждом шаге бинарного поиска. Во-первых, хеширование подстрок строки &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; и запись их в Set требует &amp;lt;tex&amp;gt;O(|s|)&amp;lt;/tex&amp;gt; шагов. Во-вторых, хеширование подстрок строки &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; и проверка их наличия в Set требует &amp;lt;tex&amp;gt;O(|t|)&amp;lt;/tex&amp;gt;. Проверка на совпадение нескольких символов подстрок требует константное время. Значит,на каждый шаг бинарного поиска требуется &amp;lt;tex&amp;gt;O(max(|s|, |t|))&amp;lt;/tex&amp;gt; действий. Заметим, что всего для завершения бинарного поиска потребуется &amp;lt;tex&amp;gt;O(\log(\min(|s|, |t|)))&amp;lt;/tex&amp;gt; шагов. Следовательно, суммарное время работы алгоритма будет &amp;lt;tex&amp;gt;O(\log(\min(|s|, |t|)) \cdot \max(|s|, |t|))&amp;lt;/tex&amp;gt; действий.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
* Кормен Т., Лейзерсон Ч., Ривест Р. Алгоритмы: построение и анализ. — 2-е изд. — М.: Издательский дом «Вильямс», 2007. — С. 1296.&lt;br /&gt;
&lt;br /&gt;
[[Категория:Алгоритмы и структуры данных]]&lt;br /&gt;
[[Категория:Поиск подстроки в строке]]&lt;/div&gt;</summary>
		<author><name>194.85.160.130</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D0%BE%D0%B8%D1%81%D0%BA_%D0%BD%D0%B0%D0%B8%D0%B1%D0%BE%D0%BB%D1%8C%D1%88%D0%B5%D0%B9_%D0%BE%D0%B1%D1%89%D0%B5%D0%B9_%D0%BF%D0%BE%D0%B4%D1%81%D1%82%D1%80%D0%BE%D0%BA%D0%B8_%D0%B4%D0%B2%D1%83%D1%85_%D1%81%D1%82%D1%80%D0%BE%D0%BA_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%85%D0%B5%D1%88%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F&amp;diff=27204</id>
		<title>Поиск наибольшей общей подстроки двух строк с использованием хеширования</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D0%BE%D0%B8%D1%81%D0%BA_%D0%BD%D0%B0%D0%B8%D0%B1%D0%BE%D0%BB%D1%8C%D1%88%D0%B5%D0%B9_%D0%BE%D0%B1%D1%89%D0%B5%D0%B9_%D0%BF%D0%BE%D0%B4%D1%81%D1%82%D1%80%D0%BE%D0%BA%D0%B8_%D0%B4%D0%B2%D1%83%D1%85_%D1%81%D1%82%D1%80%D0%BE%D0%BA_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%85%D0%B5%D1%88%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F&amp;diff=27204"/>
				<updated>2012-09-12T13:52:14Z</updated>
		
		<summary type="html">&lt;p&gt;194.85.160.130: /* Псевдокод */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Постановка задачи==&lt;br /&gt;
Имеются строки &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; такие, что элементы этих строк  &amp;lt;tex&amp;gt;-&amp;lt;/tex&amp;gt; символы из конечного алфавита &amp;lt;tex&amp;gt; \Sigma &amp;lt;/tex&amp;gt;. Говорят, что строка &amp;lt;tex&amp;gt;z[1 .. m]&amp;lt;/tex&amp;gt; является подстрокой строки &amp;lt;tex&amp;gt;s[1 .. n]&amp;lt;/tex&amp;gt;, если существует такой индекс &amp;lt;tex&amp;gt;k \in [0 .. n - m]&amp;lt;/tex&amp;gt;, что для любого &amp;lt;tex&amp;gt;i \in [1 .. m]&amp;lt;/tex&amp;gt; справедливо &amp;lt;tex&amp;gt;s[k + i] = z[i]&amp;lt;/tex&amp;gt;. Требуется найти такую строку &amp;lt;tex&amp;gt;z&amp;lt;/tex&amp;gt; максимальной длины, что &amp;lt;tex&amp;gt;z&amp;lt;/tex&amp;gt; является и подстрокой &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt;, и подстрокой &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Алгоритм==&lt;br /&gt;
Пусть длина наибольшей общей подстроки будет &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt;. Заметим, что у строк &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; обязательно найдется общая подстрока длины &amp;lt;tex&amp;gt;y \in [0 .. x]&amp;lt;/tex&amp;gt;, так как в качестве такой строки можно взять префикс наибольшей общей подстроки. Рассмотрим функцию &amp;lt;tex&amp;gt;f : [0 .. \min(|s|, |t|)] \rightarrow \{0, 1\}&amp;lt;/tex&amp;gt;, которая для &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; из области определения равна 1, если у строк &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; есть общая подстрока длины &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;, иначе она равна 0. Согласно замечанию, функция &amp;lt;tex&amp;gt;f&amp;lt;/tex&amp;gt; должна по мере возрастания &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; быть равной 1 до некоторого момента, а затем обращаться в 0. Собственно, максимальное значение, при котором функция принимает значение 1, является длиной наибольшей общей подстроки. Таким образом, требуется с помощью [[Целочисленный двоичный поиск|двоичного поиска]] найти это значение. В ходе работы придется проверять наличие общей подстроки заданной длины. Для этого будем использовать хеширование, чтобы улучшить асимптотику алгоритма. Алгоритм является эвристическим и может выдавать неверный ответ, так как совпадение хешей строк не гарантирует равенство строк. Поэтому нужно выполнить проверку нескольких случайных символов подстрок на совпадение, проиграв при этом по времени работы. Алгоритм работает следующим образом:&lt;br /&gt;
&lt;br /&gt;
1) У строки &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; хешируем подстроки заданной длины и полученные хеши записываем в Set.&lt;br /&gt;
&lt;br /&gt;
2) У строки &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; хешируем подстроки заданной длины и в случае совпадения хеша с элементом Set проверяем несколько случайных символов подстрок на совпадение.&lt;br /&gt;
&lt;br /&gt;
Хеширование будем производить так же, как и в [[Поиск подстроки в строке с использованием хеширования. Алгоритм Рабина-Карпа|алгоритме Рабина-Карпа]].&lt;br /&gt;
&lt;br /&gt;
== Псевдокод ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;f&amp;lt;/tex&amp;gt; — функция, описанная в алгоритме.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; — длина подстроки, найденная с помощью [[Целочисленный двоичный поиск|двоичного поиска]].&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;tex&amp;gt;f(i)&amp;lt;/tex&amp;gt;&lt;br /&gt;
  Записываем в &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; хеши подстрок строки &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; длины &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;;&lt;br /&gt;
  '''for''' &amp;lt;tex&amp;gt;j = 1...|t| - i + 1&amp;lt;/tex&amp;gt;&lt;br /&gt;
     Считаем хеш от подстоки &amp;lt;tex&amp;gt;t[j .. j + i - 1]&amp;lt;/tex&amp;gt;;&lt;br /&gt;
     '''if''' хеш содержится в &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;&lt;br /&gt;
       '''if''' совпали несколько случайных символов подстрок&lt;br /&gt;
          '''return''' 1;&lt;br /&gt;
       '''else'''&lt;br /&gt;
          '''continue''';&lt;br /&gt;
   '''return''' 0;&lt;br /&gt;
&lt;br /&gt;
==Время работы==&lt;br /&gt;
Проведем оценку асимптотики времени работы предложенного алгоритма. Посмотрим сколько нам потребуется действий на каждом шаге бинарного поиска. Во-первых, хеширование подстрок строки &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; и запись их в Set требует &amp;lt;tex&amp;gt;O(|s|)&amp;lt;/tex&amp;gt; шагов. Во-вторых, хеширование подстрок строки &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; и проверка их наличия в Set требует &amp;lt;tex&amp;gt;O(|t|)&amp;lt;/tex&amp;gt;. Проверка на совпадение нескольких символов подстрок требует константное время. Значит,на каждый шаг бинарного поиска требуется &amp;lt;tex&amp;gt;O(max(|s|, |t|))&amp;lt;/tex&amp;gt; действий. Заметим, что всего для завершения бинарного поиска потребуется &amp;lt;tex&amp;gt;O(\log(\min(|s|, |t|)))&amp;lt;/tex&amp;gt; шагов. Следовательно, суммарное время работы алгоритма будет &amp;lt;tex&amp;gt;O(\log(\min(|s|, |t|)) \cdot \max(|s|, |t|))&amp;lt;/tex&amp;gt; действий.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
* Кормен Т., Лейзерсон Ч., Ривест Р. Алгоритмы: построение и анализ. — 2-е изд. — М.: Издательский дом «Вильямс», 2007. — С. 1296.&lt;br /&gt;
&lt;br /&gt;
[[Категория:Алгоритмы и структуры данных]]&lt;br /&gt;
[[Категория:Поиск подстроки в строке]]&lt;/div&gt;</summary>
		<author><name>194.85.160.130</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D0%BE%D0%B8%D1%81%D0%BA_%D0%BD%D0%B0%D0%B8%D0%B1%D0%BE%D0%BB%D1%8C%D1%88%D0%B5%D0%B9_%D0%BE%D0%B1%D1%89%D0%B5%D0%B9_%D0%BF%D0%BE%D0%B4%D1%81%D1%82%D1%80%D0%BE%D0%BA%D0%B8_%D0%B4%D0%B2%D1%83%D1%85_%D1%81%D1%82%D1%80%D0%BE%D0%BA_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%85%D0%B5%D1%88%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F&amp;diff=27203</id>
		<title>Поиск наибольшей общей подстроки двух строк с использованием хеширования</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D0%BE%D0%B8%D1%81%D0%BA_%D0%BD%D0%B0%D0%B8%D0%B1%D0%BE%D0%BB%D1%8C%D1%88%D0%B5%D0%B9_%D0%BE%D0%B1%D1%89%D0%B5%D0%B9_%D0%BF%D0%BE%D0%B4%D1%81%D1%82%D1%80%D0%BE%D0%BA%D0%B8_%D0%B4%D0%B2%D1%83%D1%85_%D1%81%D1%82%D1%80%D0%BE%D0%BA_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%85%D0%B5%D1%88%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F&amp;diff=27203"/>
				<updated>2012-09-12T13:51:42Z</updated>
		
		<summary type="html">&lt;p&gt;194.85.160.130: /* Псевдокод */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Постановка задачи==&lt;br /&gt;
Имеются строки &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; такие, что элементы этих строк  &amp;lt;tex&amp;gt;-&amp;lt;/tex&amp;gt; символы из конечного алфавита &amp;lt;tex&amp;gt; \Sigma &amp;lt;/tex&amp;gt;. Говорят, что строка &amp;lt;tex&amp;gt;z[1 .. m]&amp;lt;/tex&amp;gt; является подстрокой строки &amp;lt;tex&amp;gt;s[1 .. n]&amp;lt;/tex&amp;gt;, если существует такой индекс &amp;lt;tex&amp;gt;k \in [0 .. n - m]&amp;lt;/tex&amp;gt;, что для любого &amp;lt;tex&amp;gt;i \in [1 .. m]&amp;lt;/tex&amp;gt; справедливо &amp;lt;tex&amp;gt;s[k + i] = z[i]&amp;lt;/tex&amp;gt;. Требуется найти такую строку &amp;lt;tex&amp;gt;z&amp;lt;/tex&amp;gt; максимальной длины, что &amp;lt;tex&amp;gt;z&amp;lt;/tex&amp;gt; является и подстрокой &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt;, и подстрокой &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Алгоритм==&lt;br /&gt;
Пусть длина наибольшей общей подстроки будет &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt;. Заметим, что у строк &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; обязательно найдется общая подстрока длины &amp;lt;tex&amp;gt;y \in [0 .. x]&amp;lt;/tex&amp;gt;, так как в качестве такой строки можно взять префикс наибольшей общей подстроки. Рассмотрим функцию &amp;lt;tex&amp;gt;f : [0 .. \min(|s|, |t|)] \rightarrow \{0, 1\}&amp;lt;/tex&amp;gt;, которая для &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; из области определения равна 1, если у строк &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; есть общая подстрока длины &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;, иначе она равна 0. Согласно замечанию, функция &amp;lt;tex&amp;gt;f&amp;lt;/tex&amp;gt; должна по мере возрастания &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; быть равной 1 до некоторого момента, а затем обращаться в 0. Собственно, максимальное значение, при котором функция принимает значение 1, является длиной наибольшей общей подстроки. Таким образом, требуется с помощью [[Целочисленный двоичный поиск|двоичного поиска]] найти это значение. В ходе работы придется проверять наличие общей подстроки заданной длины. Для этого будем использовать хеширование, чтобы улучшить асимптотику алгоритма. Алгоритм является эвристическим и может выдавать неверный ответ, так как совпадение хешей строк не гарантирует равенство строк. Поэтому нужно выполнить проверку нескольких случайных символов подстрок на совпадение, проиграв при этом по времени работы. Алгоритм работает следующим образом:&lt;br /&gt;
&lt;br /&gt;
1) У строки &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; хешируем подстроки заданной длины и полученные хеши записываем в Set.&lt;br /&gt;
&lt;br /&gt;
2) У строки &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; хешируем подстроки заданной длины и в случае совпадения хеша с элементом Set проверяем несколько случайных символов подстрок на совпадение.&lt;br /&gt;
&lt;br /&gt;
Хеширование будем производить так же, как и в [[Поиск подстроки в строке с использованием хеширования. Алгоритм Рабина-Карпа|алгоритме Рабина-Карпа]].&lt;br /&gt;
&lt;br /&gt;
== Псевдокод ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;f&amp;lt;/tex&amp;gt; — функция, описанная в алгоритме.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; — длина подстроки, найденная с помощью [[Целочисленный двоичный поиск|двоичного поиска]].&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;tex&amp;gt;f(i)&amp;lt;/tex&amp;gt;&lt;br /&gt;
    Записываем в &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; хеши подстрок строки &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; длины &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;;&lt;br /&gt;
    '''for''' &amp;lt;tex&amp;gt;j = 1...|t| - i + 1&amp;lt;/tex&amp;gt;&lt;br /&gt;
       Считаем хеш от подстоки &amp;lt;tex&amp;gt;t[j .. j + i - 1]&amp;lt;/tex&amp;gt;;&lt;br /&gt;
       '''if''' хеш содержится в &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;&lt;br /&gt;
         '''if''' совпали несколько случайных символов подстрок&lt;br /&gt;
            '''return''' 1;&lt;br /&gt;
         '''else'''&lt;br /&gt;
            '''continue''';&lt;br /&gt;
     '''return''' 0;&lt;br /&gt;
&lt;br /&gt;
==Время работы==&lt;br /&gt;
Проведем оценку асимптотики времени работы предложенного алгоритма. Посмотрим сколько нам потребуется действий на каждом шаге бинарного поиска. Во-первых, хеширование подстрок строки &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; и запись их в Set требует &amp;lt;tex&amp;gt;O(|s|)&amp;lt;/tex&amp;gt; шагов. Во-вторых, хеширование подстрок строки &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; и проверка их наличия в Set требует &amp;lt;tex&amp;gt;O(|t|)&amp;lt;/tex&amp;gt;. Проверка на совпадение нескольких символов подстрок требует константное время. Значит,на каждый шаг бинарного поиска требуется &amp;lt;tex&amp;gt;O(max(|s|, |t|))&amp;lt;/tex&amp;gt; действий. Заметим, что всего для завершения бинарного поиска потребуется &amp;lt;tex&amp;gt;O(\log(\min(|s|, |t|)))&amp;lt;/tex&amp;gt; шагов. Следовательно, суммарное время работы алгоритма будет &amp;lt;tex&amp;gt;O(\log(\min(|s|, |t|)) \cdot \max(|s|, |t|))&amp;lt;/tex&amp;gt; действий.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
* Кормен Т., Лейзерсон Ч., Ривест Р. Алгоритмы: построение и анализ. — 2-е изд. — М.: Издательский дом «Вильямс», 2007. — С. 1296.&lt;br /&gt;
&lt;br /&gt;
[[Категория:Алгоритмы и структуры данных]]&lt;br /&gt;
[[Категория:Поиск подстроки в строке]]&lt;/div&gt;</summary>
		<author><name>194.85.160.130</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D0%BE%D0%B8%D1%81%D0%BA_%D0%BD%D0%B0%D0%B8%D0%B1%D0%BE%D0%BB%D1%8C%D1%88%D0%B5%D0%B9_%D0%BE%D0%B1%D1%89%D0%B5%D0%B9_%D0%BF%D0%BE%D0%B4%D1%81%D1%82%D1%80%D0%BE%D0%BA%D0%B8_%D0%B4%D0%B2%D1%83%D1%85_%D1%81%D1%82%D1%80%D0%BE%D0%BA_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%85%D0%B5%D1%88%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F&amp;diff=27202</id>
		<title>Поиск наибольшей общей подстроки двух строк с использованием хеширования</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D0%BE%D0%B8%D1%81%D0%BA_%D0%BD%D0%B0%D0%B8%D0%B1%D0%BE%D0%BB%D1%8C%D1%88%D0%B5%D0%B9_%D0%BE%D0%B1%D1%89%D0%B5%D0%B9_%D0%BF%D0%BE%D0%B4%D1%81%D1%82%D1%80%D0%BE%D0%BA%D0%B8_%D0%B4%D0%B2%D1%83%D1%85_%D1%81%D1%82%D1%80%D0%BE%D0%BA_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%85%D0%B5%D1%88%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F&amp;diff=27202"/>
				<updated>2012-09-12T13:48:01Z</updated>
		
		<summary type="html">&lt;p&gt;194.85.160.130: /* Псевдокод */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Постановка задачи==&lt;br /&gt;
Имеются строки &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; такие, что элементы этих строк  &amp;lt;tex&amp;gt;-&amp;lt;/tex&amp;gt; символы из конечного алфавита &amp;lt;tex&amp;gt; \Sigma &amp;lt;/tex&amp;gt;. Говорят, что строка &amp;lt;tex&amp;gt;z[1 .. m]&amp;lt;/tex&amp;gt; является подстрокой строки &amp;lt;tex&amp;gt;s[1 .. n]&amp;lt;/tex&amp;gt;, если существует такой индекс &amp;lt;tex&amp;gt;k \in [0 .. n - m]&amp;lt;/tex&amp;gt;, что для любого &amp;lt;tex&amp;gt;i \in [1 .. m]&amp;lt;/tex&amp;gt; справедливо &amp;lt;tex&amp;gt;s[k + i] = z[i]&amp;lt;/tex&amp;gt;. Требуется найти такую строку &amp;lt;tex&amp;gt;z&amp;lt;/tex&amp;gt; максимальной длины, что &amp;lt;tex&amp;gt;z&amp;lt;/tex&amp;gt; является и подстрокой &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt;, и подстрокой &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Алгоритм==&lt;br /&gt;
Пусть длина наибольшей общей подстроки будет &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt;. Заметим, что у строк &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; обязательно найдется общая подстрока длины &amp;lt;tex&amp;gt;y \in [0 .. x]&amp;lt;/tex&amp;gt;, так как в качестве такой строки можно взять префикс наибольшей общей подстроки. Рассмотрим функцию &amp;lt;tex&amp;gt;f : [0 .. \min(|s|, |t|)] \rightarrow \{0, 1\}&amp;lt;/tex&amp;gt;, которая для &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; из области определения равна 1, если у строк &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; есть общая подстрока длины &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;, иначе она равна 0. Согласно замечанию, функция &amp;lt;tex&amp;gt;f&amp;lt;/tex&amp;gt; должна по мере возрастания &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; быть равной 1 до некоторого момента, а затем обращаться в 0. Собственно, максимальное значение, при котором функция принимает значение 1, является длиной наибольшей общей подстроки. Таким образом, требуется с помощью [[Целочисленный двоичный поиск|двоичного поиска]] найти это значение. В ходе работы придется проверять наличие общей подстроки заданной длины. Для этого будем использовать хеширование, чтобы улучшить асимптотику алгоритма. Алгоритм является эвристическим и может выдавать неверный ответ, так как совпадение хешей строк не гарантирует равенство строк. Поэтому нужно выполнить проверку нескольких случайных символов подстрок на совпадение, проиграв при этом по времени работы. Алгоритм работает следующим образом:&lt;br /&gt;
&lt;br /&gt;
1) У строки &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; хешируем подстроки заданной длины и полученные хеши записываем в Set.&lt;br /&gt;
&lt;br /&gt;
2) У строки &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; хешируем подстроки заданной длины и в случае совпадения хеша с элементом Set проверяем несколько случайных символов подстрок на совпадение.&lt;br /&gt;
&lt;br /&gt;
Хеширование будем производить так же, как и в [[Поиск подстроки в строке с использованием хеширования. Алгоритм Рабина-Карпа|алгоритме Рабина-Карпа]].&lt;br /&gt;
&lt;br /&gt;
== Псевдокод ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;f&amp;lt;/tex&amp;gt; — функция, описанная в алгоритме.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; — длина подстроки, найденная с помощью [[Целочисленный двоичный поиск|двоичного поиска]].&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;tex&amp;gt;f(i)&amp;lt;/tex&amp;gt;&lt;br /&gt;
 Записываем в &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; хеши подстрок строки &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; длины &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;;&lt;br /&gt;
 '''for''' &amp;lt;tex&amp;gt;j = 1...|t| - i + 1&amp;lt;/tex&amp;gt;&lt;br /&gt;
    Считаем хеш от подстоки &amp;lt;tex&amp;gt;t[j .. j + i - 1]&amp;lt;/tex&amp;gt;;&lt;br /&gt;
    '''if''' хеш содержится в &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;&lt;br /&gt;
      '''if''' совпали несколько случайных символов подстрок&lt;br /&gt;
         '''return''' 1;&lt;br /&gt;
      '''else'''&lt;br /&gt;
         '''continue''';&lt;br /&gt;
 '''return''' 0;&lt;br /&gt;
&lt;br /&gt;
==Время работы==&lt;br /&gt;
Проведем оценку асимптотики времени работы предложенного алгоритма. Посмотрим сколько нам потребуется действий на каждом шаге бинарного поиска. Во-первых, хеширование подстрок строки &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; и запись их в Set требует &amp;lt;tex&amp;gt;O(|s|)&amp;lt;/tex&amp;gt; шагов. Во-вторых, хеширование подстрок строки &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; и проверка их наличия в Set требует &amp;lt;tex&amp;gt;O(|t|)&amp;lt;/tex&amp;gt;. Проверка на совпадение нескольких символов подстрок требует константное время. Значит,на каждый шаг бинарного поиска требуется &amp;lt;tex&amp;gt;O(max(|s|, |t|))&amp;lt;/tex&amp;gt; действий. Заметим, что всего для завершения бинарного поиска потребуется &amp;lt;tex&amp;gt;O(\log(\min(|s|, |t|)))&amp;lt;/tex&amp;gt; шагов. Следовательно, суммарное время работы алгоритма будет &amp;lt;tex&amp;gt;O(\log(\min(|s|, |t|)) \cdot \max(|s|, |t|))&amp;lt;/tex&amp;gt; действий.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
* Кормен Т., Лейзерсон Ч., Ривест Р. Алгоритмы: построение и анализ. — 2-е изд. — М.: Издательский дом «Вильямс», 2007. — С. 1296.&lt;br /&gt;
&lt;br /&gt;
[[Категория:Алгоритмы и структуры данных]]&lt;br /&gt;
[[Категория:Поиск подстроки в строке]]&lt;/div&gt;</summary>
		<author><name>194.85.160.130</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D0%BE%D0%B8%D1%81%D0%BA_%D0%BD%D0%B0%D0%B8%D0%B1%D0%BE%D0%BB%D1%8C%D1%88%D0%B5%D0%B9_%D0%BE%D0%B1%D1%89%D0%B5%D0%B9_%D0%BF%D0%BE%D0%B4%D1%81%D1%82%D1%80%D0%BE%D0%BA%D0%B8_%D0%B4%D0%B2%D1%83%D1%85_%D1%81%D1%82%D1%80%D0%BE%D0%BA_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%85%D0%B5%D1%88%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F&amp;diff=27201</id>
		<title>Поиск наибольшей общей подстроки двух строк с использованием хеширования</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D0%BE%D0%B8%D1%81%D0%BA_%D0%BD%D0%B0%D0%B8%D0%B1%D0%BE%D0%BB%D1%8C%D1%88%D0%B5%D0%B9_%D0%BE%D0%B1%D1%89%D0%B5%D0%B9_%D0%BF%D0%BE%D0%B4%D1%81%D1%82%D1%80%D0%BE%D0%BA%D0%B8_%D0%B4%D0%B2%D1%83%D1%85_%D1%81%D1%82%D1%80%D0%BE%D0%BA_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%85%D0%B5%D1%88%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F&amp;diff=27201"/>
				<updated>2012-09-12T13:41:36Z</updated>
		
		<summary type="html">&lt;p&gt;194.85.160.130: /* Псевдокод */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Постановка задачи==&lt;br /&gt;
Имеются строки &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; такие, что элементы этих строк  &amp;lt;tex&amp;gt;-&amp;lt;/tex&amp;gt; символы из конечного алфавита &amp;lt;tex&amp;gt; \Sigma &amp;lt;/tex&amp;gt;. Говорят, что строка &amp;lt;tex&amp;gt;z[1 .. m]&amp;lt;/tex&amp;gt; является подстрокой строки &amp;lt;tex&amp;gt;s[1 .. n]&amp;lt;/tex&amp;gt;, если существует такой индекс &amp;lt;tex&amp;gt;k \in [0 .. n - m]&amp;lt;/tex&amp;gt;, что для любого &amp;lt;tex&amp;gt;i \in [1 .. m]&amp;lt;/tex&amp;gt; справедливо &amp;lt;tex&amp;gt;s[k + i] = z[i]&amp;lt;/tex&amp;gt;. Требуется найти такую строку &amp;lt;tex&amp;gt;z&amp;lt;/tex&amp;gt; максимальной длины, что &amp;lt;tex&amp;gt;z&amp;lt;/tex&amp;gt; является и подстрокой &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt;, и подстрокой &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Алгоритм==&lt;br /&gt;
Пусть длина наибольшей общей подстроки будет &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt;. Заметим, что у строк &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; обязательно найдется общая подстрока длины &amp;lt;tex&amp;gt;y \in [0 .. x]&amp;lt;/tex&amp;gt;, так как в качестве такой строки можно взять префикс наибольшей общей подстроки. Рассмотрим функцию &amp;lt;tex&amp;gt;f : [0 .. \min(|s|, |t|)] \rightarrow \{0, 1\}&amp;lt;/tex&amp;gt;, которая для &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; из области определения равна 1, если у строк &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; есть общая подстрока длины &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;, иначе она равна 0. Согласно замечанию, функция &amp;lt;tex&amp;gt;f&amp;lt;/tex&amp;gt; должна по мере возрастания &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; быть равной 1 до некоторого момента, а затем обращаться в 0. Собственно, максимальное значение, при котором функция принимает значение 1, является длиной наибольшей общей подстроки. Таким образом, требуется с помощью [[Целочисленный двоичный поиск|двоичного поиска]] найти это значение. В ходе работы придется проверять наличие общей подстроки заданной длины. Для этого будем использовать хеширование, чтобы улучшить асимптотику алгоритма. Алгоритм является эвристическим и может выдавать неверный ответ, так как совпадение хешей строк не гарантирует равенство строк. Поэтому нужно выполнить проверку нескольких случайных символов подстрок на совпадение, проиграв при этом по времени работы. Алгоритм работает следующим образом:&lt;br /&gt;
&lt;br /&gt;
1) У строки &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; хешируем подстроки заданной длины и полученные хеши записываем в Set.&lt;br /&gt;
&lt;br /&gt;
2) У строки &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; хешируем подстроки заданной длины и в случае совпадения хеша с элементом Set проверяем несколько случайных символов подстрок на совпадение.&lt;br /&gt;
&lt;br /&gt;
Хеширование будем производить так же, как и в [[Поиск подстроки в строке с использованием хеширования. Алгоритм Рабина-Карпа|алгоритме Рабина-Карпа]].&lt;br /&gt;
&lt;br /&gt;
== Псевдокод ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;f&amp;lt;/tex&amp;gt; — функция, описанная в алгоритме.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; — длина подстроки, найденная с помощью [[Целочисленный двоичный поиск|двоичного поиска]].&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;tex&amp;gt;f(i)&amp;lt;/tex&amp;gt;&lt;br /&gt;
 Записываем в &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; хэши подстрок строки &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; длины &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;;&lt;br /&gt;
 '''for''' &amp;lt;tex&amp;gt;j = 1...|t| - i + 1&amp;lt;/tex&amp;gt;&lt;br /&gt;
    Считаем хэш от подстоки &amp;lt;tex&amp;gt;t[j .. j + i - 1]&amp;lt;/tex&amp;gt;;&lt;br /&gt;
    '''if''' хэш содержится в &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;&lt;br /&gt;
      '''if''' совпали несколько случайных символов подсток&lt;br /&gt;
         return 1;&lt;br /&gt;
      '''else'''&lt;br /&gt;
         continue;&lt;br /&gt;
 return 0;&lt;br /&gt;
&lt;br /&gt;
==Время работы==&lt;br /&gt;
Проведем оценку асимптотики времени работы предложенного алгоритма. Посмотрим сколько нам потребуется действий на каждом шаге бинарного поиска. Во-первых, хеширование подстрок строки &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; и запись их в Set требует &amp;lt;tex&amp;gt;O(|s|)&amp;lt;/tex&amp;gt; шагов. Во-вторых, хеширование подстрок строки &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; и проверка их наличия в Set требует &amp;lt;tex&amp;gt;O(|t|)&amp;lt;/tex&amp;gt;. Проверка на совпадение нескольких символов подстрок требует константное время. Значит,на каждый шаг бинарного поиска требуется &amp;lt;tex&amp;gt;O(max(|s|, |t|))&amp;lt;/tex&amp;gt; действий. Заметим, что всего для завершения бинарного поиска потребуется &amp;lt;tex&amp;gt;O(\log(\min(|s|, |t|)))&amp;lt;/tex&amp;gt; шагов. Следовательно, суммарное время работы алгоритма будет &amp;lt;tex&amp;gt;O(\log(\min(|s|, |t|)) \cdot \max(|s|, |t|))&amp;lt;/tex&amp;gt; действий.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
* Кормен Т., Лейзерсон Ч., Ривест Р. Алгоритмы: построение и анализ. — 2-е изд. — М.: Издательский дом «Вильямс», 2007. — С. 1296.&lt;br /&gt;
&lt;br /&gt;
[[Категория:Алгоритмы и структуры данных]]&lt;br /&gt;
[[Категория:Поиск подстроки в строке]]&lt;/div&gt;</summary>
		<author><name>194.85.160.130</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D0%BE%D0%B8%D1%81%D0%BA_%D0%BD%D0%B0%D0%B8%D0%B1%D0%BE%D0%BB%D1%8C%D1%88%D0%B5%D0%B9_%D0%BE%D0%B1%D1%89%D0%B5%D0%B9_%D0%BF%D0%BE%D0%B4%D1%81%D1%82%D1%80%D0%BE%D0%BA%D0%B8_%D0%B4%D0%B2%D1%83%D1%85_%D1%81%D1%82%D1%80%D0%BE%D0%BA_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%85%D0%B5%D1%88%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F&amp;diff=27200</id>
		<title>Поиск наибольшей общей подстроки двух строк с использованием хеширования</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D0%BE%D0%B8%D1%81%D0%BA_%D0%BD%D0%B0%D0%B8%D0%B1%D0%BE%D0%BB%D1%8C%D1%88%D0%B5%D0%B9_%D0%BE%D0%B1%D1%89%D0%B5%D0%B9_%D0%BF%D0%BE%D0%B4%D1%81%D1%82%D1%80%D0%BE%D0%BA%D0%B8_%D0%B4%D0%B2%D1%83%D1%85_%D1%81%D1%82%D1%80%D0%BE%D0%BA_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%85%D0%B5%D1%88%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F&amp;diff=27200"/>
				<updated>2012-09-12T13:40:00Z</updated>
		
		<summary type="html">&lt;p&gt;194.85.160.130: /* Псевдокод */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Постановка задачи==&lt;br /&gt;
Имеются строки &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; такие, что элементы этих строк  &amp;lt;tex&amp;gt;-&amp;lt;/tex&amp;gt; символы из конечного алфавита &amp;lt;tex&amp;gt; \Sigma &amp;lt;/tex&amp;gt;. Говорят, что строка &amp;lt;tex&amp;gt;z[1 .. m]&amp;lt;/tex&amp;gt; является подстрокой строки &amp;lt;tex&amp;gt;s[1 .. n]&amp;lt;/tex&amp;gt;, если существует такой индекс &amp;lt;tex&amp;gt;k \in [0 .. n - m]&amp;lt;/tex&amp;gt;, что для любого &amp;lt;tex&amp;gt;i \in [1 .. m]&amp;lt;/tex&amp;gt; справедливо &amp;lt;tex&amp;gt;s[k + i] = z[i]&amp;lt;/tex&amp;gt;. Требуется найти такую строку &amp;lt;tex&amp;gt;z&amp;lt;/tex&amp;gt; максимальной длины, что &amp;lt;tex&amp;gt;z&amp;lt;/tex&amp;gt; является и подстрокой &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt;, и подстрокой &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Алгоритм==&lt;br /&gt;
Пусть длина наибольшей общей подстроки будет &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt;. Заметим, что у строк &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; обязательно найдется общая подстрока длины &amp;lt;tex&amp;gt;y \in [0 .. x]&amp;lt;/tex&amp;gt;, так как в качестве такой строки можно взять префикс наибольшей общей подстроки. Рассмотрим функцию &amp;lt;tex&amp;gt;f : [0 .. \min(|s|, |t|)] \rightarrow \{0, 1\}&amp;lt;/tex&amp;gt;, которая для &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; из области определения равна 1, если у строк &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; есть общая подстрока длины &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;, иначе она равна 0. Согласно замечанию, функция &amp;lt;tex&amp;gt;f&amp;lt;/tex&amp;gt; должна по мере возрастания &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; быть равной 1 до некоторого момента, а затем обращаться в 0. Собственно, максимальное значение, при котором функция принимает значение 1, является длиной наибольшей общей подстроки. Таким образом, требуется с помощью [[Целочисленный двоичный поиск|двоичного поиска]] найти это значение. В ходе работы придется проверять наличие общей подстроки заданной длины. Для этого будем использовать хеширование, чтобы улучшить асимптотику алгоритма. Алгоритм является эвристическим и может выдавать неверный ответ, так как совпадение хешей строк не гарантирует равенство строк. Поэтому нужно выполнить проверку нескольких случайных символов подстрок на совпадение, проиграв при этом по времени работы. Алгоритм работает следующим образом:&lt;br /&gt;
&lt;br /&gt;
1) У строки &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; хешируем подстроки заданной длины и полученные хеши записываем в Set.&lt;br /&gt;
&lt;br /&gt;
2) У строки &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; хешируем подстроки заданной длины и в случае совпадения хеша с элементом Set проверяем несколько случайных символов подстрок на совпадение.&lt;br /&gt;
&lt;br /&gt;
Хеширование будем производить так же, как и в [[Поиск подстроки в строке с использованием хеширования. Алгоритм Рабина-Карпа|алгоритме Рабина-Карпа]].&lt;br /&gt;
&lt;br /&gt;
== Псевдокод ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;f&amp;lt;/tex&amp;gt; — функция, описанная в алгоритме.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; — длина подстроки, найденная с помощью [[Целочисленный двоичный поиск|двоичного поиска]].&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;tex&amp;gt;f(i)&amp;lt;/tex&amp;gt;&lt;br /&gt;
 Записываем в &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; хэши подстрок строки &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; длины &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;;&lt;br /&gt;
 '''for''' &amp;lt;tex&amp;gt;j = 1...|t| - i&amp;lt;/tex&amp;gt;&lt;br /&gt;
    Считаем хэш от подстоки &amp;lt;tex&amp;gt;t[j .. j + i]&amp;lt;/tex&amp;gt;;&lt;br /&gt;
    '''if''' хэш содержится в &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;&lt;br /&gt;
      '''if''' совпали несколько случайных символов подсток&lt;br /&gt;
         return 1;&lt;br /&gt;
      '''else'''&lt;br /&gt;
         continue;&lt;br /&gt;
 return 0;&lt;br /&gt;
&lt;br /&gt;
==Время работы==&lt;br /&gt;
Проведем оценку асимптотики времени работы предложенного алгоритма. Посмотрим сколько нам потребуется действий на каждом шаге бинарного поиска. Во-первых, хеширование подстрок строки &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; и запись их в Set требует &amp;lt;tex&amp;gt;O(|s|)&amp;lt;/tex&amp;gt; шагов. Во-вторых, хеширование подстрок строки &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; и проверка их наличия в Set требует &amp;lt;tex&amp;gt;O(|t|)&amp;lt;/tex&amp;gt;. Проверка на совпадение нескольких символов подстрок требует константное время. Значит,на каждый шаг бинарного поиска требуется &amp;lt;tex&amp;gt;O(max(|s|, |t|))&amp;lt;/tex&amp;gt; действий. Заметим, что всего для завершения бинарного поиска потребуется &amp;lt;tex&amp;gt;O(\log(\min(|s|, |t|)))&amp;lt;/tex&amp;gt; шагов. Следовательно, суммарное время работы алгоритма будет &amp;lt;tex&amp;gt;O(\log(\min(|s|, |t|)) \cdot \max(|s|, |t|))&amp;lt;/tex&amp;gt; действий.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
* Кормен Т., Лейзерсон Ч., Ривест Р. Алгоритмы: построение и анализ. — 2-е изд. — М.: Издательский дом «Вильямс», 2007. — С. 1296.&lt;br /&gt;
&lt;br /&gt;
[[Категория:Алгоритмы и структуры данных]]&lt;br /&gt;
[[Категория:Поиск подстроки в строке]]&lt;/div&gt;</summary>
		<author><name>194.85.160.130</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D0%BE%D0%B8%D1%81%D0%BA_%D0%BD%D0%B0%D0%B8%D0%B1%D0%BE%D0%BB%D1%8C%D1%88%D0%B5%D0%B9_%D0%BE%D0%B1%D1%89%D0%B5%D0%B9_%D0%BF%D0%BE%D0%B4%D1%81%D1%82%D1%80%D0%BE%D0%BA%D0%B8_%D0%B4%D0%B2%D1%83%D1%85_%D1%81%D1%82%D1%80%D0%BE%D0%BA_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%85%D0%B5%D1%88%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F&amp;diff=27199</id>
		<title>Поиск наибольшей общей подстроки двух строк с использованием хеширования</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D0%BE%D0%B8%D1%81%D0%BA_%D0%BD%D0%B0%D0%B8%D0%B1%D0%BE%D0%BB%D1%8C%D1%88%D0%B5%D0%B9_%D0%BE%D0%B1%D1%89%D0%B5%D0%B9_%D0%BF%D0%BE%D0%B4%D1%81%D1%82%D1%80%D0%BE%D0%BA%D0%B8_%D0%B4%D0%B2%D1%83%D1%85_%D1%81%D1%82%D1%80%D0%BE%D0%BA_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%85%D0%B5%D1%88%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F&amp;diff=27199"/>
				<updated>2012-09-12T13:39:42Z</updated>
		
		<summary type="html">&lt;p&gt;194.85.160.130: /* Псевдокод */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Постановка задачи==&lt;br /&gt;
Имеются строки &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; такие, что элементы этих строк  &amp;lt;tex&amp;gt;-&amp;lt;/tex&amp;gt; символы из конечного алфавита &amp;lt;tex&amp;gt; \Sigma &amp;lt;/tex&amp;gt;. Говорят, что строка &amp;lt;tex&amp;gt;z[1 .. m]&amp;lt;/tex&amp;gt; является подстрокой строки &amp;lt;tex&amp;gt;s[1 .. n]&amp;lt;/tex&amp;gt;, если существует такой индекс &amp;lt;tex&amp;gt;k \in [0 .. n - m]&amp;lt;/tex&amp;gt;, что для любого &amp;lt;tex&amp;gt;i \in [1 .. m]&amp;lt;/tex&amp;gt; справедливо &amp;lt;tex&amp;gt;s[k + i] = z[i]&amp;lt;/tex&amp;gt;. Требуется найти такую строку &amp;lt;tex&amp;gt;z&amp;lt;/tex&amp;gt; максимальной длины, что &amp;lt;tex&amp;gt;z&amp;lt;/tex&amp;gt; является и подстрокой &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt;, и подстрокой &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Алгоритм==&lt;br /&gt;
Пусть длина наибольшей общей подстроки будет &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt;. Заметим, что у строк &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; обязательно найдется общая подстрока длины &amp;lt;tex&amp;gt;y \in [0 .. x]&amp;lt;/tex&amp;gt;, так как в качестве такой строки можно взять префикс наибольшей общей подстроки. Рассмотрим функцию &amp;lt;tex&amp;gt;f : [0 .. \min(|s|, |t|)] \rightarrow \{0, 1\}&amp;lt;/tex&amp;gt;, которая для &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; из области определения равна 1, если у строк &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; есть общая подстрока длины &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;, иначе она равна 0. Согласно замечанию, функция &amp;lt;tex&amp;gt;f&amp;lt;/tex&amp;gt; должна по мере возрастания &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; быть равной 1 до некоторого момента, а затем обращаться в 0. Собственно, максимальное значение, при котором функция принимает значение 1, является длиной наибольшей общей подстроки. Таким образом, требуется с помощью [[Целочисленный двоичный поиск|двоичного поиска]] найти это значение. В ходе работы придется проверять наличие общей подстроки заданной длины. Для этого будем использовать хеширование, чтобы улучшить асимптотику алгоритма. Алгоритм является эвристическим и может выдавать неверный ответ, так как совпадение хешей строк не гарантирует равенство строк. Поэтому нужно выполнить проверку нескольких случайных символов подстрок на совпадение, проиграв при этом по времени работы. Алгоритм работает следующим образом:&lt;br /&gt;
&lt;br /&gt;
1) У строки &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; хешируем подстроки заданной длины и полученные хеши записываем в Set.&lt;br /&gt;
&lt;br /&gt;
2) У строки &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; хешируем подстроки заданной длины и в случае совпадения хеша с элементом Set проверяем несколько случайных символов подстрок на совпадение.&lt;br /&gt;
&lt;br /&gt;
Хеширование будем производить так же, как и в [[Поиск подстроки в строке с использованием хеширования. Алгоритм Рабина-Карпа|алгоритме Рабина-Карпа]].&lt;br /&gt;
&lt;br /&gt;
== Псевдокод ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;f&amp;lt;/tex&amp;gt; — функция, описанная в алгоритме.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; - длина подстроки, найденная с помощью [[Целочисленный двоичный поиск|двоичного поиска]].&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;tex&amp;gt;f(i)&amp;lt;/tex&amp;gt;&lt;br /&gt;
 Записываем в &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; хэши подстрок строки &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; длины &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;;&lt;br /&gt;
 '''for''' &amp;lt;tex&amp;gt;j = 1...|t| - i&amp;lt;/tex&amp;gt;&lt;br /&gt;
    Считаем хэш от подстоки &amp;lt;tex&amp;gt;t[j .. j + i]&amp;lt;/tex&amp;gt;;&lt;br /&gt;
    '''if''' хэш содержится в &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;&lt;br /&gt;
      '''if''' совпали несколько случайных символов подсток&lt;br /&gt;
         return 1;&lt;br /&gt;
      '''else'''&lt;br /&gt;
         continue;&lt;br /&gt;
 return 0;&lt;br /&gt;
&lt;br /&gt;
==Время работы==&lt;br /&gt;
Проведем оценку асимптотики времени работы предложенного алгоритма. Посмотрим сколько нам потребуется действий на каждом шаге бинарного поиска. Во-первых, хеширование подстрок строки &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; и запись их в Set требует &amp;lt;tex&amp;gt;O(|s|)&amp;lt;/tex&amp;gt; шагов. Во-вторых, хеширование подстрок строки &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; и проверка их наличия в Set требует &amp;lt;tex&amp;gt;O(|t|)&amp;lt;/tex&amp;gt;. Проверка на совпадение нескольких символов подстрок требует константное время. Значит,на каждый шаг бинарного поиска требуется &amp;lt;tex&amp;gt;O(max(|s|, |t|))&amp;lt;/tex&amp;gt; действий. Заметим, что всего для завершения бинарного поиска потребуется &amp;lt;tex&amp;gt;O(\log(\min(|s|, |t|)))&amp;lt;/tex&amp;gt; шагов. Следовательно, суммарное время работы алгоритма будет &amp;lt;tex&amp;gt;O(\log(\min(|s|, |t|)) \cdot \max(|s|, |t|))&amp;lt;/tex&amp;gt; действий.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
* Кормен Т., Лейзерсон Ч., Ривест Р. Алгоритмы: построение и анализ. — 2-е изд. — М.: Издательский дом «Вильямс», 2007. — С. 1296.&lt;br /&gt;
&lt;br /&gt;
[[Категория:Алгоритмы и структуры данных]]&lt;br /&gt;
[[Категория:Поиск подстроки в строке]]&lt;/div&gt;</summary>
		<author><name>194.85.160.130</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D0%BE%D0%B8%D1%81%D0%BA_%D0%BD%D0%B0%D0%B8%D0%B1%D0%BE%D0%BB%D1%8C%D1%88%D0%B5%D0%B9_%D0%BE%D0%B1%D1%89%D0%B5%D0%B9_%D0%BF%D0%BE%D0%B4%D1%81%D1%82%D1%80%D0%BE%D0%BA%D0%B8_%D0%B4%D0%B2%D1%83%D1%85_%D1%81%D1%82%D1%80%D0%BE%D0%BA_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%85%D0%B5%D1%88%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F&amp;diff=27198</id>
		<title>Поиск наибольшей общей подстроки двух строк с использованием хеширования</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D0%BE%D0%B8%D1%81%D0%BA_%D0%BD%D0%B0%D0%B8%D0%B1%D0%BE%D0%BB%D1%8C%D1%88%D0%B5%D0%B9_%D0%BE%D0%B1%D1%89%D0%B5%D0%B9_%D0%BF%D0%BE%D0%B4%D1%81%D1%82%D1%80%D0%BE%D0%BA%D0%B8_%D0%B4%D0%B2%D1%83%D1%85_%D1%81%D1%82%D1%80%D0%BE%D0%BA_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%85%D0%B5%D1%88%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F&amp;diff=27198"/>
				<updated>2012-09-12T13:39:25Z</updated>
		
		<summary type="html">&lt;p&gt;194.85.160.130: /* Псевдокод */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Постановка задачи==&lt;br /&gt;
Имеются строки &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; такие, что элементы этих строк  &amp;lt;tex&amp;gt;-&amp;lt;/tex&amp;gt; символы из конечного алфавита &amp;lt;tex&amp;gt; \Sigma &amp;lt;/tex&amp;gt;. Говорят, что строка &amp;lt;tex&amp;gt;z[1 .. m]&amp;lt;/tex&amp;gt; является подстрокой строки &amp;lt;tex&amp;gt;s[1 .. n]&amp;lt;/tex&amp;gt;, если существует такой индекс &amp;lt;tex&amp;gt;k \in [0 .. n - m]&amp;lt;/tex&amp;gt;, что для любого &amp;lt;tex&amp;gt;i \in [1 .. m]&amp;lt;/tex&amp;gt; справедливо &amp;lt;tex&amp;gt;s[k + i] = z[i]&amp;lt;/tex&amp;gt;. Требуется найти такую строку &amp;lt;tex&amp;gt;z&amp;lt;/tex&amp;gt; максимальной длины, что &amp;lt;tex&amp;gt;z&amp;lt;/tex&amp;gt; является и подстрокой &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt;, и подстрокой &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Алгоритм==&lt;br /&gt;
Пусть длина наибольшей общей подстроки будет &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt;. Заметим, что у строк &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; обязательно найдется общая подстрока длины &amp;lt;tex&amp;gt;y \in [0 .. x]&amp;lt;/tex&amp;gt;, так как в качестве такой строки можно взять префикс наибольшей общей подстроки. Рассмотрим функцию &amp;lt;tex&amp;gt;f : [0 .. \min(|s|, |t|)] \rightarrow \{0, 1\}&amp;lt;/tex&amp;gt;, которая для &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; из области определения равна 1, если у строк &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; есть общая подстрока длины &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;, иначе она равна 0. Согласно замечанию, функция &amp;lt;tex&amp;gt;f&amp;lt;/tex&amp;gt; должна по мере возрастания &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; быть равной 1 до некоторого момента, а затем обращаться в 0. Собственно, максимальное значение, при котором функция принимает значение 1, является длиной наибольшей общей подстроки. Таким образом, требуется с помощью [[Целочисленный двоичный поиск|двоичного поиска]] найти это значение. В ходе работы придется проверять наличие общей подстроки заданной длины. Для этого будем использовать хеширование, чтобы улучшить асимптотику алгоритма. Алгоритм является эвристическим и может выдавать неверный ответ, так как совпадение хешей строк не гарантирует равенство строк. Поэтому нужно выполнить проверку нескольких случайных символов подстрок на совпадение, проиграв при этом по времени работы. Алгоритм работает следующим образом:&lt;br /&gt;
&lt;br /&gt;
1) У строки &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; хешируем подстроки заданной длины и полученные хеши записываем в Set.&lt;br /&gt;
&lt;br /&gt;
2) У строки &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; хешируем подстроки заданной длины и в случае совпадения хеша с элементом Set проверяем несколько случайных символов подстрок на совпадение.&lt;br /&gt;
&lt;br /&gt;
Хеширование будем производить так же, как и в [[Поиск подстроки в строке с использованием хеширования. Алгоритм Рабина-Карпа|алгоритме Рабина-Карпа]].&lt;br /&gt;
&lt;br /&gt;
== Псевдокод ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;f&amp;lt;/tex&amp;gt; — функция, описанная в алгоритме.&lt;br /&gt;
&amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; - длина подстроки, найденная с помощью [[Целочисленный двоичный поиск|двоичного поиска]].&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;tex&amp;gt;f(i)&amp;lt;/tex&amp;gt;&lt;br /&gt;
 Записываем в &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; хэши подстрок строки &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; длины &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;;&lt;br /&gt;
 '''for''' &amp;lt;tex&amp;gt;j = 1...|t| - i&amp;lt;/tex&amp;gt;&lt;br /&gt;
    Считаем хэш от подстоки &amp;lt;tex&amp;gt;t[j .. j + i]&amp;lt;/tex&amp;gt;;&lt;br /&gt;
    '''if''' хэш содержится в &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;&lt;br /&gt;
      '''if''' совпали несколько случайных символов подсток&lt;br /&gt;
         return 1;&lt;br /&gt;
      '''else'''&lt;br /&gt;
         continue;&lt;br /&gt;
 return 0;&lt;br /&gt;
&lt;br /&gt;
==Время работы==&lt;br /&gt;
Проведем оценку асимптотики времени работы предложенного алгоритма. Посмотрим сколько нам потребуется действий на каждом шаге бинарного поиска. Во-первых, хеширование подстрок строки &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; и запись их в Set требует &amp;lt;tex&amp;gt;O(|s|)&amp;lt;/tex&amp;gt; шагов. Во-вторых, хеширование подстрок строки &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; и проверка их наличия в Set требует &amp;lt;tex&amp;gt;O(|t|)&amp;lt;/tex&amp;gt;. Проверка на совпадение нескольких символов подстрок требует константное время. Значит,на каждый шаг бинарного поиска требуется &amp;lt;tex&amp;gt;O(max(|s|, |t|))&amp;lt;/tex&amp;gt; действий. Заметим, что всего для завершения бинарного поиска потребуется &amp;lt;tex&amp;gt;O(\log(\min(|s|, |t|)))&amp;lt;/tex&amp;gt; шагов. Следовательно, суммарное время работы алгоритма будет &amp;lt;tex&amp;gt;O(\log(\min(|s|, |t|)) \cdot \max(|s|, |t|))&amp;lt;/tex&amp;gt; действий.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
* Кормен Т., Лейзерсон Ч., Ривест Р. Алгоритмы: построение и анализ. — 2-е изд. — М.: Издательский дом «Вильямс», 2007. — С. 1296.&lt;br /&gt;
&lt;br /&gt;
[[Категория:Алгоритмы и структуры данных]]&lt;br /&gt;
[[Категория:Поиск подстроки в строке]]&lt;/div&gt;</summary>
		<author><name>194.85.160.130</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D0%BE%D0%B8%D1%81%D0%BA_%D0%BD%D0%B0%D0%B8%D0%B1%D0%BE%D0%BB%D1%8C%D1%88%D0%B5%D0%B9_%D0%BE%D0%B1%D1%89%D0%B5%D0%B9_%D0%BF%D0%BE%D0%B4%D1%81%D1%82%D1%80%D0%BE%D0%BA%D0%B8_%D0%B4%D0%B2%D1%83%D1%85_%D1%81%D1%82%D1%80%D0%BE%D0%BA_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%85%D0%B5%D1%88%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F&amp;diff=27197</id>
		<title>Поиск наибольшей общей подстроки двух строк с использованием хеширования</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D0%BE%D0%B8%D1%81%D0%BA_%D0%BD%D0%B0%D0%B8%D0%B1%D0%BE%D0%BB%D1%8C%D1%88%D0%B5%D0%B9_%D0%BE%D0%B1%D1%89%D0%B5%D0%B9_%D0%BF%D0%BE%D0%B4%D1%81%D1%82%D1%80%D0%BE%D0%BA%D0%B8_%D0%B4%D0%B2%D1%83%D1%85_%D1%81%D1%82%D1%80%D0%BE%D0%BA_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%85%D0%B5%D1%88%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F&amp;diff=27197"/>
				<updated>2012-09-12T13:38:50Z</updated>
		
		<summary type="html">&lt;p&gt;194.85.160.130: /* Псевдокод */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Постановка задачи==&lt;br /&gt;
Имеются строки &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; такие, что элементы этих строк  &amp;lt;tex&amp;gt;-&amp;lt;/tex&amp;gt; символы из конечного алфавита &amp;lt;tex&amp;gt; \Sigma &amp;lt;/tex&amp;gt;. Говорят, что строка &amp;lt;tex&amp;gt;z[1 .. m]&amp;lt;/tex&amp;gt; является подстрокой строки &amp;lt;tex&amp;gt;s[1 .. n]&amp;lt;/tex&amp;gt;, если существует такой индекс &amp;lt;tex&amp;gt;k \in [0 .. n - m]&amp;lt;/tex&amp;gt;, что для любого &amp;lt;tex&amp;gt;i \in [1 .. m]&amp;lt;/tex&amp;gt; справедливо &amp;lt;tex&amp;gt;s[k + i] = z[i]&amp;lt;/tex&amp;gt;. Требуется найти такую строку &amp;lt;tex&amp;gt;z&amp;lt;/tex&amp;gt; максимальной длины, что &amp;lt;tex&amp;gt;z&amp;lt;/tex&amp;gt; является и подстрокой &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt;, и подстрокой &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Алгоритм==&lt;br /&gt;
Пусть длина наибольшей общей подстроки будет &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt;. Заметим, что у строк &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; обязательно найдется общая подстрока длины &amp;lt;tex&amp;gt;y \in [0 .. x]&amp;lt;/tex&amp;gt;, так как в качестве такой строки можно взять префикс наибольшей общей подстроки. Рассмотрим функцию &amp;lt;tex&amp;gt;f : [0 .. \min(|s|, |t|)] \rightarrow \{0, 1\}&amp;lt;/tex&amp;gt;, которая для &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; из области определения равна 1, если у строк &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; есть общая подстрока длины &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;, иначе она равна 0. Согласно замечанию, функция &amp;lt;tex&amp;gt;f&amp;lt;/tex&amp;gt; должна по мере возрастания &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; быть равной 1 до некоторого момента, а затем обращаться в 0. Собственно, максимальное значение, при котором функция принимает значение 1, является длиной наибольшей общей подстроки. Таким образом, требуется с помощью [[Целочисленный двоичный поиск|двоичного поиска]] найти это значение. В ходе работы придется проверять наличие общей подстроки заданной длины. Для этого будем использовать хеширование, чтобы улучшить асимптотику алгоритма. Алгоритм является эвристическим и может выдавать неверный ответ, так как совпадение хешей строк не гарантирует равенство строк. Поэтому нужно выполнить проверку нескольких случайных символов подстрок на совпадение, проиграв при этом по времени работы. Алгоритм работает следующим образом:&lt;br /&gt;
&lt;br /&gt;
1) У строки &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; хешируем подстроки заданной длины и полученные хеши записываем в Set.&lt;br /&gt;
&lt;br /&gt;
2) У строки &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; хешируем подстроки заданной длины и в случае совпадения хеша с элементом Set проверяем несколько случайных символов подстрок на совпадение.&lt;br /&gt;
&lt;br /&gt;
Хеширование будем производить так же, как и в [[Поиск подстроки в строке с использованием хеширования. Алгоритм Рабина-Карпа|алгоритме Рабина-Карпа]].&lt;br /&gt;
&lt;br /&gt;
== Псевдокод ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;f&amp;lt;/tex&amp;gt; — функция, описанная в алгоритме.&lt;br /&gt;
 &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; - длина подстроки, найденная с помощью [[Целочисленный двоичный поиск|двоичного поиска]].&lt;br /&gt;
 &amp;lt;tex&amp;gt;f(i)&amp;lt;/tex&amp;gt;&lt;br /&gt;
 Записываем в &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; хэши подстрок строки &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; длины &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;;&lt;br /&gt;
 '''for''' &amp;lt;tex&amp;gt;j = 1...|t| - i&amp;lt;/tex&amp;gt;&lt;br /&gt;
    Считаем хэш от подстоки &amp;lt;tex&amp;gt;t[j .. j + i]&amp;lt;/tex&amp;gt;;&lt;br /&gt;
    '''if''' хэш содержится в &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;&lt;br /&gt;
      '''if''' совпали несколько случайных символов подсток&lt;br /&gt;
         return 1;&lt;br /&gt;
      '''else'''&lt;br /&gt;
         continue;&lt;br /&gt;
 return 0;&lt;br /&gt;
&lt;br /&gt;
==Время работы==&lt;br /&gt;
Проведем оценку асимптотики времени работы предложенного алгоритма. Посмотрим сколько нам потребуется действий на каждом шаге бинарного поиска. Во-первых, хеширование подстрок строки &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; и запись их в Set требует &amp;lt;tex&amp;gt;O(|s|)&amp;lt;/tex&amp;gt; шагов. Во-вторых, хеширование подстрок строки &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; и проверка их наличия в Set требует &amp;lt;tex&amp;gt;O(|t|)&amp;lt;/tex&amp;gt;. Проверка на совпадение нескольких символов подстрок требует константное время. Значит,на каждый шаг бинарного поиска требуется &amp;lt;tex&amp;gt;O(max(|s|, |t|))&amp;lt;/tex&amp;gt; действий. Заметим, что всего для завершения бинарного поиска потребуется &amp;lt;tex&amp;gt;O(\log(\min(|s|, |t|)))&amp;lt;/tex&amp;gt; шагов. Следовательно, суммарное время работы алгоритма будет &amp;lt;tex&amp;gt;O(\log(\min(|s|, |t|)) \cdot \max(|s|, |t|))&amp;lt;/tex&amp;gt; действий.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
* Кормен Т., Лейзерсон Ч., Ривест Р. Алгоритмы: построение и анализ. — 2-е изд. — М.: Издательский дом «Вильямс», 2007. — С. 1296.&lt;br /&gt;
&lt;br /&gt;
[[Категория:Алгоритмы и структуры данных]]&lt;br /&gt;
[[Категория:Поиск подстроки в строке]]&lt;/div&gt;</summary>
		<author><name>194.85.160.130</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D0%BE%D0%B8%D1%81%D0%BA_%D0%BD%D0%B0%D0%B8%D0%B1%D0%BE%D0%BB%D1%8C%D1%88%D0%B5%D0%B9_%D0%BE%D0%B1%D1%89%D0%B5%D0%B9_%D0%BF%D0%BE%D0%B4%D1%81%D1%82%D1%80%D0%BE%D0%BA%D0%B8_%D0%B4%D0%B2%D1%83%D1%85_%D1%81%D1%82%D1%80%D0%BE%D0%BA_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%85%D0%B5%D1%88%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F&amp;diff=27196</id>
		<title>Поиск наибольшей общей подстроки двух строк с использованием хеширования</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D0%BE%D0%B8%D1%81%D0%BA_%D0%BD%D0%B0%D0%B8%D0%B1%D0%BE%D0%BB%D1%8C%D1%88%D0%B5%D0%B9_%D0%BE%D0%B1%D1%89%D0%B5%D0%B9_%D0%BF%D0%BE%D0%B4%D1%81%D1%82%D1%80%D0%BE%D0%BA%D0%B8_%D0%B4%D0%B2%D1%83%D1%85_%D1%81%D1%82%D1%80%D0%BE%D0%BA_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%85%D0%B5%D1%88%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F&amp;diff=27196"/>
				<updated>2012-09-12T13:34:00Z</updated>
		
		<summary type="html">&lt;p&gt;194.85.160.130: /* Псевдокод */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Постановка задачи==&lt;br /&gt;
Имеются строки &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; такие, что элементы этих строк  &amp;lt;tex&amp;gt;-&amp;lt;/tex&amp;gt; символы из конечного алфавита &amp;lt;tex&amp;gt; \Sigma &amp;lt;/tex&amp;gt;. Говорят, что строка &amp;lt;tex&amp;gt;z[1 .. m]&amp;lt;/tex&amp;gt; является подстрокой строки &amp;lt;tex&amp;gt;s[1 .. n]&amp;lt;/tex&amp;gt;, если существует такой индекс &amp;lt;tex&amp;gt;k \in [0 .. n - m]&amp;lt;/tex&amp;gt;, что для любого &amp;lt;tex&amp;gt;i \in [1 .. m]&amp;lt;/tex&amp;gt; справедливо &amp;lt;tex&amp;gt;s[k + i] = z[i]&amp;lt;/tex&amp;gt;. Требуется найти такую строку &amp;lt;tex&amp;gt;z&amp;lt;/tex&amp;gt; максимальной длины, что &amp;lt;tex&amp;gt;z&amp;lt;/tex&amp;gt; является и подстрокой &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt;, и подстрокой &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Алгоритм==&lt;br /&gt;
Пусть длина наибольшей общей подстроки будет &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt;. Заметим, что у строк &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; обязательно найдется общая подстрока длины &amp;lt;tex&amp;gt;y \in [0 .. x]&amp;lt;/tex&amp;gt;, так как в качестве такой строки можно взять префикс наибольшей общей подстроки. Рассмотрим функцию &amp;lt;tex&amp;gt;f : [0 .. \min(|s|, |t|)] \rightarrow \{0, 1\}&amp;lt;/tex&amp;gt;, которая для &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; из области определения равна 1, если у строк &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; есть общая подстрока длины &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;, иначе она равна 0. Согласно замечанию, функция &amp;lt;tex&amp;gt;f&amp;lt;/tex&amp;gt; должна по мере возрастания &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; быть равной 1 до некоторого момента, а затем обращаться в 0. Собственно, максимальное значение, при котором функция принимает значение 1, является длиной наибольшей общей подстроки. Таким образом, требуется с помощью [[Целочисленный двоичный поиск|двоичного поиска]] найти это значение. В ходе работы придется проверять наличие общей подстроки заданной длины. Для этого будем использовать хеширование, чтобы улучшить асимптотику алгоритма. Алгоритм является эвристическим и может выдавать неверный ответ, так как совпадение хешей строк не гарантирует равенство строк. Поэтому нужно выполнить проверку нескольких случайных символов подстрок на совпадение, проиграв при этом по времени работы. Алгоритм работает следующим образом:&lt;br /&gt;
&lt;br /&gt;
1) У строки &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; хешируем подстроки заданной длины и полученные хеши записываем в Set.&lt;br /&gt;
&lt;br /&gt;
2) У строки &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; хешируем подстроки заданной длины и в случае совпадения хеша с элементом Set проверяем несколько случайных символов подстрок на совпадение.&lt;br /&gt;
&lt;br /&gt;
Хеширование будем производить так же, как и в [[Поиск подстроки в строке с использованием хеширования. Алгоритм Рабина-Карпа|алгоритме Рабина-Карпа]].&lt;br /&gt;
&lt;br /&gt;
== Псевдокод ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;f&amp;lt;/tex&amp;gt; — функция, описанная в алгоритме.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;tex&amp;gt;f(i)&amp;lt;/tex&amp;gt;&lt;br /&gt;
 Записываем в &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; хэши подстрок строки &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; длины &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;;&lt;br /&gt;
 '''for''' &amp;lt;tex&amp;gt;j = 1...|t| - i&amp;lt;/tex&amp;gt;&lt;br /&gt;
    Считаем хэш от подстоки &amp;lt;tex&amp;gt;t[j .. j + i]&amp;lt;/tex&amp;gt;;&lt;br /&gt;
    '''if''' хэш содержится в &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;&lt;br /&gt;
      '''if''' совпали несколько случайных символов подсток&lt;br /&gt;
         return 1;&lt;br /&gt;
      '''else'''&lt;br /&gt;
         continue;&lt;br /&gt;
 return 0;&lt;br /&gt;
&lt;br /&gt;
==Время работы==&lt;br /&gt;
Проведем оценку асимптотики времени работы предложенного алгоритма. Посмотрим сколько нам потребуется действий на каждом шаге бинарного поиска. Во-первых, хеширование подстрок строки &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; и запись их в Set требует &amp;lt;tex&amp;gt;O(|s|)&amp;lt;/tex&amp;gt; шагов. Во-вторых, хеширование подстрок строки &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; и проверка их наличия в Set требует &amp;lt;tex&amp;gt;O(|t|)&amp;lt;/tex&amp;gt;. Проверка на совпадение нескольких символов подстрок требует константное время. Значит,на каждый шаг бинарного поиска требуется &amp;lt;tex&amp;gt;O(max(|s|, |t|))&amp;lt;/tex&amp;gt; действий. Заметим, что всего для завершения бинарного поиска потребуется &amp;lt;tex&amp;gt;O(\log(\min(|s|, |t|)))&amp;lt;/tex&amp;gt; шагов. Следовательно, суммарное время работы алгоритма будет &amp;lt;tex&amp;gt;O(\log(\min(|s|, |t|)) \cdot \max(|s|, |t|))&amp;lt;/tex&amp;gt; действий.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
* Кормен Т., Лейзерсон Ч., Ривест Р. Алгоритмы: построение и анализ. — 2-е изд. — М.: Издательский дом «Вильямс», 2007. — С. 1296.&lt;br /&gt;
&lt;br /&gt;
[[Категория:Алгоритмы и структуры данных]]&lt;br /&gt;
[[Категория:Поиск подстроки в строке]]&lt;/div&gt;</summary>
		<author><name>194.85.160.130</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D0%BE%D0%B8%D1%81%D0%BA_%D0%BD%D0%B0%D0%B8%D0%B1%D0%BE%D0%BB%D1%8C%D1%88%D0%B5%D0%B9_%D0%BE%D0%B1%D1%89%D0%B5%D0%B9_%D0%BF%D0%BE%D0%B4%D1%81%D1%82%D1%80%D0%BE%D0%BA%D0%B8_%D0%B4%D0%B2%D1%83%D1%85_%D1%81%D1%82%D1%80%D0%BE%D0%BA_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%85%D0%B5%D1%88%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F&amp;diff=27195</id>
		<title>Поиск наибольшей общей подстроки двух строк с использованием хеширования</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D0%BE%D0%B8%D1%81%D0%BA_%D0%BD%D0%B0%D0%B8%D0%B1%D0%BE%D0%BB%D1%8C%D1%88%D0%B5%D0%B9_%D0%BE%D0%B1%D1%89%D0%B5%D0%B9_%D0%BF%D0%BE%D0%B4%D1%81%D1%82%D1%80%D0%BE%D0%BA%D0%B8_%D0%B4%D0%B2%D1%83%D1%85_%D1%81%D1%82%D1%80%D0%BE%D0%BA_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%85%D0%B5%D1%88%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F&amp;diff=27195"/>
				<updated>2012-09-12T13:33:23Z</updated>
		
		<summary type="html">&lt;p&gt;194.85.160.130: /* Псевдокод */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Постановка задачи==&lt;br /&gt;
Имеются строки &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; такие, что элементы этих строк  &amp;lt;tex&amp;gt;-&amp;lt;/tex&amp;gt; символы из конечного алфавита &amp;lt;tex&amp;gt; \Sigma &amp;lt;/tex&amp;gt;. Говорят, что строка &amp;lt;tex&amp;gt;z[1 .. m]&amp;lt;/tex&amp;gt; является подстрокой строки &amp;lt;tex&amp;gt;s[1 .. n]&amp;lt;/tex&amp;gt;, если существует такой индекс &amp;lt;tex&amp;gt;k \in [0 .. n - m]&amp;lt;/tex&amp;gt;, что для любого &amp;lt;tex&amp;gt;i \in [1 .. m]&amp;lt;/tex&amp;gt; справедливо &amp;lt;tex&amp;gt;s[k + i] = z[i]&amp;lt;/tex&amp;gt;. Требуется найти такую строку &amp;lt;tex&amp;gt;z&amp;lt;/tex&amp;gt; максимальной длины, что &amp;lt;tex&amp;gt;z&amp;lt;/tex&amp;gt; является и подстрокой &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt;, и подстрокой &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Алгоритм==&lt;br /&gt;
Пусть длина наибольшей общей подстроки будет &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt;. Заметим, что у строк &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; обязательно найдется общая подстрока длины &amp;lt;tex&amp;gt;y \in [0 .. x]&amp;lt;/tex&amp;gt;, так как в качестве такой строки можно взять префикс наибольшей общей подстроки. Рассмотрим функцию &amp;lt;tex&amp;gt;f : [0 .. \min(|s|, |t|)] \rightarrow \{0, 1\}&amp;lt;/tex&amp;gt;, которая для &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; из области определения равна 1, если у строк &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; есть общая подстрока длины &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;, иначе она равна 0. Согласно замечанию, функция &amp;lt;tex&amp;gt;f&amp;lt;/tex&amp;gt; должна по мере возрастания &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; быть равной 1 до некоторого момента, а затем обращаться в 0. Собственно, максимальное значение, при котором функция принимает значение 1, является длиной наибольшей общей подстроки. Таким образом, требуется с помощью [[Целочисленный двоичный поиск|двоичного поиска]] найти это значение. В ходе работы придется проверять наличие общей подстроки заданной длины. Для этого будем использовать хеширование, чтобы улучшить асимптотику алгоритма. Алгоритм является эвристическим и может выдавать неверный ответ, так как совпадение хешей строк не гарантирует равенство строк. Поэтому нужно выполнить проверку нескольких случайных символов подстрок на совпадение, проиграв при этом по времени работы. Алгоритм работает следующим образом:&lt;br /&gt;
&lt;br /&gt;
1) У строки &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; хешируем подстроки заданной длины и полученные хеши записываем в Set.&lt;br /&gt;
&lt;br /&gt;
2) У строки &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; хешируем подстроки заданной длины и в случае совпадения хеша с элементом Set проверяем несколько случайных символов подстрок на совпадение.&lt;br /&gt;
&lt;br /&gt;
Хеширование будем производить так же, как и в [[Поиск подстроки в строке с использованием хеширования. Алгоритм Рабина-Карпа|алгоритме Рабина-Карпа]].&lt;br /&gt;
&lt;br /&gt;
== Псевдокод ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;f&amp;lt;/tex&amp;gt; — функция, описанная в алгоритме.&lt;br /&gt;
&lt;br /&gt;
 f(i)&lt;br /&gt;
 Записываем в &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; хэши подстрок строки &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; длины &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;;&lt;br /&gt;
 '''for''' &amp;lt;tex&amp;gt;j = 1...|t| - i&amp;lt;/tex&amp;gt;&lt;br /&gt;
    Считаем хэш от подстоки &amp;lt;tex&amp;gt;t[j .. j + i]&amp;lt;/tex&amp;gt;;&lt;br /&gt;
    '''if''' хэш содержится в &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;&lt;br /&gt;
      '''if''' совпали несколько случайных символов подсток&lt;br /&gt;
         return 1;&lt;br /&gt;
      '''else'''&lt;br /&gt;
         continue;&lt;br /&gt;
 return 0;&lt;br /&gt;
&lt;br /&gt;
==Время работы==&lt;br /&gt;
Проведем оценку асимптотики времени работы предложенного алгоритма. Посмотрим сколько нам потребуется действий на каждом шаге бинарного поиска. Во-первых, хеширование подстрок строки &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; и запись их в Set требует &amp;lt;tex&amp;gt;O(|s|)&amp;lt;/tex&amp;gt; шагов. Во-вторых, хеширование подстрок строки &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; и проверка их наличия в Set требует &amp;lt;tex&amp;gt;O(|t|)&amp;lt;/tex&amp;gt;. Проверка на совпадение нескольких символов подстрок требует константное время. Значит,на каждый шаг бинарного поиска требуется &amp;lt;tex&amp;gt;O(max(|s|, |t|))&amp;lt;/tex&amp;gt; действий. Заметим, что всего для завершения бинарного поиска потребуется &amp;lt;tex&amp;gt;O(\log(\min(|s|, |t|)))&amp;lt;/tex&amp;gt; шагов. Следовательно, суммарное время работы алгоритма будет &amp;lt;tex&amp;gt;O(\log(\min(|s|, |t|)) \cdot \max(|s|, |t|))&amp;lt;/tex&amp;gt; действий.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
* Кормен Т., Лейзерсон Ч., Ривест Р. Алгоритмы: построение и анализ. — 2-е изд. — М.: Издательский дом «Вильямс», 2007. — С. 1296.&lt;br /&gt;
&lt;br /&gt;
[[Категория:Алгоритмы и структуры данных]]&lt;br /&gt;
[[Категория:Поиск подстроки в строке]]&lt;/div&gt;</summary>
		<author><name>194.85.160.130</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D0%BE%D0%B8%D1%81%D0%BA_%D0%BD%D0%B0%D0%B8%D0%B1%D0%BE%D0%BB%D1%8C%D1%88%D0%B5%D0%B9_%D0%BE%D0%B1%D1%89%D0%B5%D0%B9_%D0%BF%D0%BE%D0%B4%D1%81%D1%82%D1%80%D0%BE%D0%BA%D0%B8_%D0%B4%D0%B2%D1%83%D1%85_%D1%81%D1%82%D1%80%D0%BE%D0%BA_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%85%D0%B5%D1%88%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F&amp;diff=27194</id>
		<title>Поиск наибольшей общей подстроки двух строк с использованием хеширования</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D0%BE%D0%B8%D1%81%D0%BA_%D0%BD%D0%B0%D0%B8%D0%B1%D0%BE%D0%BB%D1%8C%D1%88%D0%B5%D0%B9_%D0%BE%D0%B1%D1%89%D0%B5%D0%B9_%D0%BF%D0%BE%D0%B4%D1%81%D1%82%D1%80%D0%BE%D0%BA%D0%B8_%D0%B4%D0%B2%D1%83%D1%85_%D1%81%D1%82%D1%80%D0%BE%D0%BA_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%85%D0%B5%D1%88%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F&amp;diff=27194"/>
				<updated>2012-09-12T13:32:14Z</updated>
		
		<summary type="html">&lt;p&gt;194.85.160.130: /* Псевдокод */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Постановка задачи==&lt;br /&gt;
Имеются строки &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; такие, что элементы этих строк  &amp;lt;tex&amp;gt;-&amp;lt;/tex&amp;gt; символы из конечного алфавита &amp;lt;tex&amp;gt; \Sigma &amp;lt;/tex&amp;gt;. Говорят, что строка &amp;lt;tex&amp;gt;z[1 .. m]&amp;lt;/tex&amp;gt; является подстрокой строки &amp;lt;tex&amp;gt;s[1 .. n]&amp;lt;/tex&amp;gt;, если существует такой индекс &amp;lt;tex&amp;gt;k \in [0 .. n - m]&amp;lt;/tex&amp;gt;, что для любого &amp;lt;tex&amp;gt;i \in [1 .. m]&amp;lt;/tex&amp;gt; справедливо &amp;lt;tex&amp;gt;s[k + i] = z[i]&amp;lt;/tex&amp;gt;. Требуется найти такую строку &amp;lt;tex&amp;gt;z&amp;lt;/tex&amp;gt; максимальной длины, что &amp;lt;tex&amp;gt;z&amp;lt;/tex&amp;gt; является и подстрокой &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt;, и подстрокой &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Алгоритм==&lt;br /&gt;
Пусть длина наибольшей общей подстроки будет &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt;. Заметим, что у строк &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; обязательно найдется общая подстрока длины &amp;lt;tex&amp;gt;y \in [0 .. x]&amp;lt;/tex&amp;gt;, так как в качестве такой строки можно взять префикс наибольшей общей подстроки. Рассмотрим функцию &amp;lt;tex&amp;gt;f : [0 .. \min(|s|, |t|)] \rightarrow \{0, 1\}&amp;lt;/tex&amp;gt;, которая для &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; из области определения равна 1, если у строк &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; есть общая подстрока длины &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;, иначе она равна 0. Согласно замечанию, функция &amp;lt;tex&amp;gt;f&amp;lt;/tex&amp;gt; должна по мере возрастания &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; быть равной 1 до некоторого момента, а затем обращаться в 0. Собственно, максимальное значение, при котором функция принимает значение 1, является длиной наибольшей общей подстроки. Таким образом, требуется с помощью [[Целочисленный двоичный поиск|двоичного поиска]] найти это значение. В ходе работы придется проверять наличие общей подстроки заданной длины. Для этого будем использовать хеширование, чтобы улучшить асимптотику алгоритма. Алгоритм является эвристическим и может выдавать неверный ответ, так как совпадение хешей строк не гарантирует равенство строк. Поэтому нужно выполнить проверку нескольких случайных символов подстрок на совпадение, проиграв при этом по времени работы. Алгоритм работает следующим образом:&lt;br /&gt;
&lt;br /&gt;
1) У строки &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; хешируем подстроки заданной длины и полученные хеши записываем в Set.&lt;br /&gt;
&lt;br /&gt;
2) У строки &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; хешируем подстроки заданной длины и в случае совпадения хеша с элементом Set проверяем несколько случайных символов подстрок на совпадение.&lt;br /&gt;
&lt;br /&gt;
Хеширование будем производить так же, как и в [[Поиск подстроки в строке с использованием хеширования. Алгоритм Рабина-Карпа|алгоритме Рабина-Карпа]].&lt;br /&gt;
&lt;br /&gt;
== Псевдокод ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;f&amp;lt;/tex&amp;gt; — функция, описанная в алгоритме.&lt;br /&gt;
&lt;br /&gt;
 f(i)&lt;br /&gt;
 Записываем в &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; хэши подстрок строки &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; длины &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;&lt;br /&gt;
 '''for''' &amp;lt;tex&amp;gt;j = 1...|t| - i&amp;lt;/tex&amp;gt;&lt;br /&gt;
    Считаем хэш от подстоки &amp;lt;tex&amp;gt;t[j .. j + i]&amp;lt;/tex&amp;gt;&lt;br /&gt;
    '''if''' хэш содержится в &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;&lt;br /&gt;
      '''if''' совпали несколько случайных символов подсток&lt;br /&gt;
         return 1;&lt;br /&gt;
      '''else'''&lt;br /&gt;
         continue;&lt;br /&gt;
 return 0;&lt;br /&gt;
&lt;br /&gt;
==Время работы==&lt;br /&gt;
Проведем оценку асимптотики времени работы предложенного алгоритма. Посмотрим сколько нам потребуется действий на каждом шаге бинарного поиска. Во-первых, хеширование подстрок строки &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; и запись их в Set требует &amp;lt;tex&amp;gt;O(|s|)&amp;lt;/tex&amp;gt; шагов. Во-вторых, хеширование подстрок строки &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; и проверка их наличия в Set требует &amp;lt;tex&amp;gt;O(|t|)&amp;lt;/tex&amp;gt;. Проверка на совпадение нескольких символов подстрок требует константное время. Значит,на каждый шаг бинарного поиска требуется &amp;lt;tex&amp;gt;O(max(|s|, |t|))&amp;lt;/tex&amp;gt; действий. Заметим, что всего для завершения бинарного поиска потребуется &amp;lt;tex&amp;gt;O(\log(\min(|s|, |t|)))&amp;lt;/tex&amp;gt; шагов. Следовательно, суммарное время работы алгоритма будет &amp;lt;tex&amp;gt;O(\log(\min(|s|, |t|)) \cdot \max(|s|, |t|))&amp;lt;/tex&amp;gt; действий.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
* Кормен Т., Лейзерсон Ч., Ривест Р. Алгоритмы: построение и анализ. — 2-е изд. — М.: Издательский дом «Вильямс», 2007. — С. 1296.&lt;br /&gt;
&lt;br /&gt;
[[Категория:Алгоритмы и структуры данных]]&lt;br /&gt;
[[Категория:Поиск подстроки в строке]]&lt;/div&gt;</summary>
		<author><name>194.85.160.130</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D0%BE%D0%B8%D1%81%D0%BA_%D0%BD%D0%B0%D0%B8%D0%B1%D0%BE%D0%BB%D1%8C%D1%88%D0%B5%D0%B9_%D0%BE%D0%B1%D1%89%D0%B5%D0%B9_%D0%BF%D0%BE%D0%B4%D1%81%D1%82%D1%80%D0%BE%D0%BA%D0%B8_%D0%B4%D0%B2%D1%83%D1%85_%D1%81%D1%82%D1%80%D0%BE%D0%BA_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%85%D0%B5%D1%88%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F&amp;diff=27193</id>
		<title>Поиск наибольшей общей подстроки двух строк с использованием хеширования</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D0%BE%D0%B8%D1%81%D0%BA_%D0%BD%D0%B0%D0%B8%D0%B1%D0%BE%D0%BB%D1%8C%D1%88%D0%B5%D0%B9_%D0%BE%D0%B1%D1%89%D0%B5%D0%B9_%D0%BF%D0%BE%D0%B4%D1%81%D1%82%D1%80%D0%BE%D0%BA%D0%B8_%D0%B4%D0%B2%D1%83%D1%85_%D1%81%D1%82%D1%80%D0%BE%D0%BA_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%85%D0%B5%D1%88%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F&amp;diff=27193"/>
				<updated>2012-09-12T13:29:35Z</updated>
		
		<summary type="html">&lt;p&gt;194.85.160.130: /* Псевдокод */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Постановка задачи==&lt;br /&gt;
Имеются строки &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; такие, что элементы этих строк  &amp;lt;tex&amp;gt;-&amp;lt;/tex&amp;gt; символы из конечного алфавита &amp;lt;tex&amp;gt; \Sigma &amp;lt;/tex&amp;gt;. Говорят, что строка &amp;lt;tex&amp;gt;z[1 .. m]&amp;lt;/tex&amp;gt; является подстрокой строки &amp;lt;tex&amp;gt;s[1 .. n]&amp;lt;/tex&amp;gt;, если существует такой индекс &amp;lt;tex&amp;gt;k \in [0 .. n - m]&amp;lt;/tex&amp;gt;, что для любого &amp;lt;tex&amp;gt;i \in [1 .. m]&amp;lt;/tex&amp;gt; справедливо &amp;lt;tex&amp;gt;s[k + i] = z[i]&amp;lt;/tex&amp;gt;. Требуется найти такую строку &amp;lt;tex&amp;gt;z&amp;lt;/tex&amp;gt; максимальной длины, что &amp;lt;tex&amp;gt;z&amp;lt;/tex&amp;gt; является и подстрокой &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt;, и подстрокой &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Алгоритм==&lt;br /&gt;
Пусть длина наибольшей общей подстроки будет &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt;. Заметим, что у строк &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; обязательно найдется общая подстрока длины &amp;lt;tex&amp;gt;y \in [0 .. x]&amp;lt;/tex&amp;gt;, так как в качестве такой строки можно взять префикс наибольшей общей подстроки. Рассмотрим функцию &amp;lt;tex&amp;gt;f : [0 .. \min(|s|, |t|)] \rightarrow \{0, 1\}&amp;lt;/tex&amp;gt;, которая для &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; из области определения равна 1, если у строк &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; есть общая подстрока длины &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;, иначе она равна 0. Согласно замечанию, функция &amp;lt;tex&amp;gt;f&amp;lt;/tex&amp;gt; должна по мере возрастания &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; быть равной 1 до некоторого момента, а затем обращаться в 0. Собственно, максимальное значение, при котором функция принимает значение 1, является длиной наибольшей общей подстроки. Таким образом, требуется с помощью [[Целочисленный двоичный поиск|двоичного поиска]] найти это значение. В ходе работы придется проверять наличие общей подстроки заданной длины. Для этого будем использовать хеширование, чтобы улучшить асимптотику алгоритма. Алгоритм является эвристическим и может выдавать неверный ответ, так как совпадение хешей строк не гарантирует равенство строк. Поэтому нужно выполнить проверку нескольких случайных символов подстрок на совпадение, проиграв при этом по времени работы. Алгоритм работает следующим образом:&lt;br /&gt;
&lt;br /&gt;
1) У строки &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; хешируем подстроки заданной длины и полученные хеши записываем в Set.&lt;br /&gt;
&lt;br /&gt;
2) У строки &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; хешируем подстроки заданной длины и в случае совпадения хеша с элементом Set проверяем несколько случайных символов подстрок на совпадение.&lt;br /&gt;
&lt;br /&gt;
Хеширование будем производить так же, как и в [[Поиск подстроки в строке с использованием хеширования. Алгоритм Рабина-Карпа|алгоритме Рабина-Карпа]].&lt;br /&gt;
&lt;br /&gt;
== Псевдокод ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;f&amp;lt;/tex&amp;gt; — функция, описанная в алгоритме.&lt;br /&gt;
&lt;br /&gt;
 f(i)&lt;br /&gt;
 Записываем в &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; хэши подстрок строки &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; длины &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;&lt;br /&gt;
 '''for''' &amp;lt;tex&amp;gt;j = 1...|t| - i&amp;lt;/tex&amp;gt;&lt;br /&gt;
    Считаем хэш от подстоки &amp;lt;tex&amp;gt;t[j .. j + i]&amp;lt;/tex&amp;gt;&lt;br /&gt;
    '''if''' хэш содержится в &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;&lt;br /&gt;
      '''if''' совпали несколько случайных символов подсток&lt;br /&gt;
         '''return''' 1&lt;br /&gt;
      '''else'''&lt;br /&gt;
         '''return''' 0&lt;br /&gt;
 '''return''' 0&lt;br /&gt;
&lt;br /&gt;
==Время работы==&lt;br /&gt;
Проведем оценку асимптотики времени работы предложенного алгоритма. Посмотрим сколько нам потребуется действий на каждом шаге бинарного поиска. Во-первых, хеширование подстрок строки &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; и запись их в Set требует &amp;lt;tex&amp;gt;O(|s|)&amp;lt;/tex&amp;gt; шагов. Во-вторых, хеширование подстрок строки &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; и проверка их наличия в Set требует &amp;lt;tex&amp;gt;O(|t|)&amp;lt;/tex&amp;gt;. Проверка на совпадение нескольких символов подстрок требует константное время. Значит,на каждый шаг бинарного поиска требуется &amp;lt;tex&amp;gt;O(max(|s|, |t|))&amp;lt;/tex&amp;gt; действий. Заметим, что всего для завершения бинарного поиска потребуется &amp;lt;tex&amp;gt;O(\log(\min(|s|, |t|)))&amp;lt;/tex&amp;gt; шагов. Следовательно, суммарное время работы алгоритма будет &amp;lt;tex&amp;gt;O(\log(\min(|s|, |t|)) \cdot \max(|s|, |t|))&amp;lt;/tex&amp;gt; действий.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
* Кормен Т., Лейзерсон Ч., Ривест Р. Алгоритмы: построение и анализ. — 2-е изд. — М.: Издательский дом «Вильямс», 2007. — С. 1296.&lt;br /&gt;
&lt;br /&gt;
[[Категория:Алгоритмы и структуры данных]]&lt;br /&gt;
[[Категория:Поиск подстроки в строке]]&lt;/div&gt;</summary>
		<author><name>194.85.160.130</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D0%BE%D0%B8%D1%81%D0%BA_%D0%BD%D0%B0%D0%B8%D0%B1%D0%BE%D0%BB%D1%8C%D1%88%D0%B5%D0%B9_%D0%BE%D0%B1%D1%89%D0%B5%D0%B9_%D0%BF%D0%BE%D0%B4%D1%81%D1%82%D1%80%D0%BE%D0%BA%D0%B8_%D0%B4%D0%B2%D1%83%D1%85_%D1%81%D1%82%D1%80%D0%BE%D0%BA_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%85%D0%B5%D1%88%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F&amp;diff=27192</id>
		<title>Поиск наибольшей общей подстроки двух строк с использованием хеширования</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D0%BE%D0%B8%D1%81%D0%BA_%D0%BD%D0%B0%D0%B8%D0%B1%D0%BE%D0%BB%D1%8C%D1%88%D0%B5%D0%B9_%D0%BE%D0%B1%D1%89%D0%B5%D0%B9_%D0%BF%D0%BE%D0%B4%D1%81%D1%82%D1%80%D0%BE%D0%BA%D0%B8_%D0%B4%D0%B2%D1%83%D1%85_%D1%81%D1%82%D1%80%D0%BE%D0%BA_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%85%D0%B5%D1%88%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F&amp;diff=27192"/>
				<updated>2012-09-12T13:28:47Z</updated>
		
		<summary type="html">&lt;p&gt;194.85.160.130: /* Псевдокод */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Постановка задачи==&lt;br /&gt;
Имеются строки &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; такие, что элементы этих строк  &amp;lt;tex&amp;gt;-&amp;lt;/tex&amp;gt; символы из конечного алфавита &amp;lt;tex&amp;gt; \Sigma &amp;lt;/tex&amp;gt;. Говорят, что строка &amp;lt;tex&amp;gt;z[1 .. m]&amp;lt;/tex&amp;gt; является подстрокой строки &amp;lt;tex&amp;gt;s[1 .. n]&amp;lt;/tex&amp;gt;, если существует такой индекс &amp;lt;tex&amp;gt;k \in [0 .. n - m]&amp;lt;/tex&amp;gt;, что для любого &amp;lt;tex&amp;gt;i \in [1 .. m]&amp;lt;/tex&amp;gt; справедливо &amp;lt;tex&amp;gt;s[k + i] = z[i]&amp;lt;/tex&amp;gt;. Требуется найти такую строку &amp;lt;tex&amp;gt;z&amp;lt;/tex&amp;gt; максимальной длины, что &amp;lt;tex&amp;gt;z&amp;lt;/tex&amp;gt; является и подстрокой &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt;, и подстрокой &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Алгоритм==&lt;br /&gt;
Пусть длина наибольшей общей подстроки будет &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt;. Заметим, что у строк &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; обязательно найдется общая подстрока длины &amp;lt;tex&amp;gt;y \in [0 .. x]&amp;lt;/tex&amp;gt;, так как в качестве такой строки можно взять префикс наибольшей общей подстроки. Рассмотрим функцию &amp;lt;tex&amp;gt;f : [0 .. \min(|s|, |t|)] \rightarrow \{0, 1\}&amp;lt;/tex&amp;gt;, которая для &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; из области определения равна 1, если у строк &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; есть общая подстрока длины &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;, иначе она равна 0. Согласно замечанию, функция &amp;lt;tex&amp;gt;f&amp;lt;/tex&amp;gt; должна по мере возрастания &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; быть равной 1 до некоторого момента, а затем обращаться в 0. Собственно, максимальное значение, при котором функция принимает значение 1, является длиной наибольшей общей подстроки. Таким образом, требуется с помощью [[Целочисленный двоичный поиск|двоичного поиска]] найти это значение. В ходе работы придется проверять наличие общей подстроки заданной длины. Для этого будем использовать хеширование, чтобы улучшить асимптотику алгоритма. Алгоритм является эвристическим и может выдавать неверный ответ, так как совпадение хешей строк не гарантирует равенство строк. Поэтому нужно выполнить проверку нескольких случайных символов подстрок на совпадение, проиграв при этом по времени работы. Алгоритм работает следующим образом:&lt;br /&gt;
&lt;br /&gt;
1) У строки &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; хешируем подстроки заданной длины и полученные хеши записываем в Set.&lt;br /&gt;
&lt;br /&gt;
2) У строки &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; хешируем подстроки заданной длины и в случае совпадения хеша с элементом Set проверяем несколько случайных символов подстрок на совпадение.&lt;br /&gt;
&lt;br /&gt;
Хеширование будем производить так же, как и в [[Поиск подстроки в строке с использованием хеширования. Алгоритм Рабина-Карпа|алгоритме Рабина-Карпа]].&lt;br /&gt;
&lt;br /&gt;
== Псевдокод ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;f&amp;lt;/tex&amp;gt; — функция, описанная в алгоритме.&lt;br /&gt;
&lt;br /&gt;
 f(i)&lt;br /&gt;
 Записываем в &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; хэши подстрок строки &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; длины &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;&lt;br /&gt;
 '''for''' &amp;lt;tex&amp;gt;j = 1...|t| - i&amp;lt;/tex&amp;gt;&lt;br /&gt;
    Считаем хэш от подстоки &amp;lt;tex&amp;gt;t[j .. j + i]&amp;lt;/tex&amp;gt;&lt;br /&gt;
    '''if''' хэш содержится в &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;&lt;br /&gt;
      '''if''' совпали несколько случайных символов подсток&lt;br /&gt;
	 '''return''' 1&lt;br /&gt;
      '''else'''&lt;br /&gt;
         '''return''' 0&lt;br /&gt;
 '''return''' 0&lt;br /&gt;
&lt;br /&gt;
==Время работы==&lt;br /&gt;
Проведем оценку асимптотики времени работы предложенного алгоритма. Посмотрим сколько нам потребуется действий на каждом шаге бинарного поиска. Во-первых, хеширование подстрок строки &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; и запись их в Set требует &amp;lt;tex&amp;gt;O(|s|)&amp;lt;/tex&amp;gt; шагов. Во-вторых, хеширование подстрок строки &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; и проверка их наличия в Set требует &amp;lt;tex&amp;gt;O(|t|)&amp;lt;/tex&amp;gt;. Проверка на совпадение нескольких символов подстрок требует константное время. Значит,на каждый шаг бинарного поиска требуется &amp;lt;tex&amp;gt;O(max(|s|, |t|))&amp;lt;/tex&amp;gt; действий. Заметим, что всего для завершения бинарного поиска потребуется &amp;lt;tex&amp;gt;O(\log(\min(|s|, |t|)))&amp;lt;/tex&amp;gt; шагов. Следовательно, суммарное время работы алгоритма будет &amp;lt;tex&amp;gt;O(\log(\min(|s|, |t|)) \cdot \max(|s|, |t|))&amp;lt;/tex&amp;gt; действий.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
* Кормен Т., Лейзерсон Ч., Ривест Р. Алгоритмы: построение и анализ. — 2-е изд. — М.: Издательский дом «Вильямс», 2007. — С. 1296.&lt;br /&gt;
&lt;br /&gt;
[[Категория:Алгоритмы и структуры данных]]&lt;br /&gt;
[[Категория:Поиск подстроки в строке]]&lt;/div&gt;</summary>
		<author><name>194.85.160.130</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D0%BE%D0%B8%D1%81%D0%BA_%D0%BD%D0%B0%D0%B8%D0%B1%D0%BE%D0%BB%D1%8C%D1%88%D0%B5%D0%B9_%D0%BE%D0%B1%D1%89%D0%B5%D0%B9_%D0%BF%D0%BE%D0%B4%D1%81%D1%82%D1%80%D0%BE%D0%BA%D0%B8_%D0%B4%D0%B2%D1%83%D1%85_%D1%81%D1%82%D1%80%D0%BE%D0%BA_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%85%D0%B5%D1%88%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F&amp;diff=27191</id>
		<title>Поиск наибольшей общей подстроки двух строк с использованием хеширования</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D0%BE%D0%B8%D1%81%D0%BA_%D0%BD%D0%B0%D0%B8%D0%B1%D0%BE%D0%BB%D1%8C%D1%88%D0%B5%D0%B9_%D0%BE%D0%B1%D1%89%D0%B5%D0%B9_%D0%BF%D0%BE%D0%B4%D1%81%D1%82%D1%80%D0%BE%D0%BA%D0%B8_%D0%B4%D0%B2%D1%83%D1%85_%D1%81%D1%82%D1%80%D0%BE%D0%BA_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%85%D0%B5%D1%88%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F&amp;diff=27191"/>
				<updated>2012-09-12T13:28:01Z</updated>
		
		<summary type="html">&lt;p&gt;194.85.160.130: /* Псевдокод */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Постановка задачи==&lt;br /&gt;
Имеются строки &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; такие, что элементы этих строк  &amp;lt;tex&amp;gt;-&amp;lt;/tex&amp;gt; символы из конечного алфавита &amp;lt;tex&amp;gt; \Sigma &amp;lt;/tex&amp;gt;. Говорят, что строка &amp;lt;tex&amp;gt;z[1 .. m]&amp;lt;/tex&amp;gt; является подстрокой строки &amp;lt;tex&amp;gt;s[1 .. n]&amp;lt;/tex&amp;gt;, если существует такой индекс &amp;lt;tex&amp;gt;k \in [0 .. n - m]&amp;lt;/tex&amp;gt;, что для любого &amp;lt;tex&amp;gt;i \in [1 .. m]&amp;lt;/tex&amp;gt; справедливо &amp;lt;tex&amp;gt;s[k + i] = z[i]&amp;lt;/tex&amp;gt;. Требуется найти такую строку &amp;lt;tex&amp;gt;z&amp;lt;/tex&amp;gt; максимальной длины, что &amp;lt;tex&amp;gt;z&amp;lt;/tex&amp;gt; является и подстрокой &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt;, и подстрокой &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Алгоритм==&lt;br /&gt;
Пусть длина наибольшей общей подстроки будет &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt;. Заметим, что у строк &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; обязательно найдется общая подстрока длины &amp;lt;tex&amp;gt;y \in [0 .. x]&amp;lt;/tex&amp;gt;, так как в качестве такой строки можно взять префикс наибольшей общей подстроки. Рассмотрим функцию &amp;lt;tex&amp;gt;f : [0 .. \min(|s|, |t|)] \rightarrow \{0, 1\}&amp;lt;/tex&amp;gt;, которая для &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; из области определения равна 1, если у строк &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; есть общая подстрока длины &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;, иначе она равна 0. Согласно замечанию, функция &amp;lt;tex&amp;gt;f&amp;lt;/tex&amp;gt; должна по мере возрастания &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; быть равной 1 до некоторого момента, а затем обращаться в 0. Собственно, максимальное значение, при котором функция принимает значение 1, является длиной наибольшей общей подстроки. Таким образом, требуется с помощью [[Целочисленный двоичный поиск|двоичного поиска]] найти это значение. В ходе работы придется проверять наличие общей подстроки заданной длины. Для этого будем использовать хеширование, чтобы улучшить асимптотику алгоритма. Алгоритм является эвристическим и может выдавать неверный ответ, так как совпадение хешей строк не гарантирует равенство строк. Поэтому нужно выполнить проверку нескольких случайных символов подстрок на совпадение, проиграв при этом по времени работы. Алгоритм работает следующим образом:&lt;br /&gt;
&lt;br /&gt;
1) У строки &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; хешируем подстроки заданной длины и полученные хеши записываем в Set.&lt;br /&gt;
&lt;br /&gt;
2) У строки &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; хешируем подстроки заданной длины и в случае совпадения хеша с элементом Set проверяем несколько случайных символов подстрок на совпадение.&lt;br /&gt;
&lt;br /&gt;
Хеширование будем производить так же, как и в [[Поиск подстроки в строке с использованием хеширования. Алгоритм Рабина-Карпа|алгоритме Рабина-Карпа]].&lt;br /&gt;
&lt;br /&gt;
== Псевдокод ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;f&amp;lt;/tex&amp;gt; — функция, описанная в алгоритме.&lt;br /&gt;
&lt;br /&gt;
 f(i)&lt;br /&gt;
 Записываем в &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; хэши подстрок строки &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; длины &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;&lt;br /&gt;
 '''for''' &amp;lt;tex&amp;gt;j = 1...|t| - i&amp;lt;/tex&amp;gt;&lt;br /&gt;
    Считаем хэш от подстоки &amp;lt;tex&amp;gt;t[j .. j + i]&amp;lt;/tex&amp;gt;&lt;br /&gt;
    '''if''' хэш содержится в &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;&lt;br /&gt;
      '''if''' совпали несколько случайных символов подсток&lt;br /&gt;
	 '''return''' &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt;&lt;br /&gt;
      '''else'''&lt;br /&gt;
         '''return''' &amp;lt;tex&amp;gt;0&amp;lt;/tex&amp;gt;&lt;br /&gt;
 '''return''' &amp;lt;tex&amp;gt;0&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Время работы==&lt;br /&gt;
Проведем оценку асимптотики времени работы предложенного алгоритма. Посмотрим сколько нам потребуется действий на каждом шаге бинарного поиска. Во-первых, хеширование подстрок строки &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; и запись их в Set требует &amp;lt;tex&amp;gt;O(|s|)&amp;lt;/tex&amp;gt; шагов. Во-вторых, хеширование подстрок строки &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; и проверка их наличия в Set требует &amp;lt;tex&amp;gt;O(|t|)&amp;lt;/tex&amp;gt;. Проверка на совпадение нескольких символов подстрок требует константное время. Значит,на каждый шаг бинарного поиска требуется &amp;lt;tex&amp;gt;O(max(|s|, |t|))&amp;lt;/tex&amp;gt; действий. Заметим, что всего для завершения бинарного поиска потребуется &amp;lt;tex&amp;gt;O(\log(\min(|s|, |t|)))&amp;lt;/tex&amp;gt; шагов. Следовательно, суммарное время работы алгоритма будет &amp;lt;tex&amp;gt;O(\log(\min(|s|, |t|)) \cdot \max(|s|, |t|))&amp;lt;/tex&amp;gt; действий.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
* Кормен Т., Лейзерсон Ч., Ривест Р. Алгоритмы: построение и анализ. — 2-е изд. — М.: Издательский дом «Вильямс», 2007. — С. 1296.&lt;br /&gt;
&lt;br /&gt;
[[Категория:Алгоритмы и структуры данных]]&lt;br /&gt;
[[Категория:Поиск подстроки в строке]]&lt;/div&gt;</summary>
		<author><name>194.85.160.130</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D0%BE%D0%B8%D1%81%D0%BA_%D0%BD%D0%B0%D0%B8%D0%B1%D0%BE%D0%BB%D1%8C%D1%88%D0%B5%D0%B9_%D0%BE%D0%B1%D1%89%D0%B5%D0%B9_%D0%BF%D0%BE%D0%B4%D1%81%D1%82%D1%80%D0%BE%D0%BA%D0%B8_%D0%B4%D0%B2%D1%83%D1%85_%D1%81%D1%82%D1%80%D0%BE%D0%BA_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%85%D0%B5%D1%88%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F&amp;diff=27190</id>
		<title>Поиск наибольшей общей подстроки двух строк с использованием хеширования</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D0%BE%D0%B8%D1%81%D0%BA_%D0%BD%D0%B0%D0%B8%D0%B1%D0%BE%D0%BB%D1%8C%D1%88%D0%B5%D0%B9_%D0%BE%D0%B1%D1%89%D0%B5%D0%B9_%D0%BF%D0%BE%D0%B4%D1%81%D1%82%D1%80%D0%BE%D0%BA%D0%B8_%D0%B4%D0%B2%D1%83%D1%85_%D1%81%D1%82%D1%80%D0%BE%D0%BA_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%85%D0%B5%D1%88%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F&amp;diff=27190"/>
				<updated>2012-09-12T13:00:00Z</updated>
		
		<summary type="html">&lt;p&gt;194.85.160.130: /* Алгоритм */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Постановка задачи==&lt;br /&gt;
Имеются строки &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; такие, что элементы этих строк  &amp;lt;tex&amp;gt;-&amp;lt;/tex&amp;gt; символы из конечного алфавита &amp;lt;tex&amp;gt; \Sigma &amp;lt;/tex&amp;gt;. Говорят, что строка &amp;lt;tex&amp;gt;z[1 .. m]&amp;lt;/tex&amp;gt; является подстрокой строки &amp;lt;tex&amp;gt;s[1 .. n]&amp;lt;/tex&amp;gt;, если существует такой индекс &amp;lt;tex&amp;gt;k \in [0 .. n - m]&amp;lt;/tex&amp;gt;, что для любого &amp;lt;tex&amp;gt;i \in [1 .. m]&amp;lt;/tex&amp;gt; справедливо &amp;lt;tex&amp;gt;s[k + i] = z[i]&amp;lt;/tex&amp;gt;. Требуется найти такую строку &amp;lt;tex&amp;gt;z&amp;lt;/tex&amp;gt; максимальной длины, что &amp;lt;tex&amp;gt;z&amp;lt;/tex&amp;gt; является и подстрокой &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt;, и подстрокой &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Алгоритм==&lt;br /&gt;
Пусть длина наибольшей общей подстроки будет &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt;. Заметим, что у строк &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; обязательно найдется общая подстрока длины &amp;lt;tex&amp;gt;y \in [0 .. x]&amp;lt;/tex&amp;gt;, так как в качестве такой строки можно взять префикс наибольшей общей подстроки. Рассмотрим функцию &amp;lt;tex&amp;gt;f : [0 .. \min(|s|, |t|)] \rightarrow \{0, 1\}&amp;lt;/tex&amp;gt;, которая для &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; из области определения равна 1, если у строк &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; есть общая подстрока длины &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;, иначе она равна 0. Согласно замечанию, функция &amp;lt;tex&amp;gt;f&amp;lt;/tex&amp;gt; должна по мере возрастания &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; быть равной 1 до некоторого момента, а затем обращаться в 0. Собственно, максимальное значение, при котором функция принимает значение 1, является длиной наибольшей общей подстроки. Таким образом, требуется с помощью [[Целочисленный двоичный поиск|двоичного поиска]] найти это значение. В ходе работы придется проверять наличие общей подстроки заданной длины. Для этого будем использовать хеширование, чтобы улучшить асимптотику алгоритма. Алгоритм является эвристическим и может выдавать неверный ответ, так как совпадение хешей строк не гарантирует равенство строк. Поэтому нужно выполнить проверку нескольких случайных символов подстрок на совпадение, проиграв при этом по времени работы. Алгоритм работает следующим образом:&lt;br /&gt;
&lt;br /&gt;
1) У строки &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; хешируем подстроки заданной длины и полученные хеши записываем в Set.&lt;br /&gt;
&lt;br /&gt;
2) У строки &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; хешируем подстроки заданной длины и в случае совпадения хеша с элементом Set проверяем несколько случайных символов подстрок на совпадение.&lt;br /&gt;
&lt;br /&gt;
Хеширование будем производить так же, как и в [[Поиск подстроки в строке с использованием хеширования. Алгоритм Рабина-Карпа|алгоритме Рабина-Карпа]].&lt;br /&gt;
&lt;br /&gt;
== Псевдокод ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;f&amp;lt;/tex&amp;gt; — функция, описанная в алгоритме.&lt;br /&gt;
&lt;br /&gt;
 '''findGCS'''(s, t)&lt;br /&gt;
     n = min(|s|, |t|)&lt;br /&gt;
     left = 0&lt;br /&gt;
     right = n + 1&lt;br /&gt;
     '''while''' (right - left &amp;gt; 1):&lt;br /&gt;
         val = (left + right) / 2&lt;br /&gt;
         '''if''' ('''f'''(val) == 1)&lt;br /&gt;
             left = val&lt;br /&gt;
         '''else'''&lt;br /&gt;
             right = val&lt;br /&gt;
     '''return''' left&lt;br /&gt;
&lt;br /&gt;
==Время работы==&lt;br /&gt;
Проведем оценку асимптотики времени работы предложенного алгоритма. Посмотрим сколько нам потребуется действий на каждом шаге бинарного поиска. Во-первых, хеширование подстрок строки &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; и запись их в Set требует &amp;lt;tex&amp;gt;O(|s|)&amp;lt;/tex&amp;gt; шагов. Во-вторых, хеширование подстрок строки &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; и проверка их наличия в Set требует &amp;lt;tex&amp;gt;O(|t|)&amp;lt;/tex&amp;gt;. Проверка на совпадение нескольких символов подстрок требует константное время. Значит,на каждый шаг бинарного поиска требуется &amp;lt;tex&amp;gt;O(max(|s|, |t|))&amp;lt;/tex&amp;gt; действий. Заметим, что всего для завершения бинарного поиска потребуется &amp;lt;tex&amp;gt;O(\log(\min(|s|, |t|)))&amp;lt;/tex&amp;gt; шагов. Следовательно, суммарное время работы алгоритма будет &amp;lt;tex&amp;gt;O(\log(\min(|s|, |t|)) \cdot \max(|s|, |t|))&amp;lt;/tex&amp;gt; действий.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
* Кормен Т., Лейзерсон Ч., Ривест Р. Алгоритмы: построение и анализ. — 2-е изд. — М.: Издательский дом «Вильямс», 2007. — С. 1296.&lt;br /&gt;
&lt;br /&gt;
[[Категория:Алгоритмы и структуры данных]]&lt;br /&gt;
[[Категория:Поиск подстроки в строке]]&lt;/div&gt;</summary>
		<author><name>194.85.160.130</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%97%D0%B0%D0%B4%D0%B0%D1%87%D0%B0_%D0%BC%D0%BD%D0%BE%D0%B3%D0%BE%D0%BA%D1%80%D0%B8%D1%82%D0%B5%D1%80%D0%B8%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D0%B9_%D0%BE%D0%BF%D1%82%D0%B8%D0%BC%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8._Multiobjectivization&amp;diff=26140</id>
		<title>Задача многокритериальной оптимизации. Multiobjectivization</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%97%D0%B0%D0%B4%D0%B0%D1%87%D0%B0_%D0%BC%D0%BD%D0%BE%D0%B3%D0%BE%D0%BA%D1%80%D0%B8%D1%82%D0%B5%D1%80%D0%B8%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D0%B9_%D0%BE%D0%BF%D1%82%D0%B8%D0%BC%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8._Multiobjectivization&amp;diff=26140"/>
				<updated>2012-06-20T12:58:15Z</updated>
		
		<summary type="html">&lt;p&gt;194.85.160.130: /* Hierarchical-if-and-only-if function */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Введение ==&lt;br /&gt;
В данной статье рассматривается многокритериальная оптимизация, её задача. Рассматривается понятие Парето-фронт - множество Парето оптимальных значений. Также рассматривается задача коммивояжера и предлагается алгоритм её мультиобъективизации&lt;br /&gt;
&lt;br /&gt;
== Задача многокритериальной оптимизации ==&lt;br /&gt;
=== Постановка задачи ===&lt;br /&gt;
{{Определение &lt;br /&gt;
|definition=&lt;br /&gt;
'''Задача многокритериальной оптимизации:''' &lt;br /&gt;
:&amp;lt;math&amp;gt;maximize \{f(x) = (f_1(x),\dots,f_K(x))\}&amp;lt;/math&amp;gt;&lt;br /&gt;
:&amp;lt;math&amp;gt; x \in X&amp;lt;/math&amp;gt;&lt;br /&gt;
где &amp;lt;math&amp;gt; f(x) : X \rightarrow R^K&amp;lt;/math&amp;gt; &amp;amp;ndash; целевая вектор-функция, где &amp;lt;math&amp;gt;K \ge 2&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
Так как не существует единого решение, которое было бы максимальным для всех целевых функций, вместо него можно искать множество &amp;lt;math&amp;gt;X^* \subseteq X &amp;lt;/math&amp;gt; множество Парето оптимальных значений.&lt;br /&gt;
=== Множество Парето оптимальных значений ===&lt;br /&gt;
{{Определение &lt;br /&gt;
|definition=&lt;br /&gt;
''' Множество Парето оптимальных значений: '''&lt;br /&gt;
:&amp;lt;math&amp;gt;\forall x^* \in X^* \not\exists x \in X &amp;lt;/math&amp;gt;:&amp;lt;math&amp;gt;x \succ x^*&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;x \succ x^* \Leftrightarrow (\forall i \in 1..K, (f_i(x) \ge f_i(x^*))\land (\exists i \in 1..K, f_i(x) &amp;gt; f_i(x^*)))&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
Выражение &amp;lt;math&amp;gt;x \succ x^*&amp;lt;/math&amp;gt; означает, что &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt; ''доминирует над'' &amp;lt;math&amp;gt;x^*&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Говорят,  что &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt; ''доминирует над'' &amp;lt;math&amp;gt;x^*&amp;lt;/math&amp;gt;. по&lt;br /&gt;
Парето, если &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt; не хуже &amp;lt;math&amp;gt;x^*&amp;lt;/math&amp;gt; по всем критериям и хотя бы по одному критерию превосходит &amp;lt;math&amp;gt;x^*&amp;lt;/math&amp;gt;. &lt;br /&gt;
В таком случае в выборе &amp;lt;math&amp;gt;x^*&amp;lt;/math&amp;gt; нет  смысла, т.к. &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt; по всем параметрам не уступает, а по каким-то и превосхожит &amp;lt;math&amp;gt;x^*&amp;lt;/math&amp;gt;. Если рассматривать всего&lt;br /&gt;
два критерия  то на рис. 1  показана область&lt;br /&gt;
пространства,  доминируемая данным решением А.  Эта область  «замкнута»: &lt;br /&gt;
элементы на ее границе также доминируемы А&lt;br /&gt;
[[Файл:Dogmin points.jpg|мини|200px|Рис.1 &amp;amp;ndash; Доминируемые решения ]]&lt;br /&gt;
&lt;br /&gt;
{{Определение &lt;br /&gt;
|definition=&lt;br /&gt;
Для двух решений &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;x'&amp;lt;/math&amp;gt; говорят &amp;lt;math&amp;gt;x \sim x'&amp;lt;/math&amp;gt; тогда и только тогда, когда &amp;lt;math&amp;gt;\exists i \in 1..K \colon f_i(x) &amp;gt; f_i(x') \land \exists j \in 1..K, j \ne i \colon f_j(x') &amp;gt; f_j(x)&amp;lt;/math&amp;gt; &amp;amp;ndash; такую пару решений называют '''недоминируемой'''&lt;br /&gt;
}}&lt;br /&gt;
На рис. 2 показана граница Парето для&lt;br /&gt;
возможных решений в двухкритериальном пространстве&lt;br /&gt;
[[Файл:Pareto_front.jpg|мини|200px|Рис.2 &amp;amp;ndash; Парето фронт]]&lt;br /&gt;
Множество Парето оптимальных недоминируемых решений называется '''Парето фронтом.'''&lt;br /&gt;
&lt;br /&gt;
== Multi-objectivization ==&lt;br /&gt;
Суть метода мульти-объективизации заключается в разбитии сложной задачи с одной целевой функцией на несколько подзадач, найти для каждой подзадачи решение и выбрать оптимальное решение.&lt;br /&gt;
&lt;br /&gt;
Для выполнения оптимизации многокритериальной задачи мы должны добавить в целевую функцию новые параметры, либо должны добавить новые целевые функции.&lt;br /&gt;
&lt;br /&gt;
Сложность этой процедуры заключается в разложении проблемы на ряд мелких независимых между собой подпроблем.&lt;br /&gt;
&lt;br /&gt;
== Алгоритмы ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Hill-Climbers ===&lt;br /&gt;
{{Определение &lt;br /&gt;
|definition=&lt;br /&gt;
'''Hill-Climbers''' &amp;amp;ndash; Итеративный алгоритм, который начинается с произвольного решения проблемы, а затем пытается найти лучшее решение, постепенно изменяя его. Если изменения позволяют найти лучшее решение, алгоритм сохраняет его и повторяет и повторяет своё выполнение до тех пор, пока лучшие решения не могут быть найдены&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|''Initialization:''||&amp;lt;math&amp;gt;P \leftarrow \emptyset &amp;lt;/math&amp;gt;&amp;lt;br/&amp;gt;'''Init_pop'''&amp;lt;math&amp;gt;(P)&amp;lt;/math&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|''Main Loop:''||&amp;lt;math&amp;gt;x_1 \leftarrow &amp;lt;/math&amp;gt;'''Rand_mem'''&amp;lt;math&amp;gt;(P)&amp;lt;/math&amp;gt;,&amp;lt;math&amp;gt;x'_2 \leftarrow &amp;lt;/math&amp;gt;'''Rand_mem'''&amp;lt;math&amp;gt;(P)&amp;lt;/math&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;x'_1 \leftarrow &amp;lt;/math&amp;gt;'''Mutate'''&amp;lt;math&amp;gt;(P)&amp;lt;/math&amp;gt;,&amp;lt;math&amp;gt;x_2 \leftarrow &amp;lt;/math&amp;gt;'''Mutate'''&amp;lt;math&amp;gt;(P)&amp;lt;/math&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
'''if'''&amp;lt;math&amp;gt;(H(x_1,x'_1)+H(x_2,x'_2) &amp;gt; H(x_1,x'_2)+H(x_2,x'_1))&amp;lt;/math&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
:'''Swap'''&amp;lt;math&amp;gt;(x_1,x'_2)&amp;lt;/math&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
'''if''' &amp;lt;math&amp;gt;f(x'_1) &amp;gt; f(x_1)&amp;lt;/math&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
: &amp;lt;math&amp;gt;P \leftarrow P \cup x'_1 \setminus x_1&amp;lt;/math&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
'''if''' &amp;lt;math&amp;gt;f(x'_2) &amp;gt; f(x_2)&amp;lt;/math&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
: &amp;lt;math&amp;gt;P \leftarrow P \cup x'_2 \setminus x_2&amp;lt;/math&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|''Termination:''||'''return Best'''&amp;lt;math&amp;gt;(P)&amp;lt;/math&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Задачи ==&lt;br /&gt;
====Hierarchical-if-and-only-if function====&lt;br /&gt;
'''H-IIF''' &amp;amp;ndash; предназначена для моделирования проблемы с блочной структурой, каждый блок которой строго связан с остальными блоками.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
f(B)= \begin{cases}1,&amp;amp; \mbox{if } |B| = 1, \mbox{ else}&lt;br /&gt;
\\|B|+f(B_L)+f(B_R),&amp;amp; \mbox{if }(\forall i \{b_i=0\} \mbox{ or } \forall i \{b_i = 1 \}) &lt;br /&gt;
\\f(B_L) + f(B_R), &amp;amp; \mbox{otherwise}&lt;br /&gt;
\end{cases}&lt;br /&gt;
&amp;lt;/math&amp;gt;, &lt;br /&gt;
&lt;br /&gt;
где &amp;lt;math&amp;gt;B&amp;lt;/math&amp;gt; &amp;amp;ndash; блок бит &amp;lt;math&amp;gt;\{b_1,b_2,\dots,b_n \}, |B|&amp;lt;/math&amp;gt; &amp;amp;ndash; размер блока, а &amp;lt;math&amp;gt;B_L, B_R&amp;lt;/math&amp;gt; &amp;amp;ndash; левая и правая часть блока соответственно.&lt;br /&gt;
&lt;br /&gt;
Применяя к этой задаче мультиобъективизацию, разобьём задачу &amp;lt;math&amp;gt;f&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;-задач.&lt;br /&gt;
&lt;br /&gt;
Представим, как будет выглядеть &amp;lt;math&amp;gt;f(B)&amp;lt;/math&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
f(B)= \begin{cases}&lt;br /&gt;
0, &amp;amp; \mbox{if } |B| = 1 \mbox{ and }b_1 \neq k,  \mbox{ else}&lt;br /&gt;
\\1,&amp;amp; \mbox{if } |B| = 1 \mbox{ and }b_1 = k,  \mbox{ else}&lt;br /&gt;
\\|B|+f_k(B_L)+f_k(B_R),&amp;amp; \mbox{if }(\forall i \{b_i=k\}),&lt;br /&gt;
\\f_k(B_L) + f_k(B_R), &amp;amp; \mbox{otherwise}&lt;br /&gt;
\end{cases}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;math&amp;gt;f_0(x)&amp;lt;/math&amp;gt; &amp;amp;ndash; первая цель; &amp;lt;math&amp;gt;f_0(x)&amp;lt;/math&amp;gt; &amp;amp;ndash; вторая цель.&lt;br /&gt;
&lt;br /&gt;
Данный подход помогает избежать проблему локальных максимумов (минимумов).&lt;br /&gt;
&lt;br /&gt;
==== Задача коммивояжера ====&lt;br /&gt;
Задача коммивояжера (TSP)является наиболее известно из всего класса &amp;lt;math&amp;gt;NP&amp;lt;/math&amp;gt;-сложных задач.&lt;br /&gt;
Формулируется задача следующим образом: &lt;br /&gt;
&lt;br /&gt;
Задано &amp;lt;math&amp;gt;C=\{c_1,c_2,\dots,c_N\} &amp;lt;/math&amp;gt; &amp;amp;ndash; множество городов и для каждой пары &amp;lt;math&amp;gt;\{c_i,c_j\}&amp;lt;/math&amp;gt; задано расстояние. Наша цель &amp;amp;ndash; найти цепь из городов, минимизирующую величину:&lt;br /&gt;
:&amp;lt;math&amp;gt;\sum^{N-1}_{i=1} d(C_{\pi(i)},C_{\pi(i+1)})+d(C_{\pi(N)},C_{\pi(1)})&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Применяя к этой задаче мультиобъктивизацию, нужно разбить её на подзадачи. TSP &amp;amp;ndash; является &amp;lt;math&amp;gt;NP&amp;lt;/math&amp;gt;-сложной именно потому, что нет хорошего разложения этой задачи.&lt;br /&gt;
Тем не менее задачу можно разбить на две или больше подтуров, каждый из которых мы можем минимизировать.&lt;br /&gt;
&lt;br /&gt;
Представим подтуры в виде двух городов. Тогда наша задача примет вид:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;minimize\{f(\pi,a,b) = (f_1(\pi,a,b),f_2(\pi,a,b))\}&amp;lt;/math&amp;gt;&lt;br /&gt;
::'''where'''&amp;lt;math&amp;gt;f_1(\pi,a,b)=\sum^{\pi^{-1}(b)-1}_{i=\pi^{-1}(a)} d(C_{\pi(i)},C_{\pi(i+1)})&amp;lt;/math&amp;gt;&lt;br /&gt;
::'''and'''  &amp;lt;math&amp;gt;f_2(\pi,a,b)=\sum^{N-1}_{i=\pi^{-1}(b)} d(C_{\pi(i)},C_{\pi(i+1)}) + \sum^{\pi^{-1}(a)-1}_{i=1} d(C_{\pi(i)},C_{\pi(i+1)}) &amp;lt;/math&amp;gt; &amp;lt;math&amp;gt;+ d(C_{\pi(N)},C_{\pi(1)})&amp;lt;/math&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt; &amp;amp;ndash; два города, указанных ''априори''. Если &amp;lt;math&amp;gt;\pi (a) &amp;lt; \pi (b)&amp;lt;/math&amp;gt;, меняем их местами.&lt;br /&gt;
&lt;br /&gt;
Предполагается, что &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt; выбраны произвольно.&lt;br /&gt;
&lt;br /&gt;
== Источники ==&lt;br /&gt;
* [http://ru.wikipedia.org/wiki/Многокритериальная_оптимизация Википедия: Многокритериальная оптимизация]&lt;br /&gt;
* [http://rain.ifmo.ru/~tsarev/teaching/ea-2012/lectures/3/multiobjectivization.pdf Knowles J., Watson R., Corne D. Reducing Local Optima in Single-Objective Problems by Multi-objectivization]&lt;br /&gt;
* [http://en.wikipedia.org/wiki/Multiobjective_optimization Wikipedia: Multiobjective optimization]&lt;br /&gt;
* [http://en.wikipedia.org/wiki/Hill_climbing Wikipedia: Hill climbing]&lt;/div&gt;</summary>
		<author><name>194.85.160.130</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%97%D0%B0%D0%B4%D0%B0%D1%87%D0%B0_%D0%BC%D0%BD%D0%BE%D0%B3%D0%BE%D0%BA%D1%80%D0%B8%D1%82%D0%B5%D1%80%D0%B8%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D0%B9_%D0%BE%D0%BF%D1%82%D0%B8%D0%BC%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8._Multiobjectivization&amp;diff=26138</id>
		<title>Задача многокритериальной оптимизации. Multiobjectivization</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%97%D0%B0%D0%B4%D0%B0%D1%87%D0%B0_%D0%BC%D0%BD%D0%BE%D0%B3%D0%BE%D0%BA%D1%80%D0%B8%D1%82%D0%B5%D1%80%D0%B8%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D0%B9_%D0%BE%D0%BF%D1%82%D0%B8%D0%BC%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8._Multiobjectivization&amp;diff=26138"/>
				<updated>2012-06-20T12:55:36Z</updated>
		
		<summary type="html">&lt;p&gt;194.85.160.130: /* Задачи */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Введение ==&lt;br /&gt;
В данной статье рассматривается многокритериальная оптимизация, её задача. Рассматривается понятие Парето-фронт - множество Парето оптимальных значений. Также рассматривается задача коммивояжера и предлагается алгоритм её мультиобъективизации&lt;br /&gt;
&lt;br /&gt;
== Задача многокритериальной оптимизации ==&lt;br /&gt;
=== Постановка задачи ===&lt;br /&gt;
{{Определение &lt;br /&gt;
|definition=&lt;br /&gt;
'''Задача многокритериальной оптимизации:''' &lt;br /&gt;
:&amp;lt;math&amp;gt;maximize \{f(x) = (f_1(x),\dots,f_K(x))\}&amp;lt;/math&amp;gt;&lt;br /&gt;
:&amp;lt;math&amp;gt; x \in X&amp;lt;/math&amp;gt;&lt;br /&gt;
где &amp;lt;math&amp;gt; f(x) : X \rightarrow R^K&amp;lt;/math&amp;gt; &amp;amp;ndash; целевая вектор-функция, где &amp;lt;math&amp;gt;K \ge 2&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
Так как не существует единого решение, которое было бы максимальным для всех целевых функций, вместо него можно искать множество &amp;lt;math&amp;gt;X^* \subseteq X &amp;lt;/math&amp;gt; множество Парето оптимальных значений.&lt;br /&gt;
=== Множество Парето оптимальных значений ===&lt;br /&gt;
{{Определение &lt;br /&gt;
|definition=&lt;br /&gt;
''' Множество Парето оптимальных значений: '''&lt;br /&gt;
:&amp;lt;math&amp;gt;\forall x^* \in X^* \not\exists x \in X &amp;lt;/math&amp;gt;:&amp;lt;math&amp;gt;x \succ x^*&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;x \succ x^* \Leftrightarrow (\forall i \in 1..K, (f_i(x) \ge f_i(x^*))\land (\exists i \in 1..K, f_i(x) &amp;gt; f_i(x^*)))&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
Выражение &amp;lt;math&amp;gt;x \succ x^*&amp;lt;/math&amp;gt; означает, что &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt; ''доминирует над'' &amp;lt;math&amp;gt;x^*&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Говорят,  что &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt; ''доминирует над'' &amp;lt;math&amp;gt;x^*&amp;lt;/math&amp;gt;. по&lt;br /&gt;
Парето, если &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt; не хуже &amp;lt;math&amp;gt;x^*&amp;lt;/math&amp;gt; по всем критериям и хотя бы по одному критерию превосходит &amp;lt;math&amp;gt;x^*&amp;lt;/math&amp;gt;. &lt;br /&gt;
В таком случае в выборе &amp;lt;math&amp;gt;x^*&amp;lt;/math&amp;gt; нет  смысла, т.к. &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt; по всем параметрам не уступает, а по каким-то и превосхожит &amp;lt;math&amp;gt;x^*&amp;lt;/math&amp;gt;. Если рассматривать всего&lt;br /&gt;
два критерия  то на рис. 1  показана область&lt;br /&gt;
пространства,  доминируемая данным решением А.  Эта область  «замкнута»: &lt;br /&gt;
элементы на ее границе также доминируемы А&lt;br /&gt;
[[Файл:Dogmin points.jpg|мини|200px|Рис.1 &amp;amp;ndash; Доминируемые решения ]]&lt;br /&gt;
&lt;br /&gt;
{{Определение &lt;br /&gt;
|definition=&lt;br /&gt;
Для двух решений &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;x'&amp;lt;/math&amp;gt; говорят &amp;lt;math&amp;gt;x \sim x'&amp;lt;/math&amp;gt; тогда и только тогда, когда &amp;lt;math&amp;gt;\exists i \in 1..K \colon f_i(x) &amp;gt; f_i(x') \land \exists j \in 1..K, j \ne i \colon f_j(x') &amp;gt; f_j(x)&amp;lt;/math&amp;gt; &amp;amp;ndash; такую пару решений называют '''недоминируемой'''&lt;br /&gt;
}}&lt;br /&gt;
На рис. 2 показана граница Парето для&lt;br /&gt;
возможных решений в двухкритериальном пространстве&lt;br /&gt;
[[Файл:Pareto_front.jpg|мини|200px|Рис.2 &amp;amp;ndash; Парето фронт]]&lt;br /&gt;
Множество Парето оптимальных недоминируемых решений называется '''Парето фронтом.'''&lt;br /&gt;
&lt;br /&gt;
== Multi-objectivization ==&lt;br /&gt;
Суть метода мульти-объективизации заключается в разбитии сложной задачи с одной целевой функцией на несколько подзадач, найти для каждой подзадачи решение и выбрать оптимальное решение.&lt;br /&gt;
&lt;br /&gt;
Для выполнения оптимизации многокритериальной задачи мы должны добавить в целевую функцию новые параметры, либо должны добавить новые целевые функции.&lt;br /&gt;
&lt;br /&gt;
Сложность этой процедуры заключается в разложении проблемы на ряд мелких независимых между собой подпроблем.&lt;br /&gt;
&lt;br /&gt;
== Алгоритмы ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Hill-Climbers ===&lt;br /&gt;
{{Определение &lt;br /&gt;
|definition=&lt;br /&gt;
'''Hill-Climbers''' &amp;amp;ndash; Итеративный алгоритм, который начинается с произвольного решения проблемы, а затем пытается найти лучшее решение, постепенно изменяя его. Если изменения позволяют найти лучшее решение, алгоритм сохраняет его и повторяет и повторяет своё выполнение до тех пор, пока лучшие решения не могут быть найдены&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|''Initialization:''||&amp;lt;math&amp;gt;P \leftarrow \emptyset &amp;lt;/math&amp;gt;&amp;lt;br/&amp;gt;'''Init_pop'''&amp;lt;math&amp;gt;(P)&amp;lt;/math&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|''Main Loop:''||&amp;lt;math&amp;gt;x_1 \leftarrow &amp;lt;/math&amp;gt;'''Rand_mem'''&amp;lt;math&amp;gt;(P)&amp;lt;/math&amp;gt;,&amp;lt;math&amp;gt;x'_2 \leftarrow &amp;lt;/math&amp;gt;'''Rand_mem'''&amp;lt;math&amp;gt;(P)&amp;lt;/math&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;x'_1 \leftarrow &amp;lt;/math&amp;gt;'''Mutate'''&amp;lt;math&amp;gt;(P)&amp;lt;/math&amp;gt;,&amp;lt;math&amp;gt;x_2 \leftarrow &amp;lt;/math&amp;gt;'''Mutate'''&amp;lt;math&amp;gt;(P)&amp;lt;/math&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
'''if'''&amp;lt;math&amp;gt;(H(x_1,x'_1)+H(x_2,x'_2) &amp;gt; H(x_1,x'_2)+H(x_2,x'_1))&amp;lt;/math&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
:'''Swap'''&amp;lt;math&amp;gt;(x_1,x'_2)&amp;lt;/math&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
'''if''' &amp;lt;math&amp;gt;f(x'_1) &amp;gt; f(x_1)&amp;lt;/math&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
: &amp;lt;math&amp;gt;P \leftarrow P \cup x'_1 \setminus x_1&amp;lt;/math&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
'''if''' &amp;lt;math&amp;gt;f(x'_2) &amp;gt; f(x_2)&amp;lt;/math&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
: &amp;lt;math&amp;gt;P \leftarrow P \cup x'_2 \setminus x_2&amp;lt;/math&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|''Termination:''||'''return Best'''&amp;lt;math&amp;gt;(P)&amp;lt;/math&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Задачи ==&lt;br /&gt;
====Hierarchical-if-and-only-if function====&lt;br /&gt;
'''H-IIF''' &amp;amp;ndash; предназначена для моделирования проблемы с блочной структурой, каждый блок которой строго связан с остальными блоками.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
f(B)= \begin{cases}1,&amp;amp; \mbox{if } |B| = 1, \mbox{ else}&lt;br /&gt;
\\|B|+f(B_L)+f(B_R),&amp;amp; \mbox{if }(\forall i \{b_i=0\} \mbox{ or } \forall i \{b_i = 1 \}) &lt;br /&gt;
\\f(B_L) + f(B_R), &amp;amp; \mbox{otherwise}&lt;br /&gt;
\end{cases}&lt;br /&gt;
&amp;lt;/math&amp;gt;, &lt;br /&gt;
&lt;br /&gt;
где &amp;lt;math&amp;gt;B&amp;lt;/math&amp;gt; &amp;amp;ndash; блок бит &amp;lt;math&amp;gt;\{b_1,b_2,\dots,b_n \}, |B|&amp;lt;/math&amp;gt; &amp;amp;ndash; размер блока, а &amp;lt;math&amp;gt;B_L, B_R&amp;lt;/math&amp;gt; &amp;amp;ndash; левая и правая часть блока соответственно.&lt;br /&gt;
&lt;br /&gt;
Применяя к этой задаче мультиобъективизацию, разобьём задачу &amp;lt;math&amp;gt;f&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;-задач.&lt;br /&gt;
&lt;br /&gt;
Представим, как будет выглядеть &amp;lt;math&amp;gt;f(B)&amp;lt;/math&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
f(B)= \begin{cases}&lt;br /&gt;
0, &amp;amp; \mbox{if } |B| = 1 \mbox{ and }b_1 \neq k,  \mbox{ else}&lt;br /&gt;
\\1,&amp;amp; \mbox{if } |B| = 1 \mbox{ and }b_1 = k,  \mbox{ else}&lt;br /&gt;
\\|B|+f_k(B_L)+f_k(B_R),&amp;amp; \mbox{if }(\forall i \{b_i=k\}),&lt;br /&gt;
\\f_k(B_L) + f_k(B_R), &amp;amp; \mbox{otherwise}&lt;br /&gt;
\end{cases}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;math&amp;gt;f_0(x)&amp;lt;/math&amp;gt; &amp;amp;ndash; первая цель; &amp;lt;math&amp;gt;f_0(x)&amp;lt;/math&amp;gt; &amp;amp;ndash; вторая цель.&lt;br /&gt;
&lt;br /&gt;
==== Задача коммивояжера ====&lt;br /&gt;
Задача коммивояжера (TSP)является наиболее известно из всего класса &amp;lt;math&amp;gt;NP&amp;lt;/math&amp;gt;-сложных задач.&lt;br /&gt;
Формулируется задача следующим образом: &lt;br /&gt;
&lt;br /&gt;
Задано &amp;lt;math&amp;gt;C=\{c_1,c_2,\dots,c_N\} &amp;lt;/math&amp;gt; &amp;amp;ndash; множество городов и для каждой пары &amp;lt;math&amp;gt;\{c_i,c_j\}&amp;lt;/math&amp;gt; задано расстояние. Наша цель &amp;amp;ndash; найти цепь из городов, минимизирующую величину:&lt;br /&gt;
:&amp;lt;math&amp;gt;\sum^{N-1}_{i=1} d(C_{\pi(i)},C_{\pi(i+1)})+d(C_{\pi(N)},C_{\pi(1)})&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Применяя к этой задаче мультиобъктивизацию, нужно разбить её на подзадачи. TSP &amp;amp;ndash; является &amp;lt;math&amp;gt;NP&amp;lt;/math&amp;gt;-сложной именно потому, что нет хорошего разложения этой задачи.&lt;br /&gt;
Тем не менее задачу можно разбить на две или больше подтуров, каждый из которых мы можем минимизировать.&lt;br /&gt;
&lt;br /&gt;
Представим подтуры в виде двух городов. Тогда наша задача примет вид:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;minimize\{f(\pi,a,b) = (f_1(\pi,a,b),f_2(\pi,a,b))\}&amp;lt;/math&amp;gt;&lt;br /&gt;
::'''where'''&amp;lt;math&amp;gt;f_1(\pi,a,b)=\sum^{\pi^{-1}(b)-1}_{i=\pi^{-1}(a)} d(C_{\pi(i)},C_{\pi(i+1)})&amp;lt;/math&amp;gt;&lt;br /&gt;
::'''and'''  &amp;lt;math&amp;gt;f_2(\pi,a,b)=\sum^{N-1}_{i=\pi^{-1}(b)} d(C_{\pi(i)},C_{\pi(i+1)}) + \sum^{\pi^{-1}(a)-1}_{i=1} d(C_{\pi(i)},C_{\pi(i+1)}) &amp;lt;/math&amp;gt; &amp;lt;math&amp;gt;+ d(C_{\pi(N)},C_{\pi(1)})&amp;lt;/math&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt; &amp;amp;ndash; два города, указанных ''априори''. Если &amp;lt;math&amp;gt;\pi (a) &amp;lt; \pi (b)&amp;lt;/math&amp;gt;, меняем их местами.&lt;br /&gt;
&lt;br /&gt;
Предполагается, что &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt; выбраны произвольно.&lt;br /&gt;
&lt;br /&gt;
== Источники ==&lt;br /&gt;
* [http://ru.wikipedia.org/wiki/Многокритериальная_оптимизация Википедия: Многокритериальная оптимизация]&lt;br /&gt;
* [http://rain.ifmo.ru/~tsarev/teaching/ea-2012/lectures/3/multiobjectivization.pdf Knowles J., Watson R., Corne D. Reducing Local Optima in Single-Objective Problems by Multi-objectivization]&lt;br /&gt;
* [http://en.wikipedia.org/wiki/Multiobjective_optimization Wikipedia: Multiobjective optimization]&lt;br /&gt;
* [http://en.wikipedia.org/wiki/Hill_climbing Wikipedia: Hill climbing]&lt;/div&gt;</summary>
		<author><name>194.85.160.130</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%97%D0%B0%D0%B4%D0%B0%D1%87%D0%B0_%D0%BC%D0%BD%D0%BE%D0%B3%D0%BE%D0%BA%D1%80%D0%B8%D1%82%D0%B5%D1%80%D0%B8%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D0%B9_%D0%BE%D0%BF%D1%82%D0%B8%D0%BC%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8._Multiobjectivization&amp;diff=26137</id>
		<title>Задача многокритериальной оптимизации. Multiobjectivization</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%97%D0%B0%D0%B4%D0%B0%D1%87%D0%B0_%D0%BC%D0%BD%D0%BE%D0%B3%D0%BE%D0%BA%D1%80%D0%B8%D1%82%D0%B5%D1%80%D0%B8%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D0%B9_%D0%BE%D0%BF%D1%82%D0%B8%D0%BC%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8._Multiobjectivization&amp;diff=26137"/>
				<updated>2012-06-20T12:54:56Z</updated>
		
		<summary type="html">&lt;p&gt;194.85.160.130: /* Задачи */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Введение ==&lt;br /&gt;
В данной статье рассматривается многокритериальная оптимизация, её задача. Рассматривается понятие Парето-фронт - множество Парето оптимальных значений. Также рассматривается задача коммивояжера и предлагается алгоритм её мультиобъективизации&lt;br /&gt;
&lt;br /&gt;
== Задача многокритериальной оптимизации ==&lt;br /&gt;
=== Постановка задачи ===&lt;br /&gt;
{{Определение &lt;br /&gt;
|definition=&lt;br /&gt;
'''Задача многокритериальной оптимизации:''' &lt;br /&gt;
:&amp;lt;math&amp;gt;maximize \{f(x) = (f_1(x),\dots,f_K(x))\}&amp;lt;/math&amp;gt;&lt;br /&gt;
:&amp;lt;math&amp;gt; x \in X&amp;lt;/math&amp;gt;&lt;br /&gt;
где &amp;lt;math&amp;gt; f(x) : X \rightarrow R^K&amp;lt;/math&amp;gt; &amp;amp;ndash; целевая вектор-функция, где &amp;lt;math&amp;gt;K \ge 2&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
Так как не существует единого решение, которое было бы максимальным для всех целевых функций, вместо него можно искать множество &amp;lt;math&amp;gt;X^* \subseteq X &amp;lt;/math&amp;gt; множество Парето оптимальных значений.&lt;br /&gt;
=== Множество Парето оптимальных значений ===&lt;br /&gt;
{{Определение &lt;br /&gt;
|definition=&lt;br /&gt;
''' Множество Парето оптимальных значений: '''&lt;br /&gt;
:&amp;lt;math&amp;gt;\forall x^* \in X^* \not\exists x \in X &amp;lt;/math&amp;gt;:&amp;lt;math&amp;gt;x \succ x^*&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;x \succ x^* \Leftrightarrow (\forall i \in 1..K, (f_i(x) \ge f_i(x^*))\land (\exists i \in 1..K, f_i(x) &amp;gt; f_i(x^*)))&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
Выражение &amp;lt;math&amp;gt;x \succ x^*&amp;lt;/math&amp;gt; означает, что &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt; ''доминирует над'' &amp;lt;math&amp;gt;x^*&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Говорят,  что &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt; ''доминирует над'' &amp;lt;math&amp;gt;x^*&amp;lt;/math&amp;gt;. по&lt;br /&gt;
Парето, если &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt; не хуже &amp;lt;math&amp;gt;x^*&amp;lt;/math&amp;gt; по всем критериям и хотя бы по одному критерию превосходит &amp;lt;math&amp;gt;x^*&amp;lt;/math&amp;gt;. &lt;br /&gt;
В таком случае в выборе &amp;lt;math&amp;gt;x^*&amp;lt;/math&amp;gt; нет  смысла, т.к. &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt; по всем параметрам не уступает, а по каким-то и превосхожит &amp;lt;math&amp;gt;x^*&amp;lt;/math&amp;gt;. Если рассматривать всего&lt;br /&gt;
два критерия  то на рис. 1  показана область&lt;br /&gt;
пространства,  доминируемая данным решением А.  Эта область  «замкнута»: &lt;br /&gt;
элементы на ее границе также доминируемы А&lt;br /&gt;
[[Файл:Dogmin points.jpg|мини|200px|Рис.1 &amp;amp;ndash; Доминируемые решения ]]&lt;br /&gt;
&lt;br /&gt;
{{Определение &lt;br /&gt;
|definition=&lt;br /&gt;
Для двух решений &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;x'&amp;lt;/math&amp;gt; говорят &amp;lt;math&amp;gt;x \sim x'&amp;lt;/math&amp;gt; тогда и только тогда, когда &amp;lt;math&amp;gt;\exists i \in 1..K \colon f_i(x) &amp;gt; f_i(x') \land \exists j \in 1..K, j \ne i \colon f_j(x') &amp;gt; f_j(x)&amp;lt;/math&amp;gt; &amp;amp;ndash; такую пару решений называют '''недоминируемой'''&lt;br /&gt;
}}&lt;br /&gt;
На рис. 2 показана граница Парето для&lt;br /&gt;
возможных решений в двухкритериальном пространстве&lt;br /&gt;
[[Файл:Pareto_front.jpg|мини|200px|Рис.2 &amp;amp;ndash; Парето фронт]]&lt;br /&gt;
Множество Парето оптимальных недоминируемых решений называется '''Парето фронтом.'''&lt;br /&gt;
&lt;br /&gt;
== Multi-objectivization ==&lt;br /&gt;
Суть метода мульти-объективизации заключается в разбитии сложной задачи с одной целевой функцией на несколько подзадач, найти для каждой подзадачи решение и выбрать оптимальное решение.&lt;br /&gt;
&lt;br /&gt;
Для выполнения оптимизации многокритериальной задачи мы должны добавить в целевую функцию новые параметры, либо должны добавить новые целевые функции.&lt;br /&gt;
&lt;br /&gt;
Сложность этой процедуры заключается в разложении проблемы на ряд мелких независимых между собой подпроблем.&lt;br /&gt;
&lt;br /&gt;
== Алгоритмы ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Hill-Climbers ===&lt;br /&gt;
{{Определение &lt;br /&gt;
|definition=&lt;br /&gt;
'''Hill-Climbers''' &amp;amp;ndash; Итеративный алгоритм, который начинается с произвольного решения проблемы, а затем пытается найти лучшее решение, постепенно изменяя его. Если изменения позволяют найти лучшее решение, алгоритм сохраняет его и повторяет и повторяет своё выполнение до тех пор, пока лучшие решения не могут быть найдены&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|''Initialization:''||&amp;lt;math&amp;gt;P \leftarrow \emptyset &amp;lt;/math&amp;gt;&amp;lt;br/&amp;gt;'''Init_pop'''&amp;lt;math&amp;gt;(P)&amp;lt;/math&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|''Main Loop:''||&amp;lt;math&amp;gt;x_1 \leftarrow &amp;lt;/math&amp;gt;'''Rand_mem'''&amp;lt;math&amp;gt;(P)&amp;lt;/math&amp;gt;,&amp;lt;math&amp;gt;x'_2 \leftarrow &amp;lt;/math&amp;gt;'''Rand_mem'''&amp;lt;math&amp;gt;(P)&amp;lt;/math&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;x'_1 \leftarrow &amp;lt;/math&amp;gt;'''Mutate'''&amp;lt;math&amp;gt;(P)&amp;lt;/math&amp;gt;,&amp;lt;math&amp;gt;x_2 \leftarrow &amp;lt;/math&amp;gt;'''Mutate'''&amp;lt;math&amp;gt;(P)&amp;lt;/math&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
'''if'''&amp;lt;math&amp;gt;(H(x_1,x'_1)+H(x_2,x'_2) &amp;gt; H(x_1,x'_2)+H(x_2,x'_1))&amp;lt;/math&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
:'''Swap'''&amp;lt;math&amp;gt;(x_1,x'_2)&amp;lt;/math&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
'''if''' &amp;lt;math&amp;gt;f(x'_1) &amp;gt; f(x_1)&amp;lt;/math&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
: &amp;lt;math&amp;gt;P \leftarrow P \cup x'_1 \setminus x_1&amp;lt;/math&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
'''if''' &amp;lt;math&amp;gt;f(x'_2) &amp;gt; f(x_2)&amp;lt;/math&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
: &amp;lt;math&amp;gt;P \leftarrow P \cup x'_2 \setminus x_2&amp;lt;/math&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|''Termination:''||'''return Best'''&amp;lt;math&amp;gt;(P)&amp;lt;/math&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Задачи ==&lt;br /&gt;
====Hierarchical-if-and-only-if function====&lt;br /&gt;
'''H-IIF''' &amp;amp;ndash; предназначена для моделирования проблемы с блочной структурой, каждый блок которой строго связан с остальными блоками.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
f(B)= \begin{cases}1,&amp;amp; \mbox{if } |B| = 1, \mbox{ else}&lt;br /&gt;
\\|B|+f(B_L)+f(B_R),&amp;amp; \mbox{if }(\forall i \{b_i=0\} \mbox{ or } \forall i \{b_i = 1 \}) &lt;br /&gt;
\\f(B_L) + f(B_R), &amp;amp; \mbox{otherwise}&lt;br /&gt;
\end{cases}&lt;br /&gt;
&amp;lt;/math&amp;gt;, &lt;br /&gt;
&lt;br /&gt;
где &amp;lt;math&amp;gt;B&amp;lt;/math&amp;gt; &amp;amp;ndash; блок бит &amp;lt;math&amp;gt;\{b_1,b_2,\dots,b_n \}, |B|&amp;lt;/math&amp;gt; &amp;amp;ndash; размер блока, а &amp;lt;math&amp;gt;B_L, B_R&amp;lt;/math&amp;gt; &amp;amp;ndash; левая и правая часть блока соответственно.&lt;br /&gt;
&lt;br /&gt;
Применяя к этой задаче мультиобъективизацию, разобьём задачу &amp;lt;math&amp;gt;f&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;-задач.&lt;br /&gt;
&lt;br /&gt;
Представим, как будет выглядеть &amp;lt;math&amp;gt;f(B)&amp;lt;/math&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
f(B)= \begin{cases}&lt;br /&gt;
0, &amp;amp; \mbox{if } |B| = 1 \mbox{ and }b_1 \neq k,  \mbox{ else}&lt;br /&gt;
\\1,&amp;amp; \mbox{if } |B| = 1 \mbox{ and }b_1 = k,  \mbox{ else}&lt;br /&gt;
\\|B|+f_k(B_L)+f_k(B_R),&amp;amp; \mbox{if }(\forall i \{b_i=k\},&lt;br /&gt;
\\f_k(B_L) + f_k(B_R), &amp;amp; \mbox{otherwise}&lt;br /&gt;
\end{cases}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;math&amp;gt;f_0(x)&amp;lt;/math&amp;gt; &amp;amp;ndash; первая цель; &amp;lt;math&amp;gt;f_0(x)&amp;lt;/math&amp;gt; &amp;amp;ndash; вторая цель.&lt;br /&gt;
&lt;br /&gt;
==== Задача коммивояжера ====&lt;br /&gt;
Задача коммивояжера (TSP)является наиболее известно из всего класса &amp;lt;math&amp;gt;NP&amp;lt;/math&amp;gt;-сложных задач.&lt;br /&gt;
Формулируется задача следующим образом: &lt;br /&gt;
&lt;br /&gt;
Задано &amp;lt;math&amp;gt;C=\{c_1,c_2,\dots,c_N\} &amp;lt;/math&amp;gt; &amp;amp;ndash; множество городов и для каждой пары &amp;lt;math&amp;gt;\{c_i,c_j\}&amp;lt;/math&amp;gt; задано расстояние. Наша цель &amp;amp;ndash; найти цепь из городов, минимизирующую величину:&lt;br /&gt;
:&amp;lt;math&amp;gt;\sum^{N-1}_{i=1} d(C_{\pi(i)},C_{\pi(i+1)})+d(C_{\pi(N)},C_{\pi(1)})&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Применяя к этой задаче мультиобъктивизацию, нужно разбить её на подзадачи. TSP &amp;amp;ndash; является &amp;lt;math&amp;gt;NP&amp;lt;/math&amp;gt;-сложной именно потому, что нет хорошего разложения этой задачи.&lt;br /&gt;
Тем не менее задачу можно разбить на две или больше подтуров, каждый из которых мы можем минимизировать.&lt;br /&gt;
&lt;br /&gt;
Представим подтуры в виде двух городов. Тогда наша задача примет вид:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;minimize\{f(\pi,a,b) = (f_1(\pi,a,b),f_2(\pi,a,b))\}&amp;lt;/math&amp;gt;&lt;br /&gt;
::'''where'''&amp;lt;math&amp;gt;f_1(\pi,a,b)=\sum^{\pi^{-1}(b)-1}_{i=\pi^{-1}(a)} d(C_{\pi(i)},C_{\pi(i+1)})&amp;lt;/math&amp;gt;&lt;br /&gt;
::'''and'''  &amp;lt;math&amp;gt;f_2(\pi,a,b)=\sum^{N-1}_{i=\pi^{-1}(b)} d(C_{\pi(i)},C_{\pi(i+1)}) + \sum^{\pi^{-1}(a)-1}_{i=1} d(C_{\pi(i)},C_{\pi(i+1)}) &amp;lt;/math&amp;gt; &amp;lt;math&amp;gt;+ d(C_{\pi(N)},C_{\pi(1)})&amp;lt;/math&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt; &amp;amp;ndash; два города, указанных ''априори''. Если &amp;lt;math&amp;gt;\pi (a) &amp;lt; \pi (b)&amp;lt;/math&amp;gt;, меняем их местами.&lt;br /&gt;
&lt;br /&gt;
Предполагается, что &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt; выбраны произвольно.&lt;br /&gt;
&lt;br /&gt;
== Источники ==&lt;br /&gt;
* [http://ru.wikipedia.org/wiki/Многокритериальная_оптимизация Википедия: Многокритериальная оптимизация]&lt;br /&gt;
* [http://rain.ifmo.ru/~tsarev/teaching/ea-2012/lectures/3/multiobjectivization.pdf Knowles J., Watson R., Corne D. Reducing Local Optima in Single-Objective Problems by Multi-objectivization]&lt;br /&gt;
* [http://en.wikipedia.org/wiki/Multiobjective_optimization Wikipedia: Multiobjective optimization]&lt;br /&gt;
* [http://en.wikipedia.org/wiki/Hill_climbing Wikipedia: Hill climbing]&lt;/div&gt;</summary>
		<author><name>194.85.160.130</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%97%D0%B0%D0%B4%D0%B0%D1%87%D0%B0_%D0%BC%D0%BD%D0%BE%D0%B3%D0%BE%D0%BA%D1%80%D0%B8%D1%82%D0%B5%D1%80%D0%B8%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D0%B9_%D0%BE%D0%BF%D1%82%D0%B8%D0%BC%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8._Multiobjectivization&amp;diff=26096</id>
		<title>Задача многокритериальной оптимизации. Multiobjectivization</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%97%D0%B0%D0%B4%D0%B0%D1%87%D0%B0_%D0%BC%D0%BD%D0%BE%D0%B3%D0%BE%D0%BA%D1%80%D0%B8%D1%82%D0%B5%D1%80%D0%B8%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D0%B9_%D0%BE%D0%BF%D1%82%D0%B8%D0%BC%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8._Multiobjectivization&amp;diff=26096"/>
				<updated>2012-06-20T11:41:32Z</updated>
		
		<summary type="html">&lt;p&gt;194.85.160.130: /* Введение */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Введение ==&lt;br /&gt;
В данной статье рассматривается многокритериальная оптимизация, её задача. Рассматривается понятие Парето-фронт - множество Парето оптимальных значений. Также рассматривается задача коммивояжера и предлагается алгоритм её мультиобъективизации&lt;br /&gt;
&lt;br /&gt;
== Задача многокритериальной оптимизации ==&lt;br /&gt;
=== Постановка задачи ===&lt;br /&gt;
{{Определение &lt;br /&gt;
|definition=&lt;br /&gt;
'''Задача многокритериальной оптимизации:''' &lt;br /&gt;
:&amp;lt;math&amp;gt;maximize \{f(x) = (f_1(x),\dots,f_K(x))\}&amp;lt;/math&amp;gt;&lt;br /&gt;
:&amp;lt;math&amp;gt; x \in X&amp;lt;/math&amp;gt;&lt;br /&gt;
где &amp;lt;math&amp;gt; f(x) : X \rightarrow R^K&amp;lt;/math&amp;gt; &amp;amp;ndash; целевая вектор-функция, где &amp;lt;math&amp;gt;K \ge 2&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
Так как не существует единого решение, которое было бы максимальным для всех целевых функций, вместо него можно искать множество &amp;lt;math&amp;gt;X^* \subseteq X &amp;lt;/math&amp;gt; множество Парето оптимальных значений.&lt;br /&gt;
=== Множество Парето оптимальных значений ===&lt;br /&gt;
{{Определение &lt;br /&gt;
|definition=&lt;br /&gt;
''' Множество Парето оптимальных значений: '''&lt;br /&gt;
:&amp;lt;math&amp;gt;\forall x^* \in X^* \not\exists x \in X &amp;lt;/math&amp;gt;:&amp;lt;math&amp;gt;x \succ x^*&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;x \succ x^* \Leftrightarrow (\forall i \in 1..K, (f_i(x) \ge f_i(x^*))\land (\exists i \in 1..K, f_i(x) &amp;gt; f_i(x^*)))&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
Выражение &amp;lt;math&amp;gt;x \succ x^*&amp;lt;/math&amp;gt; означает, что &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt; ''доминирует над'' &amp;lt;math&amp;gt;x^*&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Говорят,  что &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt; ''доминирует над'' &amp;lt;math&amp;gt;x^*&amp;lt;/math&amp;gt;. по&lt;br /&gt;
Парето, если &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt; не хуже &amp;lt;math&amp;gt;x^*&amp;lt;/math&amp;gt; по всем критериям и хотя бы по одному критерию превосходит &amp;lt;math&amp;gt;x^*&amp;lt;/math&amp;gt;. &lt;br /&gt;
В таком случае в выборе &amp;lt;math&amp;gt;x^*&amp;lt;/math&amp;gt; нет  смысла, т.к. &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt; по всем параметрам не уступает, а по каким-то и превосхожит &amp;lt;math&amp;gt;x^*&amp;lt;/math&amp;gt;. Если рассматривать всего&lt;br /&gt;
два критерия  то на рис. 1  показана область&lt;br /&gt;
пространства,  доминируемая данным решением А.  Эта область  «замкнута»: &lt;br /&gt;
элементы на ее границе также доминируемы А&lt;br /&gt;
[[Файл:Dogmin points.jpg|мини|200px|Рис.1 &amp;amp;ndash; Доминируемые решения ]]&lt;br /&gt;
&lt;br /&gt;
{{Определение &lt;br /&gt;
|definition=&lt;br /&gt;
Для двух решений &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;x'&amp;lt;/math&amp;gt; говорят &amp;lt;math&amp;gt;x \sim x'&amp;lt;/math&amp;gt; тогда и только тогда, когда &amp;lt;math&amp;gt;\exists i \in 1..K \colon f_i(x) &amp;gt; f_i(x') \land \exists j \in 1..K, j \ne i \colon f_j(x') &amp;gt; f_j(x)&amp;lt;/math&amp;gt; &amp;amp;ndash; такую пару решений называют '''недоминируемой'''&lt;br /&gt;
}}&lt;br /&gt;
На рис. 2 показана граница Парето для&lt;br /&gt;
возможных решений в двухкритериальном пространстве&lt;br /&gt;
[[Файл:Pareto_front.jpg|мини|200px|Рис.2 &amp;amp;ndash; Парето фронт]]&lt;br /&gt;
Множество Парето оптимальных недоминируемых решений называется '''Парето фронтом.'''&lt;br /&gt;
&lt;br /&gt;
== Multi-objectivization ==&lt;br /&gt;
Суть метода мульти-объективизации заключается в разбитии сложной задачи с одной целевой функцией на несколько подзадач, найти для каждой подзадачи решение и выбрать оптимальное решение.&lt;br /&gt;
&lt;br /&gt;
Для выполнения оптимизации многокритериальной задачи мы должны добавить в целевую функцию новые параметры, либо должны добавить новые целевые функции.&lt;br /&gt;
&lt;br /&gt;
Сложность этой процедуры заключается в разложении проблемы на ряд мелких независимых между собой подпроблем.&lt;br /&gt;
&lt;br /&gt;
== Алгоритмы ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Hill-Climbers ===&lt;br /&gt;
{{Определение &lt;br /&gt;
|definition=&lt;br /&gt;
'''Hill-Climbers''' &amp;amp;ndash; Итеративный алгоритм, который начинается с произвольного решения проблемы, а затем пытается найти лучшее решение, постепенно изменяя его. Если изменения позволяют найти лучшее решение, алгоритм сохраняет его и повторяет и повторяет своё выполнение до тех пор, пока лучшие решения не могут быть найдены&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|''Initialization:''||&amp;lt;math&amp;gt;P \leftarrow \emptyset &amp;lt;/math&amp;gt;&amp;lt;br/&amp;gt;'''Init_pop'''&amp;lt;math&amp;gt;(P)&amp;lt;/math&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|''Main Loop:''||&amp;lt;math&amp;gt;x_1 \leftarrow &amp;lt;/math&amp;gt;'''Rand_mem'''&amp;lt;math&amp;gt;(P)&amp;lt;/math&amp;gt;,&amp;lt;math&amp;gt;x'_2 \leftarrow &amp;lt;/math&amp;gt;'''Rand_mem'''&amp;lt;math&amp;gt;(P)&amp;lt;/math&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;x'_1 \leftarrow &amp;lt;/math&amp;gt;'''Mutate'''&amp;lt;math&amp;gt;(P)&amp;lt;/math&amp;gt;,&amp;lt;math&amp;gt;x_2 \leftarrow &amp;lt;/math&amp;gt;'''Mutate'''&amp;lt;math&amp;gt;(P)&amp;lt;/math&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
'''if'''&amp;lt;math&amp;gt;(H(x_1,x'_1)+H(x_2,x'_2) &amp;gt; H(x_1,x'_2)+H(x_2,x'_1))&amp;lt;/math&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
:'''Swap'''&amp;lt;math&amp;gt;(x_1,x'_2)&amp;lt;/math&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
'''if''' &amp;lt;math&amp;gt;f(x'_1) &amp;gt; f(x_1)&amp;lt;/math&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
: &amp;lt;math&amp;gt;P \leftarrow P \cup x'_1 \setminus x_1&amp;lt;/math&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
'''if''' &amp;lt;math&amp;gt;f(x'_2) &amp;gt; f(x_2)&amp;lt;/math&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
: &amp;lt;math&amp;gt;P \leftarrow P \cup x'_2 \setminus x_2&amp;lt;/math&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|''Termination:''||'''return Best'''&amp;lt;math&amp;gt;(P)&amp;lt;/math&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Задачи ==&lt;br /&gt;
Задача коммивояжера (TSP)является наиболее известно из всего класса &amp;lt;math&amp;gt;NP&amp;lt;/math&amp;gt;-сложных задач.&lt;br /&gt;
Формулируется задача следующим образом: &lt;br /&gt;
&lt;br /&gt;
Задано &amp;lt;math&amp;gt;C=\{c_1,c_2,\dots,c_N\} &amp;lt;/math&amp;gt; &amp;amp;ndash; множество городов и для каждой пары &amp;lt;math&amp;gt;\{c_i,c_j\}&amp;lt;/math&amp;gt; задано расстояние. Наша цель &amp;amp;ndash; найти цепь из городов, минимизирующую величину:&lt;br /&gt;
:&amp;lt;math&amp;gt;\sum^{N-1}_{i=1} d(C_{\pi(i)},C_{\pi(i+1)})+d(C_{\pi(N)},C_{\pi(1)})&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Применяя к этой задаче мультиобъктивизацию, нужно разбить её на подзадачи. TSP &amp;amp;ndash; является &amp;lt;math&amp;gt;NP&amp;lt;/math&amp;gt;-сложной именно потому, что нет хорошего разложения этой задачи.&lt;br /&gt;
Тем не менее задачу можно разбить на две или больше подтуров, каждый из которых мы можем минимизировать.&lt;br /&gt;
&lt;br /&gt;
Представим подтуры в виде двух городов. Тогда наша задача примет вид:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;minimize\{f(\pi,a,b) = (f_1(\pi,a,b),f_2(\pi,a,b))\}&amp;lt;/math&amp;gt;&lt;br /&gt;
::'''where'''&amp;lt;math&amp;gt;f_1(\pi,a,b)=\sum^{\pi^{-1}(b)-1}_{i=\pi^{-1}(a)} d(C_{\pi(i)},C_{\pi(i+1)})&amp;lt;/math&amp;gt;&lt;br /&gt;
::'''and'''  &amp;lt;math&amp;gt;f_2(\pi,a,b)=\sum^{N-1}_{i=\pi^{-1}(b)} d(C_{\pi(i)},C_{\pi(i+1)}) + \sum^{\pi^{-1}(a)-1}_{i=1} d(C_{\pi(i)},C_{\pi(i+1)}) &amp;lt;/math&amp;gt; &amp;lt;math&amp;gt;+ d(C_{\pi(N)},C_{\pi(1)})&amp;lt;/math&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt; &amp;amp;ndash; два города, указанных ''априори''. Если &amp;lt;math&amp;gt;\pi (a) &amp;lt; \pi (b)&amp;lt;/math&amp;gt;, меняем их местами.&lt;br /&gt;
&lt;br /&gt;
Предполагается, что &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt; выбраны произвольно.&lt;br /&gt;
&lt;br /&gt;
== Источники ==&lt;br /&gt;
* [http://ru.wikipedia.org/wiki/Многокритериальная_оптимизация Википедия: Многокритериальная оптимизация]&lt;br /&gt;
* [http://rain.ifmo.ru/~tsarev/teaching/ea-2012/lectures/3/multiobjectivization.pdf Knowles J., Watson R., Corne D. Reducing Local Optima in Single-Objective Problems by Multi-objectivization]&lt;br /&gt;
* [http://en.wikipedia.org/wiki/Multiobjective_optimization Wikipedia: Multiobjective optimization]&lt;br /&gt;
* [http://en.wikipedia.org/wiki/Hill_climbing Wikipedia: Hill climbing]&lt;/div&gt;</summary>
		<author><name>194.85.160.130</name></author>	</entry>

	</feed>