http://neerc.ifmo.ru/wiki/api.php?action=feedcontributions&user=217.197.6.98&feedformat=atomВикиконспекты - Вклад участника [ru]2024-03-29T01:30:02ZВклад участникаMediaWiki 1.30.0http://neerc.ifmo.ru/wiki/index.php?title=%D0%A2%D0%B5%D0%BE%D1%80%D0%B5%D1%82%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B8%D0%B9_%D0%BC%D0%B8%D0%BD%D0%B8%D0%BC%D1%83%D0%BC_%D0%BF%D0%BE_%D1%84%D1%83%D0%BD%D0%BA%D1%86%D0%B8%D0%BE%D0%BD%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D0%BC%D1%83_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7%D1%83_%D0%B7%D0%B0_6_%D1%81%D0%B5%D0%BC%D0%B5%D1%81%D1%82%D1%80&diff=48776Теоретический минимум по функциональному анализу за 6 семестр2015-06-23T13:31:20Z<p>217.197.6.98: /* 26 Теорема Шаудера о неподвижной точке */</p>
<hr />
<div>== 1 A* и его ограниченность ==<br />
Пусть оператор <tex> A </tex> действует из <tex> E </tex> в <tex> F </tex>, и функционал <tex> \varphi </tex> принадлежит <tex> F^* </tex>.<br />
<br />
Рассмотрим <tex> f(x) = \varphi (Ax), | f(x) | \le \| \varphi \| \| A \| \| x \| </tex>.<br />
<br />
Получили новый функционал <tex> f </tex>, принадлежащий <tex> E^* </tex>. <tex> \varphi \mapsto \varphi A </tex>.<br />
<br />
<tex> \varphi A = A^* (\varphi), A^* : F^* \to E^* </tex>. <tex> A^* </tex> {{---}} '''сопряженный оператор''' к <tex> A </tex>. <br />
<br />
{{Теорема<br />
|statement=<br />
Если <tex> A </tex> {{---}} линейный ограниченный оператор, то <tex> \| A^* \| = \| A \| </tex>.<br />
}}<br />
<br />
== 2 Ортогональные дополнения <tex> E </tex> и <tex> E^* </tex> ==<br />
{{Определение<br />
|definition=<br />
Пусть <tex> E </tex> {{---}} НП, <tex> S \subset E^* </tex>.<br />
<br />
<tex> S^{\bot} = \{ x \in E \mid \forall f \in S: f(x) = 0 \} </tex> {{---}} '''ортогональное дополнение''' <tex> S </tex>.<br />
<br />
Аналогично, если <tex> T \subset E </tex>, то <tex> T^{\bot} = \{ f \in E^* \mid \forall x \in T: f(x) = 0 \} </tex>.<br />
}}<br />
<br />
{{Утверждение<br />
|statement= <tex> \{ 0 \} = (E^*)^{\bot}, \{ \mathbf{0} \} = E^{\bot} </tex>.<br />
}}<br />
<br />
== 3 Ортогональное дополнение R(A) ==<br />
{{Теорема<br />
|statement= <tex> A \in \mathcal{L}(E,F) \implies \operatorname{Cl} R(A) = (\operatorname{Ker} A^*)^\perp </tex>.<br />
}}<br />
<br />
== 4 Ортогональное дополнение R(A*) ==<br />
{{Теорема<br />
|statement= <tex> A \in \mathcal{L}(E,F),~R(A) = \operatorname{Cl} R(A) \implies R(A^*) = (\operatorname{Ker}A )^\perp </tex>.<br />
}}<br />
<br />
== 5 Арифметика компактных операторов ==<br />
{{Определение<br />
|definition=<br />
Множество называется '''относительно компактным (предкомпактным)''', если его замыкание компактно<br />
}}<br />
{{Определение<br />
|definition=<br />
Линейный ограниченный оператор <tex> A : X \to Y </tex> называется '''компактным''', если <tex> A </tex> переводит любое ограниченное подмножество <tex> X </tex> в относительно компактное множество из <tex> Y </tex>. <br />
}}<br />
{{Утверждение<br />
|statement = <br />
<tex> A \in \mathcal{L} (X,Y), ~ B \in \mathcal{L} (Y,Z) </tex>, <tex> C = B \cdot A </tex> (произведение, суперпозиция). Тогда:<br />
<br />
# Если <tex> B </tex> — ограниченный, <tex> A </tex> — компактный, то <tex> C </tex> — компактный.<br />
# Если <tex> B </tex> — компактный, <tex> A </tex> — ограниченный, то <tex> C </tex> — компактный.<br />
}}<br />
<br />
<br />
== 9 Размерность Ker(I-A) компактного A ==<br />
{{Утверждение<br />
|statement=<br />
<tex>A</tex> {{---}} компактный оператор. Тогда <tex>\dim\operatorname{Ker}(I-A) < + \infty</tex><br />
}}<br />
<br />
== 10 Замкнутость R(I-A) компактного A ==<br />
{{Теорема<br />
|statement=<br />
Пусть <tex>T = I - A</tex>, <tex>A</tex> компактен, тогда <tex> R(T) </tex> замкнуто.<br />
}}<br />
<br />
== 11 Лемма о Ker(I-A)^n компактного A ==<br />
{{Утверждение<br />
|statement=<br />
Пусть <tex> M_n = \operatorname{Ker} ((I - A)^n), n \in \mathbb N</tex>, <tex> A </tex> — компактный оператор.<br />
Тогда <tex> \exists n_0: M_{n_0} = M_{n_0 + 1} </tex>.<br />
}}<br />
<br />
== 12 Условие справедливости равенства R(I-A)=E ==<br />
{{Утверждение<br />
|statement=<br />
Пусть <tex> A </tex> — компактный оператор на банаховом <tex> X </tex>, <tex> T = I - A </tex>.<br />
Тогда <tex> R(T) = X \iff \operatorname{Ker} T = \{0\} </tex>.<br />
}}<br />
<br />
== 13 Альтернатива Фредгольма-Шаудера ==<br />
{{Теорема<br />
|about=<br />
альтернатива Фредгольма-Шаудера<br />
|statement=<br />
Пусть <tex>A:X \to X</tex> — компактный оператор и <tex>T = A - \lambda I</tex>. Тогда возможно только две ситуации:<br />
# <tex>\operatorname{Ker} T = \{0\}</tex>, тогда <tex> y = Tx</tex> разрешимо для любого <tex>y</tex><br />
# <tex>\operatorname{Ker} T \ne \{0\}</tex>, тогда <tex> y = Tx</tex> разрешимо только для тех <tex>y</tex>, которые принадлежат <tex>(\operatorname{Ker} T^*)^\perp</tex><br />
}}<br />
<br />
== 14 Спектр компактного оператора ==<br />
Рассмотрим <tex>A - \lambda I</tex>. <br />
<br />
# <tex>\operatorname{Ker} (A - \lambda I) \ne \{0\}</tex>, тогда оператор необратим, и <tex>\lambda</tex> — собственное число, то есть <tex>\lambda \in \sigma(A)</tex>.<br />
# <tex>\operatorname{Ker} (A - \lambda I) = \{0\}</tex>, тогда по альтернативе, оператор непрерывно обратим, то есть <tex>\lambda \in \rho(A)</tex>.<br />
<br />
Таким образом, спектр состоит из собственных чисел, и, возможно, нуля. Теперь изучим мощность спектра:<br />
<br />
{{Теорема<br />
|statement=<br />
Спектр компактного оператора не более чем счётен и его предельной точкой может быть только 0.<br />
}}<br />
<br />
== 15 Определение самосопряженного оператора, неравенство для (a+ib)(I-A) ==<br />
{{Определение<br />
|definition=Оператор <tex>\mathcal{A}</tex> называется ''самосопряжённым'' (<tex>\mathcal{A} = \mathcal{A}^*</tex>), если <tex>\forall x, y : \langle \mathcal{A}x, y \rangle = \langle x, \mathcal{A}y \rangle</tex><br />
}}<br />
<tex>\lambda \in \mathbb{C}</tex>, <tex>\lambda \mathcal{I} - \mathcal{A} = (\mu\mathcal{I} - \mathcal{A}) + i\nu\mathcal{I}</tex><br />
<br />
<tex>\|(\lambda\mathcal{I}-\mathcal{A})x\| \ge |\nu|\cdot\|x\|</tex><br />
<br />
== 16 Вещественность спектра ограниченного самосопряженного оператора ==<br />
{{Утверждение<br />
|statement=Собственные числа самосопряжённого оператора вещественны<br />
}}<br />
<br />
== 17 Критерий включения в резольвентное множество ограниченного самосопряженного оператора ==<br />
{{Теорема<br />
|statement=Пусть <tex>\mathcal{A}</tex>{{---}} самосопряжённый оператор. Тогда<br />
<tex>\lambda \in \rho(\mathcal{A}) \iff \exists m > 0 : \forall x \in \mathcal{H} : \|(\lambda\mathcal{I}-\mathcal{A})x\| \ge m\|x\|</tex><br />
}}<br />
<br />
== 18 Критерий включения в спектр ограниченного самосопряженного оператора ==<br />
{{Теорема<br />
|statement=Пусть <tex>\mathcal{A}</tex>{{---}} самосопряжённый оператор. Тогда<br />
<tex>\lambda \in \sigma(\mathcal{A}) \iff \exists x_n : \|x_n\| = 1 : \|(\lambda\mathcal{I}-\mathcal{A})x_n\| \to 0 </tex><br />
}}<br />
<br />
== 19 Локализация спектра с.с. оператора посредством чисел m- и m+ ==<br />
{{Определение<br />
|definition=<tex>m_- = \inf\limits_{\|x\| = 1} \langle \mathcal{A}x, x\rangle, m_+ = \sup\limits_{\|x\| = 1} \langle \mathcal{A}x, x \rangle</tex><br />
}}<br />
{{Теорема<br />
|statement=Пусть A — самосопряженный оператор<br />
<br />
1. <tex>\sigma(\mathcal{A}) \subset [m_-; m_+]</tex><br />
<br />
2. <tex>m_+, m_- \in \sigma(\mathcal{A})</tex><br />
}}<br />
<br />
== 20 Спектральный радиус ограниченного самосопряженного оператора и его норма ==<br />
{{Утверждение<br />
|statement=Если <tex>\mathcal{A}</tex>{{---}} самосопряжённый оператор, то <tex>r_\sigma(\mathcal{A}) = \|\mathcal{A}\|</tex><br />
}}<br />
<br />
== 21 Теорема Гильберта-Шмидта ==<br />
{{Теорема<br />
|author=Гильберт, Шмидт<br />
|statement=Если <tex>\mathcal{A}</tex>{{---}} самосопряжённый компактный оператор в гильбертовом пространстве <tex>\mathcal{H}</tex>, а <tex>M_{\lambda_i}</tex>{{---}} его (оператора) собственные подпространства, то <tex>\mathcal{H} = M_{\lambda_1} \oplus M_{\lambda_2} \oplus \cdots \oplus M_{\lambda_n} \oplus \cdots </tex><br />
}}<br />
<br />
== 22 Разложение резольвенты компактного самосопряженного оператора. ==<br />
<tex>R_\lambda(y) = \sum\limits_{n=1}^\infty \frac{\langle y, \varphi_n\rangle}{\lambda-\lambda_n}\varphi_n</tex><br />
<br />
==Теорема Банаха о сжимающем отображении==<br />
<br />
{{Определение<br />
|definition=Пусть на замкнутом шаре <tex>\overline{V} \subset X</tex>, где <tex>X</tex> - метрическое пространство, определён оператор <tex>A: \overline{V} \subset X \to X</tex>. Он называется '''сжатием''' на <tex>\overline{V}</tex>, если <tex>\exists\alpha\in(0; 1)</tex> такой, что для <tex>{\forall}x,y \in M</tex> выполняется <tex>{\rho(Ax,Ay)\leqslant\alpha{\cdot}\rho(x,y)}</tex>.<br />
}}<br />
<br />
{{Теорема<br />
|statement=(''Банаха о неподвижной точке'')<br />
Пусть <tex>T : \overline{V} \to \overline{V}</tex> и является сжатием, тогда в этом шаре у оператора <tex>T</tex> <tex>\exists !</tex> неподвижная точка.<br />
}}<br />
[[Теорема Банаха о неподвижной точке]]<br />
<br />
==Дифференцирование отображений, неравенство Лагранжа.==<br />
<br />
Рассмотрим <tex>T : V_r(x_0) \to Y</tex>, где <tex>V_r(x_0) \subset X</tex> и, кроме того, <tex>X, Y</tex> - нормированные пространства.<br />
<br />
Пусть <tex>\|\delta x \| < r</tex>. Тогда, очевидно, <tex>x + \delta x \in V_r(x_0)</tex>.<br />
<br />
Обозначим <tex>\delta T(x_0, \delta x) = T(x_0 + \delta x) - T(x_0)</tex>.<br />
<br />
'''Def.''' Отображение <tex>T</tex> называется дифференцируемым по Фреше в точке <tex>x_0</tex>, если существует оператор <tex>A_{x_0} \in L(X,Y)</tex> такой, что <tex>\delta T(x_0, \delta x) = A_{x_0}(\delta x) + o(\delta x)</tex>, где <tex>o(\delta x)</tex> несёт следующий смысл: <tex>\frac{ {\|o(\delta x)\|}_Y } {{\| \delta x \|}_X} \to 0</tex>.<br />
<br />
Обычно, в случае дифференцируемого отображения используют следующее обозначение: <tex>T_{x_0}' = A_{x_0}</tex>. Подчеркнем, что <tex>T_{x_0}': X \to Y</tex>. Аргументом является "отклонение" некоторой точки <tex>x'</tex> от <tex>x_0</tex>: <tex>x - x_0</tex>. А результат применения оператора: <tex>T(x') - T(x_0)</tex> с точностью до <tex>o(\delta x = x' - x)</tex>.<br />
<br />
'''Lm.''' (''Неравенство Лагранжа'')<br />
Пусть <tex>X, Y</tex> -- нормированные пространства, <tex>V</tex> -- некоторый шар в <tex>X</tex> и дан оператор <tex>T : V \to Y</tex> и на всем этом шаре <tex>\exists T'(x)</tex>. Тогда для любых <tex>a, b \in V : \|T(b) - T(a)\| \le M {\|b - a\|}_X</tex>, где <tex>M = sup_{x \in [a, b]}\|T'(x)\|</tex>.<br />
<br />
==Локальная теорема о неявном отображении==<br />
<br />
'''Th.'''(''о неявном отображении'')<br />
<br />
Пусть <tex>V</tex> - шар в <tex> X, V \subset X</tex>, а <tex>W \subset Y</tex> - шар в <tex>Y</tex>, и задан оператор <tex>T : {V} \times {W} \rightarrow Y</tex>.<br />
<br />
Пусть <tex>x_0 \in V,\: y_0 \in W,\: T(x_0, y_0) = 0 \in Y</tex>. <br />
<br />
Пусть <tex> \forall x \in V, \forall y \in W \quad \exists T^{'}_y </tex> - дифференциал Фреше, непрерывный как отображение переменных <tex>x</tex> и <tex>y</tex>.<br />
<br />
Пусть также <tex>T^{'}_{y}(x_0, y_0)</tex> - непрерывно обратим.<br />
<br />
'''Тогда''' задача о неявном отображении для <tex>T(x, y) = 0</tex> c начальным решением <tex>T(x_0, y_0) = 0</tex> разрешима в некоторых окрестностях точек <tex>x_0, y_0</tex>, а именно: для любого <tex>x' \in V_{\delta_1}(x_0)</tex> существует единственное <tex>y' \in V_{\delta_2}(y_0) : T(x', y') = 0</tex> .<br />
<br />
http://neerc.ifmo.ru/wiki/index.php?title=Локальная_теорема_о_неявном_отображении<br />
<br />
== 24 Локальная сходимость метода Ньютона для операторных уравнений ==<br />
<tex> \mathcal{F}(x) = x - \Gamma(x) \mathcal{T} (x)</tex><br />
{{Утверждение<br />
|statement=<tex> \mathcal{F}'(\overline x) = 0 </tex><br />
}}<br />
<br />
== 25 Проекторы Шаудера ==<br />
<tex> \forall \varepsilon > 0 \exists y_1 \in M, \hdots, y_p \in M </tex> {{---}} конечная <tex> \varepsilon </tex>-сеть.<br />
<br />
Построим следующую функцию: <tex> \forall j = 1, \hdots, p, \forall y \in M: </tex><br />
<br />
<tex> \mu_j(y) = \begin{cases} <br />
0 & \mbox{if } \| y - y_j \| \ge \varepsilon \\<br />
\varepsilon - \| y - y_j \| & \mbox{if } \| y - y_j \| < \varepsilon \end{cases} <br />
</tex><br />
<br />
<tex> S(y) = \sum\limits_{j=1}^p \mu_j(y) </tex><br />
{{Определение<br />
|definition=<br />
<br />
<tex dpi = 140> P_\varepsilon (y) = \sum\limits_{j=1}^p \frac {\mu_j(y)} {S(y)} y_j </tex> {{---}} ''проектор Шаудера''.<br />
<br />
}}<br />
<br />
== 26 Теорема Шаудера о неподвижной точке ==<br />
{{Теорема<br />
|author=Шаудер<br />
|about=о неподвижной точке<br />
|statement=<br />
Пусть <tex> M </tex> {{---}} ограниченное замкнутое выпуклое подмножество B-пространства <tex> X </tex> и <tex> \mathcal{T} </tex> вполне непрерывно отображает <tex> M </tex> в себя. <br />
<br />
Тогда <tex> \exists x^* \in M : x^* = Tx^* </tex>.<br />
}}<br />
<br />
== 6 О компактности A*, сепарабельность R(A) ==<br />
<br />
{{Утверждение<br />
|statement = <br />
Пусть <tex> A </tex> — компактный, тогда <tex> R(A) </tex> — сепарабельно (то есть, в <tex> R(A) </tex> существует счетное всюду плотное подмножество).<br />
}}<br />
<br />
{{Утверждение<br />
|statement = <br />
<tex>A</tex> — компактен <tex>\implies</tex> <tex>A^*</tex> — компактен<br />
}}<br />
<br />
== 7 Базис Шаудера, лемма о координатном пространстве ==<br />
{{Определение<br />
|definition=<br />
Базисом Шаудера в банаховом пространстве <tex>X</tex> называется множество его элементов <tex>e_1, e_2 \dots e_n \dots</tex> такое, что у любого <tex>x</tex> в <tex>X</tex> существует единственное разложение <tex>x = \sum\limits_{i = 1}^{\infty} \alpha_i e_i</tex>.<br />
}}<br />
<br />
Определим <tex>F = \{(\alpha_1 \dots \alpha_n\dots) \mid \exists x \in X: \sum\limits_{n=1}^\infty \alpha_n e_n \to x \}</tex> — это линейное пространство. <br />
<br />
Так как ряд сходится, <tex>F</tex> можно превратить в НП, определив норму как <tex>\| \alpha \| = \sup\limits_n \left\| \sum\limits_{i=1}^n \alpha_i e_i\right\|</tex>.<br />
<br />
{{Утверждение<br />
|statement=<br />
Пространство <tex> F </tex> относительно этой нормы — банахово.<br />
}}<br />
<br />
== 8 Почти конечномерность компактного оператора ==<br />
{{Теорема<br />
|about=<br />
почти конечномерность компактного оператора<br />
|statement=<br />
Если <tex>X</tex> — банахово пространство с базисом Шаудера, <tex>A:X \to X</tex> — компактный, то для всех <tex>\varepsilon > 0</tex> существует разложение оператора <tex>A</tex> в сумму двух компактных операторов: <tex>A = A_1 + A_2</tex> такое, что:<br />
<br />
# <tex>\operatorname{dim}(R(A_1)) < +\infty</tex><br />
# <tex>\|A_2\| < \varepsilon</tex><br />
}}<br />
== 23 Локальная сходимость метода простой итерации ==<br />
{{Теорема<br />
|about=Локальная теорема о простой итерации<br />
|statement=<br />
Пусть известно, что существует <tex> \overline{x}: \mathcal{T}(\overline{x}) = \overline{x} </tex> и <tex> \| \mathcal{T}(\overline{x})' \| \le q < 1 </tex>.<br />
<br />
Тогда существует такой шар <tex> V_{\delta} (\overline x) </tex>, что если <tex> x_0 \in V_{\delta} (\overline x) </tex>, то:<br />
* Метод простых итераций корректно определен: <tex> \mathcal{T}x_n \in V_{\delta} (\overline x), n \ge 0</tex>.<br />
* <tex> x_n \to \overline x </tex><br />
}}<br />
<br />
<br />
[[Категория: Функциональный анализ 3 курс]]</div>217.197.6.98http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D0%BE%D1%81%D1%82%D1%80%D0%BE%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BF%D0%BE_%D0%9D%D0%9A%D0%90_%D1%8D%D0%BA%D0%B2%D0%B8%D0%B2%D0%B0%D0%BB%D0%B5%D0%BD%D1%82%D0%BD%D0%BE%D0%B3%D0%BE_%D0%94%D0%9A%D0%90,_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A2%D0%BE%D0%BC%D0%BF%D1%81%D0%BE%D0%BD%D0%B0&diff=43471Построение по НКА эквивалентного ДКА, алгоритм Томпсона2015-01-06T08:02:45Z<p>217.197.6.98: /* Алгоритм */</p>
<hr />
<div>== Описание ==<br />
Алгоритм Томпсона строит по [[Недетерминированные конечные автоматы|НКА]] эквивалентный [[Детерминированные конечные автоматы|ДКА]] следующим образом:<br />
* Начало.<br />
* '''Шаг 1.''' Помещаем в очередь <tex>Q</tex> множество, состоящее только из стартовой вершины.<br />
* '''Шаг 2.''' Затем, пока очередь не пуста выполняем следующие действия:<br />
** Достаем из очереди множество, назовем его <tex>q</tex><br />
** Для всех <tex>c \in \Sigma</tex> посмотрим в какое состояние ведет переход по символу <tex>c</tex> из каждого состояния в <tex>q</tex>. Полученное множество состояний положим в очередь <tex>Q</tex> только если оно не лежало там раньше. Каждое такое множество в итоговом ДКА будет отдельной вершиной, в которую будут вести переходы по соответствующим символам.<br />
** Если в множестве <tex>q</tex> хотя бы одна из вершин была терминальной в НКА, то соответствующая данному множеству вершина в ДКА также будет терминальной.<br />
* Конец.<br />
<br />
== Построение эквивалентного ДКА по НКА ==<br />
<br />
Пусть нам дан произвольный НКА: <tex>\langle \Sigma , Q, s \in Q, T \subset Q, \delta : Q \times \Sigma \to 2^Q \rangle</tex>.<br />
<br />
Построим по нему следующий ДКА: <tex>\langle \Sigma , Q_d, s_d \in Q_d, T_d \subset Q_d, \delta_d : Q_d \times \Sigma \to Q_d \rangle</tex>, где:<br />
# <tex>Q_d = \{q_d \mid q_d \subset 2^Q \}</tex>,<br />
# <tex>s_d = \{s\}</tex>,<br />
# <tex>T_d = \{q \in Q_d \mid \exists p \in T : p \in q\}</tex>,<br />
# <tex>\delta_d(q, c) = \{ \delta(a, c) \mid a \in q \}</tex>.<br />
<br />
===Доказательство эквивалентности===<br />
{{Теорема<br />
|statement=<br />
Построенный ДКА эквивалентен данному НКА.<br />
|proof=<br />
#Докажем, что любое слово, которое принимает НКА, будет принято построенным ДКА. Заметим, что <tex>\forall q \in q_d, \forall c \in \Sigma, \forall p \in \delta(q, c): p \in \delta_d(q_d, c)</tex>. Рассмотрим слово <tex>w=w_1 \dots w_m</tex>, которое принимает автомат НКА: <tex>\langle s, w_1w_2 \dots w_m \rangle \vdash \langle u_1, w_2 \dots w_m \rangle \vdash \langle u_m, \varepsilon \rangle, u_m \in T</tex>. Проверим, что построенный ДКА тоже принимает это слово. Заметим, что <tex>s \in s_d</tex>, а, значит, исходя из нашего наблюдения, мы получаем, что <tex>u_1 \in {u_d}_1</tex>, где <tex>{u_d}_1 = \delta_d(s, w_1)</tex>. Далее, несложно заметить, что <tex>\forall i \leqslant m : u_i \in {u_d}_i</tex>, где <tex>\langle s_d, w_1w_2 \dots w_m \rangle \vdash \langle {u_d}_1, w_2 \dots w_m \rangle \vdash \langle {u_d}_i, w_{i + 1} \dots w_m\rangle</tex>. Таким образом, <tex>u_m \in {u_d}_m</tex>, а из определения терминальных состояний в построенном ДКА мы получаем, что <tex>{u_d}_m \in T_d</tex>, то есть наш ДКА тоже принимает cлово <tex>w</tex>.<br />
#Докажем, что любое слово, которое принимает построенный ДКА, принимает и НКА. Сначала сделаем наблюдение, что если <tex>q_d=\{q\}</tex>, и мы из него достигли по строке <tex>S</tex> какого-то состояния <tex>p_d</tex>, то <tex>\forall p \in p_d</tex> существует путь из <tex>q</tex> в <tex>p</tex> в НКА по строке <tex>S</tex>. Рассмотрим слово <tex>w=w_1 \dots w_m</tex>, которое принимает автомат ДКА: <tex>\langle s_d, w_1w_2 \dots w_m \rangle \vdash \langle {u_d}_1, w_2 \dots w_m \rangle \vdash \langle {u_d}_m, \varepsilon \rangle, {u_d}_m \in T_d</tex>. Проверим, что НКА тоже принимает это слово. Так как <tex>s_d = \{s\}</tex>, и мы из <tex>s_d</tex> достигли <tex>{u_d}_m \in T_d</tex>, возьмём любое терминальное состояние <tex>u_m \in {u_d}_m</tex>. По нашему наблюдению в НКА есть путь из <tex>s</tex> в <tex>u_m</tex> по строке <tex>w</tex>, а, значит, НКА принимает это слово. <br />
Таким образом, множества слов, допускаемых ДКА и НКА, совпадают, то есть они эквивалентны.<br />
}}<br />
<br />
== Алгоритм Томпсона ==<br />
Данный алгоритм преобразовывает НКА в эквивалентный ДКА. Будем использовать вышеуказанный способ построения с одним дополнением {{---}} не будем учитывать состояния недостижимые из стартового.<br />
Поэтому в алгоритме используется обход в ширину.<br />
<br />
===Алгоритм===<br />
* <tex>\mathtt{P}</tex> {{---}} очередь состояний, соответствующих множествам, состоящих из состояний НКА. <br />
* <tex>\mathtt{Q_d}</tex> {{---}} массив множеств, соответствующих состояниям ДКА.<br />
* <tex>\mathtt{s}</tex> {{---}} стартовое состояние НКА.<br />
'''Automaton''' getDFAbyNFA(<tex>\langle \Sigma, Q, s, T, \delta \rangle</tex> : '''Automaton'''):<br />
<tex>P</tex>.push({s})<br />
<tex>Q_d</tex> = <tex>\varnothing</tex><br />
'''while''' <tex>P</tex> <tex> \neq </tex> <tex>\varnothing </tex><br />
<tex>P</tex>.pop(<tex>p_d</tex>)<br />
'''for''' <tex>c \in \Sigma</tex><br />
<tex>q_d</tex> = <tex>\varnothing</tex><br />
'''for''' <tex>p \in p_d</tex><br />
<tex>q_d</tex> = <tex>q_d \cup \{ \delta(p, c) \}</tex><br />
<tex>\delta_d(p_d, q_d)</tex> = <tex>c</tex><br />
'''if''' <tex>q_d \notin Q_d</tex><br />
<tex>P</tex>.push(<tex>q_d</tex>)<br />
<tex>Q_d</tex>.add(<tex>q_d</tex>) <br />
<tex>T_d</tex> = <tex>\{q \in Q_d \mid \exists p \in T : p \in q\}</tex><br />
'''return''' <tex>\langle \Sigma, Q_d, \{s\}, T_d, \delta_d \rangle</tex><br />
<br />
===Асимптотика===<br />
Так как количество подмножеств множества состояний НКА не более, чем <tex>2^n</tex>, а каждое подмножество мы обрабатываем ровно один раз за время <tex>O(n)</tex>, получаем верхнюю оценку времени работы алгоритма {{---}} <tex>O(n \cdot 2^n)</tex>.<br />
<br />
===Пример===<br />
Пусть нам дан [[Недетерминированные конечные автоматы|недетерминированный конечный автомат]]: <br />
<br />
[[Файл:DKA.png|250px]]<br />
<br />
По нашему заданию эквивалентного ДКА мы получаем: <br />
<br />
[[Файл:NKA_definition.png|250px]]<br />
<br />
#Помещаем в очередь множество из одной стартовой вершины — <tex>\{1\}</tex>: <tex>Q = \{\{1\}\}</tex>.<br />
#Достаём из очереди множество <tex>\{1\}</tex>: <tex>Q = \{\}</tex>.<br />
#<tex>q_d(\{1\}, a) = \{1, 2\}</tex>, кладём множество <tex>\{1, 2\}</tex> в очередь: <tex>Q = \{\{1, 2\}\}</tex>.<br />
#<tex>q_d(\{1\}, b) = \{1\}</tex>, нам не надо класть множество <tex>\{1\}</tex> в очередь, так как оно уже там было.<br />
#Достаём из очереди множество <tex>\{1, 2\}</tex>: <tex>Q = \{\}</tex>.<br />
#<tex>q_d(\{1, 2\}, a) = \{1, 2\}</tex>, нам не надо класть множество <tex>\{1, 2\}</tex> в очередь, так как оно уже там было.<br />
#<tex>q_d(\{1, 2\}, b) = \{1, 2\}</tex>, нам не надо класть множество <tex>\{1, 2\}</tex> в очередь, так как оно уже там было.<br />
#Помечаем все терминальные вершины, в данном случае — <tex>\{1, 2\}</tex>.<br />
<br />
В итоге получаем ДКА, эквивалентный исходному: <br />
<br />
[[Файл:NKA_algorithm.png|250px]].<br />
<br />
== См. также ==<br />
<br />
* [[Регулярные языки: два определения и их эквивалентность]]<br />
* [[Минимизация ДКА, алгоритм за O(n^2) с построением пар различимых состояний]]<br />
* [[Теорема Клини (совпадение классов автоматных и регулярных языков)]]<br />
<br />
== Источники информации ==<br />
* ''Серебряков В.А.'' Теория и реализация языков программирования. М.: МЗ-Пресс, 2003 (1-е изд.) и 2006 (2-е изд) — С. 294. — ISBN 5-94073-094-9<br />
<br />
[[Категория: Теория формальных языков]]<br />
[[Категория: Автоматы и регулярные языки]]</div>217.197.6.98http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D0%BE%D1%81%D1%82%D1%80%D0%BE%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BF%D0%BE_%D0%9D%D0%9A%D0%90_%D1%8D%D0%BA%D0%B2%D0%B8%D0%B2%D0%B0%D0%BB%D0%B5%D0%BD%D1%82%D0%BD%D0%BE%D0%B3%D0%BE_%D0%94%D0%9A%D0%90,_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A2%D0%BE%D0%BC%D0%BF%D1%81%D0%BE%D0%BD%D0%B0&diff=43470Построение по НКА эквивалентного ДКА, алгоритм Томпсона2015-01-06T08:00:33Z<p>217.197.6.98: /* Алгоритм */</p>
<hr />
<div>== Описание ==<br />
Алгоритм Томпсона строит по [[Недетерминированные конечные автоматы|НКА]] эквивалентный [[Детерминированные конечные автоматы|ДКА]] следующим образом:<br />
* Начало.<br />
* '''Шаг 1.''' Помещаем в очередь <tex>Q</tex> множество, состоящее только из стартовой вершины.<br />
* '''Шаг 2.''' Затем, пока очередь не пуста выполняем следующие действия:<br />
** Достаем из очереди множество, назовем его <tex>q</tex><br />
** Для всех <tex>c \in \Sigma</tex> посмотрим в какое состояние ведет переход по символу <tex>c</tex> из каждого состояния в <tex>q</tex>. Полученное множество состояний положим в очередь <tex>Q</tex> только если оно не лежало там раньше. Каждое такое множество в итоговом ДКА будет отдельной вершиной, в которую будут вести переходы по соответствующим символам.<br />
** Если в множестве <tex>q</tex> хотя бы одна из вершин была терминальной в НКА, то соответствующая данному множеству вершина в ДКА также будет терминальной.<br />
* Конец.<br />
<br />
== Построение эквивалентного ДКА по НКА ==<br />
<br />
Пусть нам дан произвольный НКА: <tex>\langle \Sigma , Q, s \in Q, T \subset Q, \delta : Q \times \Sigma \to 2^Q \rangle</tex>.<br />
<br />
Построим по нему следующий ДКА: <tex>\langle \Sigma , Q_d, s_d \in Q_d, T_d \subset Q_d, \delta_d : Q_d \times \Sigma \to Q_d \rangle</tex>, где:<br />
# <tex>Q_d = \{q_d \mid q_d \subset 2^Q \}</tex>,<br />
# <tex>s_d = \{s\}</tex>,<br />
# <tex>T_d = \{q \in Q_d \mid \exists p \in T : p \in q\}</tex>,<br />
# <tex>\delta_d(q, c) = \{ \delta(a, c) \mid a \in q \}</tex>.<br />
<br />
===Доказательство эквивалентности===<br />
{{Теорема<br />
|statement=<br />
Построенный ДКА эквивалентен данному НКА.<br />
|proof=<br />
#Докажем, что любое слово, которое принимает НКА, будет принято построенным ДКА. Заметим, что <tex>\forall q \in q_d, \forall c \in \Sigma, \forall p \in \delta(q, c): p \in \delta_d(q_d, c)</tex>. Рассмотрим слово <tex>w=w_1 \dots w_m</tex>, которое принимает автомат НКА: <tex>\langle s, w_1w_2 \dots w_m \rangle \vdash \langle u_1, w_2 \dots w_m \rangle \vdash \langle u_m, \varepsilon \rangle, u_m \in T</tex>. Проверим, что построенный ДКА тоже принимает это слово. Заметим, что <tex>s \in s_d</tex>, а, значит, исходя из нашего наблюдения, мы получаем, что <tex>u_1 \in {u_d}_1</tex>, где <tex>{u_d}_1 = \delta_d(s, w_1)</tex>. Далее, несложно заметить, что <tex>\forall i \leqslant m : u_i \in {u_d}_i</tex>, где <tex>\langle s_d, w_1w_2 \dots w_m \rangle \vdash \langle {u_d}_1, w_2 \dots w_m \rangle \vdash \langle {u_d}_i, w_{i + 1} \dots w_m\rangle</tex>. Таким образом, <tex>u_m \in {u_d}_m</tex>, а из определения терминальных состояний в построенном ДКА мы получаем, что <tex>{u_d}_m \in T_d</tex>, то есть наш ДКА тоже принимает cлово <tex>w</tex>.<br />
#Докажем, что любое слово, которое принимает построенный ДКА, принимает и НКА. Сначала сделаем наблюдение, что если <tex>q_d=\{q\}</tex>, и мы из него достигли по строке <tex>S</tex> какого-то состояния <tex>p_d</tex>, то <tex>\forall p \in p_d</tex> существует путь из <tex>q</tex> в <tex>p</tex> в НКА по строке <tex>S</tex>. Рассмотрим слово <tex>w=w_1 \dots w_m</tex>, которое принимает автомат ДКА: <tex>\langle s_d, w_1w_2 \dots w_m \rangle \vdash \langle {u_d}_1, w_2 \dots w_m \rangle \vdash \langle {u_d}_m, \varepsilon \rangle, {u_d}_m \in T_d</tex>. Проверим, что НКА тоже принимает это слово. Так как <tex>s_d = \{s\}</tex>, и мы из <tex>s_d</tex> достигли <tex>{u_d}_m \in T_d</tex>, возьмём любое терминальное состояние <tex>u_m \in {u_d}_m</tex>. По нашему наблюдению в НКА есть путь из <tex>s</tex> в <tex>u_m</tex> по строке <tex>w</tex>, а, значит, НКА принимает это слово. <br />
Таким образом, множества слов, допускаемых ДКА и НКА, совпадают, то есть они эквивалентны.<br />
}}<br />
<br />
== Алгоритм Томпсона ==<br />
Данный алгоритм преобразовывает НКА в эквивалентный ДКА. Будем использовать вышеуказанный способ построения с одним дополнением {{---}} не будем учитывать состояния недостижимые из стартового.<br />
Поэтому в алгоритме используется обход в ширину.<br />
<br />
===Алгоритм===<br />
* <tex>\mathtt{P}</tex> {{---}} очередь состояний, соответствующих множествам, состоящих из состояний НКА. <br />
* <tex>\mathtt{Q}</tex> {{---}} массив множеств, соответствующих состояниям ДКА.<br />
* <tex>\mathtt{s}</tex> {{---}} стартовое состояние НКА.<br />
'''Automaton''' getDFAbyNFA(<tex>\langle \Sigma, Q_0, s, T_0, \delta_0 \rangle</tex> : '''Automaton'''):<br />
<tex>P</tex>.push({s})<br />
<tex>Q</tex> = <tex>\varnothing</tex><br />
'''while''' <tex>P</tex> <tex> \neq </tex> <tex>\varnothing </tex><br />
<tex>P</tex>.pop(<tex>p_d</tex>)<br />
'''for''' <tex>c \in \Sigma</tex><br />
<tex>q_d</tex> = <tex>\varnothing</tex><br />
'''for''' <tex>p \in p_d</tex><br />
<tex>q_d</tex> = <tex>q_d \cup \{ \delta_0(p, c) \}</tex><br />
<tex>\delta(p_d, q_d)</tex> = <tex>c</tex><br />
'''if''' <tex>q_d \notin Q</tex><br />
<tex>P</tex>.push(<tex>q_d</tex>)<br />
<tex>Q</tex>.add(<tex>q_d</tex>) <br />
<tex>T</tex> = <tex>\{q \in Q \mid \exists p \in T : p \in q\}</tex><br />
'''return''' <tex>\langle \Sigma, Q, \{s\}, T, \delta \rangle</tex><br />
<br />
===Асимптотика===<br />
Так как количество подмножеств множества состояний НКА не более, чем <tex>2^n</tex>, а каждое подмножество мы обрабатываем ровно один раз за время <tex>O(n)</tex>, получаем верхнюю оценку времени работы алгоритма {{---}} <tex>O(n \cdot 2^n)</tex>.<br />
<br />
===Пример===<br />
Пусть нам дан [[Недетерминированные конечные автоматы|недетерминированный конечный автомат]]: <br />
<br />
[[Файл:DKA.png|250px]]<br />
<br />
По нашему заданию эквивалентного ДКА мы получаем: <br />
<br />
[[Файл:NKA_definition.png|250px]]<br />
<br />
#Помещаем в очередь множество из одной стартовой вершины — <tex>\{1\}</tex>: <tex>Q = \{\{1\}\}</tex>.<br />
#Достаём из очереди множество <tex>\{1\}</tex>: <tex>Q = \{\}</tex>.<br />
#<tex>q_d(\{1\}, a) = \{1, 2\}</tex>, кладём множество <tex>\{1, 2\}</tex> в очередь: <tex>Q = \{\{1, 2\}\}</tex>.<br />
#<tex>q_d(\{1\}, b) = \{1\}</tex>, нам не надо класть множество <tex>\{1\}</tex> в очередь, так как оно уже там было.<br />
#Достаём из очереди множество <tex>\{1, 2\}</tex>: <tex>Q = \{\}</tex>.<br />
#<tex>q_d(\{1, 2\}, a) = \{1, 2\}</tex>, нам не надо класть множество <tex>\{1, 2\}</tex> в очередь, так как оно уже там было.<br />
#<tex>q_d(\{1, 2\}, b) = \{1, 2\}</tex>, нам не надо класть множество <tex>\{1, 2\}</tex> в очередь, так как оно уже там было.<br />
#Помечаем все терминальные вершины, в данном случае — <tex>\{1, 2\}</tex>.<br />
<br />
В итоге получаем ДКА, эквивалентный исходному: <br />
<br />
[[Файл:NKA_algorithm.png|250px]].<br />
<br />
== См. также ==<br />
<br />
* [[Регулярные языки: два определения и их эквивалентность]]<br />
* [[Минимизация ДКА, алгоритм за O(n^2) с построением пар различимых состояний]]<br />
* [[Теорема Клини (совпадение классов автоматных и регулярных языков)]]<br />
<br />
== Источники информации ==<br />
* ''Серебряков В.А.'' Теория и реализация языков программирования. М.: МЗ-Пресс, 2003 (1-е изд.) и 2006 (2-е изд) — С. 294. — ISBN 5-94073-094-9<br />
<br />
[[Категория: Теория формальных языков]]<br />
[[Категория: Автоматы и регулярные языки]]</div>217.197.6.98http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D0%BE%D1%81%D1%82%D1%80%D0%BE%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BF%D0%BE_%D0%9D%D0%9A%D0%90_%D1%8D%D0%BA%D0%B2%D0%B8%D0%B2%D0%B0%D0%BB%D0%B5%D0%BD%D1%82%D0%BD%D0%BE%D0%B3%D0%BE_%D0%94%D0%9A%D0%90,_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A2%D0%BE%D0%BC%D0%BF%D1%81%D0%BE%D0%BD%D0%B0&diff=43469Построение по НКА эквивалентного ДКА, алгоритм Томпсона2015-01-06T07:54:57Z<p>217.197.6.98: /* Алгоритм */</p>
<hr />
<div>== Описание ==<br />
Алгоритм Томпсона строит по [[Недетерминированные конечные автоматы|НКА]] эквивалентный [[Детерминированные конечные автоматы|ДКА]] следующим образом:<br />
* Начало.<br />
* '''Шаг 1.''' Помещаем в очередь <tex>Q</tex> множество, состоящее только из стартовой вершины.<br />
* '''Шаг 2.''' Затем, пока очередь не пуста выполняем следующие действия:<br />
** Достаем из очереди множество, назовем его <tex>q</tex><br />
** Для всех <tex>c \in \Sigma</tex> посмотрим в какое состояние ведет переход по символу <tex>c</tex> из каждого состояния в <tex>q</tex>. Полученное множество состояний положим в очередь <tex>Q</tex> только если оно не лежало там раньше. Каждое такое множество в итоговом ДКА будет отдельной вершиной, в которую будут вести переходы по соответствующим символам.<br />
** Если в множестве <tex>q</tex> хотя бы одна из вершин была терминальной в НКА, то соответствующая данному множеству вершина в ДКА также будет терминальной.<br />
* Конец.<br />
<br />
== Построение эквивалентного ДКА по НКА ==<br />
<br />
Пусть нам дан произвольный НКА: <tex>\langle \Sigma , Q, s \in Q, T \subset Q, \delta : Q \times \Sigma \to 2^Q \rangle</tex>.<br />
<br />
Построим по нему следующий ДКА: <tex>\langle \Sigma , Q_d, s_d \in Q_d, T_d \subset Q_d, \delta_d : Q_d \times \Sigma \to Q_d \rangle</tex>, где:<br />
# <tex>Q_d = \{q_d \mid q_d \subset 2^Q \}</tex>,<br />
# <tex>s_d = \{s\}</tex>,<br />
# <tex>T_d = \{q \in Q_d \mid \exists p \in T : p \in q\}</tex>,<br />
# <tex>\delta_d(q, c) = \{ \delta(a, c) \mid a \in q \}</tex>.<br />
<br />
===Доказательство эквивалентности===<br />
{{Теорема<br />
|statement=<br />
Построенный ДКА эквивалентен данному НКА.<br />
|proof=<br />
#Докажем, что любое слово, которое принимает НКА, будет принято построенным ДКА. Заметим, что <tex>\forall q \in q_d, \forall c \in \Sigma, \forall p \in \delta(q, c): p \in \delta_d(q_d, c)</tex>. Рассмотрим слово <tex>w=w_1 \dots w_m</tex>, которое принимает автомат НКА: <tex>\langle s, w_1w_2 \dots w_m \rangle \vdash \langle u_1, w_2 \dots w_m \rangle \vdash \langle u_m, \varepsilon \rangle, u_m \in T</tex>. Проверим, что построенный ДКА тоже принимает это слово. Заметим, что <tex>s \in s_d</tex>, а, значит, исходя из нашего наблюдения, мы получаем, что <tex>u_1 \in {u_d}_1</tex>, где <tex>{u_d}_1 = \delta_d(s, w_1)</tex>. Далее, несложно заметить, что <tex>\forall i \leqslant m : u_i \in {u_d}_i</tex>, где <tex>\langle s_d, w_1w_2 \dots w_m \rangle \vdash \langle {u_d}_1, w_2 \dots w_m \rangle \vdash \langle {u_d}_i, w_{i + 1} \dots w_m\rangle</tex>. Таким образом, <tex>u_m \in {u_d}_m</tex>, а из определения терминальных состояний в построенном ДКА мы получаем, что <tex>{u_d}_m \in T_d</tex>, то есть наш ДКА тоже принимает cлово <tex>w</tex>.<br />
#Докажем, что любое слово, которое принимает построенный ДКА, принимает и НКА. Сначала сделаем наблюдение, что если <tex>q_d=\{q\}</tex>, и мы из него достигли по строке <tex>S</tex> какого-то состояния <tex>p_d</tex>, то <tex>\forall p \in p_d</tex> существует путь из <tex>q</tex> в <tex>p</tex> в НКА по строке <tex>S</tex>. Рассмотрим слово <tex>w=w_1 \dots w_m</tex>, которое принимает автомат ДКА: <tex>\langle s_d, w_1w_2 \dots w_m \rangle \vdash \langle {u_d}_1, w_2 \dots w_m \rangle \vdash \langle {u_d}_m, \varepsilon \rangle, {u_d}_m \in T_d</tex>. Проверим, что НКА тоже принимает это слово. Так как <tex>s_d = \{s\}</tex>, и мы из <tex>s_d</tex> достигли <tex>{u_d}_m \in T_d</tex>, возьмём любое терминальное состояние <tex>u_m \in {u_d}_m</tex>. По нашему наблюдению в НКА есть путь из <tex>s</tex> в <tex>u_m</tex> по строке <tex>w</tex>, а, значит, НКА принимает это слово. <br />
Таким образом, множества слов, допускаемых ДКА и НКА, совпадают, то есть они эквивалентны.<br />
}}<br />
<br />
== Алгоритм Томпсона ==<br />
Данный алгоритм преобразовывает НКА в эквивалентный ДКА. Будем использовать вышеуказанный способ построения с одним дополнением {{---}} не будем учитывать состояния недостижимые из стартового.<br />
Поэтому в алгоритме используется обход в ширину.<br />
<br />
===Алгоритм===<br />
<tex>P</tex> {{---}} очередь состояний, соответствующих множествам, состоящих из состояний НКА. <tex>Q</tex> {{---}} массив множеств, соответствующих состояниям ДКА.<br />
<tex>s</tex> {{---}} стартовое состояние НКА.<br />
'''Automaton''' getDFAbyNFA(<tex>\langle \Sigma, Q_0, s, T_0, \delta_0 \rangle</tex> : '''Automaton'''):<br />
<tex>P</tex>.push({s})<br />
<tex>Q</tex> = <tex>\varnothing</tex><br />
'''while''' <tex>P</tex> <tex> \neq </tex> <tex>\varnothing </tex><br />
<tex>P</tex>.pop(<tex>p_d</tex>)<br />
'''for''' <tex>c \in \Sigma</tex><br />
<tex>q_d</tex> = <tex>\varnothing</tex><br />
'''for''' <tex>p \in p_d</tex><br />
<tex>q_d</tex> = <tex>q_d \cup \{ \delta_0(p, c) \}</tex><br />
<tex>\delta(p_d, q_d)</tex> = <tex>c</tex><br />
'''if''' <tex>q_d \notin Q</tex><br />
<tex>P</tex>.push(<tex>q_d</tex>)<br />
<tex>Q</tex>.add(<tex>q_d</tex>) <br />
<tex>T</tex> = <tex>\{q \in Q \mid \exists p \in T : p \in q\}</tex><br />
'''return''' <tex>\langle \Sigma, Q, \{s\}, T, \delta \rangle</tex><br />
<br />
===Асимптотика===<br />
Так как количество подмножеств множества состояний НКА не более, чем <tex>2^n</tex>, а каждое подмножество мы обрабатываем ровно один раз за время <tex>O(n)</tex>, получаем верхнюю оценку времени работы алгоритма {{---}} <tex>O(n \cdot 2^n)</tex>.<br />
<br />
===Пример===<br />
Пусть нам дан [[Недетерминированные конечные автоматы|недетерминированный конечный автомат]]: <br />
<br />
[[Файл:DKA.png|250px]]<br />
<br />
По нашему заданию эквивалентного ДКА мы получаем: <br />
<br />
[[Файл:NKA_definition.png|250px]]<br />
<br />
#Помещаем в очередь множество из одной стартовой вершины — <tex>\{1\}</tex>: <tex>Q = \{\{1\}\}</tex>.<br />
#Достаём из очереди множество <tex>\{1\}</tex>: <tex>Q = \{\}</tex>.<br />
#<tex>q_d(\{1\}, a) = \{1, 2\}</tex>, кладём множество <tex>\{1, 2\}</tex> в очередь: <tex>Q = \{\{1, 2\}\}</tex>.<br />
#<tex>q_d(\{1\}, b) = \{1\}</tex>, нам не надо класть множество <tex>\{1\}</tex> в очередь, так как оно уже там было.<br />
#Достаём из очереди множество <tex>\{1, 2\}</tex>: <tex>Q = \{\}</tex>.<br />
#<tex>q_d(\{1, 2\}, a) = \{1, 2\}</tex>, нам не надо класть множество <tex>\{1, 2\}</tex> в очередь, так как оно уже там было.<br />
#<tex>q_d(\{1, 2\}, b) = \{1, 2\}</tex>, нам не надо класть множество <tex>\{1, 2\}</tex> в очередь, так как оно уже там было.<br />
#Помечаем все терминальные вершины, в данном случае — <tex>\{1, 2\}</tex>.<br />
<br />
В итоге получаем ДКА, эквивалентный исходному: <br />
<br />
[[Файл:NKA_algorithm.png|250px]].<br />
<br />
== См. также ==<br />
<br />
* [[Регулярные языки: два определения и их эквивалентность]]<br />
* [[Минимизация ДКА, алгоритм за O(n^2) с построением пар различимых состояний]]<br />
* [[Теорема Клини (совпадение классов автоматных и регулярных языков)]]<br />
<br />
== Источники информации ==<br />
* ''Серебряков В.А.'' Теория и реализация языков программирования. М.: МЗ-Пресс, 2003 (1-е изд.) и 2006 (2-е изд) — С. 294. — ISBN 5-94073-094-9<br />
<br />
[[Категория: Теория формальных языков]]<br />
[[Категория: Автоматы и регулярные языки]]</div>217.197.6.98http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D0%BE%D1%81%D1%82%D1%80%D0%BE%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BF%D0%BE_%D0%9D%D0%9A%D0%90_%D1%8D%D0%BA%D0%B2%D0%B8%D0%B2%D0%B0%D0%BB%D0%B5%D0%BD%D1%82%D0%BD%D0%BE%D0%B3%D0%BE_%D0%94%D0%9A%D0%90,_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A2%D0%BE%D0%BC%D0%BF%D1%81%D0%BE%D0%BD%D0%B0&diff=43468Построение по НКА эквивалентного ДКА, алгоритм Томпсона2015-01-06T07:52:48Z<p>217.197.6.98: /* Алгоритм */</p>
<hr />
<div>== Описание ==<br />
Алгоритм Томпсона строит по [[Недетерминированные конечные автоматы|НКА]] эквивалентный [[Детерминированные конечные автоматы|ДКА]] следующим образом:<br />
* Начало.<br />
* '''Шаг 1.''' Помещаем в очередь <tex>Q</tex> множество, состоящее только из стартовой вершины.<br />
* '''Шаг 2.''' Затем, пока очередь не пуста выполняем следующие действия:<br />
** Достаем из очереди множество, назовем его <tex>q</tex><br />
** Для всех <tex>c \in \Sigma</tex> посмотрим в какое состояние ведет переход по символу <tex>c</tex> из каждого состояния в <tex>q</tex>. Полученное множество состояний положим в очередь <tex>Q</tex> только если оно не лежало там раньше. Каждое такое множество в итоговом ДКА будет отдельной вершиной, в которую будут вести переходы по соответствующим символам.<br />
** Если в множестве <tex>q</tex> хотя бы одна из вершин была терминальной в НКА, то соответствующая данному множеству вершина в ДКА также будет терминальной.<br />
* Конец.<br />
<br />
== Построение эквивалентного ДКА по НКА ==<br />
<br />
Пусть нам дан произвольный НКА: <tex>\langle \Sigma , Q, s \in Q, T \subset Q, \delta : Q \times \Sigma \to 2^Q \rangle</tex>.<br />
<br />
Построим по нему следующий ДКА: <tex>\langle \Sigma , Q_d, s_d \in Q_d, T_d \subset Q_d, \delta_d : Q_d \times \Sigma \to Q_d \rangle</tex>, где:<br />
# <tex>Q_d = \{q_d \mid q_d \subset 2^Q \}</tex>,<br />
# <tex>s_d = \{s\}</tex>,<br />
# <tex>T_d = \{q \in Q_d \mid \exists p \in T : p \in q\}</tex>,<br />
# <tex>\delta_d(q, c) = \{ \delta(a, c) \mid a \in q \}</tex>.<br />
<br />
===Доказательство эквивалентности===<br />
{{Теорема<br />
|statement=<br />
Построенный ДКА эквивалентен данному НКА.<br />
|proof=<br />
#Докажем, что любое слово, которое принимает НКА, будет принято построенным ДКА. Заметим, что <tex>\forall q \in q_d, \forall c \in \Sigma, \forall p \in \delta(q, c): p \in \delta_d(q_d, c)</tex>. Рассмотрим слово <tex>w=w_1 \dots w_m</tex>, которое принимает автомат НКА: <tex>\langle s, w_1w_2 \dots w_m \rangle \vdash \langle u_1, w_2 \dots w_m \rangle \vdash \langle u_m, \varepsilon \rangle, u_m \in T</tex>. Проверим, что построенный ДКА тоже принимает это слово. Заметим, что <tex>s \in s_d</tex>, а, значит, исходя из нашего наблюдения, мы получаем, что <tex>u_1 \in {u_d}_1</tex>, где <tex>{u_d}_1 = \delta_d(s, w_1)</tex>. Далее, несложно заметить, что <tex>\forall i \leqslant m : u_i \in {u_d}_i</tex>, где <tex>\langle s_d, w_1w_2 \dots w_m \rangle \vdash \langle {u_d}_1, w_2 \dots w_m \rangle \vdash \langle {u_d}_i, w_{i + 1} \dots w_m\rangle</tex>. Таким образом, <tex>u_m \in {u_d}_m</tex>, а из определения терминальных состояний в построенном ДКА мы получаем, что <tex>{u_d}_m \in T_d</tex>, то есть наш ДКА тоже принимает cлово <tex>w</tex>.<br />
#Докажем, что любое слово, которое принимает построенный ДКА, принимает и НКА. Сначала сделаем наблюдение, что если <tex>q_d=\{q\}</tex>, и мы из него достигли по строке <tex>S</tex> какого-то состояния <tex>p_d</tex>, то <tex>\forall p \in p_d</tex> существует путь из <tex>q</tex> в <tex>p</tex> в НКА по строке <tex>S</tex>. Рассмотрим слово <tex>w=w_1 \dots w_m</tex>, которое принимает автомат ДКА: <tex>\langle s_d, w_1w_2 \dots w_m \rangle \vdash \langle {u_d}_1, w_2 \dots w_m \rangle \vdash \langle {u_d}_m, \varepsilon \rangle, {u_d}_m \in T_d</tex>. Проверим, что НКА тоже принимает это слово. Так как <tex>s_d = \{s\}</tex>, и мы из <tex>s_d</tex> достигли <tex>{u_d}_m \in T_d</tex>, возьмём любое терминальное состояние <tex>u_m \in {u_d}_m</tex>. По нашему наблюдению в НКА есть путь из <tex>s</tex> в <tex>u_m</tex> по строке <tex>w</tex>, а, значит, НКА принимает это слово. <br />
Таким образом, множества слов, допускаемых ДКА и НКА, совпадают, то есть они эквивалентны.<br />
}}<br />
<br />
== Алгоритм Томпсона ==<br />
Данный алгоритм преобразовывает НКА в эквивалентный ДКА. Будем использовать вышеуказанный способ построения с одним дополнением {{---}} не будем учитывать состояния недостижимые из стартового.<br />
Поэтому в алгоритме используется обход в ширину.<br />
<br />
===Алгоритм===<br />
<tex>P</tex> {{---}} очередь состояний, соответствующих множествам, состоящих из состояний НКА. <tex>Q</tex> {{---}} массив множеств, соответствующих состояниям ДКА.<br />
<tex>s</tex> {{---}} стартовое состояние НКА.<br />
'''Automaton''' getDFAbyNFA(<tex>\langle \Sigma, Q_0, s, T_0, \delta_0 \rangle</tex> : '''Automaton'''):<br />
<tex>P</tex>.push({s})<br />
<tex>Q</tex> = <tex>\varnothing</tex><br />
'''while''' (<tex>P</tex> <tex> \neq </tex> <tex>\varnothing </tex>)<br />
<tex>P</tex>.pop(<tex>p_d</tex>)<br />
'''for''' (<tex>c</tex> '''in''' <tex>\Sigma</tex>)<br />
<tex>q_d</tex> = <tex>\varnothing</tex><br />
'''for''' (<tex>p</tex> '''in''' <tex>p_d</tex>) <br />
<tex>q_d</tex> = <tex>q_d \cup \{ \delta_0(p, c) \}</tex><br />
<tex>\delta(p_d, q_d)</tex> = <tex>c</tex><br />
'''if''' (<tex>q_d</tex> '''not in''' <tex>Q</tex>)<br />
<tex>P</tex>.push(<tex>q_d</tex>)<br />
<tex>Q</tex>.add(<tex>q_d</tex>) <br />
<tex>T</tex> = <tex>\{q \in Q \mid \exists p \in T : p \in q\}</tex><br />
'''return''' <tex>\langle \Sigma, Q, \{s\}, T, \delta \rangle</tex><br />
<br />
===Асимптотика===<br />
Так как количество подмножеств множества состояний НКА не более, чем <tex>2^n</tex>, а каждое подмножество мы обрабатываем ровно один раз за время <tex>O(n)</tex>, получаем верхнюю оценку времени работы алгоритма {{---}} <tex>O(n \cdot 2^n)</tex>.<br />
<br />
===Пример===<br />
Пусть нам дан [[Недетерминированные конечные автоматы|недетерминированный конечный автомат]]: <br />
<br />
[[Файл:DKA.png|250px]]<br />
<br />
По нашему заданию эквивалентного ДКА мы получаем: <br />
<br />
[[Файл:NKA_definition.png|250px]]<br />
<br />
#Помещаем в очередь множество из одной стартовой вершины — <tex>\{1\}</tex>: <tex>Q = \{\{1\}\}</tex>.<br />
#Достаём из очереди множество <tex>\{1\}</tex>: <tex>Q = \{\}</tex>.<br />
#<tex>q_d(\{1\}, a) = \{1, 2\}</tex>, кладём множество <tex>\{1, 2\}</tex> в очередь: <tex>Q = \{\{1, 2\}\}</tex>.<br />
#<tex>q_d(\{1\}, b) = \{1\}</tex>, нам не надо класть множество <tex>\{1\}</tex> в очередь, так как оно уже там было.<br />
#Достаём из очереди множество <tex>\{1, 2\}</tex>: <tex>Q = \{\}</tex>.<br />
#<tex>q_d(\{1, 2\}, a) = \{1, 2\}</tex>, нам не надо класть множество <tex>\{1, 2\}</tex> в очередь, так как оно уже там было.<br />
#<tex>q_d(\{1, 2\}, b) = \{1, 2\}</tex>, нам не надо класть множество <tex>\{1, 2\}</tex> в очередь, так как оно уже там было.<br />
#Помечаем все терминальные вершины, в данном случае — <tex>\{1, 2\}</tex>.<br />
<br />
В итоге получаем ДКА, эквивалентный исходному: <br />
<br />
[[Файл:NKA_algorithm.png|250px]].<br />
<br />
== См. также ==<br />
<br />
* [[Регулярные языки: два определения и их эквивалентность]]<br />
* [[Минимизация ДКА, алгоритм за O(n^2) с построением пар различимых состояний]]<br />
* [[Теорема Клини (совпадение классов автоматных и регулярных языков)]]<br />
<br />
== Источники информации ==<br />
* ''Серебряков В.А.'' Теория и реализация языков программирования. М.: МЗ-Пресс, 2003 (1-е изд.) и 2006 (2-е изд) — С. 294. — ISBN 5-94073-094-9<br />
<br />
[[Категория: Теория формальных языков]]<br />
[[Категория: Автоматы и регулярные языки]]</div>217.197.6.98http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D0%BE%D1%81%D1%82%D1%80%D0%BE%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BF%D0%BE_%D0%9D%D0%9A%D0%90_%D1%8D%D0%BA%D0%B2%D0%B8%D0%B2%D0%B0%D0%BB%D0%B5%D0%BD%D1%82%D0%BD%D0%BE%D0%B3%D0%BE_%D0%94%D0%9A%D0%90,_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A2%D0%BE%D0%BC%D0%BF%D1%81%D0%BE%D0%BD%D0%B0&diff=43458Построение по НКА эквивалентного ДКА, алгоритм Томпсона2015-01-05T17:01:55Z<p>217.197.6.98: /* Описание */</p>
<hr />
<div>== Описание ==<br />
Алгоритм Томпсона строит по [[Недетерминированные конечные автоматы|НКА]] эквивалентный [[Детерминированные конечные автоматы|ДКА]] следующим образом:<br />
* Начало.<br />
* '''Шаг 1.''' Помещаем в очередь <tex>Q</tex> множество, состоящее только из стартовой вершины.<br />
* '''Шаг 2.''' Затем, пока очередь не пуста выполняем следующие действия:<br />
** Достаем из очереди множество, назовем его <tex>q</tex><br />
** Для каждого <tex>c \in \Sigma</tex> посмотрим в какое состояние ведет переход по этому <tex>c</tex> для каждого состояния из <tex>q</tex>. Полученное множество состояний положим в очередь <tex>Q</tex> только если оно не лежало там раньше. Каждое такое множество в итоговом ДКА будет отдельной вершиной, в которую будут вести переходы по соответствующим символам.<br />
** Если в множестве <tex>q</tex> хотя бы одна из вершин была терминальной в НКА, то соответствующая данному множеству вершина в ДКА также будет терминальной.<br />
* Конец.<br />
<br />
== Построение эквивалентного ДКА по НКА ==<br />
<br />
Пусть нам дан произвольный НКА: <tex>\langle \Sigma , Q, s \in Q, T \subset Q, \delta : Q \times \Sigma \to 2^Q \rangle</tex>.<br />
<br />
Построим по нему следующий ДКА: <tex>\langle \Sigma , Q_d, s_d \in Q_d, T_d \subset Q_d, \delta_d : Q_d \times \Sigma \to Q_d \rangle</tex>, где:<br />
# <tex>Q_d = \{q_d \mid q_d \subset 2^Q \}</tex>,<br />
# <tex>s_d = \{s\}</tex>,<br />
# <tex>T_d = \{q \in Q_d \mid \exists p \in T : p \in q\}</tex>,<br />
# <tex>\delta_d(q, c) = \{ \delta(a, c) \mid a \in q \}</tex>.<br />
<br />
===Доказательство эквивалентности===<br />
{{Теорема<br />
|statement=<br />
Построенный ДКА эквивалентен данному НКА.<br />
|proof=<br />
#Докажем, что любое слово, которое принимает НКА, будет принято построенным ДКА. Заметим, что <tex>\forall q \in q_d, \forall c \in \Sigma, \forall p \in \delta(q, c): p \in \delta_d(q_d, c)</tex>. Рассмотрим слово <tex>w=w_1 \dots w_m</tex>, которое принимает автомат НКА: <tex>\langle s, w_1w_2 \dots w_m \rangle \vdash \langle u_1, w_2 \dots w_m \rangle \vdash \langle u_m, \varepsilon \rangle, u_m \in T</tex>. Проверим, что построенный ДКА тоже принимает это слово. Заметим, что <tex>s \in s_d</tex>, а, значит, исходя из нашего наблюдения, мы получаем, что <tex>u_1 \in {u_d}_1</tex>, где <tex>{u_d}_1 = \delta_d(s, w_1)</tex>. Далее, несложно заметить, что <tex>\forall i \leqslant m : u_i \in {u_d}_i</tex>, где <tex>\langle s_d, w_1w_2 \dots w_m \rangle \vdash \langle {u_d}_1, w_2 \dots w_m \rangle \vdash \langle {u_d}_i, w_{i + 1} \dots w_m\rangle</tex>. Таким образом, <tex>u_m \in {u_d}_m</tex>, а из определения терминальных состояний в построенном ДКА мы получаем, что <tex>{u_d}_m \in T_d</tex>, то есть наш ДКА тоже принимает cлово <tex>w</tex>.<br />
#Докажем, что любое слово, которое принимает построенный ДКА, принимает и НКА. Сначала сделаем наблюдение, что если <tex>q_d=\{q\}</tex>, и мы из него достигли по строке <tex>S</tex> какого-то состояния <tex>p_d</tex>, то <tex>\forall p \in p_d</tex> существует путь из <tex>q</tex> в <tex>p</tex> в НКА по строке <tex>S</tex>. Рассмотрим слово <tex>w=w_1 \dots w_m</tex>, которое принимает автомат ДКА: <tex>\langle s_d, w_1w_2 \dots w_m \rangle \vdash \langle {u_d}_1, w_2 \dots w_m \rangle \vdash \langle {u_d}_m, \varepsilon \rangle, {u_d}_m \in T_d</tex>. Проверим, что НКА тоже принимает это слово. Так как <tex>s_d = \{s\}</tex>, и мы из <tex>s_d</tex> достигли <tex>{u_d}_m \in T_d</tex>, возьмём любое терминальное состояние <tex>u_m \in {u_d}_m</tex>. По нашему наблюдению в НКА есть путь из <tex>s</tex> в <tex>u_m</tex> по строке <tex>w</tex>, а, значит, НКА принимает это слово. <br />
Таким образом, множества слов, допускаемых ДКА и НКА, совпадают, то есть они эквивалентны.<br />
}}<br />
<br />
== Алгоритм Томпсона ==<br />
Данный алгоритм преобразовывает НКА в эквивалентный ДКА. Будем использовать вышеуказанный способ построения с одним дополнением {{---}} не будем учитывать состояния недостижимые из стартового.<br />
Поэтому в алгоритме используется обход в ширину.<br />
<br />
===Алгоритм===<br />
<tex>P</tex> {{---}} очередь состояний, соответствующих множествам, состоящих из состояний НКА. <tex>Q</tex> {{---}} массив множеств, соответствующих состояниям ДКА.<br />
<tex>s</tex> {{---}} стартовое состояние НКА.<br />
'''Automaton''' getDFAbyNFA(<tex>\langle \Sigma, Q_0, s, T_0, \delta_0 \rangle</tex> : '''Automaton'''):<br />
<tex>P</tex>.push({s})<br />
<tex>Q</tex> = <tex>\varnothing</tex><br />
'''while''' (<tex>P</tex> <tex> \neq </tex> <tex>\varnothing </tex>)<br />
<tex>P</tex>.pop(<tex>p_d</tex>)<br />
'''for''' (<tex>c</tex> '''in''' <tex>\Sigma</tex>)<br />
<tex>q_d</tex> = <tex>\varnothing</tex><br />
'''for''' (<tex>p</tex> '''in''' <tex>p_d</tex>) <br />
<tex>q_d</tex> = <tex>q_d \cup \{ \delta_0(p, c) \}</tex><br />
'''if''' (<tex>q_d</tex> '''not in''' <tex>Q</tex>)<br />
<tex>P</tex>.push(<tex>q_d</tex>)<br />
<tex>Q</tex>.add(<tex>q_d</tex>) <br />
<tex>\delta(p_d, q_d)</tex> = <tex>c</tex><br />
'''for''' (<tex>q</tex> '''in''' <tex>Q</tex>)<br />
'''if''' (<tex>\exists q_d</tex> '''in''' <tex>q: q_d</tex> '''in''' <tex>T_0</tex>)<br />
<tex>T</tex>.add<tex>(q)</tex><br />
'''return''' <tex>\langle \Sigma, Q, \{s\}, T, \delta \rangle</tex><br />
<br />
===Асимптотика===<br />
Так как количество подмножеств множества состояний НКА не более, чем <tex>2^n</tex>, а каждое подмножество мы обрабатываем ровно один раз за время <tex>O(n)</tex>, получаем верхнюю оценку времени работы алгоритма {{---}} <tex>O(n \cdot 2^n)</tex>.<br />
<br />
===Пример===<br />
Пусть нам дан [[Недетерминированные конечные автоматы|недетерминированный конечный автомат]]: <br />
<br />
[[Файл:DKA.png|250px]]<br />
<br />
По нашему заданию эквивалентного ДКА мы получаем: <br />
<br />
[[Файл:NKA_definition.png|250px]]<br />
<br />
#Помещаем в очередь множество из одной стартовой вершины — <tex>\{1\}</tex>: <tex>Q = \{\{1\}\}</tex>.<br />
#Достаём из очереди множество <tex>\{1\}</tex>: <tex>Q = \{\}</tex>.<br />
#<tex>q_d(\{1\}, a) = \{1, 2\}</tex>, кладём множество <tex>\{1, 2\}</tex> в очередь: <tex>Q = \{\{1, 2\}\}</tex>.<br />
#<tex>q_d(\{1\}, b) = \{1\}</tex>, нам не надо класть множество <tex>\{1\}</tex> в очередь, так как оно уже там было.<br />
#Достаём из очереди множество <tex>\{1, 2\}</tex>: <tex>Q = \{\}</tex>.<br />
#<tex>q_d(\{1, 2\}, a) = \{1, 2\}</tex>, нам не надо класть множество <tex>\{1, 2\}</tex> в очередь, так как оно уже там было.<br />
#<tex>q_d(\{1, 2\}, b) = \{1, 2\}</tex>, нам не надо класть множество <tex>\{1, 2\}</tex> в очередь, так как оно уже там было.<br />
#Помечаем все терминальные вершины, в данном случае — <tex>\{1, 2\}</tex>.<br />
<br />
В итоге получаем ДКА, эквивалентный исходному: <br />
<br />
[[Файл:NKA_algorithm.png|250px]].<br />
<br />
== См. также ==<br />
<br />
* [[Регулярные языки: два определения и их эквивалентность]]<br />
* [[Минимизация ДКА, алгоритм за O(n^2) с построением пар различимых состояний]]<br />
* [[Теорема Клини (совпадение классов автоматных и регулярных языков)]]<br />
<br />
== Источники информации ==<br />
* ''Серебряков В.А.'' Теория и реализация языков программирования. М.: МЗ-Пресс, 2003 (1-е изд.) и 2006 (2-е изд) — С. 294. — ISBN 5-94073-094-9<br />
<br />
[[Категория: Теория формальных языков]]<br />
[[Категория: Автоматы и регулярные языки]]</div>217.197.6.98http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D0%BE%D1%81%D1%82%D1%80%D0%BE%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BF%D0%BE_%D0%9D%D0%9A%D0%90_%D1%8D%D0%BA%D0%B2%D0%B8%D0%B2%D0%B0%D0%BB%D0%B5%D0%BD%D1%82%D0%BD%D0%BE%D0%B3%D0%BE_%D0%94%D0%9A%D0%90,_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A2%D0%BE%D0%BC%D0%BF%D1%81%D0%BE%D0%BD%D0%B0&diff=43457Построение по НКА эквивалентного ДКА, алгоритм Томпсона2015-01-05T16:47:12Z<p>217.197.6.98: /* Алгоритм */</p>
<hr />
<div>== Описание ==<br />
Алгоритм Томпсона строит по [[Недетерминированные конечные автоматы|НКА]] эквивалентный [[Детерминированные конечные автоматы|ДКА]] следующим образом:<br />
* Начало.<br />
* '''Шаг 1.''' Помещаем в очередь <tex>Q</tex> множество, состоящее только из стартовой вершины.<br />
* '''Шаг 2.''' Затем, пока очередь не пуста выполняем следующие действия:<br />
** Достаем из очереди множество, назовем его <tex>q</tex><br />
** Для каждого <tex>c \in \Sigma</tex> построим множество, содержащее состояния, в которые ведет символ <tex>c</tex> из каждого состояния из <tex>q</tex>. Затем положим построенное множество в очередь <tex>Q</tex> только если оно не лежало там раньше. Каждое такое множество в итоговом ДКА будет отдельной вершиной, в которую будут вести переходы по соответствующим символам.<br />
** Если в множестве <tex>q</tex> хотя бы одна из вершин была терминальной в НКА, то соответствующая данному множеству вершина в ДКА также будет терминальной.<br />
* Конец.<br />
<br />
== Построение эквивалентного ДКА по НКА ==<br />
<br />
Пусть нам дан произвольный НКА: <tex>\langle \Sigma , Q, s \in Q, T \subset Q, \delta : Q \times \Sigma \to 2^Q \rangle</tex>.<br />
<br />
Построим по нему следующий ДКА: <tex>\langle \Sigma , Q_d, s_d \in Q_d, T_d \subset Q_d, \delta_d : Q_d \times \Sigma \to Q_d \rangle</tex>, где:<br />
# <tex>Q_d = \{q_d \mid q_d \subset 2^Q \}</tex>,<br />
# <tex>s_d = \{s\}</tex>,<br />
# <tex>T_d = \{q \in Q_d \mid \exists p \in T : p \in q\}</tex>,<br />
# <tex>\delta_d(q, c) = \{ \delta(a, c) \mid a \in q \}</tex>.<br />
<br />
===Доказательство эквивалентности===<br />
{{Теорема<br />
|statement=<br />
Построенный ДКА эквивалентен данному НКА.<br />
|proof=<br />
#Докажем, что любое слово, которое принимает НКА, будет принято построенным ДКА. Заметим, что <tex>\forall q \in q_d, \forall c \in \Sigma, \forall p \in \delta(q, c): p \in \delta_d(q_d, c)</tex>. Рассмотрим слово <tex>w=w_1 \dots w_m</tex>, которое принимает автомат НКА: <tex>\langle s, w_1w_2 \dots w_m \rangle \vdash \langle u_1, w_2 \dots w_m \rangle \vdash \langle u_m, \varepsilon \rangle, u_m \in T</tex>. Проверим, что построенный ДКА тоже принимает это слово. Заметим, что <tex>s \in s_d</tex>, а, значит, исходя из нашего наблюдения, мы получаем, что <tex>u_1 \in {u_d}_1</tex>, где <tex>{u_d}_1 = \delta_d(s, w_1)</tex>. Далее, несложно заметить, что <tex>\forall i \leqslant m : u_i \in {u_d}_i</tex>, где <tex>\langle s_d, w_1w_2 \dots w_m \rangle \vdash \langle {u_d}_1, w_2 \dots w_m \rangle \vdash \langle {u_d}_i, w_{i + 1} \dots w_m\rangle</tex>. Таким образом, <tex>u_m \in {u_d}_m</tex>, а из определения терминальных состояний в построенном ДКА мы получаем, что <tex>{u_d}_m \in T_d</tex>, то есть наш ДКА тоже принимает cлово <tex>w</tex>.<br />
#Докажем, что любое слово, которое принимает построенный ДКА, принимает и НКА. Сначала сделаем наблюдение, что если <tex>q_d=\{q\}</tex>, и мы из него достигли по строке <tex>S</tex> какого-то состояния <tex>p_d</tex>, то <tex>\forall p \in p_d</tex> существует путь из <tex>q</tex> в <tex>p</tex> в НКА по строке <tex>S</tex>. Рассмотрим слово <tex>w=w_1 \dots w_m</tex>, которое принимает автомат ДКА: <tex>\langle s_d, w_1w_2 \dots w_m \rangle \vdash \langle {u_d}_1, w_2 \dots w_m \rangle \vdash \langle {u_d}_m, \varepsilon \rangle, {u_d}_m \in T_d</tex>. Проверим, что НКА тоже принимает это слово. Так как <tex>s_d = \{s\}</tex>, и мы из <tex>s_d</tex> достигли <tex>{u_d}_m \in T_d</tex>, возьмём любое терминальное состояние <tex>u_m \in {u_d}_m</tex>. По нашему наблюдению в НКА есть путь из <tex>s</tex> в <tex>u_m</tex> по строке <tex>w</tex>, а, значит, НКА принимает это слово. <br />
Таким образом, множества слов, допускаемых ДКА и НКА, совпадают, то есть они эквивалентны.<br />
}}<br />
<br />
== Алгоритм Томпсона ==<br />
Данный алгоритм преобразовывает НКА в эквивалентный ДКА. Будем использовать вышеуказанный способ построения с одним дополнением {{---}} не будем учитывать состояния недостижимые из стартового.<br />
Поэтому в алгоритме используется обход в ширину.<br />
<br />
===Алгоритм===<br />
<tex>P</tex> {{---}} очередь состояний, соответствующих множествам, состоящих из состояний НКА. <tex>Q</tex> {{---}} массив множеств, соответствующих состояниям ДКА.<br />
<tex>s</tex> {{---}} стартовое состояние НКА.<br />
'''Automaton''' getDFAbyNFA(<tex>\langle \Sigma, Q_0, s, T_0, \delta_0 \rangle</tex> : '''Automaton'''):<br />
<tex>P</tex>.push({s})<br />
<tex>Q</tex> = <tex>\varnothing</tex><br />
'''while''' (<tex>P</tex> <tex> \neq </tex> <tex>\varnothing </tex>)<br />
<tex>P</tex>.pop(<tex>p_d</tex>)<br />
'''for''' (<tex>c</tex> '''in''' <tex>\Sigma</tex>)<br />
<tex>q_d</tex> = <tex>\varnothing</tex><br />
'''for''' (<tex>p</tex> '''in''' <tex>p_d</tex>) <br />
<tex>q_d</tex> = <tex>q_d \cup \{ \delta_0(p, c) \}</tex><br />
'''if''' (<tex>q_d</tex> '''not in''' <tex>Q</tex>)<br />
<tex>P</tex>.push(<tex>q_d</tex>)<br />
<tex>Q</tex>.add(<tex>q_d</tex>) <br />
<tex>\delta(p_d, q_d)</tex> = <tex>c</tex><br />
'''for''' (<tex>q</tex> '''in''' <tex>Q</tex>)<br />
'''if''' (<tex>\exists q_d</tex> '''in''' <tex>q: q_d</tex> '''in''' <tex>T_0</tex>)<br />
<tex>T</tex>.add<tex>(q)</tex><br />
'''return''' <tex>\langle \Sigma, Q, \{s\}, T, \delta \rangle</tex><br />
<br />
===Асимптотика===<br />
Так как количество подмножеств множества состояний НКА не более, чем <tex>2^n</tex>, а каждое подмножество мы обрабатываем ровно один раз за время <tex>O(n)</tex>, получаем верхнюю оценку времени работы алгоритма {{---}} <tex>O(n \cdot 2^n)</tex>.<br />
<br />
===Пример===<br />
Пусть нам дан [[Недетерминированные конечные автоматы|недетерминированный конечный автомат]]: <br />
<br />
[[Файл:DKA.png|250px]]<br />
<br />
По нашему заданию эквивалентного ДКА мы получаем: <br />
<br />
[[Файл:NKA_definition.png|250px]]<br />
<br />
#Помещаем в очередь множество из одной стартовой вершины — <tex>\{1\}</tex>: <tex>Q = \{\{1\}\}</tex>.<br />
#Достаём из очереди множество <tex>\{1\}</tex>: <tex>Q = \{\}</tex>.<br />
#<tex>q_d(\{1\}, a) = \{1, 2\}</tex>, кладём множество <tex>\{1, 2\}</tex> в очередь: <tex>Q = \{\{1, 2\}\}</tex>.<br />
#<tex>q_d(\{1\}, b) = \{1\}</tex>, нам не надо класть множество <tex>\{1\}</tex> в очередь, так как оно уже там было.<br />
#Достаём из очереди множество <tex>\{1, 2\}</tex>: <tex>Q = \{\}</tex>.<br />
#<tex>q_d(\{1, 2\}, a) = \{1, 2\}</tex>, нам не надо класть множество <tex>\{1, 2\}</tex> в очередь, так как оно уже там было.<br />
#<tex>q_d(\{1, 2\}, b) = \{1, 2\}</tex>, нам не надо класть множество <tex>\{1, 2\}</tex> в очередь, так как оно уже там было.<br />
#Помечаем все терминальные вершины, в данном случае — <tex>\{1, 2\}</tex>.<br />
<br />
В итоге получаем ДКА, эквивалентный исходному: <br />
<br />
[[Файл:NKA_algorithm.png|250px]].<br />
<br />
== См. также ==<br />
<br />
* [[Регулярные языки: два определения и их эквивалентность]]<br />
* [[Минимизация ДКА, алгоритм за O(n^2) с построением пар различимых состояний]]<br />
* [[Теорема Клини (совпадение классов автоматных и регулярных языков)]]<br />
<br />
== Источники информации ==<br />
* ''Серебряков В.А.'' Теория и реализация языков программирования. М.: МЗ-Пресс, 2003 (1-е изд.) и 2006 (2-е изд) — С. 294. — ISBN 5-94073-094-9<br />
<br />
[[Категория: Теория формальных языков]]<br />
[[Категория: Автоматы и регулярные языки]]</div>217.197.6.98http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D0%BE%D1%81%D1%82%D1%80%D0%BE%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BF%D0%BE_%D0%9D%D0%9A%D0%90_%D1%8D%D0%BA%D0%B2%D0%B8%D0%B2%D0%B0%D0%BB%D0%B5%D0%BD%D1%82%D0%BD%D0%BE%D0%B3%D0%BE_%D0%94%D0%9A%D0%90,_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A2%D0%BE%D0%BC%D0%BF%D1%81%D0%BE%D0%BD%D0%B0&diff=43456Построение по НКА эквивалентного ДКА, алгоритм Томпсона2015-01-05T14:44:15Z<p>217.197.6.98: /* Доказательство эквивалентности */</p>
<hr />
<div>== Описание ==<br />
Алгоритм Томпсона строит по [[Недетерминированные конечные автоматы|НКА]] эквивалентный [[Детерминированные конечные автоматы|ДКА]] следующим образом:<br />
* Начало.<br />
* '''Шаг 1.''' Помещаем в очередь <tex>Q</tex> множество, состоящее только из стартовой вершины.<br />
* '''Шаг 2.''' Затем, пока очередь не пуста выполняем следующие действия:<br />
** Достаем из очереди множество, назовем его <tex>q</tex><br />
** Для каждого <tex>c \in \Sigma</tex> построим множество, содержащее состояния, в которые ведет символ <tex>c</tex> из каждого состояния из <tex>q</tex>. Затем положим построенное множество в очередь <tex>Q</tex> только если оно не лежало там раньше. Каждое такое множество в итоговом ДКА будет отдельной вершиной, в которую будут вести переходы по соответствующим символам.<br />
** Если в множестве <tex>q</tex> хотя бы одна из вершин была терминальной в НКА, то соответствующая данному множеству вершина в ДКА также будет терминальной.<br />
* Конец.<br />
<br />
== Построение эквивалентного ДКА по НКА ==<br />
<br />
Пусть нам дан произвольный НКА: <tex>\langle \Sigma , Q, s \in Q, T \subset Q, \delta : Q \times \Sigma \to 2^Q \rangle</tex>.<br />
<br />
Построим по нему следующий ДКА: <tex>\langle \Sigma , Q_d, s_d \in Q_d, T_d \subset Q_d, \delta_d : Q_d \times \Sigma \to Q_d \rangle</tex>, где:<br />
# <tex>Q_d = \{q_d \mid q_d \subset 2^Q \}</tex>,<br />
# <tex>s_d = \{s\}</tex>,<br />
# <tex>T_d = \{q \in Q_d \mid \exists p \in T : p \in q\}</tex>,<br />
# <tex>\delta_d(q, c) = \{ \delta(a, c) \mid a \in q \}</tex>.<br />
<br />
===Доказательство эквивалентности===<br />
{{Теорема<br />
|statement=<br />
Построенный ДКА эквивалентен данному НКА.<br />
|proof=<br />
#Докажем, что любое слово, которое принимает НКА, будет принято построенным ДКА. Заметим, что <tex>\forall q \in q_d, \forall c \in \Sigma, \forall p \in \delta(q, c): p \in \delta_d(q_d, c)</tex>. Рассмотрим слово <tex>w=w_1 \dots w_m</tex>, которое принимает автомат НКА: <tex>\langle s, w_1w_2 \dots w_m \rangle \vdash \langle u_1, w_2 \dots w_m \rangle \vdash \langle u_m, \varepsilon \rangle, u_m \in T</tex>. Проверим, что построенный ДКА тоже принимает это слово. Заметим, что <tex>s \in s_d</tex>, а, значит, исходя из нашего наблюдения, мы получаем, что <tex>u_1 \in {u_d}_1</tex>, где <tex>{u_d}_1 = \delta_d(s, w_1)</tex>. Далее, несложно заметить, что <tex>\forall i \leqslant m : u_i \in {u_d}_i</tex>, где <tex>\langle s_d, w_1w_2 \dots w_m \rangle \vdash \langle {u_d}_1, w_2 \dots w_m \rangle \vdash \langle {u_d}_i, w_{i + 1} \dots w_m\rangle</tex>. Таким образом, <tex>u_m \in {u_d}_m</tex>, а из определения терминальных состояний в построенном ДКА мы получаем, что <tex>{u_d}_m \in T_d</tex>, то есть наш ДКА тоже принимает cлово <tex>w</tex>.<br />
#Докажем, что любое слово, которое принимает построенный ДКА, принимает и НКА. Сначала сделаем наблюдение, что если <tex>q_d=\{q\}</tex>, и мы из него достигли по строке <tex>S</tex> какого-то состояния <tex>p_d</tex>, то <tex>\forall p \in p_d</tex> существует путь из <tex>q</tex> в <tex>p</tex> в НКА по строке <tex>S</tex>. Рассмотрим слово <tex>w=w_1 \dots w_m</tex>, которое принимает автомат ДКА: <tex>\langle s_d, w_1w_2 \dots w_m \rangle \vdash \langle {u_d}_1, w_2 \dots w_m \rangle \vdash \langle {u_d}_m, \varepsilon \rangle, {u_d}_m \in T_d</tex>. Проверим, что НКА тоже принимает это слово. Так как <tex>s_d = \{s\}</tex>, и мы из <tex>s_d</tex> достигли <tex>{u_d}_m \in T_d</tex>, возьмём любое терминальное состояние <tex>u_m \in {u_d}_m</tex>. По нашему наблюдению в НКА есть путь из <tex>s</tex> в <tex>u_m</tex> по строке <tex>w</tex>, а, значит, НКА принимает это слово. <br />
Таким образом, множества слов, допускаемых ДКА и НКА, совпадают, то есть они эквивалентны.<br />
}}<br />
<br />
== Алгоритм Томпсона ==<br />
Данный алгоритм преобразовывает НКА в эквивалентный ДКА. Будем использовать вышеуказанный способ построения с одним дополнением {{---}} не будем учитывать состояния недостижимые из стартового.<br />
Поэтому в алгоритме используется обход в ширину.<br />
<br />
===Алгоритм===<br />
<tex>Q</tex> {{---}} очередь состояний, соответствующих множествам, состоящих из состояний НКА.<br />
<tex>s</tex> {{---}} стартовое состояние НКА.<br />
'''Automaton''' getDFAbyNFA(<tex>\langle \Sigma, Q_0, s, T_0, \delta_0 \rangle</tex> : '''Automaton'''):<br />
<tex>Q</tex>.push({s})<br />
'''while''' (<tex>Q</tex> <tex> \neq </tex> <tex>\varnothing </tex>)<br />
<tex>Q</tex>.pop(<tex>q_d</tex>)<br />
'''for''' (<tex>c</tex> '''in''' <tex>\Sigma</tex>)<br />
<tex>p_d</tex> = <tex>\varnothing</tex><br />
'''for''' (<tex>q</tex> '''in''' <tex>q_d</tex>) <br />
<tex>p_d</tex> = <tex>p_d \cup \{ \delta_0(q, c) \}</tex><br />
'''if''' ('''not''' visited[<tex>p_d</tex>])<br />
<tex>Q</tex>.push(<tex>p_d</tex>)<br />
<tex>\delta(q_d, p_d)</tex> = <tex>c</tex><br />
'''if''' (<tex>\exists q</tex> '''in''' <tex>q_d: q</tex> '''in''' <tex>T_0</tex>)<br />
<tex>T</tex>.add<tex>(q_d)</tex><br />
'''return''' <tex>\langle \Sigma, Q, \{s\}, T, \delta \rangle</tex><br />
<br />
===Асимптотика===<br />
Так как количество подмножеств множества состояний НКА не более, чем <tex>2^n</tex>, а каждое подмножество мы обрабатываем ровно один раз за время <tex>O(n)</tex>, получаем верхнюю оценку времени работы алгоритма {{---}} <tex>O(n \cdot 2^n)</tex>.<br />
<br />
===Пример===<br />
Пусть нам дан [[Недетерминированные конечные автоматы|недетерминированный конечный автомат]]: <br />
<br />
[[Файл:DKA.png|250px]]<br />
<br />
По нашему заданию эквивалентного ДКА мы получаем: <br />
<br />
[[Файл:NKA_definition.png|250px]]<br />
<br />
#Помещаем в очередь множество из одной стартовой вершины — <tex>\{1\}</tex>: <tex>Q = \{\{1\}\}</tex>.<br />
#Достаём из очереди множество <tex>\{1\}</tex>: <tex>Q = \{\}</tex>.<br />
#<tex>q_d(\{1\}, a) = \{1, 2\}</tex>, кладём множество <tex>\{1, 2\}</tex> в очередь: <tex>Q = \{\{1, 2\}\}</tex>.<br />
#<tex>q_d(\{1\}, b) = \{1\}</tex>, нам не надо класть множество <tex>\{1\}</tex> в очередь, так как оно уже там было.<br />
#Достаём из очереди множество <tex>\{1, 2\}</tex>: <tex>Q = \{\}</tex>.<br />
#<tex>q_d(\{1, 2\}, a) = \{1, 2\}</tex>, нам не надо класть множество <tex>\{1, 2\}</tex> в очередь, так как оно уже там было.<br />
#<tex>q_d(\{1, 2\}, b) = \{1, 2\}</tex>, нам не надо класть множество <tex>\{1, 2\}</tex> в очередь, так как оно уже там было.<br />
#Помечаем все терминальные вершины, в данном случае — <tex>\{1, 2\}</tex>.<br />
<br />
В итоге получаем ДКА, эквивалентный исходному: <br />
<br />
[[Файл:NKA_algorithm.png|250px]].<br />
<br />
== См. также ==<br />
<br />
* [[Регулярные языки: два определения и их эквивалентность]]<br />
* [[Минимизация ДКА, алгоритм за O(n^2) с построением пар различимых состояний]]<br />
* [[Теорема Клини (совпадение классов автоматных и регулярных языков)]]<br />
<br />
== Источники информации ==<br />
* ''Серебряков В.А.'' Теория и реализация языков программирования. М.: МЗ-Пресс, 2003 (1-е изд.) и 2006 (2-е изд) — С. 294. — ISBN 5-94073-094-9<br />
<br />
[[Категория: Теория формальных языков]]<br />
[[Категория: Автоматы и регулярные языки]]</div>217.197.6.98http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D0%BE%D1%81%D1%82%D1%80%D0%BE%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BF%D0%BE_%D0%9D%D0%9A%D0%90_%D1%8D%D0%BA%D0%B2%D0%B8%D0%B2%D0%B0%D0%BB%D0%B5%D0%BD%D1%82%D0%BD%D0%BE%D0%B3%D0%BE_%D0%94%D0%9A%D0%90,_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A2%D0%BE%D0%BC%D0%BF%D1%81%D0%BE%D0%BD%D0%B0&diff=43455Построение по НКА эквивалентного ДКА, алгоритм Томпсона2015-01-05T14:43:31Z<p>217.197.6.98: /* Описание */</p>
<hr />
<div>== Описание ==<br />
Алгоритм Томпсона строит по [[Недетерминированные конечные автоматы|НКА]] эквивалентный [[Детерминированные конечные автоматы|ДКА]] следующим образом:<br />
* Начало.<br />
* '''Шаг 1.''' Помещаем в очередь <tex>Q</tex> множество, состоящее только из стартовой вершины.<br />
* '''Шаг 2.''' Затем, пока очередь не пуста выполняем следующие действия:<br />
** Достаем из очереди множество, назовем его <tex>q</tex><br />
** Для каждого <tex>c \in \Sigma</tex> построим множество, содержащее состояния, в которые ведет символ <tex>c</tex> из каждого состояния из <tex>q</tex>. Затем положим построенное множество в очередь <tex>Q</tex> только если оно не лежало там раньше. Каждое такое множество в итоговом ДКА будет отдельной вершиной, в которую будут вести переходы по соответствующим символам.<br />
** Если в множестве <tex>q</tex> хотя бы одна из вершин была терминальной в НКА, то соответствующая данному множеству вершина в ДКА также будет терминальной.<br />
* Конец.<br />
<br />
== Построение эквивалентного ДКА по НКА ==<br />
<br />
Пусть нам дан произвольный НКА: <tex>\langle \Sigma , Q, s \in Q, T \subset Q, \delta : Q \times \Sigma \to 2^Q \rangle</tex>.<br />
<br />
Построим по нему следующий ДКА: <tex>\langle \Sigma , Q_d, s_d \in Q_d, T_d \subset Q_d, \delta_d : Q_d \times \Sigma \to Q_d \rangle</tex>, где:<br />
# <tex>Q_d = \{q_d \mid q_d \subset 2^Q \}</tex>,<br />
# <tex>s_d = \{s\}</tex>,<br />
# <tex>T_d = \{q \in Q_d \mid \exists p \in T : p \in q\}</tex>,<br />
# <tex>\delta_d(q, c) = \{ \delta(a, c) \mid a \in q \}</tex>.<br />
<br />
===Доказательство эквивалентности===<br />
{{Теорема<br />
|statement=<br />
Построенный ДКА эквивалентен данному НКА.<br />
|proof=<br />
#Докажем, что любое слово, которое принимает НКА, будет принято построенным ДКА. Заметим, что <tex>\forall q \in q_d, \forall c \in \Sigma, \forall p \in \delta(q, c): p \in \delta_d(q_d, c)</tex>. Рассмотрим слово <tex>w=w_1...w_m</tex>, которое принимает автомат НКА: <tex>\langle s, w_1w_2 \dots w_m \rangle \vdash \langle u_1, w_2 \dots w_m \rangle \vdash \langle u_m, \varepsilon \rangle, u_m \in T</tex>. Проверим, что построенный ДКА тоже принимает это слово. Заметим, что <tex>s \in s_d</tex>, а, значит, исходя из нашего наблюдения, мы получаем, что <tex>u_1 \in {u_d}_1</tex>, где <tex>{u_d}_1 = \delta_d(s, w_1)</tex>. Далее, несложно заметить, что <tex>\forall i \leqslant m : u_i \in {u_d}_i</tex>, где <tex>\langle s_d, w_1w_2 \dots w_m \rangle \vdash \langle {u_d}_1, w_2 \dots w_m \rangle \vdash \langle {u_d}_i, w_{i + 1} \dots w_m\rangle</tex>. Таким образом, <tex>u_m \in {u_d}_m</tex>, а из определения терминальных состояний в построенном ДКА мы получаем, что <tex>{u_d}_m \in T_d</tex>, то есть наш ДКА тоже принимает cлово <tex>w</tex>.<br />
#Докажем, что любое слово, которое принимает построенный ДКА, принимает и НКА. Сначала сделаем наблюдение, что если <tex>q_d=\{q\}</tex>, и мы из него достигли по строке <tex>S</tex> какого-то состояния <tex>p_d</tex>, то <tex>\forall p \in p_d</tex> существует путь из <tex>q</tex> в <tex>p</tex> в НКА по строке <tex>S</tex>. Рассмотрим слово <tex>w=w_1 \dots w_m</tex>, которое принимает автомат ДКА: <tex>\langle s_d, w_1w_2 \dots w_m \rangle \vdash \langle {u_d}_1, w_2 \dots w_m \rangle \vdash \langle {u_d}_m, \varepsilon \rangle, {u_d}_m \in T_d</tex>. Проверим, что НКА тоже принимает это слово. Так как <tex>s_d = \{s\}</tex>, и мы из <tex>s_d</tex> достигли <tex>{u_d}_m \in T_d</tex>, возьмём любое терминальное состояние <tex>u_m \in {u_d}_m</tex>. По нашему наблюдению в НКА есть путь из <tex>s</tex> в <tex>u_m</tex> по строке <tex>w</tex>, а, значит, НКА принимает это слово. <br />
Таким образом, множества слов, допускаемых ДКА и НКА, совпадают, то есть они эквивалентны.<br />
}}<br />
<br />
== Алгоритм Томпсона ==<br />
Данный алгоритм преобразовывает НКА в эквивалентный ДКА. Будем использовать вышеуказанный способ построения с одним дополнением {{---}} не будем учитывать состояния недостижимые из стартового.<br />
Поэтому в алгоритме используется обход в ширину.<br />
<br />
===Алгоритм===<br />
<tex>Q</tex> {{---}} очередь состояний, соответствующих множествам, состоящих из состояний НКА.<br />
<tex>s</tex> {{---}} стартовое состояние НКА.<br />
'''Automaton''' getDFAbyNFA(<tex>\langle \Sigma, Q_0, s, T_0, \delta_0 \rangle</tex> : '''Automaton'''):<br />
<tex>Q</tex>.push({s})<br />
'''while''' (<tex>Q</tex> <tex> \neq </tex> <tex>\varnothing </tex>)<br />
<tex>Q</tex>.pop(<tex>q_d</tex>)<br />
'''for''' (<tex>c</tex> '''in''' <tex>\Sigma</tex>)<br />
<tex>p_d</tex> = <tex>\varnothing</tex><br />
'''for''' (<tex>q</tex> '''in''' <tex>q_d</tex>) <br />
<tex>p_d</tex> = <tex>p_d \cup \{ \delta_0(q, c) \}</tex><br />
'''if''' ('''not''' visited[<tex>p_d</tex>])<br />
<tex>Q</tex>.push(<tex>p_d</tex>)<br />
<tex>\delta(q_d, p_d)</tex> = <tex>c</tex><br />
'''if''' (<tex>\exists q</tex> '''in''' <tex>q_d: q</tex> '''in''' <tex>T_0</tex>)<br />
<tex>T</tex>.add<tex>(q_d)</tex><br />
'''return''' <tex>\langle \Sigma, Q, \{s\}, T, \delta \rangle</tex><br />
<br />
===Асимптотика===<br />
Так как количество подмножеств множества состояний НКА не более, чем <tex>2^n</tex>, а каждое подмножество мы обрабатываем ровно один раз за время <tex>O(n)</tex>, получаем верхнюю оценку времени работы алгоритма {{---}} <tex>O(n \cdot 2^n)</tex>.<br />
<br />
===Пример===<br />
Пусть нам дан [[Недетерминированные конечные автоматы|недетерминированный конечный автомат]]: <br />
<br />
[[Файл:DKA.png|250px]]<br />
<br />
По нашему заданию эквивалентного ДКА мы получаем: <br />
<br />
[[Файл:NKA_definition.png|250px]]<br />
<br />
#Помещаем в очередь множество из одной стартовой вершины — <tex>\{1\}</tex>: <tex>Q = \{\{1\}\}</tex>.<br />
#Достаём из очереди множество <tex>\{1\}</tex>: <tex>Q = \{\}</tex>.<br />
#<tex>q_d(\{1\}, a) = \{1, 2\}</tex>, кладём множество <tex>\{1, 2\}</tex> в очередь: <tex>Q = \{\{1, 2\}\}</tex>.<br />
#<tex>q_d(\{1\}, b) = \{1\}</tex>, нам не надо класть множество <tex>\{1\}</tex> в очередь, так как оно уже там было.<br />
#Достаём из очереди множество <tex>\{1, 2\}</tex>: <tex>Q = \{\}</tex>.<br />
#<tex>q_d(\{1, 2\}, a) = \{1, 2\}</tex>, нам не надо класть множество <tex>\{1, 2\}</tex> в очередь, так как оно уже там было.<br />
#<tex>q_d(\{1, 2\}, b) = \{1, 2\}</tex>, нам не надо класть множество <tex>\{1, 2\}</tex> в очередь, так как оно уже там было.<br />
#Помечаем все терминальные вершины, в данном случае — <tex>\{1, 2\}</tex>.<br />
<br />
В итоге получаем ДКА, эквивалентный исходному: <br />
<br />
[[Файл:NKA_algorithm.png|250px]].<br />
<br />
== См. также ==<br />
<br />
* [[Регулярные языки: два определения и их эквивалентность]]<br />
* [[Минимизация ДКА, алгоритм за O(n^2) с построением пар различимых состояний]]<br />
* [[Теорема Клини (совпадение классов автоматных и регулярных языков)]]<br />
<br />
== Источники информации ==<br />
* ''Серебряков В.А.'' Теория и реализация языков программирования. М.: МЗ-Пресс, 2003 (1-е изд.) и 2006 (2-е изд) — С. 294. — ISBN 5-94073-094-9<br />
<br />
[[Категория: Теория формальных языков]]<br />
[[Категория: Автоматы и регулярные языки]]</div>217.197.6.98http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D0%BE%D1%81%D1%82%D1%80%D0%BE%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BF%D0%BE_%D0%9D%D0%9A%D0%90_%D1%8D%D0%BA%D0%B2%D0%B8%D0%B2%D0%B0%D0%BB%D0%B5%D0%BD%D1%82%D0%BD%D0%BE%D0%B3%D0%BE_%D0%94%D0%9A%D0%90,_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A2%D0%BE%D0%BC%D0%BF%D1%81%D0%BE%D0%BD%D0%B0&diff=43447Построение по НКА эквивалентного ДКА, алгоритм Томпсона2015-01-05T07:12:51Z<p>217.197.6.98: /* Алгоритм */</p>
<hr />
<div>== Описание ==<br />
Алгоритм Томпсона строит по [[Недетерминированные конечные автоматы|НКА]] эквивалентный [[Детерминированные конечные автоматы|ДКА]] следующим образом:<br />
* Начало.<br />
* '''Шаг 1''' Помещаем в очередь <tex>Q</tex> множество, состоящее только из стартовой вершины.<br />
* '''Шаг 2''' Затем, пока очередь не пуста выполняем следующие действия:<br />
** Достаем из очереди множество, назовем его <tex>q</tex><br />
** Для каждого <tex>c \in \Sigma</tex> построим множество, содержащее состояния, в которые ведет символ <tex>c</tex> из каждого состояния из <tex>q</tex>. Затем положим построенное множество в очередь <tex>Q</tex> только если оно не лежало там раньше. Каждое такое множество в итоговом ДКА будет отдельной вершиной, в которую будут вести переходы по соответствующим символам.<br />
** Если в множестве <tex>q</tex> хотя бы одна из вершин была терминальной в НКА, то соответствующая данному множеству вершина в ДКА также будет терминальной.<br />
* Конец<br />
<br />
== Построение эквивалентного ДКА по НКА ==<br />
<br />
Пусть нам дан произвольный НКА: <tex>\langle \Sigma , Q, s \in Q, T \subset Q, \delta : Q \times \Sigma \to 2^Q \rangle</tex>.<br />
<br />
Построим по нему следующий ДКА: <tex>\langle \Sigma , Q_d, s_d \in Q_d, T_d \subset Q_d, \delta_d : Q_d \times \Sigma \to Q_d \rangle</tex>, где:<br />
# <tex>Q_d = \{q_d \mid q_d \subset 2^Q \}</tex>,<br />
# <tex>s_d = \{s\}</tex>,<br />
# <tex>T_d = \{q \in Q_d \mid \exists p \in T : p \in q\}</tex>,<br />
# <tex>\delta_d(q, c) = \{ \delta(a, c) \mid a \in q \}</tex>.<br />
<br />
===Доказательство эквивалентности===<br />
{{Теорема<br />
|statement=<br />
Построенный ДКА эквивалентен данному НКА.<br />
|proof=<br />
#Докажем, что любое слово, которое принимает НКА, будет принято построенным ДКА. Заметим, что <tex>\forall q \in q_d, \forall c \in \Sigma, \forall p \in \delta(q, c): p \in \delta_d(q_d, c)</tex>. Рассмотрим слово <tex>w=w_1...w_m</tex>, которое принимает автомат НКА: <tex>\langle s, w_1w_2 \dots w_m \rangle \vdash \langle u_1, w_2 \dots w_m \rangle \vdash \langle u_m, \varepsilon \rangle, u_m \in T</tex>. Проверим, что построенный ДКА тоже принимает это слово. Заметим, что <tex>s \in s_d</tex>, а, значит, исходя из нашего наблюдения, мы получаем, что <tex>u_1 \in {u_d}_1</tex>, где <tex>{u_d}_1 = \delta_d(s, w_1)</tex>. Далее, несложно заметить, что <tex>\forall i \leqslant m : u_i \in {u_d}_i</tex>, где <tex>\langle s_d, w_1w_2 \dots w_m \rangle \vdash \langle {u_d}_1, w_2 \dots w_m \rangle \vdash \langle {u_d}_i, w_{i + 1} \dots w_m\rangle</tex>. Таким образом, <tex>u_m \in {u_d}_m</tex>, а из определения терминальных состояний в построенном ДКА мы получаем, что <tex>{u_d}_m \in T_d</tex>, то есть наш ДКА тоже принимает cлово <tex>w</tex>.<br />
#Докажем, что любое слово, которое принимает построенный ДКА, принимает и НКА. Сначала сделаем наблюдение, что если <tex>q_d=\{q\}</tex>, и мы из него достигли по строке <tex>S</tex> какого-то состояния <tex>p_d</tex>, то <tex>\forall p \in p_d</tex> существует путь из <tex>q</tex> в <tex>p</tex> в НКА по строке <tex>S</tex>. Рассмотрим слово <tex>w=w_1 \dots w_m</tex>, которое принимает автомат ДКА: <tex>\langle s_d, w_1w_2 \dots w_m \rangle \vdash \langle {u_d}_1, w_2 \dots w_m \rangle \vdash \langle {u_d}_m, \varepsilon \rangle, {u_d}_m \in T_d</tex>. Проверим, что НКА тоже принимает это слово. Так как <tex>s_d = \{s\}</tex>, и мы из <tex>s_d</tex> достигли <tex>{u_d}_m \in T_d</tex>, возьмём любое терминальное состояние <tex>u_m \in {u_d}_m</tex>. По нашему наблюдению в НКА есть путь из <tex>s</tex> в <tex>u_m</tex> по строке <tex>w</tex>, а, значит, НКА принимает это слово. <br />
Таким образом, множества слов, допускаемых ДКА и НКА, совпадают, то есть они эквивалентны.<br />
}}<br />
<br />
== Алгоритм Томпсона ==<br />
Данный алгоритм преобразовывает НКА в эквивалентный ДКА. Будем использовать вышеуказанный способ построения с одним дополнением {{---}} не будем учитывать состояния недостижимые из стартового.<br />
Поэтому в алгоритме используется обход в ширину.<br />
<br />
===Алгоритм===<br />
<tex>Q</tex> {{---}} очередь состояний, соответствующих множествам, состоящих из состояний НКА.<br />
<tex>s</tex> {{---}} стартовое состояние НКА.<br />
'''Automaton''' getDFAbyNFA(<tex>\langle \Sigma, Q_0, s, T_0, \delta_0 \rangle</tex> : '''Automaton'''):<br />
<tex>Q</tex>.push({s})<br />
'''while''' (<tex>Q</tex> <tex> \neq </tex> <tex>\varnothing </tex>)<br />
<tex>Q</tex>.pop(<tex>q_d</tex>)<br />
'''for''' (<tex>c</tex> '''in''' <tex>\Sigma</tex>)<br />
<tex>p_d</tex> = <tex>\varnothing</tex><br />
'''for''' (<tex>q</tex> '''in''' <tex>q_d</tex>) <br />
<tex>p_d</tex> = <tex>p_d \cup \{ \delta_0(q, c) \}</tex><br />
'''if''' ('''not''' visited[<tex>p_d</tex>])<br />
<tex>Q</tex>.push(<tex>p_d</tex>)<br />
<tex>\delta(q_d, p_d)</tex> = <tex>c</tex><br />
'''if''' (<tex>\exists q</tex> '''in''' <tex>q_d: q</tex> '''in''' <tex>T_0</tex>)<br />
<tex>T</tex>.add<tex>(q_d)</tex><br />
'''return''' <tex>\langle \Sigma, Q, \{s\}, T, \delta \rangle</tex><br />
<br />
===Асимптотика===<br />
Так как количество подмножеств множества состояний НКА не более, чем <tex>2^n</tex>, а каждое подмножество мы обрабатываем ровно один раз за время <tex>O(n)</tex>, получаем верхнюю оценку времени работы алгоритма {{---}} <tex>O(n \cdot 2^n)</tex>.<br />
<br />
===Пример===<br />
Пусть нам дан [[Недетерминированные конечные автоматы|недетерминированный конечный автомат]]: <br />
<br />
[[Файл:DKA.png|250px]]<br />
<br />
По нашему заданию эквивалентного ДКА мы получаем: <br />
<br />
[[Файл:NKA_definition.png|250px]]<br />
<br />
#Помещаем в очередь множество из одной стартовой вершины — <tex>\{1\}</tex>: <tex>Q = \{\{1\}\}</tex>.<br />
#Достаём из очереди множество <tex>\{1\}</tex>: <tex>Q = \{\}</tex>.<br />
#<tex>q_d(\{1\}, a) = \{1, 2\}</tex>, кладём множество <tex>\{1, 2\}</tex> в очередь: <tex>Q = \{\{1, 2\}\}</tex>.<br />
#<tex>q_d(\{1\}, b) = \{1\}</tex>, нам не надо класть множество <tex>\{1\}</tex> в очередь, так как оно уже там было.<br />
#Достаём из очереди множество <tex>\{1, 2\}</tex>: <tex>Q = \{\}</tex>.<br />
#<tex>q_d(\{1, 2\}, a) = \{1, 2\}</tex>, нам не надо класть множество <tex>\{1, 2\}</tex> в очередь, так как оно уже там было.<br />
#<tex>q_d(\{1, 2\}, b) = \{1, 2\}</tex>, нам не надо класть множество <tex>\{1, 2\}</tex> в очередь, так как оно уже там было.<br />
#Помечаем все терминальные вершины, в данном случае — <tex>\{1, 2\}</tex>.<br />
<br />
В итоге получаем ДКА, эквивалентный исходному: <br />
<br />
[[Файл:NKA_algorithm.png|250px]].<br />
<br />
== См. также ==<br />
<br />
* [[Регулярные языки: два определения и их эквивалентность]]<br />
* [[Минимизация ДКА, алгоритм за O(n^2) с построением пар различимых состояний]]<br />
* [[Теорема Клини (совпадение классов автоматных и регулярных языков)]]<br />
<br />
== Источники информации ==<br />
* ''Серебряков В.А.'' Теория и реализация языков программирования. М.: МЗ-Пресс, 2003 (1-е изд.) и 2006 (2-е изд) — С. 294. — ISBN 5-94073-094-9<br />
<br />
[[Категория: Теория формальных языков]]<br />
[[Категория: Автоматы и регулярные языки]]</div>217.197.6.98http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D0%BE%D1%81%D1%82%D1%80%D0%BE%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BF%D0%BE_%D0%9D%D0%9A%D0%90_%D1%8D%D0%BA%D0%B2%D0%B8%D0%B2%D0%B0%D0%BB%D0%B5%D0%BD%D1%82%D0%BD%D0%BE%D0%B3%D0%BE_%D0%94%D0%9A%D0%90,_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A2%D0%BE%D0%BC%D0%BF%D1%81%D0%BE%D0%BD%D0%B0&diff=43446Построение по НКА эквивалентного ДКА, алгоритм Томпсона2015-01-05T06:50:30Z<p>217.197.6.98: /* См. также */</p>
<hr />
<div>== Описание ==<br />
Алгоритм Томпсона строит по [[Недетерминированные конечные автоматы|НКА]] эквивалентный [[Детерминированные конечные автоматы|ДКА]] следующим образом:<br />
* Начало.<br />
* '''Шаг 1''' Помещаем в очередь <tex>Q</tex> множество, состоящее только из стартовой вершины.<br />
* '''Шаг 2''' Затем, пока очередь не пуста выполняем следующие действия:<br />
** Достаем из очереди множество, назовем его <tex>q</tex><br />
** Для каждого <tex>c \in \Sigma</tex> построим множество, содержащее состояния, в которые ведет символ <tex>c</tex> из каждого состояния из <tex>q</tex>. Затем положим построенное множество в очередь <tex>Q</tex> только если оно не лежало там раньше. Каждое такое множество в итоговом ДКА будет отдельной вершиной, в которую будут вести переходы по соответствующим символам.<br />
** Если в множестве <tex>q</tex> хотя бы одна из вершин была терминальной в НКА, то соответствующая данному множеству вершина в ДКА также будет терминальной.<br />
* Конец<br />
<br />
== Построение эквивалентного ДКА по НКА ==<br />
<br />
Пусть нам дан произвольный НКА: <tex>\langle \Sigma , Q, s \in Q, T \subset Q, \delta : Q \times \Sigma \to 2^Q \rangle</tex>.<br />
<br />
Построим по нему следующий ДКА: <tex>\langle \Sigma , Q_d, s_d \in Q_d, T_d \subset Q_d, \delta_d : Q_d \times \Sigma \to Q_d \rangle</tex>, где:<br />
# <tex>Q_d = \{q_d \mid q_d \subset 2^Q \}</tex>,<br />
# <tex>s_d = \{s\}</tex>,<br />
# <tex>T_d = \{q \in Q_d \mid \exists p \in T : p \in q\}</tex>,<br />
# <tex>\delta_d(q, c) = \{ \delta(a, c) \mid a \in q \}</tex>.<br />
<br />
===Доказательство эквивалентности===<br />
{{Теорема<br />
|statement=<br />
Построенный ДКА эквивалентен данному НКА.<br />
|proof=<br />
#Докажем, что любое слово, которое принимает НКА, будет принято построенным ДКА. Заметим, что <tex>\forall q \in q_d, \forall c \in \Sigma, \forall p \in \delta(q, c): p \in \delta_d(q_d, c)</tex>. Рассмотрим слово <tex>w=w_1...w_m</tex>, которое принимает автомат НКА: <tex>\langle s, w_1w_2 \dots w_m \rangle \vdash \langle u_1, w_2 \dots w_m \rangle \vdash \langle u_m, \varepsilon \rangle, u_m \in T</tex>. Проверим, что построенный ДКА тоже принимает это слово. Заметим, что <tex>s \in s_d</tex>, а, значит, исходя из нашего наблюдения, мы получаем, что <tex>u_1 \in {u_d}_1</tex>, где <tex>{u_d}_1 = \delta_d(s, w_1)</tex>. Далее, несложно заметить, что <tex>\forall i \leqslant m : u_i \in {u_d}_i</tex>, где <tex>\langle s_d, w_1w_2 \dots w_m \rangle \vdash \langle {u_d}_1, w_2 \dots w_m \rangle \vdash \langle {u_d}_i, w_{i + 1} \dots w_m\rangle</tex>. Таким образом, <tex>u_m \in {u_d}_m</tex>, а из определения терминальных состояний в построенном ДКА мы получаем, что <tex>{u_d}_m \in T_d</tex>, то есть наш ДКА тоже принимает cлово <tex>w</tex>.<br />
#Докажем, что любое слово, которое принимает построенный ДКА, принимает и НКА. Сначала сделаем наблюдение, что если <tex>q_d=\{q\}</tex>, и мы из него достигли по строке <tex>S</tex> какого-то состояния <tex>p_d</tex>, то <tex>\forall p \in p_d</tex> существует путь из <tex>q</tex> в <tex>p</tex> в НКА по строке <tex>S</tex>. Рассмотрим слово <tex>w=w_1 \dots w_m</tex>, которое принимает автомат ДКА: <tex>\langle s_d, w_1w_2 \dots w_m \rangle \vdash \langle {u_d}_1, w_2 \dots w_m \rangle \vdash \langle {u_d}_m, \varepsilon \rangle, {u_d}_m \in T_d</tex>. Проверим, что НКА тоже принимает это слово. Так как <tex>s_d = \{s\}</tex>, и мы из <tex>s_d</tex> достигли <tex>{u_d}_m \in T_d</tex>, возьмём любое терминальное состояние <tex>u_m \in {u_d}_m</tex>. По нашему наблюдению в НКА есть путь из <tex>s</tex> в <tex>u_m</tex> по строке <tex>w</tex>, а, значит, НКА принимает это слово. <br />
Таким образом, множества слов, допускаемых ДКА и НКА, совпадают, то есть они эквивалентны.<br />
}}<br />
<br />
== Алгоритм Томпсона ==<br />
Данный алгоритм преобразовывает НКА в эквивалентный ДКА. Будем использовать вышеуказанный способ построения с одним дополнением {{---}} не будем учитывать состояния недостижимые из стартового.<br />
Поэтому в алгоритме используется обход в ширину.<br />
<br />
===Алгоритм===<br />
<tex>Q</tex> {{---}} очередь состояний, соответствующих множествам, состоящих из состояний НКА.<br />
<tex>s</tex> {{---}} стартовое состояние НКА.<br />
'''Automaton''' getDFAbyNFA(<tex>\langle \Sigma, Q_0, s, T, \delta_0 \rangle</tex> : '''Automaton'''):<br />
<tex>Q</tex>.push({s})<br />
'''while''' (<tex>Q</tex> <tex> \neq </tex> <tex>\varnothing </tex>)<br />
<tex>Q</tex>.pop(<tex>q_d</tex>)<br />
'''for''' (<tex>c</tex> '''in''' <tex>\Sigma</tex>)<br />
<tex>p_d</tex> = <tex>\varnothing</tex><br />
'''for''' (<tex>q</tex> '''in''' <tex>q_d</tex>) <br />
<tex>p_d</tex> = <tex>p_d \cup \{ \delta_0(q, c) \}</tex><br />
'''if''' ('''not''' visited[<tex>p_d</tex>])<br />
<tex>Q</tex>.push(<tex>p_d</tex>)<br />
'''return''' <tex>\langle \Sigma, Q, s, T, \delta \rangle</tex><br />
<br />
===Асимптотика===<br />
Так как количество подмножеств множества состояний НКА не более, чем <tex>2^n</tex>, а каждое подмножество мы обрабатываем ровно один раз за время <tex>O(n)</tex>, получаем верхнюю оценку времени работы алгоритма {{---}} <tex>O(n \cdot 2^n)</tex>.<br />
<br />
===Пример===<br />
Пусть нам дан [[Недетерминированные конечные автоматы|недетерминированный конечный автомат]]: <br />
<br />
[[Файл:DKA.png|250px]]<br />
<br />
По нашему заданию эквивалентного ДКА мы получаем: <br />
<br />
[[Файл:NKA_definition.png|250px]]<br />
<br />
#Помещаем в очередь множество из одной стартовой вершины — <tex>\{1\}</tex>: <tex>Q = \{\{1\}\}</tex>.<br />
#Достаём из очереди множество <tex>\{1\}</tex>: <tex>Q = \{\}</tex>.<br />
#<tex>q_d(\{1\}, a) = \{1, 2\}</tex>, кладём множество <tex>\{1, 2\}</tex> в очередь: <tex>Q = \{\{1, 2\}\}</tex>.<br />
#<tex>q_d(\{1\}, b) = \{1\}</tex>, нам не надо класть множество <tex>\{1\}</tex> в очередь, так как оно уже там было.<br />
#Достаём из очереди множество <tex>\{1, 2\}</tex>: <tex>Q = \{\}</tex>.<br />
#<tex>q_d(\{1, 2\}, a) = \{1, 2\}</tex>, нам не надо класть множество <tex>\{1, 2\}</tex> в очередь, так как оно уже там было.<br />
#<tex>q_d(\{1, 2\}, b) = \{1, 2\}</tex>, нам не надо класть множество <tex>\{1, 2\}</tex> в очередь, так как оно уже там было.<br />
#Помечаем все терминальные вершины, в данном случае — <tex>\{1, 2\}</tex>.<br />
<br />
В итоге получаем ДКА, эквивалентный исходному: <br />
<br />
[[Файл:NKA_algorithm.png|250px]].<br />
<br />
== См. также ==<br />
<br />
* [[Регулярные языки: два определения и их эквивалентность]]<br />
* [[Минимизация ДКА, алгоритм за O(n^2) с построением пар различимых состояний]]<br />
* [[Теорема Клини (совпадение классов автоматных и регулярных языков)]]<br />
<br />
== Источники информации ==<br />
* ''Серебряков В.А.'' Теория и реализация языков программирования. М.: МЗ-Пресс, 2003 (1-е изд.) и 2006 (2-е изд) — С. 294. — ISBN 5-94073-094-9<br />
<br />
[[Категория: Теория формальных языков]]<br />
[[Категория: Автоматы и регулярные языки]]</div>217.197.6.98http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D0%BE%D1%81%D1%82%D1%80%D0%BE%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BF%D0%BE_%D0%9D%D0%9A%D0%90_%D1%8D%D0%BA%D0%B2%D0%B8%D0%B2%D0%B0%D0%BB%D0%B5%D0%BD%D1%82%D0%BD%D0%BE%D0%B3%D0%BE_%D0%94%D0%9A%D0%90,_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A2%D0%BE%D0%BC%D0%BF%D1%81%D0%BE%D0%BD%D0%B0&diff=43445Построение по НКА эквивалентного ДКА, алгоритм Томпсона2015-01-05T06:40:41Z<p>217.197.6.98: /* См. также */</p>
<hr />
<div>== Описание ==<br />
Алгоритм Томпсона строит по [[Недетерминированные конечные автоматы|НКА]] эквивалентный [[Детерминированные конечные автоматы|ДКА]] следующим образом:<br />
* Начало.<br />
* '''Шаг 1''' Помещаем в очередь <tex>Q</tex> множество, состоящее только из стартовой вершины.<br />
* '''Шаг 2''' Затем, пока очередь не пуста выполняем следующие действия:<br />
** Достаем из очереди множество, назовем его <tex>q</tex><br />
** Для каждого <tex>c \in \Sigma</tex> построим множество, содержащее состояния, в которые ведет символ <tex>c</tex> из каждого состояния из <tex>q</tex>. Затем положим построенное множество в очередь <tex>Q</tex> только если оно не лежало там раньше. Каждое такое множество в итоговом ДКА будет отдельной вершиной, в которую будут вести переходы по соответствующим символам.<br />
** Если в множестве <tex>q</tex> хотя бы одна из вершин была терминальной в НКА, то соответствующая данному множеству вершина в ДКА также будет терминальной.<br />
* Конец<br />
<br />
== Построение эквивалентного ДКА по НКА ==<br />
<br />
Пусть нам дан произвольный НКА: <tex>\langle \Sigma , Q, s \in Q, T \subset Q, \delta : Q \times \Sigma \to 2^Q \rangle</tex>.<br />
<br />
Построим по нему следующий ДКА: <tex>\langle \Sigma , Q_d, s_d \in Q_d, T_d \subset Q_d, \delta_d : Q_d \times \Sigma \to Q_d \rangle</tex>, где:<br />
# <tex>Q_d = \{q_d \mid q_d \subset 2^Q \}</tex>,<br />
# <tex>s_d = \{s\}</tex>,<br />
# <tex>T_d = \{q \in Q_d \mid \exists p \in T : p \in q\}</tex>,<br />
# <tex>\delta_d(q, c) = \{ \delta(a, c) \mid a \in q \}</tex>.<br />
<br />
===Доказательство эквивалентности===<br />
{{Теорема<br />
|statement=<br />
Построенный ДКА эквивалентен данному НКА.<br />
|proof=<br />
#Докажем, что любое слово, которое принимает НКА, будет принято построенным ДКА. Заметим, что <tex>\forall q \in q_d, \forall c \in \Sigma, \forall p \in \delta(q, c): p \in \delta_d(q_d, c)</tex>. Рассмотрим слово <tex>w=w_1...w_m</tex>, которое принимает автомат НКА: <tex>\langle s, w_1w_2 \dots w_m \rangle \vdash \langle u_1, w_2 \dots w_m \rangle \vdash \langle u_m, \varepsilon \rangle, u_m \in T</tex>. Проверим, что построенный ДКА тоже принимает это слово. Заметим, что <tex>s \in s_d</tex>, а, значит, исходя из нашего наблюдения, мы получаем, что <tex>u_1 \in {u_d}_1</tex>, где <tex>{u_d}_1 = \delta_d(s, w_1)</tex>. Далее, несложно заметить, что <tex>\forall i \leqslant m : u_i \in {u_d}_i</tex>, где <tex>\langle s_d, w_1w_2 \dots w_m \rangle \vdash \langle {u_d}_1, w_2 \dots w_m \rangle \vdash \langle {u_d}_i, w_{i + 1} \dots w_m\rangle</tex>. Таким образом, <tex>u_m \in {u_d}_m</tex>, а из определения терминальных состояний в построенном ДКА мы получаем, что <tex>{u_d}_m \in T_d</tex>, то есть наш ДКА тоже принимает cлово <tex>w</tex>.<br />
#Докажем, что любое слово, которое принимает построенный ДКА, принимает и НКА. Сначала сделаем наблюдение, что если <tex>q_d=\{q\}</tex>, и мы из него достигли по строке <tex>S</tex> какого-то состояния <tex>p_d</tex>, то <tex>\forall p \in p_d</tex> существует путь из <tex>q</tex> в <tex>p</tex> в НКА по строке <tex>S</tex>. Рассмотрим слово <tex>w=w_1 \dots w_m</tex>, которое принимает автомат ДКА: <tex>\langle s_d, w_1w_2 \dots w_m \rangle \vdash \langle {u_d}_1, w_2 \dots w_m \rangle \vdash \langle {u_d}_m, \varepsilon \rangle, {u_d}_m \in T_d</tex>. Проверим, что НКА тоже принимает это слово. Так как <tex>s_d = \{s\}</tex>, и мы из <tex>s_d</tex> достигли <tex>{u_d}_m \in T_d</tex>, возьмём любое терминальное состояние <tex>u_m \in {u_d}_m</tex>. По нашему наблюдению в НКА есть путь из <tex>s</tex> в <tex>u_m</tex> по строке <tex>w</tex>, а, значит, НКА принимает это слово. <br />
Таким образом, множества слов, допускаемых ДКА и НКА, совпадают, то есть они эквивалентны.<br />
}}<br />
<br />
== Алгоритм Томпсона ==<br />
Данный алгоритм преобразовывает НКА в эквивалентный ДКА. Будем использовать вышеуказанный способ построения с одним дополнением {{---}} не будем учитывать состояния недостижимые из стартового.<br />
Поэтому в алгоритме используется обход в ширину.<br />
<br />
===Алгоритм===<br />
<tex>Q</tex> {{---}} очередь состояний, соответствующих множествам, состоящих из состояний НКА.<br />
<tex>s</tex> {{---}} стартовое состояние НКА.<br />
'''Automaton''' getDFAbyNFA(<tex>\langle \Sigma, Q_0, s, T, \delta_0 \rangle</tex> : '''Automaton'''):<br />
<tex>Q</tex>.push({s})<br />
'''while''' (<tex>Q</tex> <tex> \neq </tex> <tex>\varnothing </tex>)<br />
<tex>Q</tex>.pop(<tex>q_d</tex>)<br />
'''for''' (<tex>c</tex> '''in''' <tex>\Sigma</tex>)<br />
<tex>p_d</tex> = <tex>\varnothing</tex><br />
'''for''' (<tex>q</tex> '''in''' <tex>q_d</tex>) <br />
<tex>p_d</tex> = <tex>p_d \cup \{ \delta_0(q, c) \}</tex><br />
'''if''' ('''not''' visited[<tex>p_d</tex>])<br />
<tex>Q</tex>.push(<tex>p_d</tex>)<br />
'''return''' <tex>\langle \Sigma, Q, s, T, \delta \rangle</tex><br />
<br />
===Асимптотика===<br />
Так как количество подмножеств множества состояний НКА не более, чем <tex>2^n</tex>, а каждое подмножество мы обрабатываем ровно один раз за время <tex>O(n)</tex>, получаем верхнюю оценку времени работы алгоритма {{---}} <tex>O(n \cdot 2^n)</tex>.<br />
<br />
===Пример===<br />
Пусть нам дан [[Недетерминированные конечные автоматы|недетерминированный конечный автомат]]: <br />
<br />
[[Файл:DKA.png|250px]]<br />
<br />
По нашему заданию эквивалентного ДКА мы получаем: <br />
<br />
[[Файл:NKA_definition.png|250px]]<br />
<br />
#Помещаем в очередь множество из одной стартовой вершины — <tex>\{1\}</tex>: <tex>Q = \{\{1\}\}</tex>.<br />
#Достаём из очереди множество <tex>\{1\}</tex>: <tex>Q = \{\}</tex>.<br />
#<tex>q_d(\{1\}, a) = \{1, 2\}</tex>, кладём множество <tex>\{1, 2\}</tex> в очередь: <tex>Q = \{\{1, 2\}\}</tex>.<br />
#<tex>q_d(\{1\}, b) = \{1\}</tex>, нам не надо класть множество <tex>\{1\}</tex> в очередь, так как оно уже там было.<br />
#Достаём из очереди множество <tex>\{1, 2\}</tex>: <tex>Q = \{\}</tex>.<br />
#<tex>q_d(\{1, 2\}, a) = \{1, 2\}</tex>, нам не надо класть множество <tex>\{1, 2\}</tex> в очередь, так как оно уже там было.<br />
#<tex>q_d(\{1, 2\}, b) = \{1, 2\}</tex>, нам не надо класть множество <tex>\{1, 2\}</tex> в очередь, так как оно уже там было.<br />
#Помечаем все терминальные вершины, в данном случае — <tex>\{1, 2\}</tex>.<br />
<br />
В итоге получаем ДКА, эквивалентный исходному: <br />
<br />
[[Файл:NKA_algorithm.png|250px]].<br />
<br />
== См. также ==<br />
<br />
* [[Регулярные языки: два определения и их эквивалентность]]<br />
* [[Минимизация ДКА, алгоритм за O(n^2) с построением пар различимых состояний]]<br />
* [[Теорема Клини (совпадение классов автоматных и регулярных языков)]]<br />
<br />
[[Категория: Теория формальных языков]]<br />
[[Категория: Автоматы и регулярные языки]]</div>217.197.6.98http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D0%BE%D1%81%D1%82%D1%80%D0%BE%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BF%D0%BE_%D0%9D%D0%9A%D0%90_%D1%8D%D0%BA%D0%B2%D0%B8%D0%B2%D0%B0%D0%BB%D0%B5%D0%BD%D1%82%D0%BD%D0%BE%D0%B3%D0%BE_%D0%94%D0%9A%D0%90,_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A2%D0%BE%D0%BC%D0%BF%D1%81%D0%BE%D0%BD%D0%B0&diff=43444Построение по НКА эквивалентного ДКА, алгоритм Томпсона2015-01-05T06:34:43Z<p>217.197.6.98: /* Пример */</p>
<hr />
<div>== Описание ==<br />
Алгоритм Томпсона строит по [[Недетерминированные конечные автоматы|НКА]] эквивалентный [[Детерминированные конечные автоматы|ДКА]] следующим образом:<br />
* Начало.<br />
* '''Шаг 1''' Помещаем в очередь <tex>Q</tex> множество, состоящее только из стартовой вершины.<br />
* '''Шаг 2''' Затем, пока очередь не пуста выполняем следующие действия:<br />
** Достаем из очереди множество, назовем его <tex>q</tex><br />
** Для каждого <tex>c \in \Sigma</tex> построим множество, содержащее состояния, в которые ведет символ <tex>c</tex> из каждого состояния из <tex>q</tex>. Затем положим построенное множество в очередь <tex>Q</tex> только если оно не лежало там раньше. Каждое такое множество в итоговом ДКА будет отдельной вершиной, в которую будут вести переходы по соответствующим символам.<br />
** Если в множестве <tex>q</tex> хотя бы одна из вершин была терминальной в НКА, то соответствующая данному множеству вершина в ДКА также будет терминальной.<br />
* Конец<br />
<br />
== Построение эквивалентного ДКА по НКА ==<br />
<br />
Пусть нам дан произвольный НКА: <tex>\langle \Sigma , Q, s \in Q, T \subset Q, \delta : Q \times \Sigma \to 2^Q \rangle</tex>.<br />
<br />
Построим по нему следующий ДКА: <tex>\langle \Sigma , Q_d, s_d \in Q_d, T_d \subset Q_d, \delta_d : Q_d \times \Sigma \to Q_d \rangle</tex>, где:<br />
# <tex>Q_d = \{q_d \mid q_d \subset 2^Q \}</tex>,<br />
# <tex>s_d = \{s\}</tex>,<br />
# <tex>T_d = \{q \in Q_d \mid \exists p \in T : p \in q\}</tex>,<br />
# <tex>\delta_d(q, c) = \{ \delta(a, c) \mid a \in q \}</tex>.<br />
<br />
===Доказательство эквивалентности===<br />
{{Теорема<br />
|statement=<br />
Построенный ДКА эквивалентен данному НКА.<br />
|proof=<br />
#Докажем, что любое слово, которое принимает НКА, будет принято построенным ДКА. Заметим, что <tex>\forall q \in q_d, \forall c \in \Sigma, \forall p \in \delta(q, c): p \in \delta_d(q_d, c)</tex>. Рассмотрим слово <tex>w=w_1...w_m</tex>, которое принимает автомат НКА: <tex>\langle s, w_1w_2 \dots w_m \rangle \vdash \langle u_1, w_2 \dots w_m \rangle \vdash \langle u_m, \varepsilon \rangle, u_m \in T</tex>. Проверим, что построенный ДКА тоже принимает это слово. Заметим, что <tex>s \in s_d</tex>, а, значит, исходя из нашего наблюдения, мы получаем, что <tex>u_1 \in {u_d}_1</tex>, где <tex>{u_d}_1 = \delta_d(s, w_1)</tex>. Далее, несложно заметить, что <tex>\forall i \leqslant m : u_i \in {u_d}_i</tex>, где <tex>\langle s_d, w_1w_2 \dots w_m \rangle \vdash \langle {u_d}_1, w_2 \dots w_m \rangle \vdash \langle {u_d}_i, w_{i + 1} \dots w_m\rangle</tex>. Таким образом, <tex>u_m \in {u_d}_m</tex>, а из определения терминальных состояний в построенном ДКА мы получаем, что <tex>{u_d}_m \in T_d</tex>, то есть наш ДКА тоже принимает cлово <tex>w</tex>.<br />
#Докажем, что любое слово, которое принимает построенный ДКА, принимает и НКА. Сначала сделаем наблюдение, что если <tex>q_d=\{q\}</tex>, и мы из него достигли по строке <tex>S</tex> какого-то состояния <tex>p_d</tex>, то <tex>\forall p \in p_d</tex> существует путь из <tex>q</tex> в <tex>p</tex> в НКА по строке <tex>S</tex>. Рассмотрим слово <tex>w=w_1 \dots w_m</tex>, которое принимает автомат ДКА: <tex>\langle s_d, w_1w_2 \dots w_m \rangle \vdash \langle {u_d}_1, w_2 \dots w_m \rangle \vdash \langle {u_d}_m, \varepsilon \rangle, {u_d}_m \in T_d</tex>. Проверим, что НКА тоже принимает это слово. Так как <tex>s_d = \{s\}</tex>, и мы из <tex>s_d</tex> достигли <tex>{u_d}_m \in T_d</tex>, возьмём любое терминальное состояние <tex>u_m \in {u_d}_m</tex>. По нашему наблюдению в НКА есть путь из <tex>s</tex> в <tex>u_m</tex> по строке <tex>w</tex>, а, значит, НКА принимает это слово. <br />
Таким образом, множества слов, допускаемых ДКА и НКА, совпадают, то есть они эквивалентны.<br />
}}<br />
<br />
== Алгоритм Томпсона ==<br />
Данный алгоритм преобразовывает НКА в эквивалентный ДКА. Будем использовать вышеуказанный способ построения с одним дополнением {{---}} не будем учитывать состояния недостижимые из стартового.<br />
Поэтому в алгоритме используется обход в ширину.<br />
<br />
===Алгоритм===<br />
<tex>Q</tex> {{---}} очередь состояний, соответствующих множествам, состоящих из состояний НКА.<br />
<tex>s</tex> {{---}} стартовое состояние НКА.<br />
'''Automaton''' getDFAbyNFA(<tex>\langle \Sigma, Q_0, s, T, \delta_0 \rangle</tex> : '''Automaton'''):<br />
<tex>Q</tex>.push({s})<br />
'''while''' (<tex>Q</tex> <tex> \neq </tex> <tex>\varnothing </tex>)<br />
<tex>Q</tex>.pop(<tex>q_d</tex>)<br />
'''for''' (<tex>c</tex> '''in''' <tex>\Sigma</tex>)<br />
<tex>p_d</tex> = <tex>\varnothing</tex><br />
'''for''' (<tex>q</tex> '''in''' <tex>q_d</tex>) <br />
<tex>p_d</tex> = <tex>p_d \cup \{ \delta_0(q, c) \}</tex><br />
'''if''' ('''not''' visited[<tex>p_d</tex>])<br />
<tex>Q</tex>.push(<tex>p_d</tex>)<br />
'''return''' <tex>\langle \Sigma, Q, s, T, \delta \rangle</tex><br />
<br />
===Асимптотика===<br />
Так как количество подмножеств множества состояний НКА не более, чем <tex>2^n</tex>, а каждое подмножество мы обрабатываем ровно один раз за время <tex>O(n)</tex>, получаем верхнюю оценку времени работы алгоритма {{---}} <tex>O(n \cdot 2^n)</tex>.<br />
<br />
===Пример===<br />
Пусть нам дан [[Недетерминированные конечные автоматы|недетерминированный конечный автомат]]: <br />
<br />
[[Файл:DKA.png|250px]]<br />
<br />
По нашему заданию эквивалентного ДКА мы получаем: <br />
<br />
[[Файл:NKA_definition.png|250px]]<br />
<br />
#Помещаем в очередь множество из одной стартовой вершины — <tex>\{1\}</tex>: <tex>Q = \{\{1\}\}</tex>.<br />
#Достаём из очереди множество <tex>\{1\}</tex>: <tex>Q = \{\}</tex>.<br />
#<tex>q_d(\{1\}, a) = \{1, 2\}</tex>, кладём множество <tex>\{1, 2\}</tex> в очередь: <tex>Q = \{\{1, 2\}\}</tex>.<br />
#<tex>q_d(\{1\}, b) = \{1\}</tex>, нам не надо класть множество <tex>\{1\}</tex> в очередь, так как оно уже там было.<br />
#Достаём из очереди множество <tex>\{1, 2\}</tex>: <tex>Q = \{\}</tex>.<br />
#<tex>q_d(\{1, 2\}, a) = \{1, 2\}</tex>, нам не надо класть множество <tex>\{1, 2\}</tex> в очередь, так как оно уже там было.<br />
#<tex>q_d(\{1, 2\}, b) = \{1, 2\}</tex>, нам не надо класть множество <tex>\{1, 2\}</tex> в очередь, так как оно уже там было.<br />
#Помечаем все терминальные вершины, в данном случае — <tex>\{1, 2\}</tex>.<br />
<br />
В итоге получаем ДКА, эквивалентный исходному: <br />
<br />
[[Файл:NKA_algorithm.png|250px]].<br />
<br />
== См. также ==<br />
<br />
* [[Детерминированные конечные автоматы]]<br />
* [[Недетерминированные конечные автоматы]]<br />
<br />
[[Категория: Теория формальных языков]]<br />
[[Категория: Автоматы и регулярные языки]]</div>217.197.6.98http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D0%BE%D1%81%D1%82%D1%80%D0%BE%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BF%D0%BE_%D0%9D%D0%9A%D0%90_%D1%8D%D0%BA%D0%B2%D0%B8%D0%B2%D0%B0%D0%BB%D0%B5%D0%BD%D1%82%D0%BD%D0%BE%D0%B3%D0%BE_%D0%94%D0%9A%D0%90,_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A2%D0%BE%D0%BC%D0%BF%D1%81%D0%BE%D0%BD%D0%B0&diff=43443Построение по НКА эквивалентного ДКА, алгоритм Томпсона2015-01-05T06:17:32Z<p>217.197.6.98: </p>
<hr />
<div>== Описание ==<br />
Алгоритм Томпсона строит по [[Недетерминированные конечные автоматы|НКА]] эквивалентный [[Детерминированные конечные автоматы|ДКА]] следующим образом:<br />
* Начало.<br />
* '''Шаг 1''' Помещаем в очередь <tex>Q</tex> множество, состоящее только из стартовой вершины.<br />
* '''Шаг 2''' Затем, пока очередь не пуста выполняем следующие действия:<br />
** Достаем из очереди множество, назовем его <tex>q</tex><br />
** Для каждого <tex>c \in \Sigma</tex> построим множество, содержащее состояния, в которые ведет символ <tex>c</tex> из каждого состояния из <tex>q</tex>. Затем положим построенное множество в очередь <tex>Q</tex> только если оно не лежало там раньше. Каждое такое множество в итоговом ДКА будет отдельной вершиной, в которую будут вести переходы по соответствующим символам.<br />
** Если в множестве <tex>q</tex> хотя бы одна из вершин была терминальной в НКА, то соответствующая данному множеству вершина в ДКА также будет терминальной.<br />
* Конец<br />
<br />
== Построение эквивалентного ДКА по НКА ==<br />
<br />
Пусть нам дан произвольный НКА: <tex>\langle \Sigma , Q, s \in Q, T \subset Q, \delta : Q \times \Sigma \to 2^Q \rangle</tex>.<br />
<br />
Построим по нему следующий ДКА: <tex>\langle \Sigma , Q_d, s_d \in Q_d, T_d \subset Q_d, \delta_d : Q_d \times \Sigma \to Q_d \rangle</tex>, где:<br />
# <tex>Q_d = \{q_d \mid q_d \subset 2^Q \}</tex>,<br />
# <tex>s_d = \{s\}</tex>,<br />
# <tex>T_d = \{q \in Q_d \mid \exists p \in T : p \in q\}</tex>,<br />
# <tex>\delta_d(q, c) = \{ \delta(a, c) \mid a \in q \}</tex>.<br />
<br />
===Доказательство эквивалентности===<br />
{{Теорема<br />
|statement=<br />
Построенный ДКА эквивалентен данному НКА.<br />
|proof=<br />
#Докажем, что любое слово, которое принимает НКА, будет принято построенным ДКА. Заметим, что <tex>\forall q \in q_d, \forall c \in \Sigma, \forall p \in \delta(q, c): p \in \delta_d(q_d, c)</tex>. Рассмотрим слово <tex>w=w_1...w_m</tex>, которое принимает автомат НКА: <tex>\langle s, w_1w_2 \dots w_m \rangle \vdash \langle u_1, w_2 \dots w_m \rangle \vdash \langle u_m, \varepsilon \rangle, u_m \in T</tex>. Проверим, что построенный ДКА тоже принимает это слово. Заметим, что <tex>s \in s_d</tex>, а, значит, исходя из нашего наблюдения, мы получаем, что <tex>u_1 \in {u_d}_1</tex>, где <tex>{u_d}_1 = \delta_d(s, w_1)</tex>. Далее, несложно заметить, что <tex>\forall i \leqslant m : u_i \in {u_d}_i</tex>, где <tex>\langle s_d, w_1w_2 \dots w_m \rangle \vdash \langle {u_d}_1, w_2 \dots w_m \rangle \vdash \langle {u_d}_i, w_{i + 1} \dots w_m\rangle</tex>. Таким образом, <tex>u_m \in {u_d}_m</tex>, а из определения терминальных состояний в построенном ДКА мы получаем, что <tex>{u_d}_m \in T_d</tex>, то есть наш ДКА тоже принимает cлово <tex>w</tex>.<br />
#Докажем, что любое слово, которое принимает построенный ДКА, принимает и НКА. Сначала сделаем наблюдение, что если <tex>q_d=\{q\}</tex>, и мы из него достигли по строке <tex>S</tex> какого-то состояния <tex>p_d</tex>, то <tex>\forall p \in p_d</tex> существует путь из <tex>q</tex> в <tex>p</tex> в НКА по строке <tex>S</tex>. Рассмотрим слово <tex>w=w_1 \dots w_m</tex>, которое принимает автомат ДКА: <tex>\langle s_d, w_1w_2 \dots w_m \rangle \vdash \langle {u_d}_1, w_2 \dots w_m \rangle \vdash \langle {u_d}_m, \varepsilon \rangle, {u_d}_m \in T_d</tex>. Проверим, что НКА тоже принимает это слово. Так как <tex>s_d = \{s\}</tex>, и мы из <tex>s_d</tex> достигли <tex>{u_d}_m \in T_d</tex>, возьмём любое терминальное состояние <tex>u_m \in {u_d}_m</tex>. По нашему наблюдению в НКА есть путь из <tex>s</tex> в <tex>u_m</tex> по строке <tex>w</tex>, а, значит, НКА принимает это слово. <br />
Таким образом, множества слов, допускаемых ДКА и НКА, совпадают, то есть они эквивалентны.<br />
}}<br />
<br />
== Алгоритм Томпсона ==<br />
Данный алгоритм преобразовывает НКА в эквивалентный ДКА. Будем использовать вышеуказанный способ построения с одним дополнением {{---}} не будем учитывать состояния недостижимые из стартового.<br />
Поэтому в алгоритме используется обход в ширину.<br />
<br />
===Алгоритм===<br />
<tex>Q</tex> {{---}} очередь состояний, соответствующих множествам, состоящих из состояний НКА.<br />
<tex>s</tex> {{---}} стартовое состояние НКА.<br />
'''Automaton''' getDFAbyNFA(<tex>\langle \Sigma, Q_0, s, T, \delta_0 \rangle</tex> : '''Automaton'''):<br />
<tex>Q</tex>.push({s})<br />
'''while''' (<tex>Q</tex> <tex> \neq </tex> <tex>\varnothing </tex>)<br />
<tex>Q</tex>.pop(<tex>q_d</tex>)<br />
'''for''' (<tex>c</tex> '''in''' <tex>\Sigma</tex>)<br />
<tex>p_d</tex> = <tex>\varnothing</tex><br />
'''for''' (<tex>q</tex> '''in''' <tex>q_d</tex>) <br />
<tex>p_d</tex> = <tex>p_d \cup \{ \delta_0(q, c) \}</tex><br />
'''if''' ('''not''' visited[<tex>p_d</tex>])<br />
<tex>Q</tex>.push(<tex>p_d</tex>)<br />
'''return''' <tex>\langle \Sigma, Q, s, T, \delta \rangle</tex><br />
<br />
===Асимптотика===<br />
Так как количество подмножеств множества состояний НКА не более, чем <tex>2^n</tex>, а каждое подмножество мы обрабатываем ровно один раз за время <tex>O(n)</tex>, получаем верхнюю оценку времени работы алгоритма {{---}} <tex>O(n \cdot 2^n)</tex>.<br />
<br />
===Пример===<br />
Пусть нам дан [[Недетерминированные конечные автоматы|недетерминированный конечный автомат]]: [[Файл:DKA.png|250px]]<br />
<br />
По нашему заданию эквивалентного ДКА мы получаем: [[Файл:NKA_definition.png|250px]]<br />
<br />
#Помещаем в очередь множество из одной стартовой вершины — <tex>\{1\}</tex>: <tex>Q = \{\{1\}\}</tex>.<br />
#Достаём из очереди множество <tex>\{1\}</tex>: <tex>Q = \{\}</tex>.<br />
#<tex>q_d(\{1\}, a) = \{1, 2\}</tex>, кладём множество <tex>\{1, 2\}</tex> в очередь: <tex>Q = \{\{1, 2\}\}</tex>.<br />
#<tex>q_d(\{1\}, b) = \{1\}</tex>, нам не надо класть множество <tex>\{1\}</tex> в очередь, так как оно уже там было.<br />
#Достаём из очереди множество <tex>\{1, 2\}</tex>: <tex>Q = \{\}</tex>.<br />
#<tex>q_d(\{1, 2\}, a) = \{1, 2\}</tex>, нам не надо класть множество <tex>\{1, 2\}</tex> в очередь, так как оно уже там было.<br />
#<tex>q_d(\{1, 2\}, b) = \{1, 2\}</tex>, нам не надо класть множество <tex>\{1, 2\}</tex> в очередь, так как оно уже там было.<br />
#Помечаем все терминальные вершины, в данном случае — <tex>\{1, 2\}</tex>.<br />
<br />
В итоге получаем ДКА, эквивалентный исходному: [[Файл:NKA_algorithm.png|250px]].<br />
<br />
== См. также ==<br />
<br />
* [[Детерминированные конечные автоматы]]<br />
* [[Недетерминированные конечные автоматы]]<br />
<br />
[[Категория: Теория формальных языков]]<br />
[[Категория: Автоматы и регулярные языки]]</div>217.197.6.98http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D0%BE%D1%81%D1%82%D1%80%D0%BE%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BF%D0%BE_%D0%9D%D0%9A%D0%90_%D1%8D%D0%BA%D0%B2%D0%B8%D0%B2%D0%B0%D0%BB%D0%B5%D0%BD%D1%82%D0%BD%D0%BE%D0%B3%D0%BE_%D0%94%D0%9A%D0%90,_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A2%D0%BE%D0%BC%D0%BF%D1%81%D0%BE%D0%BD%D0%B0&diff=43381Построение по НКА эквивалентного ДКА, алгоритм Томпсона2015-01-04T13:42:20Z<p>217.197.6.98: /* Алгоритм */</p>
<hr />
<div>== Описание ==<br />
Алгоритм Томпсона строит по [[Недетерминированные конечные автоматы|НКА]] эквивалентный [[Детерминированные конечные автоматы|ДКА]] следующим образом:<br />
* Помещаем в очередь <tex>Q</tex> множество, состоящее только из стартовой вершины.<br />
* Затем, пока очередь не пуста выполняем следующие действия:<br />
** Достаем из очереди множество, назовем его <tex>q</tex><br />
** Для каждого <tex>c \in \Sigma</tex> построим множество, содержащее состояния, в которые ведет <tex>c</tex> по каждому состоянию из <tex>q</tex>. Затем положим построенное множество в очередь <tex>Q</tex> только если оно не лежало там раньше. Каждое такое множество в итоговом ДКА будет отдельной вершиной, в которую будут вести переходы по соответствующим символам.<br />
* Если в множестве <tex>q</tex> хотя бы одна из вершин была терминальной в НКА, то соответствующая данному множеству вершина в ДКА также будет терминальной.<br />
<br />
== Построение эквивалентного ДКА по НКА ==<br />
<br />
Пусть нам дан произвольный НКА: <tex>\langle \Sigma , Q, s \in Q, T \subset Q, \delta : Q \times \Sigma \to 2^Q \rangle</tex>.<br />
<br />
Построим по нему следующий ДКА: <tex>\langle \Sigma , Q_d, s_d \in Q_d, T_d \subset Q_d, \delta_d : Q_d \times \Sigma \to Q_d \rangle</tex>, где:<br />
# <tex>Q_d = \{q_d \mid q_d \subset 2^Q \}</tex>,<br />
# <tex>s_d = \{s\}</tex>,<br />
# <tex>T_d = \{q \in Q_d \mid \exists p \in T : p \in q\}</tex>,<br />
# <tex>\delta_d(q, c) = \{ \delta(a, c) \mid a \in q \}</tex>.<br />
<br />
===Доказательство эквивалентности===<br />
{{Теорема<br />
|statement=<br />
Построенный ДКА эквивалентен данному НКА.<br />
|proof=<br />
#Докажем, что любое слово, которое принимает НКА, будет принято построенным ДКА. Заметим, что <tex>\forall q \in q_d, \forall c \in \Sigma, \forall p \in \delta(q, c): p \in \delta_d(q_d, c)</tex>. Рассмотрим слово <tex>w=w_1...w_m</tex>, которое принимает автомат НКА: <tex>\langle s, w_1w_2...w_m \rangle \vdash \langle u_1, w_2...w_m \rangle \vdash \langle u_m, \varepsilon \rangle, u_m \in T</tex>. Проверим, что построенный ДКА тоже принимает это слово. Заметим, что <tex>s \in s_d</tex>, а, значит, исходя из нашего наблюдения, мы получаем, что <tex>u_1 \in {u_d}_1</tex>, где <tex>{u_d}_1 = \delta_d(s, w_1)</tex>. Далее, несложно заметить, что <tex>\forall i \leqslant m : u_i \in {u_d}_i</tex>, где <tex>\langle s_d, w_1w_2...w_m \rangle \vdash \langle {u_d}_1, w_2...w_m \rangle \vdash \langle {u_d}_i, w_{i + 1}...w_m\rangle</tex>. Таким образом, <tex>u_m \in {u_d}_m</tex>, а из определения терминальных состояний в построенном ДКА мы получаем, что <tex>{u_d}_m \in T_d</tex>, то есть наш ДКА тоже принимает cлово <tex>w</tex>.<br />
#Докажем, что любое слово, которое принимает построенный ДКА, принимает и НКА. Сначала сделаем наблюдение, что если <tex>q_d=\{q\}</tex>, и мы из него достигли по строке <tex>S</tex> какого-то состояния <tex>p_d</tex>, то <tex>\forall p \in p_d</tex> существует путь из <tex>q</tex> в <tex>p</tex> в НКА по строке <tex>S</tex>. Рассмотрим слово <tex>w=w_1...w_m</tex>, которое принимает автомат ДКА: <tex>\langle s_d, w_1w_2...w_m \rangle \vdash \langle {u_d}_1, w_2...w_m \rangle \vdash \langle {u_d}_m, \varepsilon \rangle, {u_d}_m \in T_d</tex>. Проверим, что НКА тоже принимает это слово. Так как <tex>s_d = \{s\}</tex>, и мы из <tex>s_d</tex> достигли <tex>{u_d}_m \in T_d</tex>, возьмём любое терминальное состояние <tex>u_m \in {u_d}_m</tex>. По нашему наблюдению в НКА есть путь из <tex>s</tex> в <tex>u_m</tex> по строке <tex>w</tex>, а, значит, НКА принимает это слово. <br />
Таким образом, множества слов, допускаемых ДКА и НКА, совпадают, то есть они эквивалентны.<br />
}}<br />
<br />
== Алгоритм Томпсона ==<br />
Данный алгоритм преобразовывает НКА в эквивалентный ДКА. Будем использовать вышеуказанный способ построения с одним дополнением {{---}} не будем учитывать состояния недостижимые из стартового.<br />
Поэтому в алгоритме используется обход в ширину.<br />
<br />
===Алгоритм===<br />
<tex>Q</tex> {{---}} очередь состояний, соответствующих множествам, состоящих из состояний НКА.<br />
<tex>s</tex> {{---}} стартовое состояние НКА.<br />
'''Automaton''' getDFAbyNFA(<tex>\langle \Sigma, Q_0, s, T, \delta_0 \rangle</tex> : '''Automaton'''):<br />
<tex>Q</tex>.push({s})<br />
'''while''' (<tex>Q</tex> <tex> \neq </tex> <tex>\varnothing </tex>)<br />
<tex>Q</tex>.pop(<tex>q_d</tex>)<br />
'''for''' (<tex>c</tex> '''in''' <tex>\Sigma</tex>)<br />
<tex>p_d</tex> = <tex>\varnothing</tex><br />
'''for''' (<tex>q</tex> '''in''' <tex>q_d</tex>) <br />
<tex>p_d</tex> = <tex>p_d \cup \{ \delta_0(q, c) \}</tex><br />
'''if''' ('''not''' visited[<tex>p_d</tex>])<br />
<tex>Q</tex>.push(<tex>p_d</tex>)<br />
'''return''' <tex>\langle \Sigma, Q, s, T, \delta \rangle</tex><br />
<br />
===Асимптотика===<br />
Так как количество подмножеств множества состояний НКА не более, чем <tex>2^n</tex>, а каждое подмножество мы обрабатываем ровно один раз за время <tex>O(n)</tex>, получаем верхнюю оценку времени работы алгоритма {{---}} <tex>O(n \cdot 2^n)</tex>.<br />
<br />
===Пример===<br />
Пусть нам дан [[Недетерминированные конечные автоматы|недетерминированный конечный автомат]]: [[Файл:DKA.png|250px]]<br />
<br />
По нашему заданию эквивалентного ДКА мы получаем: [[Файл:NKA_definition.png|250px]]<br />
<br />
#Помещаем в очередь множество из одной стартовой вершины — <tex>\{1\}</tex>: <tex>Q = \{\{1\}\}</tex>.<br />
#Достаём из очереди множество <tex>\{1\}</tex>: <tex>Q = \{\}</tex>.<br />
#<tex>q_d(\{1\}, a) = \{1, 2\}</tex>, кладём множество <tex>\{1, 2\}</tex> в очередь: <tex>Q = \{\{1, 2\}\}</tex>.<br />
#<tex>q_d(\{1\}, b) = \{1\}</tex>, нам не надо класть множество <tex>\{1\}</tex> в очередь, так как оно уже там было.<br />
#Достаём из очереди множество <tex>\{1, 2\}</tex>: <tex>Q = \{\}</tex>.<br />
#<tex>q_d(\{1, 2\}, a) = \{1, 2\}</tex>, нам не надо класть множество <tex>\{1, 2\}</tex> в очередь, так как оно уже там было.<br />
#<tex>q_d(\{1, 2\}, b) = \{1, 2\}</tex>, нам не надо класть множество <tex>\{1, 2\}</tex> в очередь, так как оно уже там было.<br />
#Помечаем все терминальные вершины, в данном случае — <tex>\{1, 2\}</tex>.<br />
<br />
В итоге получаем ДКА, эквивалентный исходному: [[Файл:NKA_algorithm.png|250px]].<br />
<br />
== См. также ==<br />
<br />
* [[Детерминированные конечные автоматы]]<br />
* [[Недетерминированные конечные автоматы]]<br />
<br />
[[Категория: Теория формальных языков]]<br />
[[Категория: Автоматы и регулярные языки]]</div>217.197.6.98http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D0%BE%D1%81%D1%82%D1%80%D0%BE%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BF%D0%BE_%D0%9D%D0%9A%D0%90_%D1%8D%D0%BA%D0%B2%D0%B8%D0%B2%D0%B0%D0%BB%D0%B5%D0%BD%D1%82%D0%BD%D0%BE%D0%B3%D0%BE_%D0%94%D0%9A%D0%90,_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A2%D0%BE%D0%BC%D0%BF%D1%81%D0%BE%D0%BD%D0%B0&diff=43380Построение по НКА эквивалентного ДКА, алгоритм Томпсона2015-01-04T13:41:38Z<p>217.197.6.98: /* Алгоритм */</p>
<hr />
<div>== Описание ==<br />
Алгоритм Томпсона строит по [[Недетерминированные конечные автоматы|НКА]] эквивалентный [[Детерминированные конечные автоматы|ДКА]] следующим образом:<br />
* Помещаем в очередь <tex>Q</tex> множество, состоящее только из стартовой вершины.<br />
* Затем, пока очередь не пуста выполняем следующие действия:<br />
** Достаем из очереди множество, назовем его <tex>q</tex><br />
** Для каждого <tex>c \in \Sigma</tex> построим множество, содержащее состояния, в которые ведет <tex>c</tex> по каждому состоянию из <tex>q</tex>. Затем положим построенное множество в очередь <tex>Q</tex> только если оно не лежало там раньше. Каждое такое множество в итоговом ДКА будет отдельной вершиной, в которую будут вести переходы по соответствующим символам.<br />
* Если в множестве <tex>q</tex> хотя бы одна из вершин была терминальной в НКА, то соответствующая данному множеству вершина в ДКА также будет терминальной.<br />
<br />
== Построение эквивалентного ДКА по НКА ==<br />
<br />
Пусть нам дан произвольный НКА: <tex>\langle \Sigma , Q, s \in Q, T \subset Q, \delta : Q \times \Sigma \to 2^Q \rangle</tex>.<br />
<br />
Построим по нему следующий ДКА: <tex>\langle \Sigma , Q_d, s_d \in Q_d, T_d \subset Q_d, \delta_d : Q_d \times \Sigma \to Q_d \rangle</tex>, где:<br />
# <tex>Q_d = \{q_d \mid q_d \subset 2^Q \}</tex>,<br />
# <tex>s_d = \{s\}</tex>,<br />
# <tex>T_d = \{q \in Q_d \mid \exists p \in T : p \in q\}</tex>,<br />
# <tex>\delta_d(q, c) = \{ \delta(a, c) \mid a \in q \}</tex>.<br />
<br />
===Доказательство эквивалентности===<br />
{{Теорема<br />
|statement=<br />
Построенный ДКА эквивалентен данному НКА.<br />
|proof=<br />
#Докажем, что любое слово, которое принимает НКА, будет принято построенным ДКА. Заметим, что <tex>\forall q \in q_d, \forall c \in \Sigma, \forall p \in \delta(q, c): p \in \delta_d(q_d, c)</tex>. Рассмотрим слово <tex>w=w_1...w_m</tex>, которое принимает автомат НКА: <tex>\langle s, w_1w_2...w_m \rangle \vdash \langle u_1, w_2...w_m \rangle \vdash \langle u_m, \varepsilon \rangle, u_m \in T</tex>. Проверим, что построенный ДКА тоже принимает это слово. Заметим, что <tex>s \in s_d</tex>, а, значит, исходя из нашего наблюдения, мы получаем, что <tex>u_1 \in {u_d}_1</tex>, где <tex>{u_d}_1 = \delta_d(s, w_1)</tex>. Далее, несложно заметить, что <tex>\forall i \leqslant m : u_i \in {u_d}_i</tex>, где <tex>\langle s_d, w_1w_2...w_m \rangle \vdash \langle {u_d}_1, w_2...w_m \rangle \vdash \langle {u_d}_i, w_{i + 1}...w_m\rangle</tex>. Таким образом, <tex>u_m \in {u_d}_m</tex>, а из определения терминальных состояний в построенном ДКА мы получаем, что <tex>{u_d}_m \in T_d</tex>, то есть наш ДКА тоже принимает cлово <tex>w</tex>.<br />
#Докажем, что любое слово, которое принимает построенный ДКА, принимает и НКА. Сначала сделаем наблюдение, что если <tex>q_d=\{q\}</tex>, и мы из него достигли по строке <tex>S</tex> какого-то состояния <tex>p_d</tex>, то <tex>\forall p \in p_d</tex> существует путь из <tex>q</tex> в <tex>p</tex> в НКА по строке <tex>S</tex>. Рассмотрим слово <tex>w=w_1...w_m</tex>, которое принимает автомат ДКА: <tex>\langle s_d, w_1w_2...w_m \rangle \vdash \langle {u_d}_1, w_2...w_m \rangle \vdash \langle {u_d}_m, \varepsilon \rangle, {u_d}_m \in T_d</tex>. Проверим, что НКА тоже принимает это слово. Так как <tex>s_d = \{s\}</tex>, и мы из <tex>s_d</tex> достигли <tex>{u_d}_m \in T_d</tex>, возьмём любое терминальное состояние <tex>u_m \in {u_d}_m</tex>. По нашему наблюдению в НКА есть путь из <tex>s</tex> в <tex>u_m</tex> по строке <tex>w</tex>, а, значит, НКА принимает это слово. <br />
Таким образом, множества слов, допускаемых ДКА и НКА, совпадают, то есть они эквивалентны.<br />
}}<br />
<br />
== Алгоритм Томпсона ==<br />
Данный алгоритм преобразовывает НКА в эквивалентный ДКА. Будем использовать вышеуказанный способ построения с одним дополнением {{---}} не будем учитывать состояния недостижимые из стартового.<br />
Поэтому в алгоритме используется обход в ширину.<br />
<br />
===Алгоритм===<br />
<tex>Q</tex> {{---}} очередь состояний, соответствующих множествам, состоящих из состояний НКА.<br />
<tex>s</tex> {{---}} стартовое состояние НКА.<br />
'''Automaton''' getDFAbyNFA(<tex>\langle \Sigma, Q_0, s, T, \delta \rangle</tex> : '''Automaton'''):<br />
<tex>Q</tex>.push({s})<br />
'''while''' (<tex>Q</tex> <tex> \neq </tex> <tex>\varnothing </tex>)<br />
<tex>Q</tex>.pop(<tex>q_d</tex>)<br />
'''for''' (<tex>c</tex> '''in''' <tex>\Sigma</tex>)<br />
<tex>p_d</tex> = <tex>\varnothing</tex><br />
'''for''' (<tex>q</tex> '''in''' <tex>q_d</tex>) <br />
<tex>p_d</tex> = <tex>p_d \cup \{ \delta(q, c) \}</tex><br />
'''if''' ('''not''' visited[<tex>p_d</tex>])<br />
<tex>Q</tex>.push(<tex>p_d</tex>)<br />
'''return''' <tex>\langle \Sigma, Q, s, T, \delta \rangle</tex><br />
<br />
===Асимптотика===<br />
Так как количество подмножеств множества состояний НКА не более, чем <tex>2^n</tex>, а каждое подмножество мы обрабатываем ровно один раз за время <tex>O(n)</tex>, получаем верхнюю оценку времени работы алгоритма {{---}} <tex>O(n \cdot 2^n)</tex>.<br />
<br />
===Пример===<br />
Пусть нам дан [[Недетерминированные конечные автоматы|недетерминированный конечный автомат]]: [[Файл:DKA.png|250px]]<br />
<br />
По нашему заданию эквивалентного ДКА мы получаем: [[Файл:NKA_definition.png|250px]]<br />
<br />
#Помещаем в очередь множество из одной стартовой вершины — <tex>\{1\}</tex>: <tex>Q = \{\{1\}\}</tex>.<br />
#Достаём из очереди множество <tex>\{1\}</tex>: <tex>Q = \{\}</tex>.<br />
#<tex>q_d(\{1\}, a) = \{1, 2\}</tex>, кладём множество <tex>\{1, 2\}</tex> в очередь: <tex>Q = \{\{1, 2\}\}</tex>.<br />
#<tex>q_d(\{1\}, b) = \{1\}</tex>, нам не надо класть множество <tex>\{1\}</tex> в очередь, так как оно уже там было.<br />
#Достаём из очереди множество <tex>\{1, 2\}</tex>: <tex>Q = \{\}</tex>.<br />
#<tex>q_d(\{1, 2\}, a) = \{1, 2\}</tex>, нам не надо класть множество <tex>\{1, 2\}</tex> в очередь, так как оно уже там было.<br />
#<tex>q_d(\{1, 2\}, b) = \{1, 2\}</tex>, нам не надо класть множество <tex>\{1, 2\}</tex> в очередь, так как оно уже там было.<br />
#Помечаем все терминальные вершины, в данном случае — <tex>\{1, 2\}</tex>.<br />
<br />
В итоге получаем ДКА, эквивалентный исходному: [[Файл:NKA_algorithm.png|250px]].<br />
<br />
== См. также ==<br />
<br />
* [[Детерминированные конечные автоматы]]<br />
* [[Недетерминированные конечные автоматы]]<br />
<br />
[[Категория: Теория формальных языков]]<br />
[[Категория: Автоматы и регулярные языки]]</div>217.197.6.98http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D0%BE%D1%81%D1%82%D1%80%D0%BE%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BF%D0%BE_%D0%9D%D0%9A%D0%90_%D1%8D%D0%BA%D0%B2%D0%B8%D0%B2%D0%B0%D0%BB%D0%B5%D0%BD%D1%82%D0%BD%D0%BE%D0%B3%D0%BE_%D0%94%D0%9A%D0%90,_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A2%D0%BE%D0%BC%D0%BF%D1%81%D0%BE%D0%BD%D0%B0&diff=43376Построение по НКА эквивалентного ДКА, алгоритм Томпсона2015-01-04T13:31:50Z<p>217.197.6.98: </p>
<hr />
<div>== Описание ==<br />
Алгоритм Томпсона строит по [[Недетерминированные конечные автоматы|НКА]] эквивалентный [[Детерминированные конечные автоматы|ДКА]] следующим образом:<br />
* Помещаем в очередь <tex>Q</tex> множество, состоящее только из стартовой вершины.<br />
* Затем, пока очередь не пуста выполняем следующие действия:<br />
** Достаем из очереди множество, назовем его <tex>q</tex><br />
** Для каждого <tex>c \in \Sigma</tex> построим множество, содержащее состояния, в которые ведет <tex>c</tex> по каждому состоянию из <tex>q</tex>. Затем положим построенное множество в очередь <tex>Q</tex> только если оно не лежало там раньше. Каждое такое множество в итоговом ДКА будет отдельной вершиной, в которую будут вести переходы по соответствующим символам.<br />
* Если в множестве <tex>q</tex> хотя бы одна из вершин была терминальной в НКА, то соответствующая данному множеству вершина в ДКА также будет терминальной.<br />
<br />
== Построение эквивалентного ДКА по НКА ==<br />
<br />
Пусть нам дан произвольный НКА: <tex>\langle \Sigma , Q, s \in Q, T \subset Q, \delta : Q \times \Sigma \to 2^Q \rangle</tex>.<br />
<br />
Построим по нему следующий ДКА: <tex>\langle \Sigma , Q_d, s_d \in Q_d, T_d \subset Q_d, \delta_d : Q_d \times \Sigma \to Q_d \rangle</tex>, где:<br />
# <tex>Q_d = \{q_d \mid q_d \subset 2^Q \}</tex>,<br />
# <tex>s_d = \{s\}</tex>,<br />
# <tex>T_d = \{q \in Q_d \mid \exists p \in T : p \in q\}</tex>,<br />
# <tex>\delta_d(q, c) = \{ \delta(a, c) \mid a \in q \}</tex>.<br />
<br />
===Доказательство эквивалентности===<br />
{{Теорема<br />
|statement=<br />
Построенный ДКА эквивалентен данному НКА.<br />
|proof=<br />
#Докажем, что любое слово, которое принимает НКА, будет принято построенным ДКА. Заметим, что <tex>\forall q \in q_d, \forall c \in \Sigma, \forall p \in \delta(q, c): p \in \delta_d(q_d, c)</tex>. Рассмотрим слово <tex>w=w_1...w_m</tex>, которое принимает автомат НКА: <tex>\langle s, w_1w_2...w_m \rangle \vdash \langle u_1, w_2...w_m \rangle \vdash \langle u_m, \varepsilon \rangle, u_m \in T</tex>. Проверим, что построенный ДКА тоже принимает это слово. Заметим, что <tex>s \in s_d</tex>, а, значит, исходя из нашего наблюдения, мы получаем, что <tex>u_1 \in {u_d}_1</tex>, где <tex>{u_d}_1 = \delta_d(s, w_1)</tex>. Далее, несложно заметить, что <tex>\forall i \leqslant m : u_i \in {u_d}_i</tex>, где <tex>\langle s_d, w_1w_2...w_m \rangle \vdash \langle {u_d}_1, w_2...w_m \rangle \vdash \langle {u_d}_i, w_{i + 1}...w_m\rangle</tex>. Таким образом, <tex>u_m \in {u_d}_m</tex>, а из определения терминальных состояний в построенном ДКА мы получаем, что <tex>{u_d}_m \in T_d</tex>, то есть наш ДКА тоже принимает cлово <tex>w</tex>.<br />
#Докажем, что любое слово, которое принимает построенный ДКА, принимает и НКА. Сначала сделаем наблюдение, что если <tex>q_d=\{q\}</tex>, и мы из него достигли по строке <tex>S</tex> какого-то состояния <tex>p_d</tex>, то <tex>\forall p \in p_d</tex> существует путь из <tex>q</tex> в <tex>p</tex> в НКА по строке <tex>S</tex>. Рассмотрим слово <tex>w=w_1...w_m</tex>, которое принимает автомат ДКА: <tex>\langle s_d, w_1w_2...w_m \rangle \vdash \langle {u_d}_1, w_2...w_m \rangle \vdash \langle {u_d}_m, \varepsilon \rangle, {u_d}_m \in T_d</tex>. Проверим, что НКА тоже принимает это слово. Так как <tex>s_d = \{s\}</tex>, и мы из <tex>s_d</tex> достигли <tex>{u_d}_m \in T_d</tex>, возьмём любое терминальное состояние <tex>u_m \in {u_d}_m</tex>. По нашему наблюдению в НКА есть путь из <tex>s</tex> в <tex>u_m</tex> по строке <tex>w</tex>, а, значит, НКА принимает это слово. <br />
Таким образом, множества слов, допускаемых ДКА и НКА, совпадают, то есть они эквивалентны.<br />
}}<br />
<br />
== Алгоритм Томпсона ==<br />
Данный алгоритм преобразовывает НКА в эквивалентный ДКА. Будем использовать вышеуказанный способ построения с одним дополнением {{---}} не будем учитывать состояния недостижимые из стартового.<br />
Поэтому в алгоритме используется обход в ширину.<br />
<br />
===Алгоритм===<br />
<tex>Q</tex> {{---}} очередь состояний, соответствующих множествам, состоящих из состояний НКА.<br />
<tex>s</tex> {{---}} стартовое состояние НКА.<br />
'''Automaton''' getDFAbyNFA(NFA : '''Automaton'''):<br />
<tex>Q</tex>.push({s})<br />
'''while''' (<tex>Q</tex> <tex> \neq </tex> <tex>\varnothing </tex>)<br />
<tex>Q</tex>.pop(<tex>q_d</tex>)<br />
'''for''' (<tex>c</tex> '''in''' <tex>\Sigma</tex>)<br />
<tex>p_d</tex> = <tex>\varnothing</tex><br />
'''for''' (<tex>q</tex> '''in''' <tex>q_d</tex>) <br />
<tex>p_d</tex> = <tex>p_d \cup \{ \delta(q, c) \}</tex><br />
'''if''' ('''not''' visited[<tex>p_d</tex>])<br />
<tex>Q</tex>.push(<tex>p_d</tex>)<br />
'''return''' <tex>\langle \Sigma, Q, s, T, \delta \rangle</tex><br />
<br />
===Асимптотика===<br />
Так как количество подмножеств множества состояний НКА не более, чем <tex>2^n</tex>, а каждое подмножество мы обрабатываем ровно один раз за время <tex>O(n)</tex>, получаем верхнюю оценку времени работы алгоритма {{---}} <tex>O(n \cdot 2^n)</tex>.<br />
<br />
===Пример===<br />
Пусть нам дан [[Недетерминированные конечные автоматы|недетерминированный конечный автомат]]: [[Файл:DKA.png|250px]]<br />
<br />
По нашему заданию эквивалентного ДКА мы получаем: [[Файл:NKA_definition.png|250px]]<br />
<br />
#Помещаем в очередь множество из одной стартовой вершины — <tex>\{1\}</tex>: <tex>Q = \{\{1\}\}</tex>.<br />
#Достаём из очереди множество <tex>\{1\}</tex>: <tex>Q = \{\}</tex>.<br />
#<tex>q_d(\{1\}, a) = \{1, 2\}</tex>, кладём множество <tex>\{1, 2\}</tex> в очередь: <tex>Q = \{\{1, 2\}\}</tex>.<br />
#<tex>q_d(\{1\}, b) = \{1\}</tex>, нам не надо класть множество <tex>\{1\}</tex> в очередь, так как оно уже там было.<br />
#Достаём из очереди множество <tex>\{1, 2\}</tex>: <tex>Q = \{\}</tex>.<br />
#<tex>q_d(\{1, 2\}, a) = \{1, 2\}</tex>, нам не надо класть множество <tex>\{1, 2\}</tex> в очередь, так как оно уже там было.<br />
#<tex>q_d(\{1, 2\}, b) = \{1, 2\}</tex>, нам не надо класть множество <tex>\{1, 2\}</tex> в очередь, так как оно уже там было.<br />
#Помечаем все терминальные вершины, в данном случае — <tex>\{1, 2\}</tex>.<br />
<br />
В итоге получаем ДКА, эквивалентный исходному: [[Файл:NKA_algorithm.png|250px]].<br />
<br />
== См. также ==<br />
<br />
* [[Детерминированные конечные автоматы]]<br />
* [[Недетерминированные конечные автоматы]]<br />
<br />
[[Категория: Теория формальных языков]]<br />
[[Категория: Автоматы и регулярные языки]]</div>217.197.6.98http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D0%BE%D1%81%D1%82%D1%80%D0%BE%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BF%D0%BE_%D0%9D%D0%9A%D0%90_%D1%8D%D0%BA%D0%B2%D0%B8%D0%B2%D0%B0%D0%BB%D0%B5%D0%BD%D1%82%D0%BD%D0%BE%D0%B3%D0%BE_%D0%94%D0%9A%D0%90,_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A2%D0%BE%D0%BC%D0%BF%D1%81%D0%BE%D0%BD%D0%B0&diff=43374Построение по НКА эквивалентного ДКА, алгоритм Томпсона2015-01-04T12:41:02Z<p>217.197.6.98: /* Доказательство эквивалентности */</p>
<hr />
<div>== Построение эквивалентного ДКА по НКА ==<br />
<br />
Пусть нам дан произвольный [[Недетерминированные конечные автоматы|НКА]]: <tex>\langle \Sigma , Q, s \in Q, T \subset Q, \delta : Q \times \Sigma \to 2^Q \rangle</tex>.<br />
<br />
Построим по нему следующий [[Детерминированные конечные автоматы|ДКА]]: <tex>\langle \Sigma , Q_d, s_d \in Q_d, T_d \subset Q_d, \delta_d : Q_d \times \Sigma \to Q_d \rangle</tex>, где:<br />
# <tex>Q_d = \{q_d \mid q_d \subset 2^Q \}</tex>,<br />
# <tex>s_d = \{s\}</tex>,<br />
# <tex>T_d = \{q \in Q_d \mid \exists p \in T : p \in q\}</tex>,<br />
# <tex>\delta_d(q, c) = \{ \delta(a, c) \mid a \in q \}</tex>.<br />
<br />
===Доказательство эквивалентности===<br />
{{Теорема<br />
|statement=<br />
Построенный ДКА эквивалентен данному НКА.<br />
|proof=<br />
#Докажем, что любое слово, которое принимает НКА, будет принято построенным ДКА. Заметим, что <tex>\forall q \in q_d, \forall c \in \Sigma, \forall p \in \delta(q, c): p \in \delta_d(q_d, c)</tex>. Рассмотрим слово <tex>w=w_1...w_m</tex>, которое принимает автомат НКА: <tex>\langle s, w_1w_2...w_m \rangle \vdash \langle u_1, w_2...w_m \rangle \vdash \langle u_m, \varepsilon \rangle, u_m \in T</tex>. Проверим, что построенный ДКА тоже принимает это слово. Заметим, что <tex>s \in s_d</tex>, а, значит, исходя из нашего наблюдения, мы получаем, что <tex>u_1 \in {u_d}_1</tex>, где <tex>{u_d}_1 = \delta_d(s, w_1)</tex>. Далее, несложно заметить, что <tex>\forall i \leqslant m : u_i \in {u_d}_i</tex>, где <tex>\langle s_d, w_1w_2...w_m \rangle \vdash \langle {u_d}_1, w_2...w_m \rangle \vdash \langle {u_d}_i, w_{i + 1}...w_m\rangle</tex>. Таким образом, <tex>u_m \in {u_d}_m</tex>, а из определения терминальных состояний в построенном ДКА мы получаем, что <tex>{u_d}_m \in T_d</tex>, то есть наш ДКА тоже принимает cлово <tex>w</tex>.<br />
#Докажем, что любое слово, которое принимает построенный ДКА, принимает и НКА. Сначала сделаем наблюдение, что если <tex>q_d=\{q\}</tex>, и мы из него достигли по строке <tex>S</tex> какого-то состояния <tex>p_d</tex>, то <tex>\forall p \in p_d</tex> существует путь из <tex>q</tex> в <tex>p</tex> в НКА по строке <tex>S</tex>. Рассмотрим слово <tex>w=w_1...w_m</tex>, которое принимает автомат ДКА: <tex>\langle s_d, w_1w_2...w_m \rangle \vdash \langle {u_d}_1, w_2...w_m \rangle \vdash \langle {u_d}_m, \varepsilon \rangle, {u_d}_m \in T_d</tex>. Проверим, что НКА тоже принимает это слово. Так как <tex>s_d = \{s\}</tex>, и мы из <tex>s_d</tex> достигли <tex>{u_d}_m \in T_d</tex>, возьмём любое терминальное состояние <tex>u_m \in {u_d}_m</tex>. По нашему наблюдению в НКА есть путь из <tex>s</tex> в <tex>u_m</tex> по строке <tex>w</tex>, а, значит, НКА принимает это слово. <br />
Таким образом, множества слов, допускаемых ДКА и НКА, совпадают, то есть они эквивалентны.<br />
}}<br />
<br />
== Алгоритм Томпсона ==<br />
Данный алгоритм преобразовывает НКА в эквивалентный ДКА. Будем использовать вышеуказанный способ построения с одним дополнением {{---}} не будем учитывать состояния недостижимые из стартового.<br />
Поэтому в алгоритме используется обход в ширину.<br />
<br />
===Алгоритм===<br />
<tex>Q</tex> {{---}} очередь состояний, соответствующих множествам, состоящих из состояний НКА.<br />
<tex>s</tex> {{---}} стартовое состояние НКА.<br />
'''Automaton''' getDFAbyNFA(NFA : '''Automaton'''):<br />
<tex>Q</tex>.push({s})<br />
'''while''' (<tex>Q</tex> <tex> \neq </tex> <tex>\varnothing </tex>)<br />
<tex>Q</tex>.pop(<tex>q_d</tex>)<br />
'''for''' (<tex>c</tex> '''in''' <tex>\Sigma</tex>)<br />
<tex>p_d</tex> = <tex>\varnothing</tex><br />
'''for''' (<tex>q</tex> '''in''' <tex>q_d</tex>) <br />
<tex>p_d</tex> = <tex>p_d \cup \{ \delta(q, c) \}</tex><br />
'''if''' ('''not''' visited[<tex>p_d</tex>])<br />
<tex>Q</tex>.push(<tex>p_d</tex>)<br />
'''return''' <tex>\langle \Sigma, Q, s, T, \delta \rangle</tex><br />
<br />
===Асимптотика===<br />
Так как количество подмножеств множества состояний НКА не более, чем <tex>2^n</tex>, а каждое подмножество мы обрабатываем ровно один раз за время <tex>O(n)</tex>, получаем верхнюю оценку времени работы алгоритма {{---}} <tex>O(n \cdot 2^n)</tex>.<br />
<br />
===Пример===<br />
Пусть нам дан [[Недетерминированные конечные автоматы|недетерминированный конечный автомат]]: [[Файл:DKA.png|250px]]<br />
<br />
По нашему заданию эквивалентного ДКА мы получаем: [[Файл:NKA_definition.png|250px]]<br />
<br />
#Помещаем в очередь множество из одной стартовой вершины — <tex>\{1\}</tex>: <tex>Q = \{\{1\}\}</tex>.<br />
#Достаём из очереди множество <tex>\{1\}</tex>: <tex>Q = \{\}</tex>.<br />
#<tex>q_d(\{1\}, a) = \{1, 2\}</tex>, кладём множество <tex>\{1, 2\}</tex> в очередь: <tex>Q = \{\{1, 2\}\}</tex>.<br />
#<tex>q_d(\{1\}, b) = \{1\}</tex>, нам не надо класть множество <tex>\{1\}</tex> в очередь, так как оно уже там было.<br />
#Достаём из очереди множество <tex>\{1, 2\}</tex>: <tex>Q = \{\}</tex>.<br />
#<tex>q_d(\{1, 2\}, a) = \{1, 2\}</tex>, нам не надо класть множество <tex>\{1, 2\}</tex> в очередь, так как оно уже там было.<br />
#<tex>q_d(\{1, 2\}, b) = \{1, 2\}</tex>, нам не надо класть множество <tex>\{1, 2\}</tex> в очередь, так как оно уже там было.<br />
#Помечаем все терминальные вершины, в данном случае — <tex>\{1, 2\}</tex>.<br />
<br />
В итоге получаем ДКА, эквивалентный исходному: [[Файл:NKA_algorithm.png|250px]].<br />
<br />
== См. также ==<br />
<br />
* [[Детерминированные конечные автоматы]]<br />
* [[Недетерминированные конечные автоматы]]<br />
<br />
[[Категория: Теория формальных языков]]<br />
[[Категория: Автоматы и регулярные языки]]</div>217.197.6.98http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D0%BE%D1%81%D1%82%D1%80%D0%BE%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BF%D0%BE_%D0%9D%D0%9A%D0%90_%D1%8D%D0%BA%D0%B2%D0%B8%D0%B2%D0%B0%D0%BB%D0%B5%D0%BD%D1%82%D0%BD%D0%BE%D0%B3%D0%BE_%D0%94%D0%9A%D0%90,_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A2%D0%BE%D0%BC%D0%BF%D1%81%D0%BE%D0%BD%D0%B0&diff=43372Построение по НКА эквивалентного ДКА, алгоритм Томпсона2015-01-04T12:38:25Z<p>217.197.6.98: </p>
<hr />
<div>== Построение эквивалентного ДКА по НКА ==<br />
<br />
Пусть нам дан произвольный [[Недетерминированные конечные автоматы|НКА]]: <tex>\langle \Sigma , Q, s \in Q, T \subset Q, \delta : Q \times \Sigma \to 2^Q \rangle</tex>.<br />
<br />
Построим по нему следующий [[Детерминированные конечные автоматы|ДКА]]: <tex>\langle \Sigma , Q_d, s_d \in Q_d, T_d \subset Q_d, \delta_d : Q_d \times \Sigma \to Q_d \rangle</tex>, где:<br />
# <tex>Q_d = \{q_d \mid q_d \subset 2^Q \}</tex>,<br />
# <tex>s_d = \{s\}</tex>,<br />
# <tex>T_d = \{q \in Q_d \mid \exists p \in T : p \in q\}</tex>,<br />
# <tex>\delta_d(q, c) = \{ \delta(a, c) \mid a \in q \}</tex>.<br />
<br />
===Доказательство эквивалентности===<br />
{{Теорема<br />
|statement=<br />
Построенный ДКА эквивалентен данному НКА.<br />
|proof=<br />
#Докажем, что любое слово, которое принимает НКА, будет принято построенным ДКА. Заметим, что <tex>\forall q \in q_d, \forall c \in \Sigma, \forall p \in \delta(q, c): p \in \delta_d(q_d, c)</tex>. Рассмотрим слово <tex>w=w_1...w_m</tex>, которое принимает автомат НКА: <tex>\langle s, w_1w_2...w_m \rangle \vdash \langle u_1, w_2...w_m \rangle \vdash \langle u_m, \varepsilon \rangle, u_m \in T</tex>. Проверим, что построенный ДКА тоже принимает это слово. Заметим, что <tex>s \in s_d</tex>, а, значит, исходя из нашего наблюдения, мы получаем, что <tex>u_1 \in {u_d}_1</tex>, где <tex>{u_d}_1 = \delta_d(s, w_1)</tex>. Далее, несложно заметить, что <tex>\forall i \leq m : u_i \in {u_d}_i</tex>, где <tex>\langle s_d, w_1w_2...w_m \rangle \vdash \langle {u_d}_1, w_2...w_m \rangle \vdash \langle {u_d}_i, w_{i + 1}...w_m\rangle</tex>. Таким образом, <tex>u_m \in {u_d}_m</tex>, а из определения терминальных состояний в построенном ДКА мы получаем, что <tex>{u_d}_m \in T_d</tex>, то есть наш ДКА тоже принимает cлово <tex>w</tex>.<br />
#Докажем, что любое слово, которое принимает построенный ДКА, принимает и НКА. Сначала сделаем наблюдение, что если <tex>q_d=\{q\}</tex>, и мы из него достигли по строке <tex>S</tex> какого-то состояния <tex>p_d</tex>, то <tex>\forall p \in p_d</tex> существует путь из <tex>q</tex> в <tex>p</tex> в НКА по строке <tex>S</tex>. Рассмотрим слово <tex>w=w_1...w_m</tex>, которое принимает автомат ДКА: <tex>\langle s_d, w_1w_2...w_m \rangle \vdash \langle {u_d}_1, w_2...w_m \rangle \vdash \langle {u_d}_m, \varepsilon \rangle, {u_d}_m \in T_d</tex>. Проверим, что НКА тоже принимает это слово. Так как <tex>s_d = \{s\}</tex>, и мы из <tex>s_d</tex> достигли <tex>{u_d}_m \in T_d</tex>, возьмём любое терминальное состояние <tex>u_m \in {u_d}_m</tex>. По нашему наблюдению в НКА есть путь из <tex>s</tex> в <tex>u_m</tex> по строке <tex>w</tex>, а, значит, НКА принимает это слово. <br />
Таким образом, множества слов, допускаемых ДКА и НКА, совпадают, то есть они эквивалентны.<br />
}}<br />
<br />
== Алгоритм Томпсона ==<br />
Данный алгоритм преобразовывает НКА в эквивалентный ДКА. Будем использовать вышеуказанный способ построения с одним дополнением {{---}} не будем учитывать состояния недостижимые из стартового.<br />
Поэтому в алгоритме используется обход в ширину.<br />
<br />
===Алгоритм===<br />
<tex>Q</tex> {{---}} очередь состояний, соответствующих множествам, состоящих из состояний НКА.<br />
<tex>s</tex> {{---}} стартовое состояние НКА.<br />
'''Automaton''' getDFAbyNFA(NFA : '''Automaton'''):<br />
<tex>Q</tex>.push({s})<br />
'''while''' (<tex>Q</tex> <tex> \neq </tex> <tex>\varnothing </tex>)<br />
<tex>Q</tex>.pop(<tex>q_d</tex>)<br />
'''for''' (<tex>c</tex> '''in''' <tex>\Sigma</tex>)<br />
<tex>p_d</tex> = <tex>\varnothing</tex><br />
'''for''' (<tex>q</tex> '''in''' <tex>q_d</tex>) <br />
<tex>p_d</tex> = <tex>p_d \cup \{ \delta(q, c) \}</tex><br />
'''if''' ('''not''' visited[<tex>p_d</tex>])<br />
<tex>Q</tex>.push(<tex>p_d</tex>)<br />
'''return''' <tex>\langle \Sigma, Q, s, T, \delta \rangle</tex><br />
<br />
===Асимптотика===<br />
Так как количество подмножеств множества состояний НКА не более, чем <tex>2^n</tex>, а каждое подмножество мы обрабатываем ровно один раз за время <tex>O(n)</tex>, получаем верхнюю оценку времени работы алгоритма {{---}} <tex>O(n \cdot 2^n)</tex>.<br />
<br />
===Пример===<br />
Пусть нам дан [[Недетерминированные конечные автоматы|недетерминированный конечный автомат]]: [[Файл:DKA.png|250px]]<br />
<br />
По нашему заданию эквивалентного ДКА мы получаем: [[Файл:NKA_definition.png|250px]]<br />
<br />
#Помещаем в очередь множество из одной стартовой вершины — <tex>\{1\}</tex>: <tex>Q = \{\{1\}\}</tex>.<br />
#Достаём из очереди множество <tex>\{1\}</tex>: <tex>Q = \{\}</tex>.<br />
#<tex>q_d(\{1\}, a) = \{1, 2\}</tex>, кладём множество <tex>\{1, 2\}</tex> в очередь: <tex>Q = \{\{1, 2\}\}</tex>.<br />
#<tex>q_d(\{1\}, b) = \{1\}</tex>, нам не надо класть множество <tex>\{1\}</tex> в очередь, так как оно уже там было.<br />
#Достаём из очереди множество <tex>\{1, 2\}</tex>: <tex>Q = \{\}</tex>.<br />
#<tex>q_d(\{1, 2\}, a) = \{1, 2\}</tex>, нам не надо класть множество <tex>\{1, 2\}</tex> в очередь, так как оно уже там было.<br />
#<tex>q_d(\{1, 2\}, b) = \{1, 2\}</tex>, нам не надо класть множество <tex>\{1, 2\}</tex> в очередь, так как оно уже там было.<br />
#Помечаем все терминальные вершины, в данном случае — <tex>\{1, 2\}</tex>.<br />
<br />
В итоге получаем ДКА, эквивалентный исходному: [[Файл:NKA_algorithm.png|250px]].<br />
<br />
== См. также ==<br />
<br />
* [[Детерминированные конечные автоматы]]<br />
* [[Недетерминированные конечные автоматы]]<br />
<br />
[[Категория: Теория формальных языков]]<br />
[[Категория: Автоматы и регулярные языки]]</div>217.197.6.98http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D0%BE%D1%81%D1%82%D1%80%D0%BE%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BF%D0%BE_%D0%9D%D0%9A%D0%90_%D1%8D%D0%BA%D0%B2%D0%B8%D0%B2%D0%B0%D0%BB%D0%B5%D0%BD%D1%82%D0%BD%D0%BE%D0%B3%D0%BE_%D0%94%D0%9A%D0%90,_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A2%D0%BE%D0%BC%D0%BF%D1%81%D0%BE%D0%BD%D0%B0&diff=43370Построение по НКА эквивалентного ДКА, алгоритм Томпсона2015-01-04T12:35:32Z<p>217.197.6.98: /* Алгоритм */</p>
<hr />
<div>== Построение эквивалентного ДКА по НКА ==<br />
<br />
Пусть нам дан произвольный [[Недетерминированные конечные автоматы|НКА]]: <tex>\langle \Sigma , Q, s \in Q, T \subset Q, \delta : Q \times \Sigma \to 2^Q \rangle</tex>.<br />
<br />
Построим по нему следующий [[Детерминированные конечные автоматы|ДКА]]: <tex>\langle \Sigma , Q_d, s_d \in Q_d, T_d \subset Q_d, \delta_d : Q_d \times \Sigma \to Q_d \rangle</tex>, где:<br />
# <tex>Q_d = \{q_d \mid q_d \subset 2^Q \}</tex>,<br />
# <tex>s_d = \{s\}</tex>,<br />
# <tex>T_d = \{q \in Q_d \mid \exists p \in T : p \in q\}</tex>,<br />
# <tex>\delta_d(q, c) = \{ \delta(a, c) \mid a \in q \}</tex>.<br />
<br />
===Доказательство эквивалентности===<br />
{{Теорема<br />
|statement=<br />
Построенный ДКА эквивалентен данному НКА.<br />
|proof=<br />
#Докажем, что любое слово, которое принимает НКА, будет принято построенным ДКА. Заметим, что <tex>\forall q \in q_d, \forall c \in \Sigma, \forall p \in \delta(q, c): p \in \delta_d(q_d, c)</tex>. Рассмотрим слово <tex>w=w_1...w_m</tex>, которое принимает автомат НКА: <tex>\langle s, w_1w_2...w_m \rangle \vdash \langle u_1, w_2...w_m \rangle \vdash \langle u_m, \varepsilon \rangle, u_m \in T</tex>. Проверим, что построенный ДКА тоже принимает это слово. Заметим, что <tex>s \in s_d</tex>, а, значит, исходя из нашего наблюдения, мы получаем, что <tex>u_1 \in {u_d}_1</tex>, где <tex>{u_d}_1 = \delta_d(s, w_1)</tex>. Далее, несложно заметить, что <tex>\forall i \leq m : u_i \in {u_d}_i</tex>, где <tex>\langle s_d, w_1w_2...w_m \rangle \vdash \langle {u_d}_1, w_2...w_m \rangle \vdash \langle {u_d}_i, w_{i + 1}...w_m\rangle</tex>. Таким образом, <tex>u_m \in {u_d}_m</tex>, а из определения терминальных состояний в построенном ДКА мы получаем, что <tex>{u_d}_m \in T_d</tex>, то есть наш ДКА тоже принимает cлово <tex>w</tex>.<br />
#Докажем, что любое слово, которое принимает построенный ДКА, принимает и НКА. Сначала сделаем наблюдение, что если <tex>q_d=\{q\}</tex>, и мы из него достигли по строке <tex>S</tex> какого-то состояния <tex>p_d</tex>, то <tex>\forall p \in p_d</tex> существует путь из <tex>q</tex> в <tex>p</tex> в НКА по строке <tex>S</tex>. Рассмотрим слово <tex>w=w_1...w_m</tex>, которое принимает автомат ДКА: <tex>\langle s_d, w_1w_2...w_m \rangle \vdash \langle {u_d}_1, w_2...w_m \rangle \vdash \langle {u_d}_m, \varepsilon \rangle, {u_d}_m \in T_d</tex>. Проверим, что НКА тоже принимает это слово. Так как <tex>s_d = \{s\}</tex>, и мы из <tex>s_d</tex> достигли <tex>{u_d}_m \in T_d</tex>, возьмём любое терминальное состояние <tex>u_m \in {u_d}_m</tex>. По нашему наблюдению в НКА есть путь из <tex>s</tex> в <tex>u_m</tex> по строке <tex>w</tex>, а, значит, НКА принимает это слово. <br />
Таким образом, множества слов, допускаемых ДКА и НКА, совпадают, то есть они эквивалентны.<br />
}}<br />
<br />
== Алгоритм Томпсона ==<br />
Данный алгоритм преобразовывает НКА в эквивалентный ДКА. Будем использовать вышеуказанный способ построения с одним дополнением {{---}} не будем учитывать состояния недостижимые из стартового.<br />
Поэтому в алгоритме используется обход в ширину.<br />
<br />
===Алгоритм===<br />
<tex>Q</tex> {{---}} очередь состояний, соответствующих множествам, состоящих из состояний НКА.<br />
<tex>s</tex> {{---}} стартовое состояние НКА.<br />
'''Automaton''' getDFAbyNFA(NFA : '''Automaton'''):<br />
<tex>Q</tex>.push({s})<br />
'''while''' (<tex>Q</tex> <tex> \neq </tex> <tex>\varnothing </tex>)<br />
<tex>Q</tex>.pop(<tex>q_d</tex>)<br />
'''for''' (<tex>c</tex> '''in''' <tex>\Sigma</tex>)<br />
<tex>p_d</tex> = <tex>\varnothing</tex><br />
'''for''' (<tex>q</tex> '''in''' <tex>q_d</tex>) <br />
<tex>p_d</tex> = <tex>p_d \cup \{ \delta(q, c) \}</tex><br />
'''if''' ('''not''' visited[<tex>p_d</tex>])<br />
<tex>Q</tex>.push(<tex>p_d</tex>)<br />
'''return''' <tex>\langle \Sigma, Q, s, T, \delta \rangle</tex><br />
<br />
===Асимптотика===<br />
Так как количество подмножеств множества состояний НКА не более, чем <tex>2^n</tex>, а каждое подмножество мы обрабатываем ровно один раз за время <tex>O(n)</tex>, получаем верхнюю оценку времени работы алгоритма {{---}} <tex>O(n \cdot 2^n)</tex>.<br />
<br />
===Пример===<br />
Пусть нам дан [[Недетерминированные конечные автоматы|недетерминированный конечный автомат]]: [[Файл:DKA.png|250px]]<br />
<br />
По нашему заданию эквивалентного ДКА мы получаем: [[Файл:NKA_definition.png|250px]]<br />
<br />
#Помещаем в очередь множество из одной стартовой вершины — <tex>\{1\}</tex>: <tex>Q = \{\{1\}\}</tex>.<br />
#Достаём из очереди множество <tex>\{1\}</tex>: <tex>Q = \{\}</tex>.<br />
#<tex>q_d(\{1\}, a) = \{1, 2\}</tex>, кладём множество <tex>\{1, 2\}</tex> в очередь: <tex>Q = \{\{1, 2\}\}</tex>.<br />
#<tex>q_d(\{1\}, b) = \{1\}</tex>, нам не надо класть множество <tex>\{1\}</tex> в очередь, так как оно уже там было.<br />
#Достаём из очереди множество <tex>\{1, 2\}</tex>: <tex>Q = \{\}</tex>.<br />
#<tex>q_d(\{1, 2\}, a) = \{1, 2\}</tex>, нам не надо класть множество <tex>\{1, 2\}</tex> в очередь, так как оно уже там было.<br />
#<tex>q_d(\{1, 2\}, b) = \{1, 2\}</tex>, нам не надо класть множество <tex>\{1, 2\}</tex> в очередь, так как оно уже там было.<br />
#Помечаем все терминальные вершины, в данном случае — <tex>\{1, 2\}</tex>.<br />
<br />
В итоге получаем ДКА, эквивалентный исходному: [[Файл:NKA_algorithm.png|250px]].<br />
<br />
[[Категория: Теория формальных языков]]<br />
[[Категория: Автоматы и регулярные языки]]</div>217.197.6.98http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D0%BE%D1%81%D1%82%D1%80%D0%BE%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BF%D0%BE_%D0%9D%D0%9A%D0%90_%D1%8D%D0%BA%D0%B2%D0%B8%D0%B2%D0%B0%D0%BB%D0%B5%D0%BD%D1%82%D0%BD%D0%BE%D0%B3%D0%BE_%D0%94%D0%9A%D0%90,_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A2%D0%BE%D0%BC%D0%BF%D1%81%D0%BE%D0%BD%D0%B0&diff=42601Построение по НКА эквивалентного ДКА, алгоритм Томпсона2014-12-20T19:20:16Z<p>217.197.6.98: /* Алгоритм */</p>
<hr />
<div>== Построение эквивалентного ДКА по НКА ==<br />
<br />
Пусть нам дан произвольный [[Недетерминированные конечные автоматы|НКА]]: <tex>\langle \Sigma , Q, s \in Q, T \subset Q, \delta : Q \times \Sigma \to 2^Q \rangle</tex>.<br />
<br />
Построим по нему следующий [[Детерминированные конечные автоматы|ДКА]]: <tex>\langle \Sigma , Q_d, s_d \in Q_d, T_d \subset Q_d, \delta_d : Q_d \times \Sigma \to Q_d \rangle</tex>, где:<br />
# <tex>Q_d = \{q_d \mid q_d \subset 2^Q \}</tex>,<br />
# <tex>s_d = \{s\}</tex>,<br />
# <tex>T_d = \{q \in Q_d \mid \exists p \in T : p \in q\}</tex>,<br />
# <tex>\delta_d(q, c) = \{ \delta(a, c) \mid a \in q \}</tex>.<br />
<br />
===Доказательство эквивалентности===<br />
{{Теорема<br />
|statement=<br />
Построенный ДКА эквивалентен данному НКА.<br />
|proof=<br />
#Докажем, что любое слово, которое принимает НКА, будет принято построенным ДКА. Заметим, что <tex>\forall q \in q_d, \forall c \in \Sigma, \forall p \in \delta(q, c): p \in \delta_d(q_d, c)</tex>. Рассмотрим слово <tex>w=w_1...w_m</tex>, которое принимает автомат НКА: <tex>\langle s, w_1w_2...w_m \rangle \vdash \langle u_1, w_2...w_m \rangle \vdash \langle u_m, \varepsilon \rangle, u_m \in T</tex>. Проверим, что построенный ДКА тоже принимает это слово. Заметим, что <tex>s \in s_d</tex>, а, значит, исходя из нашего наблюдения, мы получаем, что <tex>u_1 \in {u_d}_1</tex>, где <tex>{u_d}_1 = \delta_d(s, w_1)</tex>. Далее, несложно заметить, что <tex>\forall i \leq m : u_i \in {u_d}_i</tex>, где <tex>\langle s_d, w_1w_2...w_m \rangle \vdash \langle {u_d}_1, w_2...w_m \rangle \vdash \langle {u_d}_i, w_{i + 1}...w_m\rangle</tex>. Таким образом, <tex>u_m \in {u_d}_m</tex>, а из определения терминальных состояний в построенном ДКА мы получаем, что <tex>{u_d}_m \in T_d</tex>, то есть наш ДКА тоже принимает cлово <tex>w</tex>.<br />
#Докажем, что любое слово, которое принимает построенный ДКА, принимает и НКА. Сначала сделаем наблюдение, что если <tex>q_d=\{q\}</tex>, и мы из него достигли по строке <tex>S</tex> какого-то состояния <tex>p_d</tex>, то <tex>\forall p \in p_d</tex> существует путь из <tex>q</tex> в <tex>p</tex> в НКА по строке <tex>S</tex>. Рассмотрим слово <tex>w=w_1...w_m</tex>, которое принимает автомат ДКА: <tex>\langle s_d, w_1w_2...w_m \rangle \vdash \langle {u_d}_1, w_2...w_m \rangle \vdash \langle {u_d}_m, \varepsilon \rangle, {u_d}_m \in T_d</tex>. Проверим, что НКА тоже принимает это слово. Так как <tex>s_d = \{s\}</tex>, и мы из <tex>s_d</tex> достигли <tex>{u_d}_m \in T_d</tex>, возьмём любое терминальное состояние <tex>u_m \in {u_d}_m</tex>. По нашему наблюдению в НКА есть путь из <tex>s</tex> в <tex>u_m</tex> по строке <tex>w</tex>, а, значит, НКА принимает это слово. <br />
Таким образом, множества слов, допускаемых ДКА и НКА, совпадают, то есть они эквивалентны.<br />
}}<br />
<br />
== Алгоритм Томпсона ==<br />
Данный алгоритм преобразовывает НКА в эквивалентный ДКА. Будем использовать вышеуказанный способ построения с одним дополнением {{---}} не будем учитывать состояния недостижимые из стартового.<br />
Поэтому в алгоритме используется обход в ширину.<br />
<br />
===Алгоритм===<br />
<tex>Q</tex> {{---}} очередь состояний, соответствующих множествам, состоящих из состояний НКА.<br />
<tex>s</tex> {{---}} стартовое состояние НКА.<br />
'''Automaton''' getDKAbyNKA(<tex>\langle \Sigma, Q, s, T, \delta \rangle</tex>: '''Automaton'''):<br />
<tex>Q</tex>.push({s})<br />
'''while''' (<tex>Q</tex> <tex> \neq </tex> <tex>\varnothing </tex>)<br />
<tex>Q</tex>.pop(<tex>q_d</tex>)<br />
'''for''' (<tex>c</tex> '''in''' <tex>\Sigma</tex>)<br />
<tex>p_d</tex> = <tex>\varnothing</tex><br />
'''for''' (<tex>q</tex> '''in''' <tex>q_d</tex>) <br />
<tex>p_d</tex> = <tex>p_d \cup \{ \delta(q, c) \}</tex><br />
'''if''' ('''not''' visited[<tex>p_d</tex>])<br />
<tex>Q</tex>.push(<tex>p_d</tex>)<br />
<br />
===Асимптотика===<br />
Так как количество подмножеств множества состояний НКА не более, чем <tex>2^n</tex>, а каждое подмножество мы обрабатываем ровно один раз за время <tex>O(n)</tex>, получаем верхнюю оценку времени работы алгоритма {{---}} <tex>O(n \cdot 2^n)</tex>.<br />
<br />
===Пример===<br />
Пусть нам дан [[Недетерминированные конечные автоматы|недетерминированный конечный автомат]]: [[Файл:DKA.png|250px]]<br />
<br />
По нашему заданию эквивалентного ДКА мы получаем: [[Файл:NKA_definition.png|250px]]<br />
<br />
#Помещаем в очередь множество из одной стартовой вершины — <tex>\{1\}</tex>: <tex>Q = \{\{1\}\}</tex>.<br />
#Достаём из очереди множество <tex>\{1\}</tex>: <tex>Q = \{\}</tex>.<br />
#<tex>q_d(\{1\}, a) = \{1, 2\}</tex>, кладём множество <tex>\{1, 2\}</tex> в очередь: <tex>Q = \{\{1, 2\}\}</tex>.<br />
#<tex>q_d(\{1\}, b) = \{1\}</tex>, нам не надо класть множество <tex>\{1\}</tex> в очередь, так как оно уже там было.<br />
#Достаём из очереди множество <tex>\{1, 2\}</tex>: <tex>Q = \{\}</tex>.<br />
#<tex>q_d(\{1, 2\}, a) = \{1, 2\}</tex>, нам не надо класть множество <tex>\{1, 2\}</tex> в очередь, так как оно уже там было.<br />
#<tex>q_d(\{1, 2\}, b) = \{1, 2\}</tex>, нам не надо класть множество <tex>\{1, 2\}</tex> в очередь, так как оно уже там было.<br />
#Помечаем все терминальные вершины, в данном случае — <tex>\{1, 2\}</tex>.<br />
<br />
В итоге получаем ДКА, эквивалентный исходному: [[Файл:NKA_algorithm.png|250px]].<br />
<br />
[[Категория: Теория формальных языков]]<br />
[[Категория: Автоматы и регулярные языки]]</div>217.197.6.98http://neerc.ifmo.ru/wiki/index.php?title=%D0%9B%D0%B5%D0%BC%D0%BC%D0%B0_%D0%BE_%D0%B5%D0%B4%D0%B8%D0%BD%D1%81%D1%82%D0%B2%D0%B5%D0%BD%D0%BD%D0%BE%D0%BC_%D0%BF%D0%B0%D1%80%D0%BE%D1%81%D0%BE%D1%87%D0%B5%D1%82%D0%B0%D0%BD%D0%B8%D0%B8_%D0%B2_%D0%B3%D1%80%D0%B0%D1%84%D0%B5_%D0%B7%D0%B0%D0%BC%D0%B5%D0%BD&diff=37425Лемма о единственном паросочетании в графе замен2014-05-30T20:04:42Z<p>217.197.6.98: </p>
<hr />
<div>{{Утверждение<br />
|statement=Пусть [[Двудольные_графы_и_раскраска_в_2_цвета|двудольный граф]] <tex>G</tex> содержит единственное [[Связь_максимального_паросочетания_и_минимального_вершинного_покрытия_в_двудольных_графах#Связь_максимального_паросочетания_и_минимального_вершинного_покрытия_в_двудольном_графе|полное паросочетание]] <tex>M</tex>. Тогда можно упорядочить вершины левой <tex>(a_i \in A)</tex> и правой <tex>(b_i \in B)</tex> долей таким образом, что <tex>\forall j > i : (a_i b_j) \notin G</tex>. При этом рёбра паросочетания будут иметь вид <tex>(a_i b_i)</tex>.<br />
<br />
|proof=Индукция по <tex>|A|</tex>.<br/><br />
При <tex>|A|=1</tex> утверждение очевидно. <br/><br />
Пусть <tex>|A|=n>1</tex> (для <tex>|A|=n-1</tex> утверждение верно). Возьмем произвольную вершину в левой доли. Будем строить из неё [[Теорема о максимальном паросочетании и дополняющих цепях#Паросочетание в двудольном графе|чередующуюся цепь]], добавляя по очереди ребро, входящее в <tex>M</tex>, и ребро, не входящее в <tex>M</tex>. Заметим, что такой путь не содержит циклов (циклы нечётной длины невозможны, так как граф двудольный, циклы чётной длины отсутствуют из-за единственности паросочетания). Если последнее добавленное ребро не принадлежит <tex>M</tex>, то присоединим к цепи ребро из <tex>M</tex>, инцидентное последней вершине. Значит, построение цепи прервется только при добавлении ребра из <tex>M</tex> при достижении вершины [[Основные определения теории графов#Степень вершины|степени]] <tex>1</tex>. <br/><br />
Таким образом, последнее ребро в цепи имеет вид <tex>(ab) \in M</tex>, где <tex>a \in A, b \in B, \deg b = 1</tex>. Положим <tex>a_n=a, b_n=b</tex>. Для <tex>G \setminus \{a_n \cup b_n \}</tex> утверждение верно по предположению индукции. С другой стороны, так как <tex>\deg b_n = 1</tex>, то <tex>(a_i b_n) \notin G</tex> при <tex>i<n</tex>, поэтому для <tex>j = n</tex> утверждение также верно.<br/><br />
}}<br />
<br />
<br />
{{Лемма<br />
|about=<br />
о единственном паросочетании в графе замен<br />
|statement= Дан [[Определение матроида|матроид]] <tex>M = \langle X,I \rangle </tex>. Пусть двудольный граф <tex>G_M(A) = \{ (x, y) | x \in A, y \notin A, A \setminus x \cup y \in I \}</tex> содержит единственное полное паросочетание на <tex>A \triangle B</tex>, где <tex>A\in I</tex> и <tex>|A| = |B|</tex>. Тогда <tex>B \in I</tex>.<br />
|proof= <br />
[[Файл:Graph replacement.png|thumb|left|160px|]]<br />
Упорядочим вершины левой <tex>(y_i \in A \setminus B)</tex> и правой <tex>(z_j \in B \setminus A)</tex> долей таким образом, что <tex>\forall j > i : (y_i z_j) \notin G_M(A)</tex>. При таком упорядочивании ребра паросочетания имеют вид <tex>(y_i z_i)</tex>.<br />
<br />
Требуется доказать, что <tex>B</tex> независимо. Предположим обратное. Пусть <tex>B \notin I</tex>, тогда существует [[Теорема о циклах|цикл]] <tex>C \subset B</tex>.<br/> Выберем минимальное <tex>i</tex> такое, что <tex>z_i \in C</tex>. Так как <tex>\forall j > i : (y_i z_j) \notin G_M(A)</tex>, то <tex>A \setminus y_i \cup z_j \notin I</tex>, следовательно, <tex>C \setminus z_i \subset \langle A \setminus y_i \rangle </tex>. По [[Оператор замыкания для матроидов#theorem|свойствам замыкания 1 и 3]] получаем:<br/><br />
<tex>C \setminus z_i \subset \langle A \setminus y_i \rangle \Rightarrow \langle C \setminus z_i \rangle \subset \langle \langle A \setminus y_i \rangle \rangle \Rightarrow \langle C \setminus z_i \rangle \subset \langle A \setminus y_i \rangle</tex> <br/><br />
Так как <tex>z_i \in \langle C \setminus z_i \rangle \subset \langle A \setminus y_i \rangle</tex>, то <tex>A \setminus y_i \cup z_i \notin I</tex>, то есть в <tex>G_M(A)</tex> не существует ребра <tex>(y_i z_i)</tex>. Но тогда, как было отмечено ранее, не существует полного паросочетания. Получили противоречие.<br />
}}<br />
<br />
== Источник ==<br />
''Chandra Chekuri'' — [http://www.cs.illinois.edu/class/sp10/cs598csc/Lectures/Lecture16.pdf '''Combinatorial Optimization'''], с. 6<br />
<br />
[[Категория:Алгоритмы и структуры данных]]<br />
[[Категория:Матроиды]]</div>217.197.6.98http://neerc.ifmo.ru/wiki/index.php?title=%D0%9B%D0%B5%D0%BC%D0%BC%D0%B0_%D0%BE_%D0%B5%D0%B4%D0%B8%D0%BD%D1%81%D1%82%D0%B2%D0%B5%D0%BD%D0%BD%D0%BE%D0%BC_%D0%BF%D0%B0%D1%80%D0%BE%D1%81%D0%BE%D1%87%D0%B5%D1%82%D0%B0%D0%BD%D0%B8%D0%B8_%D0%B2_%D0%B3%D1%80%D0%B0%D1%84%D0%B5_%D0%B7%D0%B0%D0%BC%D0%B5%D0%BD&diff=37423Лемма о единственном паросочетании в графе замен2014-05-30T20:03:10Z<p>217.197.6.98: </p>
<hr />
<div>{{Утверждение<br />
|statement=Пусть [[Двудольные_графы_и_раскраска_в_2_цвета|двудольный граф]] <tex>G</tex> содержит единственное [[Связь_максимального_паросочетания_и_минимального_вершинного_покрытия_в_двудольных_графах#Связь_максимального_паросочетания_и_минимального_вершинного_покрытия_в_двудольном_графе|полное паросочетание]] <tex>M</tex>. Тогда можно упорядочить вершины левой <tex>(a_i \in A)</tex> и правой <tex>(b_i \in B)</tex> долей таким образом, что <tex>\forall j > i : (a_i b_j) \notin G</tex>. При этом рёбра паросочетания будут иметь вид <tex>(a_i b_i)</tex>.<br />
<br />
|proof=Индукция по <tex>|A|</tex>.<br/><br />
При <tex>|A|=1</tex> утверждение очевидно. <br/><br />
Пусть <tex>|A|=n>1</tex> (для <tex>|A|=n-1</tex> утверждение верно). Возьмем произвольную вершину в левой доли. Будем строить из неё [[Теорема о максимальном паросочетании и дополняющих цепях#Паросочетание в двудольном графе|чередующуюся цепь]], добавляя по очереди ребро, входящее в <tex>M</tex>, и ребро, не входящее в <tex>M</tex>. Заметим, что такой путь не содержит циклов (циклы нечётной длины невозможны, так как граф двудольный, циклы чётной длины отсутствуют из-за единственности паросочетания). Если последнее добавленное ребро не принадлежит <tex>M</tex>, то присоединим к цепи ребро из <tex>M</tex>, инцидентное последней вершине. Значит, построение цепи прервется только при добавлении ребра из <tex>M</tex> при достижении вершины [[Основные определения теории графов#Степень вершины|степени]] 1. <br/><br />
Таким образом, последнее ребро в цепи имеет вид <tex>(ab) \in M</tex>, где <tex>a \in A, b \in B, \deg b = 1</tex>. Положим <tex>a_n=a, b_n=b</tex>. Для <tex>G \setminus \{a_n \cup b_n \}</tex> утверждение верно по предположению индукции. С другой стороны, так как <tex>\deg b_n = 1</tex>, то <tex>(a_i b_n) \notin G</tex> при <tex>i<n</tex>, поэтому для <tex>j = n</tex> утверждение также верно.<br/><br />
}}<br />
<br />
<br />
{{Лемма<br />
|about=<br />
о единственном паросочетании в графе замен<br />
|statement= Дан [[Определение матроида|матроид]] <tex>M = \langle X,I \rangle </tex>. Пусть двудольный граф <tex>G_M(A) = \{ (x, y) | x \in A, y \notin A, A \setminus x \cup y \in I \}</tex> содержит единственное полное паросочетание на <tex>A \triangle B</tex>, где <tex>A\in I</tex> и <tex>|A| = |B|</tex>. Тогда <tex>B \in I</tex>.<br />
|proof= <br />
[[Файл:Graph replacement.png|thumb|left|160px|]]<br />
Упорядочим вершины левой <tex>(y_i \in A \setminus B)</tex> и правой <tex>(z_j \in B \setminus A)</tex> долей таким образом, что <tex>\forall j > i : (y_i z_j) \notin G_M(A)</tex>. При таком упорядочивании ребра паросочетания имеют вид <tex>(y_i z_i)</tex>.<br />
<br />
Требуется доказать, что <tex>B</tex> независимо. Предположим обратное. Пусть <tex>B \notin I</tex>, тогда существует [[Теорема о циклах|цикл]] <tex>C \subset B</tex>.<br/> Выберем минимальное <tex>i</tex> такое, что <tex>z_i \in C</tex>. Так как <tex>\forall j > i : (y_i z_j) \notin G_M(A)</tex>, то <tex>A \setminus y_i \cup z_j \notin I</tex>, следовательно, <tex>C \setminus z_i \subset \langle A \setminus y_i \rangle </tex>. По [[Оператор замыкания для матроидов#theorem|свойствам замыкания 1 и 3]] получаем:<br/><br />
<tex>C \setminus z_i \subset \langle A \setminus y_i \rangle \Rightarrow \langle C \setminus z_i \rangle \subset \langle \langle A \setminus y_i \rangle \rangle \Rightarrow \langle C \setminus z_i \rangle \subset \langle A \setminus y_i \rangle</tex> <br/><br />
Так как <tex>z_i \in \langle C \setminus z_i \rangle \subset \langle A \setminus y_i \rangle</tex>, то <tex>A \setminus y_i \cup z_i \notin I</tex>, то есть в <tex>G_M(A)</tex> не существует ребра <tex>(y_i z_i)</tex>. Но тогда, как было отмечено ранее, не существует полного паросочетания. Получили противоречие.<br />
}}<br />
<br />
== Источник ==<br />
''Chandra Chekuri'' — [http://www.cs.illinois.edu/class/sp10/cs598csc/Lectures/Lecture16.pdf '''Combinatorial Optimization'''], с. 6<br />
<br />
[[Категория:Алгоритмы и структуры данных]]<br />
[[Категория:Матроиды]]</div>217.197.6.98http://neerc.ifmo.ru/wiki/index.php?title=%D0%9B%D0%B5%D0%BC%D0%BC%D0%B0_%D0%BE_%D0%B5%D0%B4%D0%B8%D0%BD%D1%81%D1%82%D0%B2%D0%B5%D0%BD%D0%BD%D0%BE%D0%BC_%D0%BF%D0%B0%D1%80%D0%BE%D1%81%D0%BE%D1%87%D0%B5%D1%82%D0%B0%D0%BD%D0%B8%D0%B8_%D0%B2_%D0%B3%D1%80%D0%B0%D1%84%D0%B5_%D0%B7%D0%B0%D0%BC%D0%B5%D0%BD&diff=37420Лемма о единственном паросочетании в графе замен2014-05-30T20:00:34Z<p>217.197.6.98: </p>
<hr />
<div>{{Утверждение<br />
|statement=Пусть [[Двудольные_графы_и_раскраска_в_2_цвета|двудольный граф]] <tex>G</tex> содержит единственное [[Связь_максимального_паросочетания_и_минимального_вершинного_покрытия_в_двудольных_графах#Связь_максимального_паросочетания_и_минимального_вершинного_покрытия_в_двудольном_графе|полное паросочетание]] <tex>M</tex>. Тогда можно упорядочить вершины левой <tex>(a_i \in A)</tex> и правой <tex>(b_i \in B)</tex> долей таким образом, что <tex>\forall j > i : (a_i b_j) \notin G</tex>. При этом рёбра паросочетания будут иметь вид <tex>(a_i b_i)</tex>.<br />
<br />
|proof=Индукция по <tex>|A|</tex>.<br/><br />
При <tex>|A|=1</tex> утверждение очевидно. <br/><br />
Пусть <tex>|A|=n>1</tex> (для <tex>|A|=n-1</tex> утверждение верно). Возьмем произвольную вершину в левой доли. Будем строить из неё [[Теорема о максимальном паросочетании и дополняющих цепях#Паросочетание в двудольном графе|чередующуюся цепь]], добавляя по очереди ребро, входящее в <tex>M</tex>, и ребро, не входящее в <tex>M</tex>. Заметим, что такой путь не содержит циклов (циклы нечётной длины невозможны, так как граф двудольный, циклы чётной длины отсутствуют из-за единственности паросочетания). Если последнее добавленное ребро не принадлежит <tex>M</tex>, то присоединим к цепи ребро из <tex>M</tex>, инцидентное последней вершине. Значит, построение цепи прервется только при добавлении ребра из <tex>M</tex> при достижении вершины [[Основные определения теории графов#Степень вершины|степени]] 1. <br/><br />
Таким образом, последнее ребро в цепи имеет вид <tex>(ab) \in M</tex>, где <tex>a \in A, b \in B, \deg b = 1</tex>. Положим <tex>a_n=a, b_n=b</tex>. Для <tex>G \setminus \{a_n \cup b_n \}</tex> утверждение верно по предположению индукции. С другой стороны, так как <tex>\deg b_n = 1</tex>, то <tex>(a_i b_n) \notin G</tex> при <tex>i<n</tex>, поэтому для <tex>j = n</tex> утверждение также верно.<br/><br />
}}<br />
<br />
<br />
{{Лемма<br />
|about=<br />
о единственном паросочетании в графе замен<br />
|statement= Дан [[Определение матроида|матроид]] <tex>M = \langle X,I \rangle </tex>. Пусть двудольный граф <tex>G_M(A) = \{ (x, y) | x \in A, y \notin A, A \setminus x \cup y \in I \}</tex> содержит единственное полное паросочетание на <tex>A ^ B</tex>, где <tex>A\in I</tex> и <tex>|A| = |B|</tex>. Тогда <tex>B \in I</tex>.<br />
|proof= <br />
[[Файл:Graph replacement.png|thumb|left|160px|]]<br />
Упорядочим вершины левой <tex>(y_i \in A \setminus B)</tex> и правой <tex>(z_j \in B \setminus A)</tex> долей таким образом, что <tex>\forall j > i : (y_i z_j) \notin G_M(A)</tex>. При таком упорядочивании ребра паросочетания имеют вид <tex>(y_i z_i)</tex>.<br />
<br />
Требуется доказать, что <tex>B</tex> независимо. Предположим обратное. Пусть <tex>B \notin I</tex>, тогда существует [[Теорема о циклах|цикл]] <tex>C \subset B</tex>.<br/> Выберем минимальное <tex>i</tex> такое, что <tex>z_i \in C</tex>. Так как <tex>\forall j > i : (y_i z_j) \notin G_M(A)</tex>, то <tex>A \setminus y_i \cup z_j \notin I</tex>, следовательно, <tex>C \setminus z_i \subset \langle A \setminus y_i \rangle </tex>. По [[Оператор замыкания для матроидов#theorem|свойствам замыкания 1 и 3]] получаем:<br/><br />
<tex>C \setminus z_i \subset \langle A \setminus y_i \rangle \Rightarrow \langle C \setminus z_i \rangle \subset \langle \langle A \setminus y_i \rangle \rangle \Rightarrow \langle C \setminus z_i \rangle \subset \langle A \setminus y_i \rangle</tex> <br/><br />
Так как <tex>z_i \in \langle C \setminus z_i \rangle \subset \langle A \setminus y_i \rangle</tex>, то <tex>A \setminus y_i \cup z_i \notin I</tex>, то есть в <tex>G_M(A)</tex> не существует ребра <tex>(y_i z_i)</tex>. Но тогда, как было отмечено ранее, не существует полного паросочетания. Получили противоречие.<br />
}}<br />
<br />
== Источник ==<br />
''Chandra Chekuri'' — [http://www.cs.illinois.edu/class/sp10/cs598csc/Lectures/Lecture16.pdf '''Combinatorial Optimization'''], с. 6<br />
<br />
[[Категория:Алгоритмы и структуры данных]]<br />
[[Категория:Матроиды]]</div>217.197.6.98http://neerc.ifmo.ru/wiki/index.php?title=%D0%9B%D0%B5%D0%BC%D0%BC%D0%B0_%D0%BE_%D0%B5%D0%B4%D0%B8%D0%BD%D1%81%D1%82%D0%B2%D0%B5%D0%BD%D0%BD%D0%BE%D0%BC_%D0%BF%D0%B0%D1%80%D0%BE%D1%81%D0%BE%D1%87%D0%B5%D1%82%D0%B0%D0%BD%D0%B8%D0%B8_%D0%B2_%D0%B3%D1%80%D0%B0%D1%84%D0%B5_%D0%B7%D0%B0%D0%BC%D0%B5%D0%BD&diff=37418Лемма о единственном паросочетании в графе замен2014-05-30T19:58:27Z<p>217.197.6.98: /* См. также */</p>
<hr />
<div>{{Утверждение<br />
|statement=Пусть [[Двудольные_графы_и_раскраска_в_2_цвета|двудольный граф]] <tex>G</tex> содержит единственное [[Связь_максимального_паросочетания_и_минимального_вершинного_покрытия_в_двудольных_графах#Связь_максимального_паросочетания_и_минимального_вершинного_покрытия_в_двудольном_графе|полное паросочетание]] <tex>M</tex>. Тогда можно упорядочить вершины левой <tex>(a_i \in A)</tex> и правой <tex>(b_i \in B)</tex> долей таким образом, что <tex>\forall j > i : (a_i b_j) \notin G</tex>. При этом рёбра паросочетания будут иметь вид <tex>(a_i b_i)</tex>.<br />
<br />
|proof=Индукция по <tex>|A|</tex>.<br/><br />
При <tex>|A|=1</tex> утверждение очевидно. <br/><br />
Пусть <tex>|A|=n>1</tex> (для <tex>|A|=n-1</tex> утверждение верно). Возьмем произвольную вершину в левой доли. Будем строить из неё [[Теорема о максимальном паросочетании и дополняющих цепях#Паросочетание в двудольном графе|чередующуюся цепь]], добавляя по очереди ребро, входящее в <tex>M</tex>, и ребро, не входящее в <tex>M</tex>. Заметим, что такой путь не содержит циклов (циклы нечётной длины невозможны, так как граф двудольный, циклы чётной длины отсутствуют из-за единственности паросочетания). Если последнее добавленное ребро не принадлежит <tex>M</tex>, то присоединим к цепи ребро из <tex>M</tex>, инцидентное последней вершине. Значит, построение цепи прервется только при добавлении ребра из <tex>M</tex> при достижении вершины [[Основные определения теории графов#Степень вершины|степени]] 1. <br/><br />
Таким образом, последнее ребро в цепи имеет вид <tex>(ab) \in M</tex>, где <tex>a \in A, b \in B, \deg b = 1</tex>. Положим <tex>a_n=a, b_n=b</tex>. Для <tex>G \setminus \{a_n \cup b_n \}</tex> утверждение верно по предположению индукции. С другой стороны, так как <tex>\deg b_n = 1</tex>, то <tex>(a_i b_n) \notin G</tex> при <tex>i<n</tex>, поэтому для <tex>j = n</tex> утверждение также верно.<br/><br />
}}<br />
<br />
<br />
{{Лемма<br />
|about=<br />
о единственном паросочетании в графе замен<br />
|statement= Дан [[Определение матроида|матроид]] <tex>M = \langle X,I \rangle </tex>. Пусть двудольный граф <tex>G_M(A) = \{ (x, y) | x \in A, y \notin A, A \setminus x \cup y \in I \}</tex> содержит единственное полное паросочетание на <tex>A \oplus B</tex>, где <tex>A\in I</tex> и <tex>|A| = |B|</tex>. Тогда <tex>B \in I</tex>.<br />
|proof= <br />
[[Файл:Graph replacement.png|thumb|left|160px|]]<br />
Упорядочим вершины левой <tex>(y_i \in A \setminus B)</tex> и правой <tex>(z_j \in B \setminus A)</tex> долей таким образом, что <tex>\forall j > i : (y_i z_j) \notin G_M(A)</tex>. При таком упорядочивании ребра паросочетания имеют вид <tex>(y_i z_i)</tex>.<br />
<br />
Требуется доказать, что <tex>B</tex> независимо. Предположим обратное. Пусть <tex>B \notin I</tex>, тогда существует [[Теорема о циклах|цикл]] <tex>C \subset B</tex>.<br/> Выберем минимальное <tex>i</tex> такое, что <tex>z_i \in C</tex>. Так как <tex>\forall j > i : (y_i z_j) \notin G_M(A)</tex>, то <tex>A \setminus y_i \cup z_j \notin I</tex>, следовательно, <tex>C \setminus z_i \subset \langle A \setminus y_i \rangle </tex>. По [[Оператор замыкания для матроидов#theorem|свойствам замыкания 1 и 3]] получаем:<br/><br />
<tex>C \setminus z_i \subset \langle A \setminus y_i \rangle \Rightarrow \langle C \setminus z_i \rangle \subset \langle \langle A \setminus y_i \rangle \rangle \Rightarrow \langle C \setminus z_i \rangle \subset \langle A \setminus y_i \rangle</tex> <br/><br />
Так как <tex>z_i \in \langle C \setminus z_i \rangle \subset \langle A \setminus y_i \rangle</tex>, то <tex>A \setminus y_i \cup z_i \notin I</tex>, то есть в <tex>G_M(A)</tex> не существует ребра <tex>(y_i z_i)</tex>. Но тогда, как было отмечено ранее, не существует полного паросочетания. Получили противоречие.<br />
}}<br />
<br />
== Источник ==<br />
''Chandra Chekuri'' — [http://www.cs.illinois.edu/class/sp10/cs598csc/Lectures/Lecture16.pdf '''Combinatorial Optimization'''], с. 6<br />
<br />
[[Категория:Алгоритмы и структуры данных]]<br />
[[Категория:Матроиды]]</div>217.197.6.98http://neerc.ifmo.ru/wiki/index.php?title=%D0%9B%D0%B5%D0%BC%D0%BC%D0%B0_%D0%BE_%D0%B5%D0%B4%D0%B8%D0%BD%D1%81%D1%82%D0%B2%D0%B5%D0%BD%D0%BD%D0%BE%D0%BC_%D0%BF%D0%B0%D1%80%D0%BE%D1%81%D0%BE%D1%87%D0%B5%D1%82%D0%B0%D0%BD%D0%B8%D0%B8_%D0%B2_%D0%B3%D1%80%D0%B0%D1%84%D0%B5_%D0%B7%D0%B0%D0%BC%D0%B5%D0%BD&diff=37416Лемма о единственном паросочетании в графе замен2014-05-30T19:55:17Z<p>217.197.6.98: </p>
<hr />
<div>{{Утверждение<br />
|statement=Пусть [[Двудольные_графы_и_раскраска_в_2_цвета|двудольный граф]] <tex>G</tex> содержит единственное [[Связь_максимального_паросочетания_и_минимального_вершинного_покрытия_в_двудольных_графах#Связь_максимального_паросочетания_и_минимального_вершинного_покрытия_в_двудольном_графе|полное паросочетание]] <tex>M</tex>. Тогда можно упорядочить вершины левой <tex>(a_i \in A)</tex> и правой <tex>(b_i \in B)</tex> долей таким образом, что <tex>\forall j > i : (a_i b_j) \notin G</tex>. При этом рёбра паросочетания будут иметь вид <tex>(a_i b_i)</tex>.<br />
<br />
|proof=Индукция по <tex>|A|</tex>.<br/><br />
При <tex>|A|=1</tex> утверждение очевидно. <br/><br />
Пусть <tex>|A|=n>1</tex> (для <tex>|A|=n-1</tex> утверждение верно). Возьмем произвольную вершину в левой доли. Будем строить из неё [[Теорема о максимальном паросочетании и дополняющих цепях#Паросочетание в двудольном графе|чередующуюся цепь]], добавляя по очереди ребро, входящее в <tex>M</tex>, и ребро, не входящее в <tex>M</tex>. Заметим, что такой путь не содержит циклов (циклы нечётной длины невозможны, так как граф двудольный, циклы чётной длины отсутствуют из-за единственности паросочетания). Если последнее добавленное ребро не принадлежит <tex>M</tex>, то присоединим к цепи ребро из <tex>M</tex>, инцидентное последней вершине. Значит, построение цепи прервется только при добавлении ребра из <tex>M</tex> при достижении вершины [[Основные определения теории графов#Степень вершины|степени]] 1. <br/><br />
Таким образом, последнее ребро в цепи имеет вид <tex>(ab) \in M</tex>, где <tex>a \in A, b \in B, \deg b = 1</tex>. Положим <tex>a_n=a, b_n=b</tex>. Для <tex>G \setminus \{a_n \cup b_n \}</tex> утверждение верно по предположению индукции. С другой стороны, так как <tex>\deg b_n = 1</tex>, то <tex>(a_i b_n) \notin G</tex> при <tex>i<n</tex>, поэтому для <tex>j = n</tex> утверждение также верно.<br/><br />
}}<br />
<br />
<br />
{{Лемма<br />
|about=<br />
о единственном паросочетании в графе замен<br />
|statement= Дан [[Определение матроида|матроид]] <tex>M = \langle X,I \rangle </tex>. Пусть двудольный граф <tex>G_M(A) = \{ (x, y) | x \in A, y \notin A, A \setminus x \cup y \in I \}</tex> содержит единственное полное паросочетание на <tex>A \oplus B</tex>, где <tex>A\in I</tex> и <tex>|A| = |B|</tex>. Тогда <tex>B \in I</tex>.<br />
|proof= <br />
[[Файл:Graph replacement.png|thumb|left|160px|]]<br />
Упорядочим вершины левой <tex>(y_i \in A \setminus B)</tex> и правой <tex>(z_j \in B \setminus A)</tex> долей таким образом, что <tex>\forall j > i : (y_i z_j) \notin G_M(A)</tex>. При таком упорядочивании ребра паросочетания имеют вид <tex>(y_i z_i)</tex>.<br />
<br />
Требуется доказать, что <tex>B</tex> независимо. Предположим обратное. Пусть <tex>B \notin I</tex>, тогда существует [[Теорема о циклах|цикл]] <tex>C \subset B</tex>.<br/> Выберем минимальное <tex>i</tex> такое, что <tex>z_i \in C</tex>. Так как <tex>\forall j > i : (y_i z_j) \notin G_M(A)</tex>, то <tex>A \setminus y_i \cup z_j \notin I</tex>, следовательно, <tex>C \setminus z_i \subset \langle A \setminus y_i \rangle </tex>. По [[Оператор замыкания для матроидов#theorem|свойствам замыкания 1 и 3]] получаем:<br/><br />
<tex>C \setminus z_i \subset \langle A \setminus y_i \rangle \Rightarrow \langle C \setminus z_i \rangle \subset \langle \langle A \setminus y_i \rangle \rangle \Rightarrow \langle C \setminus z_i \rangle \subset \langle A \setminus y_i \rangle</tex> <br/><br />
Так как <tex>z_i \in \langle C \setminus z_i \rangle \subset \langle A \setminus y_i \rangle</tex>, то <tex>A \setminus y_i \cup z_i \notin I</tex>, то есть в <tex>G_M(A)</tex> не существует ребра <tex>(y_i z_i)</tex>. Но тогда, как было отмечено ранее, не существует полного паросочетания. Получили противоречие.<br />
}}<br />
<br />
==См. также==<br />
*[[Алгоритм_Дейкстры|Алгоритм Дейкстры]]<br />
== Источник ==<br />
''Chandra Chekuri'' — [http://www.cs.illinois.edu/class/sp10/cs598csc/Lectures/Lecture16.pdf '''Combinatorial Optimization'''], с. 6<br />
<br />
[[Категория:Алгоритмы и структуры данных]]<br />
[[Категория:Матроиды]]</div>217.197.6.98http://neerc.ifmo.ru/wiki/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%9A%D0%BD%D1%83%D1%82%D0%B0-%D0%9C%D0%BE%D1%80%D1%80%D0%B8%D1%81%D0%B0-%D0%9F%D1%80%D0%B0%D1%82%D1%82%D0%B0&diff=37408Алгоритм Кнута-Морриса-Пратта2014-05-30T19:36:08Z<p>217.197.6.98: </p>
<hr />
<div>'''Алгоритм Кнута — Морриса — Пратта''' (англ. ''Knuth–Morris–Pratt algorithm'') — алгоритм [[Наивный алгоритм поиска подстроки в строке#Постановка задачи|поиска подстроки в строке]].<br />
<br />
==Описание алгоритма==<br />
Дана цепочка <tex>T</tex> и образец <tex>P</tex>. Требуется найти все позиции, начиная с которых <tex>P</tex> входит в <tex>T</tex>.<br />
<br><br />
Построим строку <tex>S = P\#T</tex>, где <tex>\#</tex> — любой символ, не входящий в алфавит <tex>P</tex> и <tex>T</tex>. Посчитаем на ней значение [[Префикс-функция|префикс-функции]]. Благодаря разделительному символу <tex>\#</tex>, выполняется <tex>\forall i: \pi[i] \leqslant |P|</tex>. Заметим, что по определению [[Префикс-функция|префикс-функции]] при <tex>i > |P|</tex> и <tex>\pi[i] = |P|</tex> подстроки длины <tex>P</tex>, начинающиеся с позиций <tex>0</tex> и <tex>i - |P| + 1</tex>, совпадают. Соберем все такие позиции <tex>i - |P| + 1</tex> строки <tex>S</tex>, вычтем из каждой позиции <tex>|P| + 1</tex>, это и будет ответ. Другими словами, если в какой-то позиции <tex>i</tex> выполняется условие <tex>\pi[i]=|P|</tex>, то в этой позиции начинается очередное вхождение образца в цепочку.<br />
<br><br />
[[Файл:kmp_pict2.png|640px]]<br />
<br />
==Псевдокод==<br />
'''int'''[] kmp('''string''' T, '''string''' P)<br />
'''int''' p = P.length<br />
'''int''' t = T.length<br />
'''int'''[] answer<br />
'''int'''[] <tex>\pi</tex> = [[Префикс-функция#Эффективный_алгоритм|prefixFunction(P + "#" + T)]]<br />
'''int''' count = 0<br />
'''for''' i = 0 .. t - 1<br />
'''if''' <tex>\pi</tex>[p + i + 1] == p<br />
answer[count++] = i<br />
'''return''' answer<br />
<br />
==Время работы==<br />
Префикс-функция от строки <tex>S</tex> строится за <tex>O(S) = O(P + T)</tex>. Проход цикла по строке <tex>S</tex> содержит <tex>O(T)</tex> итераций. Итого, время работы алгоритма оценивается как <tex>O(P + T)</tex>.<br />
<br />
==Оценка по памяти==<br />
Предложенная реализация имеет оценку по памяти <tex>O(P+T)</tex>. Оценки <tex>O(P)</tex> можно добиться за счет отказа от запоминания значений префикс-функции для позиций в <tex>S</tex>, меньших <tex>p + 1</tex> (т.е. до начала цепочки <tex>T</tex>). Это возможно, так как значение префикс функции не может превысить длину образца, благодаря разделительному символу <tex>\#</tex>.<br />
<br />
==См. также==<br />
*[[Алгоритм Ахо-Корасик|Алгоритм Ахо-Корасик]]<br />
*[[Алгоритм Бойера-Мура|Алгоритм Бойера-Мура]]<br />
*[[Алгоритм Колусси|Алгоритм Колусси]]<br />
<br />
==Источники==<br />
*[[wikipedia:ru:Алгоритм Кнута — Морриса — Пратта | Википедия {{---}} Алгоритм Кнута — Морриса — Пратта]]<br />
*[[wikipedia:en:Knuth–Morris–Pratt algorithm | Wikipedia {{---}} Knuth–Morris–Pratt algorithm]]<br />
*Кормен, Т., Лейзерсон, Ч., Ривест, Р., Штайн — Алгоритмы: построение и анализ / пер. с англ. — изд. 2-е — М.: Издательский дом «Вильямс», 2009. — с.1036. — ISBN 978-5-8459-0857-5.<br />
<br />
[[Категория: Алгоритмы и структуры данных]]<br />
[[Категория: Поиск подстроки в строке]]</div>217.197.6.98http://neerc.ifmo.ru/wiki/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%9A%D0%BD%D1%83%D1%82%D0%B0-%D0%9C%D0%BE%D1%80%D1%80%D0%B8%D1%81%D0%B0-%D0%9F%D1%80%D0%B0%D1%82%D1%82%D0%B0&diff=37406Алгоритм Кнута-Морриса-Пратта2014-05-30T19:31:48Z<p>217.197.6.98: /* Псевдокод */</p>
<hr />
<div>'''Алгоритм Кнута — Морриса — Пратта''' (англ. ''Knuth–Morris–Pratt algorithm'') — алгоритм [[Наивный алгоритм поиска подстроки в строке#Постановка задачи|поиска подстроки в строке]].<br />
<br />
==Описание алгоритма==<br />
Дана цепочка <tex>T</tex> и образец <tex>P</tex>. Требуется найти все позиции, начиная с которых <tex>P</tex> входит в <tex>T</tex>.<br />
<br><br />
Построим строку <tex>S = P\#T</tex>, где <tex>\#</tex> — любой символ, не входящий в алфавит <tex>P</tex> и <tex>T</tex>. Посчитаем на ней значение [[Префикс-функция|префикс-функции]]. Благодаря разделительному символу <tex>\#</tex>, выполняется <tex>\forall i: \pi(i) \leqslant |P|</tex>. Заметим, что по определению [[Префикс-функция|префикс-функции]] при <tex>i > |P|</tex> и <tex>\pi(i) = |P|</tex> подстроки длины <tex>P</tex>, начинающиеся с позиций <tex>0</tex> и <tex>i - |P| + 1</tex>, совпадают. Соберем все такие позиции <tex>i - |P| + 1</tex> строки <tex>S</tex>, вычтем из каждой позиции <tex>|P| + 1</tex>, это и будет ответ. Другими словами, если в какой-то позиции <tex>i</tex> выполняется условие <tex>\pi(i)=|P|</tex>, то в этой позиции начинается очередное вхождение образца в цепочку.<br />
<br><br />
[[Файл:kmp_pict2.png|640px]]<br />
<br />
==Псевдокод==<br />
'''int'''[] kmp('''string''' T, '''string''' P)<br />
'''int''' p = P.length<br />
'''int''' t = T.length<br />
'''int'''[] answer<br />
'''int'''[] <tex>\pi</tex> = [[Префикс-функция#Эффективный_алгоритм|prefixFunction(P + "#" + T)]]<br />
'''int''' count = 0<br />
'''for''' i = 0 .. (t - 1)<br />
'''if''' <tex>\pi</tex>[p + i + 1] == p<br />
answer[count++] = i<br />
'''return''' answer<br />
<br />
==Время работы==<br />
Префикс-функция от строки <tex>S</tex> строится за <tex>O(S) = O(P + T)</tex>. Проход цикла по строке <tex>S</tex> содержит <tex>O(T)</tex> итераций. Итого, время работы алгоритма оценивается как <tex>O(P + T)</tex>.<br />
<br />
==Оценка по памяти==<br />
Предложенная реализация имеет оценку по памяти <tex>O(P+T)</tex>. Оценки <tex>O(P)</tex> можно добиться за счет отказа от запоминания значений префикс-функции для позиций в <tex>S</tex>, меньших <tex>p + 1</tex> (т.е. до начала цепочки <tex>T</tex>). Это возможно, так как значение префикс функции не может превысить длину образца, благодаря разделительному символу <tex>\#</tex>.<br />
<br />
==См. также==<br />
*[[Алгоритм Ахо-Корасик|Алгоритм Ахо-Корасик]]<br />
*[[Алгоритм Бойера-Мура|Алгоритм Бойера-Мура]]<br />
*[[Алгоритм Колусси|Алгоритм Колусси]]<br />
<br />
==Источники==<br />
*[[wikipedia:ru:Алгоритм Кнута — Морриса — Пратта | Википедия {{---}} Алгоритм Кнута — Морриса — Пратта]]<br />
*[[wikipedia:en:Knuth–Morris–Pratt algorithm | Wikipedia {{---}} Knuth–Morris–Pratt algorithm]]<br />
*Кормен, Т., Лейзерсон, Ч., Ривест, Р., Штайн — Алгоритмы: построение и анализ / пер. с англ. — изд. 2-е — М.: Издательский дом «Вильямс», 2009. — с.1036. — ISBN 978-5-8459-0857-5.<br />
<br />
[[Категория: Алгоритмы и структуры данных]]<br />
[[Категория: Поиск подстроки в строке]]</div>217.197.6.98http://neerc.ifmo.ru/wiki/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%9A%D0%BD%D1%83%D1%82%D0%B0-%D0%9C%D0%BE%D1%80%D1%80%D0%B8%D1%81%D0%B0-%D0%9F%D1%80%D0%B0%D1%82%D1%82%D0%B0&diff=37404Алгоритм Кнута-Морриса-Пратта2014-05-30T19:30:21Z<p>217.197.6.98: /* Псевдокод */</p>
<hr />
<div>'''Алгоритм Кнута — Морриса — Пратта''' (англ. ''Knuth–Morris–Pratt algorithm'') — алгоритм [[Наивный алгоритм поиска подстроки в строке#Постановка задачи|поиска подстроки в строке]].<br />
<br />
==Описание алгоритма==<br />
Дана цепочка <tex>T</tex> и образец <tex>P</tex>. Требуется найти все позиции, начиная с которых <tex>P</tex> входит в <tex>T</tex>.<br />
<br><br />
Построим строку <tex>S = P\#T</tex>, где <tex>\#</tex> — любой символ, не входящий в алфавит <tex>P</tex> и <tex>T</tex>. Посчитаем на ней значение [[Префикс-функция|префикс-функции]]. Благодаря разделительному символу <tex>\#</tex>, выполняется <tex>\forall i: \pi(i) \leqslant |P|</tex>. Заметим, что по определению [[Префикс-функция|префикс-функции]] при <tex>i > |P|</tex> и <tex>\pi(i) = |P|</tex> подстроки длины <tex>P</tex>, начинающиеся с позиций <tex>0</tex> и <tex>i - |P| + 1</tex>, совпадают. Соберем все такие позиции <tex>i - |P| + 1</tex> строки <tex>S</tex>, вычтем из каждой позиции <tex>|P| + 1</tex>, это и будет ответ. Другими словами, если в какой-то позиции <tex>i</tex> выполняется условие <tex>\pi(i)=|P|</tex>, то в этой позиции начинается очередное вхождение образца в цепочку.<br />
<br><br />
[[Файл:kmp_pict2.png|640px]]<br />
<br />
==Псевдокод==<br />
'''int'''[] kmp('''string''' T, '''string''' P)<br />
'''int''' p = P.length<br />
'''int''' t = T.length<br />
'''int'''[] answer<br />
'''int'''[] <tex>\pi</tex> = [[Префикс-функция#Эффективный_алгоритм|prefixFunction(P + "#" + T)]]<br />
count = 0<br />
'''for''' i = 0 .. (t - 1)<br />
'''if''' <tex>\pi</tex>[p + i + 1] == p<br />
answer[count++] = i + 1 - p<br />
'''return''' answer<br />
<br />
==Время работы==<br />
Префикс-функция от строки <tex>S</tex> строится за <tex>O(S) = O(P + T)</tex>. Проход цикла по строке <tex>S</tex> содержит <tex>O(T)</tex> итераций. Итого, время работы алгоритма оценивается как <tex>O(P + T)</tex>.<br />
<br />
==Оценка по памяти==<br />
Предложенная реализация имеет оценку по памяти <tex>O(P+T)</tex>. Оценки <tex>O(P)</tex> можно добиться за счет отказа от запоминания значений префикс-функции для позиций в <tex>S</tex>, меньших <tex>p + 1</tex> (т.е. до начала цепочки <tex>T</tex>). Это возможно, так как значение префикс функции не может превысить длину образца, благодаря разделительному символу <tex>\#</tex>.<br />
<br />
==См. также==<br />
*[[Алгоритм Ахо-Корасик|Алгоритм Ахо-Корасик]]<br />
*[[Алгоритм Бойера-Мура|Алгоритм Бойера-Мура]]<br />
*[[Алгоритм Колусси|Алгоритм Колусси]]<br />
<br />
==Источники==<br />
*[[wikipedia:ru:Алгоритм Кнута — Морриса — Пратта | Википедия {{---}} Алгоритм Кнута — Морриса — Пратта]]<br />
*[[wikipedia:en:Knuth–Morris–Pratt algorithm | Wikipedia {{---}} Knuth–Morris–Pratt algorithm]]<br />
*Кормен, Т., Лейзерсон, Ч., Ривест, Р., Штайн — Алгоритмы: построение и анализ / пер. с англ. — изд. 2-е — М.: Издательский дом «Вильямс», 2009. — с.1036. — ISBN 978-5-8459-0857-5.<br />
<br />
[[Категория: Алгоритмы и структуры данных]]<br />
[[Категория: Поиск подстроки в строке]]</div>217.197.6.98http://neerc.ifmo.ru/wiki/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%9A%D0%BD%D1%83%D1%82%D0%B0-%D0%9C%D0%BE%D1%80%D1%80%D0%B8%D1%81%D0%B0-%D0%9F%D1%80%D0%B0%D1%82%D1%82%D0%B0&diff=37403Алгоритм Кнута-Морриса-Пратта2014-05-30T19:28:55Z<p>217.197.6.98: Отмена правки 37399 участника 217.197.6.98 (обсуждение)</p>
<hr />
<div>'''Алгоритм Кнута — Морриса — Пратта''' (англ. ''Knuth–Morris–Pratt algorithm'') — алгоритм [[Наивный алгоритм поиска подстроки в строке#Постановка задачи|поиска подстроки в строке]].<br />
<br />
==Описание алгоритма==<br />
Дана цепочка <tex>T</tex> и образец <tex>P</tex>. Требуется найти все позиции, начиная с которых <tex>P</tex> входит в <tex>T</tex>.<br />
<br><br />
Построим строку <tex>S = P\#T</tex>, где <tex>\#</tex> — любой символ, не входящий в алфавит <tex>P</tex> и <tex>T</tex>. Посчитаем на ней значение [[Префикс-функция|префикс-функции]]. Благодаря разделительному символу <tex>\#</tex>, выполняется <tex>\forall i: \pi(i) \leqslant |P|</tex>. Заметим, что по определению [[Префикс-функция|префикс-функции]] при <tex>i > |P|</tex> и <tex>\pi(i) = |P|</tex> подстроки длины <tex>P</tex>, начинающиеся с позиций <tex>0</tex> и <tex>i - |P| + 1</tex>, совпадают. Соберем все такие позиции <tex>i - |P| + 1</tex> строки <tex>S</tex>, вычтем из каждой позиции <tex>|P| + 1</tex>, это и будет ответ. Другими словами, если в какой-то позиции <tex>i</tex> выполняется условие <tex>\pi(i)=|P|</tex>, то в этой позиции начинается очередное вхождение образца в цепочку.<br />
<br><br />
[[Файл:kmp_pict2.png|640px]]<br />
<br />
==Псевдокод==<br />
'''int'''[] kmp('''string''' T, '''string''' P)<br />
'''int''' p = P.length<br />
'''int''' t = T.length<br />
'''int'''[] answer<br />
count = 0<br />
'''for''' i = 0 .. (t - 1)<br />
'''if''' <tex>\pi</tex>(p + i + 1) == p<br />
answer[count++] = i + 1 - p<br />
'''return''' answer<br />
<br />
==Время работы==<br />
Префикс-функция от строки <tex>S</tex> строится за <tex>O(S) = O(P + T)</tex>. Проход цикла по строке <tex>S</tex> содержит <tex>O(T)</tex> итераций. Итого, время работы алгоритма оценивается как <tex>O(P + T)</tex>.<br />
<br />
==Оценка по памяти==<br />
Предложенная реализация имеет оценку по памяти <tex>O(P+T)</tex>. Оценки <tex>O(P)</tex> можно добиться за счет отказа от запоминания значений префикс-функции для позиций в <tex>S</tex>, меньших <tex>p + 1</tex> (т.е. до начала цепочки <tex>T</tex>). Это возможно, так как значение префикс функции не может превысить длину образца, благодаря разделительному символу <tex>\#</tex>.<br />
<br />
==См. также==<br />
*[[Алгоритм Ахо-Корасик|Алгоритм Ахо-Корасик]]<br />
*[[Алгоритм Бойера-Мура|Алгоритм Бойера-Мура]]<br />
*[[Алгоритм Колусси|Алгоритм Колусси]]<br />
<br />
==Источники==<br />
*[[wikipedia:ru:Алгоритм Кнута — Морриса — Пратта | Википедия {{---}} Алгоритм Кнута — Морриса — Пратта]]<br />
*[[wikipedia:en:Knuth–Morris–Pratt algorithm | Wikipedia {{---}} Knuth–Morris–Pratt algorithm]]<br />
*Кормен, Т., Лейзерсон, Ч., Ривест, Р., Штайн — Алгоритмы: построение и анализ / пер. с англ. — изд. 2-е — М.: Издательский дом «Вильямс», 2009. — с.1036. — ISBN 978-5-8459-0857-5.<br />
<br />
[[Категория: Алгоритмы и структуры данных]]<br />
[[Категория: Поиск подстроки в строке]]</div>217.197.6.98http://neerc.ifmo.ru/wiki/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%9A%D0%BD%D1%83%D1%82%D0%B0-%D0%9C%D0%BE%D1%80%D1%80%D0%B8%D1%81%D0%B0-%D0%9F%D1%80%D0%B0%D1%82%D1%82%D0%B0&diff=37402Алгоритм Кнута-Морриса-Пратта2014-05-30T19:28:38Z<p>217.197.6.98: Отмена правки 37401 участника 217.197.6.98 (обсуждение)</p>
<hr />
<div>'''Алгоритм Кнута — Морриса — Пратта''' (англ. ''Knuth–Morris–Pratt algorithm'') — алгоритм [[Наивный алгоритм поиска подстроки в строке#Постановка задачи|поиска подстроки в строке]].<br />
<br />
==Описание алгоритма==<br />
Дана цепочка <tex>T</tex> и образец <tex>P</tex>. Требуется найти все позиции, начиная с которых <tex>P</tex> входит в <tex>T</tex>.<br />
<br><br />
Построим строку <tex>S = P\#T</tex>, где <tex>\#</tex> — любой символ, не входящий в алфавит <tex>P</tex> и <tex>T</tex>. Посчитаем на ней значение [[Префикс-функция|префикс-функции]]. Благодаря разделительному символу <tex>\#</tex>, выполняется <tex>\forall i: \pi(i) \leqslant |P|</tex>. Заметим, что по определению [[Префикс-функция|префикс-функции]] при <tex>i > |P|</tex> и <tex>\pi(i) = |P|</tex> подстроки длины <tex>P</tex>, начинающиеся с позиций <tex>0</tex> и <tex>i - |P| + 1</tex>, совпадают. Соберем все такие позиции <tex>i - |P| + 1</tex> строки <tex>S</tex>, вычтем из каждой позиции <tex>|P| + 1</tex>, это и будет ответ. Другими словами, если в какой-то позиции <tex>i</tex> выполняется условие <tex>\pi(i)=|P|</tex>, то в этой позиции начинается очередное вхождение образца в цепочку.<br />
<br><br />
[[Файл:kmp_pict2.png|640px]]<br />
<br />
==Псевдокод==<br />
'''int'''[] kmp('''string''' T, '''string''' P)<br />
'''string''' S = P + "#" + T<br />
'''return''' [[Префикс-функция#Эффективный_алгоритм#Псевдокод|prefixFunction(S)]]<br />
<br />
==Время работы==<br />
Префикс-функция от строки <tex>S</tex> строится за <tex>O(S) = O(P + T)</tex>. Проход цикла по строке <tex>S</tex> содержит <tex>O(T)</tex> итераций. Итого, время работы алгоритма оценивается как <tex>O(P + T)</tex>.<br />
<br />
==Оценка по памяти==<br />
Предложенная реализация имеет оценку по памяти <tex>O(P+T)</tex>. Оценки <tex>O(P)</tex> можно добиться за счет отказа от запоминания значений префикс-функции для позиций в <tex>S</tex>, меньших <tex>p + 1</tex> (т.е. до начала цепочки <tex>T</tex>). Это возможно, так как значение префикс функции не может превысить длину образца, благодаря разделительному символу <tex>\#</tex>.<br />
<br />
==См. также==<br />
*[[Алгоритм Ахо-Корасик|Алгоритм Ахо-Корасик]]<br />
*[[Алгоритм Бойера-Мура|Алгоритм Бойера-Мура]]<br />
*[[Алгоритм Колусси|Алгоритм Колусси]]<br />
<br />
==Источники==<br />
*[[wikipedia:ru:Алгоритм Кнута — Морриса — Пратта | Википедия {{---}} Алгоритм Кнута — Морриса — Пратта]]<br />
*[[wikipedia:en:Knuth–Morris–Pratt algorithm | Wikipedia {{---}} Knuth–Morris–Pratt algorithm]]<br />
*Кормен, Т., Лейзерсон, Ч., Ривест, Р., Штайн — Алгоритмы: построение и анализ / пер. с англ. — изд. 2-е — М.: Издательский дом «Вильямс», 2009. — с.1036. — ISBN 978-5-8459-0857-5.<br />
<br />
[[Категория: Алгоритмы и структуры данных]]<br />
[[Категория: Поиск подстроки в строке]]</div>217.197.6.98http://neerc.ifmo.ru/wiki/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%9A%D0%BD%D1%83%D1%82%D0%B0-%D0%9C%D0%BE%D1%80%D1%80%D0%B8%D1%81%D0%B0-%D0%9F%D1%80%D0%B0%D1%82%D1%82%D0%B0&diff=37401Алгоритм Кнута-Морриса-Пратта2014-05-30T19:22:55Z<p>217.197.6.98: /* Псевдокод */</p>
<hr />
<div>'''Алгоритм Кнута — Морриса — Пратта''' (англ. ''Knuth–Morris–Pratt algorithm'') — алгоритм [[Наивный алгоритм поиска подстроки в строке#Постановка задачи|поиска подстроки в строке]].<br />
<br />
==Описание алгоритма==<br />
Дана цепочка <tex>T</tex> и образец <tex>P</tex>. Требуется найти все позиции, начиная с которых <tex>P</tex> входит в <tex>T</tex>.<br />
<br><br />
Построим строку <tex>S = P\#T</tex>, где <tex>\#</tex> — любой символ, не входящий в алфавит <tex>P</tex> и <tex>T</tex>. Посчитаем на ней значение [[Префикс-функция|префикс-функции]]. Благодаря разделительному символу <tex>\#</tex>, выполняется <tex>\forall i: \pi(i) \leqslant |P|</tex>. Заметим, что по определению [[Префикс-функция|префикс-функции]] при <tex>i > |P|</tex> и <tex>\pi(i) = |P|</tex> подстроки длины <tex>P</tex>, начинающиеся с позиций <tex>0</tex> и <tex>i - |P| + 1</tex>, совпадают. Соберем все такие позиции <tex>i - |P| + 1</tex> строки <tex>S</tex>, вычтем из каждой позиции <tex>|P| + 1</tex>, это и будет ответ. Другими словами, если в какой-то позиции <tex>i</tex> выполняется условие <tex>\pi(i)=|P|</tex>, то в этой позиции начинается очередное вхождение образца в цепочку.<br />
<br><br />
[[Файл:kmp_pict2.png|640px]]<br />
<br />
==Псевдокод==<br />
'''int'''[] kmp('''string''' T, '''string''' P)<br />
'''string''' S = P + "#" + T<br />
'''return''' [[Префикс-функция#Эффективный_алгоритм|prefixFunction(S)]]<br />
<br />
==Время работы==<br />
Префикс-функция от строки <tex>S</tex> строится за <tex>O(S) = O(P + T)</tex>. Проход цикла по строке <tex>S</tex> содержит <tex>O(T)</tex> итераций. Итого, время работы алгоритма оценивается как <tex>O(P + T)</tex>.<br />
<br />
==Оценка по памяти==<br />
Предложенная реализация имеет оценку по памяти <tex>O(P+T)</tex>. Оценки <tex>O(P)</tex> можно добиться за счет отказа от запоминания значений префикс-функции для позиций в <tex>S</tex>, меньших <tex>p + 1</tex> (т.е. до начала цепочки <tex>T</tex>). Это возможно, так как значение префикс функции не может превысить длину образца, благодаря разделительному символу <tex>\#</tex>.<br />
<br />
==См. также==<br />
*[[Алгоритм Ахо-Корасик|Алгоритм Ахо-Корасик]]<br />
*[[Алгоритм Бойера-Мура|Алгоритм Бойера-Мура]]<br />
*[[Алгоритм Колусси|Алгоритм Колусси]]<br />
<br />
==Источники==<br />
*[[wikipedia:ru:Алгоритм Кнута — Морриса — Пратта | Википедия {{---}} Алгоритм Кнута — Морриса — Пратта]]<br />
*[[wikipedia:en:Knuth–Morris–Pratt algorithm | Wikipedia {{---}} Knuth–Morris–Pratt algorithm]]<br />
*Кормен, Т., Лейзерсон, Ч., Ривест, Р., Штайн — Алгоритмы: построение и анализ / пер. с англ. — изд. 2-е — М.: Издательский дом «Вильямс», 2009. — с.1036. — ISBN 978-5-8459-0857-5.<br />
<br />
[[Категория: Алгоритмы и структуры данных]]<br />
[[Категория: Поиск подстроки в строке]]</div>217.197.6.98http://neerc.ifmo.ru/wiki/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%9A%D0%BD%D1%83%D1%82%D0%B0-%D0%9C%D0%BE%D1%80%D1%80%D0%B8%D1%81%D0%B0-%D0%9F%D1%80%D0%B0%D1%82%D1%82%D0%B0&diff=37399Алгоритм Кнута-Морриса-Пратта2014-05-30T19:22:06Z<p>217.197.6.98: /* Псевдокод */</p>
<hr />
<div>'''Алгоритм Кнута — Морриса — Пратта''' (англ. ''Knuth–Morris–Pratt algorithm'') — алгоритм [[Наивный алгоритм поиска подстроки в строке#Постановка задачи|поиска подстроки в строке]].<br />
<br />
==Описание алгоритма==<br />
Дана цепочка <tex>T</tex> и образец <tex>P</tex>. Требуется найти все позиции, начиная с которых <tex>P</tex> входит в <tex>T</tex>.<br />
<br><br />
Построим строку <tex>S = P\#T</tex>, где <tex>\#</tex> — любой символ, не входящий в алфавит <tex>P</tex> и <tex>T</tex>. Посчитаем на ней значение [[Префикс-функция|префикс-функции]]. Благодаря разделительному символу <tex>\#</tex>, выполняется <tex>\forall i: \pi(i) \leqslant |P|</tex>. Заметим, что по определению [[Префикс-функция|префикс-функции]] при <tex>i > |P|</tex> и <tex>\pi(i) = |P|</tex> подстроки длины <tex>P</tex>, начинающиеся с позиций <tex>0</tex> и <tex>i - |P| + 1</tex>, совпадают. Соберем все такие позиции <tex>i - |P| + 1</tex> строки <tex>S</tex>, вычтем из каждой позиции <tex>|P| + 1</tex>, это и будет ответ. Другими словами, если в какой-то позиции <tex>i</tex> выполняется условие <tex>\pi(i)=|P|</tex>, то в этой позиции начинается очередное вхождение образца в цепочку.<br />
<br><br />
[[Файл:kmp_pict2.png|640px]]<br />
<br />
==Псевдокод==<br />
'''int'''[] kmp('''string''' T, '''string''' P)<br />
'''string''' S = P + "#" + T<br />
'''return''' [[Префикс-функция#Эффективный_алгоритм#Псевдокод|prefixFunction(S)]]<br />
<br />
==Время работы==<br />
Префикс-функция от строки <tex>S</tex> строится за <tex>O(S) = O(P + T)</tex>. Проход цикла по строке <tex>S</tex> содержит <tex>O(T)</tex> итераций. Итого, время работы алгоритма оценивается как <tex>O(P + T)</tex>.<br />
<br />
==Оценка по памяти==<br />
Предложенная реализация имеет оценку по памяти <tex>O(P+T)</tex>. Оценки <tex>O(P)</tex> можно добиться за счет отказа от запоминания значений префикс-функции для позиций в <tex>S</tex>, меньших <tex>p + 1</tex> (т.е. до начала цепочки <tex>T</tex>). Это возможно, так как значение префикс функции не может превысить длину образца, благодаря разделительному символу <tex>\#</tex>.<br />
<br />
==См. также==<br />
*[[Алгоритм Ахо-Корасик|Алгоритм Ахо-Корасик]]<br />
*[[Алгоритм Бойера-Мура|Алгоритм Бойера-Мура]]<br />
*[[Алгоритм Колусси|Алгоритм Колусси]]<br />
<br />
==Источники==<br />
*[[wikipedia:ru:Алгоритм Кнута — Морриса — Пратта | Википедия {{---}} Алгоритм Кнута — Морриса — Пратта]]<br />
*[[wikipedia:en:Knuth–Morris–Pratt algorithm | Wikipedia {{---}} Knuth–Morris–Pratt algorithm]]<br />
*Кормен, Т., Лейзерсон, Ч., Ривест, Р., Штайн — Алгоритмы: построение и анализ / пер. с англ. — изд. 2-е — М.: Издательский дом «Вильямс», 2009. — с.1036. — ISBN 978-5-8459-0857-5.<br />
<br />
[[Категория: Алгоритмы и структуры данных]]<br />
[[Категория: Поиск подстроки в строке]]</div>217.197.6.98http://neerc.ifmo.ru/wiki/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%9A%D0%BD%D1%83%D1%82%D0%B0-%D0%9C%D0%BE%D1%80%D1%80%D0%B8%D1%81%D0%B0-%D0%9F%D1%80%D0%B0%D1%82%D1%82%D0%B0&diff=37398Алгоритм Кнута-Морриса-Пратта2014-05-30T19:17:21Z<p>217.197.6.98: /* Описание алгоритма */</p>
<hr />
<div>'''Алгоритм Кнута — Морриса — Пратта''' (англ. ''Knuth–Morris–Pratt algorithm'') — алгоритм [[Наивный алгоритм поиска подстроки в строке#Постановка задачи|поиска подстроки в строке]].<br />
<br />
==Описание алгоритма==<br />
Дана цепочка <tex>T</tex> и образец <tex>P</tex>. Требуется найти все позиции, начиная с которых <tex>P</tex> входит в <tex>T</tex>.<br />
<br><br />
Построим строку <tex>S = P\#T</tex>, где <tex>\#</tex> — любой символ, не входящий в алфавит <tex>P</tex> и <tex>T</tex>. Посчитаем на ней значение [[Префикс-функция|префикс-функции]]. Благодаря разделительному символу <tex>\#</tex>, выполняется <tex>\forall i: \pi(i) \leqslant |P|</tex>. Заметим, что по определению [[Префикс-функция|префикс-функции]] при <tex>i > |P|</tex> и <tex>\pi(i) = |P|</tex> подстроки длины <tex>P</tex>, начинающиеся с позиций <tex>0</tex> и <tex>i - |P| + 1</tex>, совпадают. Соберем все такие позиции <tex>i - |P| + 1</tex> строки <tex>S</tex>, вычтем из каждой позиции <tex>|P| + 1</tex>, это и будет ответ. Другими словами, если в какой-то позиции <tex>i</tex> выполняется условие <tex>\pi(i)=|P|</tex>, то в этой позиции начинается очередное вхождение образца в цепочку.<br />
<br><br />
[[Файл:kmp_pict2.png|640px]]<br />
<br />
==Псевдокод==<br />
'''int'''[] kmp('''string''' T, '''string''' P)<br />
'''int''' p = P.length<br />
'''int''' t = T.length<br />
'''int'''[] answer<br />
count = 0<br />
'''for''' i = 0 .. (t - 1)<br />
'''if''' <tex>\pi</tex>(p + i + 1) == p<br />
answer[count++] = i + 1 - p<br />
'''return''' answer<br />
<br />
==Время работы==<br />
Префикс-функция от строки <tex>S</tex> строится за <tex>O(S) = O(P + T)</tex>. Проход цикла по строке <tex>S</tex> содержит <tex>O(T)</tex> итераций. Итого, время работы алгоритма оценивается как <tex>O(P + T)</tex>.<br />
<br />
==Оценка по памяти==<br />
Предложенная реализация имеет оценку по памяти <tex>O(P+T)</tex>. Оценки <tex>O(P)</tex> можно добиться за счет отказа от запоминания значений префикс-функции для позиций в <tex>S</tex>, меньших <tex>p + 1</tex> (т.е. до начала цепочки <tex>T</tex>). Это возможно, так как значение префикс функции не может превысить длину образца, благодаря разделительному символу <tex>\#</tex>.<br />
<br />
==См. также==<br />
*[[Алгоритм Ахо-Корасик|Алгоритм Ахо-Корасик]]<br />
*[[Алгоритм Бойера-Мура|Алгоритм Бойера-Мура]]<br />
*[[Алгоритм Колусси|Алгоритм Колусси]]<br />
<br />
==Источники==<br />
*[[wikipedia:ru:Алгоритм Кнута — Морриса — Пратта | Википедия {{---}} Алгоритм Кнута — Морриса — Пратта]]<br />
*[[wikipedia:en:Knuth–Morris–Pratt algorithm | Wikipedia {{---}} Knuth–Morris–Pratt algorithm]]<br />
*Кормен, Т., Лейзерсон, Ч., Ривест, Р., Штайн — Алгоритмы: построение и анализ / пер. с англ. — изд. 2-е — М.: Издательский дом «Вильямс», 2009. — с.1036. — ISBN 978-5-8459-0857-5.<br />
<br />
[[Категория: Алгоритмы и структуры данных]]<br />
[[Категория: Поиск подстроки в строке]]</div>217.197.6.98http://neerc.ifmo.ru/wiki/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%9A%D0%BD%D1%83%D1%82%D0%B0-%D0%9C%D0%BE%D1%80%D1%80%D0%B8%D1%81%D0%B0-%D0%9F%D1%80%D0%B0%D1%82%D1%82%D0%B0&diff=37397Алгоритм Кнута-Морриса-Пратта2014-05-30T19:16:44Z<p>217.197.6.98: /* Оценка по памяти */</p>
<hr />
<div>'''Алгоритм Кнута — Морриса — Пратта''' (англ. ''Knuth–Morris–Pratt algorithm'') — алгоритм [[Наивный алгоритм поиска подстроки в строке#Постановка задачи|поиска подстроки в строке]].<br />
<br />
==Описание алгоритма==<br />
Дана цепочка <tex>T</tex> и образец <tex>P</tex>. Требуется найти все позиции, начиная с которых <tex>P</tex> входит в <tex>T</tex>.<br />
<br><br />
Построим строку <tex>S = P\#T</tex>, где <tex>\#</tex> — любой символ, не входящий в алфавит <tex>P</tex> и <tex>T</tex>. Посчитаем на ней [[Префикс-функция|префикс-функцию]] <tex>\pi()</tex>. Благодаря разделительному символу <tex>\#</tex>, выполняется <tex>\forall i: \pi(i) \leqslant |P|</tex>. Заметим, что по определению [[Префикс-функция|префикс-функции]] при <tex>i > |P|</tex> и <tex>\pi(i) = |P|</tex> подстроки длины <tex>P</tex>, начинающиеся с позиций <tex>0</tex> и <tex>i - |P| + 1</tex>, совпадают. Соберем все такие позиции <tex>i - |P| + 1</tex> строки <tex>S</tex>, вычтем из каждой позиции <tex>|P| + 1</tex>, это и будет ответ. Другими словами, если в какой-то позиции <tex>i</tex> выполняется условие <tex>\pi(i)=|P|</tex>, то в этой позиции начинается очередное вхождение образца в цепочку.<br />
<br><br />
[[Файл:kmp_pict2.png|640px]]<br />
<br />
==Псевдокод==<br />
'''int'''[] kmp('''string''' T, '''string''' P)<br />
'''int''' p = P.length<br />
'''int''' t = T.length<br />
'''int'''[] answer<br />
count = 0<br />
'''for''' i = 0 .. (t - 1)<br />
'''if''' <tex>\pi</tex>(p + i + 1) == p<br />
answer[count++] = i + 1 - p<br />
'''return''' answer<br />
<br />
==Время работы==<br />
Префикс-функция от строки <tex>S</tex> строится за <tex>O(S) = O(P + T)</tex>. Проход цикла по строке <tex>S</tex> содержит <tex>O(T)</tex> итераций. Итого, время работы алгоритма оценивается как <tex>O(P + T)</tex>.<br />
<br />
==Оценка по памяти==<br />
Предложенная реализация имеет оценку по памяти <tex>O(P+T)</tex>. Оценки <tex>O(P)</tex> можно добиться за счет отказа от запоминания значений префикс-функции для позиций в <tex>S</tex>, меньших <tex>p + 1</tex> (т.е. до начала цепочки <tex>T</tex>). Это возможно, так как значение префикс функции не может превысить длину образца, благодаря разделительному символу <tex>\#</tex>.<br />
<br />
==См. также==<br />
*[[Алгоритм Ахо-Корасик|Алгоритм Ахо-Корасик]]<br />
*[[Алгоритм Бойера-Мура|Алгоритм Бойера-Мура]]<br />
*[[Алгоритм Колусси|Алгоритм Колусси]]<br />
<br />
==Источники==<br />
*[[wikipedia:ru:Алгоритм Кнута — Морриса — Пратта | Википедия {{---}} Алгоритм Кнута — Морриса — Пратта]]<br />
*[[wikipedia:en:Knuth–Morris–Pratt algorithm | Wikipedia {{---}} Knuth–Morris–Pratt algorithm]]<br />
*Кормен, Т., Лейзерсон, Ч., Ривест, Р., Штайн — Алгоритмы: построение и анализ / пер. с англ. — изд. 2-е — М.: Издательский дом «Вильямс», 2009. — с.1036. — ISBN 978-5-8459-0857-5.<br />
<br />
[[Категория: Алгоритмы и структуры данных]]<br />
[[Категория: Поиск подстроки в строке]]</div>217.197.6.98http://neerc.ifmo.ru/wiki/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%9A%D0%BD%D1%83%D1%82%D0%B0-%D0%9C%D0%BE%D1%80%D1%80%D0%B8%D1%81%D0%B0-%D0%9F%D1%80%D0%B0%D1%82%D1%82%D0%B0&diff=37396Алгоритм Кнута-Морриса-Пратта2014-05-30T19:13:24Z<p>217.197.6.98: /* Оценка по памяти */</p>
<hr />
<div>'''Алгоритм Кнута — Морриса — Пратта''' (англ. ''Knuth–Morris–Pratt algorithm'') — алгоритм [[Наивный алгоритм поиска подстроки в строке#Постановка задачи|поиска подстроки в строке]].<br />
<br />
==Описание алгоритма==<br />
Дана цепочка <tex>T</tex> и образец <tex>P</tex>. Требуется найти все позиции, начиная с которых <tex>P</tex> входит в <tex>T</tex>.<br />
<br><br />
Построим строку <tex>S = P\#T</tex>, где <tex>\#</tex> — любой символ, не входящий в алфавит <tex>P</tex> и <tex>T</tex>. Посчитаем на ней [[Префикс-функция|префикс-функцию]] <tex>\pi()</tex>. Благодаря разделительному символу <tex>\#</tex>, выполняется <tex>\forall i: \pi(i) \leqslant |P|</tex>. Заметим, что по определению [[Префикс-функция|префикс-функции]] при <tex>i > |P|</tex> и <tex>\pi(i) = |P|</tex> подстроки длины <tex>P</tex>, начинающиеся с позиций <tex>0</tex> и <tex>i - |P| + 1</tex>, совпадают. Соберем все такие позиции <tex>i - |P| + 1</tex> строки <tex>S</tex>, вычтем из каждой позиции <tex>|P| + 1</tex>, это и будет ответ. Другими словами, если в какой-то позиции <tex>i</tex> выполняется условие <tex>\pi(i)=|P|</tex>, то в этой позиции начинается очередное вхождение образца в цепочку.<br />
<br><br />
[[Файл:kmp_pict2.png|640px]]<br />
<br />
==Псевдокод==<br />
'''int'''[] kmp('''string''' T, '''string''' P)<br />
'''int''' p = P.length<br />
'''int''' t = T.length<br />
'''int'''[] answer<br />
count = 0<br />
'''for''' i = 0 .. (t - 1)<br />
'''if''' <tex>\pi</tex>(p + i + 1) == p<br />
answer[count++] = i + 1 - p<br />
'''return''' answer<br />
<br />
==Время работы==<br />
Префикс-функция от строки <tex>S</tex> строится за <tex>O(S) = O(P + T)</tex>. Проход цикла по строке <tex>S</tex> содержит <tex>O(T)</tex> итераций. Итого, время работы алгоритма оценивается как <tex>O(P + T)</tex>.<br />
<br />
==Оценка по памяти==<br />
Предложенная реализация имеет оценку по памяти <tex>O(P+T)</tex>. Оценки <tex>O(T)</tex> можно добиться за счет отказа от запоминания значений префикс-функции для позиций в <tex>S</tex>, меньших <tex>p + 1</tex> (т.е. до начала цепочки <tex>T</tex>). Это возможно, так как значение префикс функции не может превысить длину образца, благодаря разделительному символу <tex>\#</tex>.<br />
<br />
==См. также==<br />
*[[Алгоритм Ахо-Корасик|Алгоритм Ахо-Корасик]]<br />
*[[Алгоритм Бойера-Мура|Алгоритм Бойера-Мура]]<br />
*[[Алгоритм Колусси|Алгоритм Колусси]]<br />
<br />
==Источники==<br />
*[[wikipedia:ru:Алгоритм Кнута — Морриса — Пратта | Википедия {{---}} Алгоритм Кнута — Морриса — Пратта]]<br />
*[[wikipedia:en:Knuth–Morris–Pratt algorithm | Wikipedia {{---}} Knuth–Morris–Pratt algorithm]]<br />
*Кормен, Т., Лейзерсон, Ч., Ривест, Р., Штайн — Алгоритмы: построение и анализ / пер. с англ. — изд. 2-е — М.: Издательский дом «Вильямс», 2009. — с.1036. — ISBN 978-5-8459-0857-5.<br />
<br />
[[Категория: Алгоритмы и структуры данных]]<br />
[[Категория: Поиск подстроки в строке]]</div>217.197.6.98http://neerc.ifmo.ru/wiki/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%9A%D0%BD%D1%83%D1%82%D0%B0-%D0%9C%D0%BE%D1%80%D1%80%D0%B8%D1%81%D0%B0-%D0%9F%D1%80%D0%B0%D1%82%D1%82%D0%B0&diff=37393Алгоритм Кнута-Морриса-Пратта2014-05-30T18:54:14Z<p>217.197.6.98: /* Множественный поиск образцов */</p>
<hr />
<div>'''Алгоритм Кнута — Морриса — Пратта''' (англ. ''Knuth–Morris–Pratt algorithm'') — алгоритм [[Наивный алгоритм поиска подстроки в строке#Постановка задачи|поиска подстроки в строке]].<br />
<br />
==Описание алгоритма==<br />
Дана цепочка <tex>T</tex> и образец <tex>P</tex>. Требуется найти все позиции, начиная с которых <tex>P</tex> входит в <tex>T</tex>.<br />
<br><br />
Построим строку <tex>S = P\#T</tex>, где <tex>\#</tex> — любой символ, не входящий в алфавит <tex>P</tex> и <tex>T</tex>. Посчитаем на ней [[Префикс-функция|префикс-функцию]] <tex>\pi()</tex>. Благодаря разделительному символу <tex>\#</tex>, выполняется <tex>\forall i: \pi(i) \leqslant |P|</tex>. Заметим, что по определению [[Префикс-функция|префикс-функции]] при <tex>i > |P|</tex> и <tex>\pi(i) = |P|</tex> подстроки длины <tex>P</tex>, начинающиеся с позиций <tex>0</tex> и <tex>i - |P| + 1</tex>, совпадают. Соберем все такие позиции <tex>i - |P| + 1</tex> строки <tex>S</tex>, вычтем из каждой позиции <tex>|P| + 1</tex>, это и будет ответ. Другими словами, если в какой-то позиции <tex>i</tex> выполняется условие <tex>\pi(i)=|P|</tex>, то в этой позиции начинается очередное вхождение образца в цепочку.<br />
<br><br />
[[Файл:kmp_pict2.png|640px]]<br />
<br />
==Псевдокод==<br />
'''int'''[] kmp('''string''' T, '''string''' P)<br />
'''int''' p = P.length<br />
'''int''' t = T.length<br />
'''int'''[] answer<br />
count = 0<br />
'''for''' i = 0 .. (t - 1)<br />
'''if''' <tex>\pi</tex>(p + i + 1) == p<br />
answer[count++] = i + 1 - p<br />
'''return''' answer<br />
<br />
==Множественный поиск образцов==<br />
Если мы хотим произвести множественный поиск образцов в тексте, то для начала построим префикс-функции для всех образцов, а затем, при проходе по символам текста, будем сразу подсчитывать префикс-функции для каждого образца.<br />
<br />
==Время работы==<br />
Префикс-функция от строки <tex>S</tex> строится за <tex>O(S) = O(P + T)</tex>. Проход цикла по строке <tex>S</tex> содержит <tex>O(T)</tex> итераций. Итого, время работы алгоритма оценивается как <tex>O(P + T)</tex>. Для множественного поиска общее время работы оценивается как <tex>O(Q + mT)</tex>, где <tex>m</tex>{{---}} количество образцов, а <tex>Q</tex> {{---}} суммарное время построения префикс-функций для всех образцов.<br />
<br />
==Оценка по памяти==<br />
Предложенная реализация имеет оценку по памяти <tex>O(P+T)</tex>. Оценки <tex>O(T)</tex> можно добиться за счет отказа от запоминания значений префикс-функции для позиций в <tex>S</tex>, меньших <tex>p + 1</tex> (т.е. до начала цепочки <tex>T</tex>), это возможно из-за того, что мы точно знаем, что значение префикс функции не может превысить длину образца, благодаря разделительному символу <tex>\#</tex>.<br />
<br />
==См. также==<br />
*[[Алгоритм Ахо-Корасик|Алгоритм Ахо-Корасик]]<br />
*[[Алгоритм Бойера-Мура|Алгоритм Бойера-Мура]]<br />
*[[Алгоритм Колусси|Алгоритм Колусси]]<br />
<br />
==Источники==<br />
*[[wikipedia:ru:Алгоритм Кнута — Морриса — Пратта | Википедия {{---}} Алгоритм Кнута — Морриса — Пратта]]<br />
*[[wikipedia:en:Knuth–Morris–Pratt algorithm | Wikipedia {{---}} Knuth–Morris–Pratt algorithm]]<br />
*Кормен, Т., Лейзерсон, Ч., Ривест, Р., Штайн — Алгоритмы: построение и анализ / пер. с англ. — изд. 2-е — М.: Издательский дом «Вильямс», 2009. — с.1036. — ISBN 978-5-8459-0857-5.<br />
<br />
[[Категория: Алгоритмы и структуры данных]]<br />
[[Категория: Поиск подстроки в строке]]</div>217.197.6.98http://neerc.ifmo.ru/wiki/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%9A%D0%BD%D1%83%D1%82%D0%B0-%D0%9C%D0%BE%D1%80%D1%80%D0%B8%D1%81%D0%B0-%D0%9F%D1%80%D0%B0%D1%82%D1%82%D0%B0&diff=37392Алгоритм Кнута-Морриса-Пратта2014-05-30T18:52:42Z<p>217.197.6.98: </p>
<hr />
<div>'''Алгоритм Кнута — Морриса — Пратта''' (англ. ''Knuth–Morris–Pratt algorithm'') — алгоритм [[Наивный алгоритм поиска подстроки в строке#Постановка задачи|поиска подстроки в строке]].<br />
<br />
==Описание алгоритма==<br />
Дана цепочка <tex>T</tex> и образец <tex>P</tex>. Требуется найти все позиции, начиная с которых <tex>P</tex> входит в <tex>T</tex>.<br />
<br><br />
Построим строку <tex>S = P\#T</tex>, где <tex>\#</tex> — любой символ, не входящий в алфавит <tex>P</tex> и <tex>T</tex>. Посчитаем на ней [[Префикс-функция|префикс-функцию]] <tex>\pi()</tex>. Благодаря разделительному символу <tex>\#</tex>, выполняется <tex>\forall i: \pi(i) \leqslant |P|</tex>. Заметим, что по определению [[Префикс-функция|префикс-функции]] при <tex>i > |P|</tex> и <tex>\pi(i) = |P|</tex> подстроки длины <tex>P</tex>, начинающиеся с позиций <tex>0</tex> и <tex>i - |P| + 1</tex>, совпадают. Соберем все такие позиции <tex>i - |P| + 1</tex> строки <tex>S</tex>, вычтем из каждой позиции <tex>|P| + 1</tex>, это и будет ответ. Другими словами, если в какой-то позиции <tex>i</tex> выполняется условие <tex>\pi(i)=|P|</tex>, то в этой позиции начинается очередное вхождение образца в цепочку.<br />
<br><br />
[[Файл:kmp_pict2.png|640px]]<br />
<br />
==Псевдокод==<br />
'''int'''[] kmp('''string''' T, '''string''' P)<br />
'''int''' p = P.length<br />
'''int''' t = T.length<br />
'''int'''[] answer<br />
count = 0<br />
'''for''' i = 0 .. (t - 1)<br />
'''if''' <tex>\pi</tex>(p + i + 1) == p<br />
answer[count++] = i + 1 - p<br />
'''return''' answer<br />
<br />
==Множественный поиск образцов==<br />
Если мы хотим произвести множественный поиск образцов в тексте, то нам необходимо хранить значение префикс-функции символа текста для каждого образца. Перед этим нужно построить префикс-функции для всех образцов.<br />
<br />
==Время работы==<br />
Префикс-функция от строки <tex>S</tex> строится за <tex>O(S) = O(P + T)</tex>. Проход цикла по строке <tex>S</tex> содержит <tex>O(T)</tex> итераций. Итого, время работы алгоритма оценивается как <tex>O(P + T)</tex>. Для множественного поиска общее время работы оценивается как <tex>O(Q + mT)</tex>, где <tex>m</tex>{{---}} количество образцов, а <tex>Q</tex> {{---}} суммарное время построения префикс-функций для всех образцов.<br />
<br />
==Оценка по памяти==<br />
Предложенная реализация имеет оценку по памяти <tex>O(P+T)</tex>. Оценки <tex>O(T)</tex> можно добиться за счет отказа от запоминания значений префикс-функции для позиций в <tex>S</tex>, меньших <tex>p + 1</tex> (т.е. до начала цепочки <tex>T</tex>), это возможно из-за того, что мы точно знаем, что значение префикс функции не может превысить длину образца, благодаря разделительному символу <tex>\#</tex>.<br />
<br />
==См. также==<br />
*[[Алгоритм Ахо-Корасик|Алгоритм Ахо-Корасик]]<br />
*[[Алгоритм Бойера-Мура|Алгоритм Бойера-Мура]]<br />
*[[Алгоритм Колусси|Алгоритм Колусси]]<br />
<br />
==Источники==<br />
*[[wikipedia:ru:Алгоритм Кнута — Морриса — Пратта | Википедия {{---}} Алгоритм Кнута — Морриса — Пратта]]<br />
*[[wikipedia:en:Knuth–Morris–Pratt algorithm | Wikipedia {{---}} Knuth–Morris–Pratt algorithm]]<br />
*Кормен, Т., Лейзерсон, Ч., Ривест, Р., Штайн — Алгоритмы: построение и анализ / пер. с англ. — изд. 2-е — М.: Издательский дом «Вильямс», 2009. — с.1036. — ISBN 978-5-8459-0857-5.<br />
<br />
[[Категория: Алгоритмы и структуры данных]]<br />
[[Категория: Поиск подстроки в строке]]</div>217.197.6.98http://neerc.ifmo.ru/wiki/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%9A%D0%BD%D1%83%D1%82%D0%B0-%D0%9C%D0%BE%D1%80%D1%80%D0%B8%D1%81%D0%B0-%D0%9F%D1%80%D0%B0%D1%82%D1%82%D0%B0&diff=37391Алгоритм Кнута-Морриса-Пратта2014-05-30T18:47:51Z<p>217.197.6.98: </p>
<hr />
<div>'''Алгоритм Кнута — Морриса — Пратта''' (англ. ''Knuth–Morris–Pratt algorithm'') — алгоритм [[Наивный алгоритм поиска подстроки в строке#Постановка задачи|поиска подстроки в строке]].<br />
<br />
==Описание алгоритма==<br />
Дана цепочка <tex>T</tex> и образец <tex>P</tex>. Требуется найти все позиции, начиная с которых <tex>P</tex> входит в <tex>T</tex>.<br />
<br><br />
Построим строку <tex>S = P\#T</tex>, где <tex>\#</tex> — любой символ, не входящий в алфавит <tex>P</tex> и <tex>T</tex>. Посчитаем на ней [[Префикс-функция|префикс-функцию]] <tex>\pi()</tex>. Благодаря разделительному символу <tex>\#</tex>, выполняется <tex>\forall i: \pi(i) \leqslant |P|</tex>. Заметим, что по определению [[Префикс-функция|префикс-функции]] при <tex>i > |P|</tex> и <tex>\pi(i) = |P|</tex> подстроки длины <tex>P</tex>, начинающиеся с позиций <tex>0</tex> и <tex>i - |P| + 1</tex>, совпадают. Соберем все такие позиции <tex>i - |P| + 1</tex> строки <tex>S</tex>, вычтем из каждой позиции <tex>|P| + 1</tex>, это и будет ответ. Другими словами, если в какой-то позиции <tex>i</tex> выполняется условие <tex>\pi(i)=|P|</tex>, то в этой позиции начинается очередное вхождение образца в цепочку.<br />
<br><br />
[[Файл:kmp_pict2.png|640px]]<br />
<br />
==Псевдокод==<br />
'''int'''[] kmp('''string''' T, '''string''' P)<br />
'''int''' p = P.length<br />
'''int''' t = T.length<br />
'''int'''[] answer<br />
count = 0<br />
'''for''' i = 0 .. (t - 1)<br />
'''if''' <tex>\pi</tex>(p + i + 1) == p<br />
answer[count++] = i + 1 - p<br />
'''return''' answer<br />
<br />
==Время работы==<br />
Префикс-функция от строки <tex>S</tex> строится за <tex>O(S) = O(P + T)</tex>. Проход цикла по строке <tex>S</tex> содержит <tex>O(T)</tex> итераций. Итого, время работы алгоритма оценивается как <tex>O(P + T)</tex>. Если мы хотим произвести множественный поиск образцов в тексте, то необходимо построить префикс-функцию для каждого из образцов в отдельности, тогда, учитывая, что длина образца обычно много меньше, чем длина текста, то общее время работы оценивается как <tex>O(mT)</tex>, где <tex>m</tex>{{---}} количество образцов.<br />
<br />
==Оценка по памяти==<br />
Предложенная реализация имеет оценку по памяти <tex>O(P+T)</tex>. Оценки <tex>O(T)</tex> можно добиться за счет отказа от запоминания значений префикс-функции для позиций в <tex>S</tex>, меньших <tex>p + 1</tex> (т.е. до начала цепочки <tex>T</tex>), это возможно из-за того, что мы точно знаем, что значение префикс функции не может превысить длину образца, благодаря разделительному символу <tex>\#</tex>.<br />
<br />
==См. также==<br />
*[[Алгоритм Ахо-Корасик|Алгоритм Ахо-Корасик]]<br />
*[[Алгоритм Бойера-Мура|Алгоритм Бойера-Мура]]<br />
*[[Алгоритм Колусси|Алгоритм Колусси]]<br />
<br />
==Источники==<br />
*[[wikipedia:ru:Алгоритм Кнута — Морриса — Пратта | Википедия {{---}} Алгоритм Кнута — Морриса — Пратта]]<br />
*[[wikipedia:en:Knuth–Morris–Pratt algorithm | Wikipedia {{---}} Knuth–Morris–Pratt algorithm]]<br />
*Кормен, Т., Лейзерсон, Ч., Ривест, Р., Штайн — Алгоритмы: построение и анализ / пер. с англ. — изд. 2-е — М.: Издательский дом «Вильямс», 2009. — с.1036. — ISBN 978-5-8459-0857-5.<br />
<br />
[[Категория: Алгоритмы и структуры данных]]<br />
[[Категория: Поиск подстроки в строке]]</div>217.197.6.98http://neerc.ifmo.ru/wiki/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%9A%D0%BD%D1%83%D1%82%D0%B0-%D0%9C%D0%BE%D1%80%D1%80%D0%B8%D1%81%D0%B0-%D0%9F%D1%80%D0%B0%D1%82%D1%82%D0%B0&diff=37382Алгоритм Кнута-Морриса-Пратта2014-05-30T15:09:48Z<p>217.197.6.98: </p>
<hr />
<div>'''Алгоритм Кнута — Морриса — Пратта''' (англ. ''Knuth–Morris–Pratt algorithm'') — алгоритм [[Наивный алгоритм поиска подстроки в строке#Постановка задачи|поиска подстроки в строке]].<br />
<br />
==Описание алгоритма==<br />
Дана цепочка <tex>T</tex> и образец <tex>P</tex>. Требуется найти все позиции, начиная с которых <tex>P</tex> входит в <tex>T</tex>.<br />
<br><br />
Построим строку <tex>S = P\#T</tex>, где <tex>\#</tex> — любой символ, не входящий в алфавит <tex>P</tex> и <tex>T</tex>. Посчитаем на ней [[Префикс-функция|префикс-функцию]] <tex>\pi()</tex>. Благодаря разделительному символу <tex>\#</tex>, выполняется <tex>\forall i: \pi(i) \le |P|</tex>. Заметим, что по определению [[Префикс-функция|префикс-функции]] при <tex>i > |P|</tex> и <tex>\pi(i) = |P|</tex> подстроки длины <tex>P</tex>, начинающиеся с позиций <tex>0</tex> и <tex>i - |P| + 1</tex>, совпадают. Соберем все такие позиции <tex>i - |P| + 1</tex> строки <tex>S</tex>, вычтем из каждой позиции <tex>|P| + 1</tex>, это и будет ответ.<br />
<br><br />
[[Файл:kmp_pict.png|640px]]<br />
<br />
==Псевдокод==<br />
Пусть <tex>p = |P|</tex>, <tex>t = |T|</tex>.<br />
count = 0<br />
'''for''' (i = 0 .. (t - 1))<br />
'''if''' (<tex>\pi</tex>(p + i + 1) == p)<br />
answer[count++] = i + 1 - p<br />
<br />
==Время работы==<br />
Префикс-функция от строки <tex>S</tex> строится за <tex>O(S) = O(P + T)</tex>. Проход цикла по строке <tex>S</tex> содержит <tex>O(T)</tex> итераций. Итого, время работы алгоритма оценивается как <tex>O(P + T)</tex>.<br />
<br />
==Оценка по памяти==<br />
Предложенная реализация имеет оценку по памяти <tex>O(P+T)</tex>. Оценки <tex>O(T)</tex> можно добиться за счет незапоминания значений <tex>\pi()</tex> для позиций в <tex>S</tex>, меньших <tex>p + 1</tex> (т.е. до начала цепочки <tex>T</tex>).<br />
<br />
==Источники==<br />
*[[wikipedia:ru:Алгоритм Кнута — Морриса — Пратта | Википедия {{---}} Алгоритм Кнута — Морриса — Пратта]]<br />
*[[wikipedia:en:Knuth–Morris–Pratt algorithm | Wikipedia {{---}} Knuth–Morris–Pratt algorithm]]<br />
*Кормен, Т., Лейзерсон, Ч., Ривест, Р., Штайн — Алгоритмы: построение и анализ / пер. с англ. — изд. 2-е — М.: Издательский дом «Вильямс», 2009. — с.1036. — ISBN 978-5-8459-0857-5.<br />
<br />
[[Категория: Алгоритмы и структуры данных]]<br />
[[Категория: Поиск подстроки в строке]]</div>217.197.6.98http://neerc.ifmo.ru/wiki/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%9A%D0%BD%D1%83%D1%82%D0%B0-%D0%9C%D0%BE%D1%80%D1%80%D0%B8%D1%81%D0%B0-%D0%9F%D1%80%D0%B0%D1%82%D1%82%D0%B0&diff=37381Алгоритм Кнута-Морриса-Пратта2014-05-30T15:08:08Z<p>217.197.6.98: /* Источники */</p>
<hr />
<div>Алгоритм Кнута — Морриса — Пратта — алгоритм [[Наивный алгоритм поиска подстроки в строке#Постановка задачи|поиска подстроки в строке]].<br />
<br />
==Описание алгоритма==<br />
Дана цепочка <tex>T</tex> и образец <tex>P</tex>. Требуется найти все позиции, начиная с которых <tex>P</tex> входит в <tex>T</tex>.<br />
<br><br />
Построим строку <tex>S = P\#T</tex>, где <tex>\#</tex> — любой символ, не входящий в алфавит <tex>P</tex> и <tex>T</tex>. Посчитаем на ней [[Префикс-функция|префикс-функцию]] <tex>\pi()</tex>. Благодаря разделительному символу <tex>\#</tex>, выполняется <tex>\forall i: \pi(i) \le |P|</tex>. Заметим, что по определению [[Префикс-функция|префикс-функции]] при <tex>i > |P|</tex> и <tex>\pi(i) = |P|</tex> подстроки длины <tex>P</tex>, начинающиеся с позиций <tex>0</tex> и <tex>i - |P| + 1</tex>, совпадают. Соберем все такие позиции <tex>i - |P| + 1</tex> строки <tex>S</tex>, вычтем из каждой позиции <tex>|P| + 1</tex>, это и будет ответ.<br />
<br><br />
[[Файл:kmp_pict.png|640px]]<br />
<br />
==Псевдокод==<br />
Пусть <tex>p = |P|</tex>, <tex>t = |T|</tex>.<br />
count = 0<br />
'''for''' (i = 0 .. (t - 1))<br />
'''if''' (<tex>\pi</tex>(p + i + 1) == p)<br />
answer[count++] = i + 1 - p<br />
<br />
==Время работы==<br />
Префикс-функция от строки <tex>S</tex> строится за <tex>O(S) = O(P + T)</tex>. Проход цикла по строке <tex>S</tex> содержит <tex>O(T)</tex> итераций. Итого, время работы алгоритма оценивается как <tex>O(P + T)</tex>.<br />
<br />
==Оценка по памяти==<br />
Предложенная реализация имеет оценку по памяти <tex>O(P+T)</tex>. Оценки <tex>O(T)</tex> можно добиться за счет незапоминания значений <tex>\pi()</tex> для позиций в <tex>S</tex>, меньших <tex>p + 1</tex> (т.е. до начала цепочки <tex>T</tex>).<br />
<br />
==Источники==<br />
*[[wikipedia:ru:Алгоритм Кнута — Морриса — Пратта | Википедия {{---}} Алгоритм Кнута — Морриса — Пратта]]<br />
*[[wikipedia:en:Knuth–Morris–Pratt algorithm | Wikipedia {{---}} Knuth–Morris–Pratt algorithm]]<br />
*Кормен, Т., Лейзерсон, Ч., Ривест, Р., Штайн — Алгоритмы: построение и анализ / пер. с англ. — изд. 2-е — М.: Издательский дом «Вильямс», 2009. — с.1036. — ISBN 978-5-8459-0857-5.<br />
<br />
[[Категория: Алгоритмы и структуры данных]]<br />
[[Категория: Поиск подстроки в строке]]</div>217.197.6.98http://neerc.ifmo.ru/wiki/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%9A%D0%BD%D1%83%D1%82%D0%B0-%D0%9C%D0%BE%D1%80%D1%80%D0%B8%D1%81%D0%B0-%D0%9F%D1%80%D0%B0%D1%82%D1%82%D0%B0&diff=37380Алгоритм Кнута-Морриса-Пратта2014-05-30T15:05:51Z<p>217.197.6.98: /* Источники */</p>
<hr />
<div>Алгоритм Кнута — Морриса — Пратта — алгоритм [[Наивный алгоритм поиска подстроки в строке#Постановка задачи|поиска подстроки в строке]].<br />
<br />
==Описание алгоритма==<br />
Дана цепочка <tex>T</tex> и образец <tex>P</tex>. Требуется найти все позиции, начиная с которых <tex>P</tex> входит в <tex>T</tex>.<br />
<br><br />
Построим строку <tex>S = P\#T</tex>, где <tex>\#</tex> — любой символ, не входящий в алфавит <tex>P</tex> и <tex>T</tex>. Посчитаем на ней [[Префикс-функция|префикс-функцию]] <tex>\pi()</tex>. Благодаря разделительному символу <tex>\#</tex>, выполняется <tex>\forall i: \pi(i) \le |P|</tex>. Заметим, что по определению [[Префикс-функция|префикс-функции]] при <tex>i > |P|</tex> и <tex>\pi(i) = |P|</tex> подстроки длины <tex>P</tex>, начинающиеся с позиций <tex>0</tex> и <tex>i - |P| + 1</tex>, совпадают. Соберем все такие позиции <tex>i - |P| + 1</tex> строки <tex>S</tex>, вычтем из каждой позиции <tex>|P| + 1</tex>, это и будет ответ.<br />
<br><br />
[[Файл:kmp_pict.png|640px]]<br />
<br />
==Псевдокод==<br />
Пусть <tex>p = |P|</tex>, <tex>t = |T|</tex>.<br />
count = 0<br />
'''for''' (i = 0 .. (t - 1))<br />
'''if''' (<tex>\pi</tex>(p + i + 1) == p)<br />
answer[count++] = i + 1 - p<br />
<br />
==Время работы==<br />
Префикс-функция от строки <tex>S</tex> строится за <tex>O(S) = O(P + T)</tex>. Проход цикла по строке <tex>S</tex> содержит <tex>O(T)</tex> итераций. Итого, время работы алгоритма оценивается как <tex>O(P + T)</tex>.<br />
<br />
==Оценка по памяти==<br />
Предложенная реализация имеет оценку по памяти <tex>O(P+T)</tex>. Оценки <tex>O(T)</tex> можно добиться за счет незапоминания значений <tex>\pi()</tex> для позиций в <tex>S</tex>, меньших <tex>p + 1</tex> (т.е. до начала цепочки <tex>T</tex>).<br />
<br />
==Источники==<br />
[http://en.wikipedia.org/wiki/Knuth%E2%80%93Morris%E2%80%93Pratt_algorithm Knuth–Morris–Pratt algorithm]<br><br />
Кормен, Т., Лейзерсон, Ч., Ривест, Р., Штайн — Алгоритмы: построение и анализ / пер. с англ. — изд. 2-е — М.: Издательский дом «Вильямс», 2009. — с.1036. — ISBN 978-5-8459-0857-5.<br />
<br />
[[Категория: Алгоритмы и структуры данных]]<br />
[[Категория: Поиск подстроки в строке]]</div>217.197.6.98http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D1%80%D0%B5%D1%84%D0%B8%D0%BA%D1%81-%D1%84%D1%83%D0%BD%D0%BA%D1%86%D0%B8%D1%8F&diff=36999Префикс-функция2014-05-14T14:36:31Z<p>217.197.6.98: </p>
<hr />
<div>{{Определение<br />
|definition = '''Префикс-функция''' ''(англ. prefix-function)'' от строки {{---}} длина наибольшего [[Период_и_бордер,_их_связь#Определения|бордера]] этой строки}}<br />
Здесь и далее считаем, что символы в строках нумеруются с <tex>1</tex>.<br />
<br />
Определим префикс-функцию от строки <tex>s</tex> в позиции <tex>i</tex> следующим образом: <tex>\pi(s, i) = \max\limits_{k = 1..i - 1} \{k : </tex> <tex>s[1..k] = s[i - k + 1..i] \}</tex>. Если мы не нашли такого <tex>k</tex>, то <tex>\pi(s, i)=0</tex>. <br />
<br />
==Наивный алгоритм==<br />
Наивный алгоритм вычисляет префикс функцию непосредственно по определению, сравнивая префиксы и суффиксы строк. Обозначим длину строки за <tex>n</tex><br />
<br />
===Псевдокод===<br />
'''Prefix_function''' (<tex>s</tex>)<br />
fill(<tex>\pi</tex>, 0)<br />
'''for''' i = 1 '''to''' n<br />
'''for''' k = 1 '''to''' i<br />
'''if''' s[1..k] == s[i - k + 1..i])<br />
<tex>\pi</tex>[i] = k<br />
'''return''' <tex>\pi</tex><br />
<br />
===Пример===<br />
Рассмотрим строку abcabcd, для которой значение префикс-функции равно <tex>[0,0,0,1,2,3,0]</tex>.<br />
{| class="wikitable"<br />
! Шаг || Строка || Значение функции<br />
|-<br />
| <tex>1</tex> || a || 0<br />
|-<br />
| <tex>2</tex> || ab || 0<br />
|-<br />
| <tex>3</tex> || abc || 0<br />
|-<br />
| <tex>4</tex> || abca || 1<br />
|-<br />
| <tex>5</tex> || abcab || 2<br />
|-<br />
| <tex>6</tex> || abcabc || 3<br />
|-<br />
| <tex>7</tex> || abcabcd || 0<br />
|}<br />
<br />
===Время работы===<br />
Всего <tex>O(n^2)</tex> итераций цикла, на каждой из который происходит сравнение строк за <tex>O(n)</tex>, что дает в итоге <tex>O(n^3)</tex>.<br />
<br />
==Эффективный алгоритм==<br />
Вносятся несколько важных замечаний:<br />
*Заметим, что <tex>\pi[i + 1] \leqslant \pi[i] + 1</tex>. Чтобы показать это, рассмотрим суффикс,оканчивающийся на позиции <tex>i + 1</tex> и имеющий длину <tex>\pi[i + 1]</tex>, удалив из него последний символ, мы получим суффикс, оканчивающийся на позиции <tex>i</tex> и имеющий длину <tex>\pi[i + 1] - 1</tex>, следовательно неравенство <tex>\pi[i + 1] > \pi[i] + 1</tex> неверно.<br />
*Избавимся от явных сравнений строк. <br />
**Пусть мы вычислили <tex>\pi[i]</tex>, тогда, если <tex>s[i + 1] = s[\pi[i]]</tex>, то <tex>\pi[i + 1] = \pi[i] + 1</tex>. <br />
**Если окажется, что <tex>s[i + 1] \ne s[\pi[i]]</tex>, то нужно попытаться попробовать подстроку меньшей длины. Хотелось бы сразу перейти к такому [[Период_и_бордер,_их_связь#Определения|бордеру]] наибольшей длины, для этого подберем такое <tex>k</tex>, что <tex>k = \pi(i) - 1</tex>. Делаем это следующим образом. За исходное <tex>k</tex> необходимо взять <tex>\pi(i - 1)</tex>, что следует из первого пункта. В случае, когда символы <tex>s[k+1]</tex> и <tex>s[i]</tex> не совпадают, <tex>\pi(k)</tex> {{---}} следующее потенциальное наибольшее значение <tex>k</tex>, что видно из рисунка. Последнее утверждение верно, пока <tex>k>0</tex>, что позволит всегда найти его следующее значение. Если <tex>k=0</tex>, то <tex>\pi(i)=1</tex> при <tex>s[i] = s[1]</tex> , иначе <tex>\pi(i)=0</tex>.<br />
<br />
[[Файл:Prfx.jpg]]<br />
===Псевдокод===<br />
'''Prefix_function''' (<tex>s</tex>)<br />
<tex>\pi</tex>[1] = 0<br />
'''for''' i = 2 '''to''' n<br />
k = <tex>\pi</tex>[i-1]<br />
'''while''' k > 0 && s[i] != s[k + 1] <br />
k = <tex>\pi</tex>[k]<br />
'''if''' s[i] == s[k + 1]<br />
k++<br />
<tex>\pi</tex>[i] = k<br />
'''return''' <tex>\pi</tex><br />
<br />
===Время работы===<br />
Время работы алгоритма составит <tex>O(n)</tex>. Для доказательства этого нужно заметить, что итоговое количество итераций цикла <tex>while</tex> определяет асимптотику алгоритма. Теперь стоит отметить, что <tex>k</tex> увеличивается на каждом шаге не более чем на единицу, значит максимально возможное значение <tex>k = n - 1</tex>. Поскольку внутри цикла <tex>while</tex> значение <tex>k</tex> лишь уменьшается, получается, что <tex>k</tex> не может суммарно уменьшиться больше, чем <tex>n-1</tex> раз. Значит цикл <tex>while</tex> в итоге выполнится не более <tex>n</tex> раз, что дает итоговую оценку времени алгоритма <tex>O(n)</tex>.<br />
<br />
==Построение строки по префикс-функции==<br />
===Постановка задачи=== <br />
Восстановить строку по префикс-функции за <tex>O(N)</tex>, считая алфавит неограниченным.<br />
<br />
===Описание алгоритма===<br />
<br />
Пусть в массиве <tex>p</tex> хранятся значения префикс-функции, в <tex>s</tex> будет записан ответ. Пойдем по массиву <tex>p</tex> слева направо.<br />
<br />
Пусть мы хотим узнать значение <tex>s[i]</tex>. Для этого посмотрим на значение <tex>p[i]</tex>: если <tex>p[i] =0</tex> тогда в <tex>s[i]</tex> запишем новый символ, иначе <tex>s[i] = s[p[i]]</tex>. Обратим внимание, что <tex>s[p[i]]</tex> нам уже известно, так как <tex>p[i] < i</tex>.<br />
<br />
=== Реализация ===<br />
'''string''' buildFromPrefix('''int'''[] p):<br />
s = "" <br />
'''for''' i = 0 '''to''' p.length - 1<br />
'''if''' p[i] == 0 <br />
s += new character<br />
'''else'''<br />
s += s[p[i]]<br />
'''return''' s<br />
<br />
===Доказательство корректности алгоритма===<br />
Докажем, что если нам дали корректную префикс-функцию, то наш алгоритм построит строку с такой же префикс-функцией. Также заметим, что строк с такой префикс-функцией может быть много, и алгоритм строит только одну из них.<br />
<br />
Пусть <tex>p</tex> данная префикс-функция, <tex>s'</tex> правильная строка, строку <tex>s</tex> построил наш алгоритм, <tex> q </tex> массив значений префикс-функции для <tex>s</tex>.<br />
<br />
Докажем корректность индукцией по длине массива префикс-функции полученной строки. Для начала заметим, что на предыдущие значения массива <tex> q </tex> прибавление нового символа не влияет, так как при подсчёте префикс-функции на <tex> i </tex>-ой позиции рассматриваются символы на позициях не больше <tex> i </tex>. Поэтому достаточно показать, что очередное значение префикс-функции будет вычислено правильно.<br />
* База очевидна для строки длины <tex>1</tex>.<br />
* Переход: пусть до <tex>n</tex>-ой позиции мы построили строку, что <tex>p[1..n - 1] = q[1..n - 1]</tex>. Возможны два случая:<br />
** <tex>p[n] = 0</tex>. Тогда мы добавляем новый символ, поэтому <tex>q[n]</tex> тоже будет равно <tex>0</tex>. <br />
** <tex>p[n] > 0</tex>. По свойствам префикс-функции <tex> s'[p[n]] = s'[n] </tex> {{---}} суффикс и префикс строки <tex> s' </tex> длины <tex> p[n] </tex> продолжаются одним символом, значит, надо на текущую позицию строки <tex> s </tex> поставить символ <tex> s[p[n]] </tex>. Если значение префикс-функции увеличивается, значит, текущим символом продолжается префикс длины <tex> p[n - 1] </tex>, а из свойств следует, что <tex> p[n - 1] \geqslant p[n] - 1 </tex>. По предположению индукцию значение <tex> q[n - 1] </tex> будет вычислено верно. А если значение префикс-функции не увеличивается, значит, символ <tex> s[n] </tex> должен продолжить префикс меньшей длины, а в текущее значение префикс-функции запишется как раз длина нового [[Период_и_бордер,_их_связь#Определения|бордера]]. Для этого будут использованы значения префикс-функции с меньшими индексами, которые посчитаны верно, опять же по предположению индукции.<br />
<br />
== Источники информации ==<br />
* Кормен Т., Лейзерсон Ч., Ривест Р. Алгоритмы: построение и анализ. {{---}} 2-е изд. {{---}} М.: Издательский дом «Вильямс», 2007. {{---}} С. 1296 ISBN 978-5-8459-0857-5<br />
*<br />
<br />
== См. также ==<br />
<br />
<br />
[[Категория: Алгоритмы и структуры данных]]<br />
[[Категория: Поиск подстроки в строке]]</div>217.197.6.98http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D1%80%D0%B5%D1%84%D0%B8%D0%BA%D1%81-%D1%84%D1%83%D0%BD%D0%BA%D1%86%D0%B8%D1%8F&diff=36996Префикс-функция2014-05-14T12:56:22Z<p>217.197.6.98: /* Псевдокод */</p>
<hr />
<div>{{Определение<br />
|definition = '''Префикс-функция''' ''(prefix-function)'' от строки(обозначается <tex>\pi(s,i)</tex>) - длина наибольшего префикса строки <tex>S[1..i]</tex>, который не совпадает с этой строкой и одновременно является ее суффиксом}}<br />
<br />
Префикс-функция строки <tex>s</tex> {{---}} функция <tex>\pi(s, i) = \max\limits_{k = 1..i - 1} \{ 0, k : </tex> <tex>s[1..k] = s[i - k + 1..i] \}</tex>.<br />
<br />
Здесь и далее считаем, что символы в строках нумеруются с <tex>1</tex>.<br />
==Наивный алгоритм==<br />
Наивный алгоритм вычисляет префикс функцию непосредственно по определению, сравнивая префиксы и суффиксы строк.<br />
<br />
===Псевдокод===<br />
'''Prefix_function''' (<tex>s</tex>)<br />
fill(<tex>\pi</tex>, 0)<br />
'''for''' i = 1 '''to''' s.length <br />
'''for''' k = 1 '''to''' i<br />
'''if''' s[1..k] == s[i - k + 1..i])<br />
<tex>\pi</tex>[i] = k<br />
'''return''' <tex>\pi</tex><br />
<br />
===Пример===<br />
Рассмотрим строку abcabcd, для которой значение префикс-функции равно <tex>[0,0,0,1,2,3,0]</tex>.<br />
{| class="wikitable"<br />
! Шаг || Строка || Значение функции<br />
|-<br />
| <tex>1</tex> || a || 0<br />
|-<br />
| <tex>2</tex> || ab || 0<br />
|-<br />
| <tex>3</tex> || abc || 0<br />
|-<br />
| <tex>4</tex> || abca || 1<br />
|-<br />
| <tex>5</tex> || abcab || 2<br />
|-<br />
| <tex>6</tex> || abcabc || 3<br />
|-<br />
| <tex>7</tex> || abcabcd || 0<br />
|}<br />
<br />
===Время работы===<br />
Всего <tex>O(n^2)</tex> итераций цикла, на каждой из который происходит сравнение строк за <tex>O(n)</tex>, что дает в итоге <tex>O(n^3)</tex>.<br />
<br />
==Эффективный алгоритм==<br />
Вносятся несколько важных замечаний:<br />
*Заметим, что <tex>\pi[i + 1] \le \pi[i] + 1</tex>. Чтобы показать это, рассмотрим суффикс,оканчивающийся на позиции <tex>i + 1</tex> и имеющий длину <tex>\pi[i + 1]</tex>, удалив из него последний символ, мы получим суффикс, оканчивающийся на позиции <tex>i</tex> и имеющий длину <tex>\pi[i + 1] - 1</tex>, следовательно неравенство <tex>\pi[i + 1] > \pi[i] + 1</tex> неверно.<br />
*Избавимся от явных сравнений строк. Пусть мы вычислили <tex>\pi[i]</tex>, тогда, если <tex>s[i + 1] = s[\pi[i]]</tex>, то <tex>\pi[i + 1] = \pi[i] + 1</tex>. Если окажется, что <tex>s[i + 1] \ne s[\pi[i]]</tex>, то нужно попытаться попробовать подстроку меньшей длины. Для этого подберем такое <tex>k</tex>, что <tex>k = \pi(i) - 1</tex>. Делаем это следующим образом. За исходное <tex>k</tex> необходимо взять <tex>\pi(i - 1)</tex>, что следует из первого пункта. В случае, когда символы <tex>s[k+1]</tex> и <tex>s[i]</tex> не совпадают, <tex>\pi(k)</tex> {{---}} следующее потенциальное наибольшее значение <tex>k</tex>, что видно из рисунка. Последнее утверждение верно, пока <tex>k>0</tex>, что позволит всегда найти его следующее значение. Если <tex>k=0</tex>, то <tex>\pi(i)=1</tex> при <tex>s[i] = s[1]</tex> , иначе <tex>\pi(i)=0</tex>.<br />
<br />
[[Файл:Prfx.jpg]]<br />
===Псевдокод===<br />
'''Prefix_function''' (<tex>s</tex>)<br />
<tex>\pi</tex>[1] = 0<br />
k = 0<br />
'''for''' i = 2 '''to''' s.length<br />
k = <tex>\pi</tex>[i-1]<br />
'''while''' k > 0 && s[i] != s[k + 1] <br />
k = <tex>\pi</tex>[k]<br />
'''if''' s[i] == s[k + 1]<br />
k++<br />
<tex>\pi</tex>[i] = k<br />
'''return''' <tex>\pi</tex><br />
<br />
===Время работы===<br />
Время работы алгоритма составит <tex>O(n)</tex>. Для доказательства этого нужно заметить, что итоговое количество итераций цикла <tex>while</tex> определяет асимптотику алгоритма. Теперь стоит отметить, что <tex>k</tex> увеличивается на каждом шаге не более чем на единицу, значит максимально возможное значение <tex>k = n - 1</tex>. Поскольку внутри цикла <tex>while</tex> значение <tex>k</tex> лишь уменьшается, получается, что <tex>k</tex> не может суммарно уменьшиться больше, чем <tex>n-1</tex> раз. Значит цикл <tex>while</tex> в итоге выполнится не более <tex>n</tex> раз, что дает итоговую оценку времени алгоритма <tex>O(n)</tex>.<br />
<br />
==Построение строки по префикс-функции==<br />
===Постановка задачи=== <br />
Восстановить строку по префикс-функции за <tex>O(N)</tex>, считая алфавит неограниченным.<br />
<br />
===Описание алгоритма===<br />
<br />
Пусть в массиве <tex>p</tex> хранятся значения префикс-функции, в <tex>s</tex> будет записан ответ. Пойдем по массиву <tex>p</tex> слева направо.<br />
<br />
Пусть мы хотим узнать значение <tex>s[i]</tex>. Для этого посмотрим на значение <tex>p[i]</tex>: если <tex>p[i] =0</tex> тогда в <tex>s[i]</tex> запишем новый символ, иначе <tex>s[i] = s[p[i]]</tex>. Обратим внимание, что <tex>s[p[i]]</tex> нам уже известно, так как <tex>p[i] < i</tex>.<br />
<br />
=== Реализация ===<br />
'''string''' buildFromPrefix('''int'''[] p):<br />
s = "" <br />
'''for''' i = 0 '''to''' p.length - 1<br />
'''if''' p[i] == 0 <br />
s += new character<br />
'''else'''<br />
s += s[p[i]]<br />
'''return''' s<br />
<br />
===Доказательство корректности алгоритма===<br />
Докажем, что если нам дали корректную префикс-функцию, то наш алгоритм построит строку с такой же префикс-функцией. Также заметим, что строк с такой префикс-функцией может быть много, и алгоритм строит только одну из них.<br />
<br />
Пусть <tex>p</tex> данная префикс-функция, <tex>s'</tex> правильная строка, строку <tex>s</tex> построил наш алгоритм, <tex> q </tex> массив значений префикс-функции для <tex>s</tex>.<br />
<br />
Докажем корректность индукцией по длине массива префикс-функции полученной строки. Для начала заметим, что на предыдущие значения массива <tex> q </tex> прибавление нового символа не влияет, так как при подсчёте префикс-функции на <tex> i </tex>-ой позиции рассматриваются символы на позициях не больше <tex> i </tex>. Поэтому достаточно показать, что очередное значение префикс-функции будет вычислено правильно.<br />
* База очевидна для строки длины <tex>1</tex>.<br />
* Переход: пусть до <tex>n</tex>-ой позиции мы построили строку, что <tex>p[1..n - 1] = q[1..n - 1]</tex>. Возможны два случая:<br />
** <tex>p[n] = 0</tex>. Тогда мы добавляем новый символ, поэтому <tex>q[n]</tex> тоже будет равно <tex>0</tex>. <br />
** <tex>p[n] > 0</tex>. По свойствам префикс-функции <tex> s'[p[n]] = s'[n] </tex> {{---}} суффикс и префикс строки <tex> s' </tex> длины <tex> p[n] </tex> продолжаются одним символом, значит, надо на текущую позицию строки <tex> s </tex> поставить символ <tex> s[p[n]] </tex>. Если значение префикс-функции увеличивается, значит, текущим символом продолжается префикс длины <tex> p[n - 1] </tex>, а из свойств следует, что <tex> p[n - 1] \geqslant p[n] - 1 </tex>. По предположению индукцию значение <tex> q[n - 1] </tex> будет вычислено верно. А если значение префикс-функции не увеличивается, значит, символ <tex> s[n] </tex> должен продолжить префикс меньшей длины, а в текущее значение префикс-функции запишется как раз длина нового бордера. Для этого будут использованы значения префикс-функции с меньшими индексами, которые посчитаны верно, опять же по препдположению индукции.<br />
<br />
== Литература ==<br />
* Кормен Т., Лейзерсон Ч., Ривест Р. Алгоритмы: построение и анализ. {{---}} 2-е изд. {{---}} М.: Издательский дом «Вильямс», 2007. {{---}} С. 1296 ISBN 978-5-8459-0857-5<br />
<br />
[[Категория: Алгоритмы и структуры данных]]<br />
[[Категория: Поиск подстроки в строке]]</div>217.197.6.98http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D1%80%D0%B5%D1%84%D0%B8%D0%BA%D1%81-%D1%84%D1%83%D0%BD%D0%BA%D1%86%D0%B8%D1%8F&diff=36995Префикс-функция2014-05-14T12:49:52Z<p>217.197.6.98: /* Псевдокод */</p>
<hr />
<div>{{Определение<br />
|definition = '''Префикс-функция''' ''(prefix-function)'' от строки(обозначается <tex>\pi(s,i)</tex>) - длина наибольшего префикса строки <tex>S[1..i]</tex>, который не совпадает с этой строкой и одновременно является ее суффиксом}}<br />
<br />
Префикс-функция строки <tex>s</tex> {{---}} функция <tex>\pi(s, i) = \max\limits_{k = 1..i - 1} \{ 0, k : </tex> <tex>s[1..k] = s[i - k + 1..i] \}</tex>.<br />
<br />
Здесь и далее считаем, что символы в строках нумеруются с <tex>1</tex>.<br />
==Наивный алгоритм==<br />
Наивный алгоритм вычисляет префикс функцию непосредственно по определению, сравнивая префиксы и суффиксы строк.<br />
<br />
===Псевдокод===<br />
'''Prefix_function''' (<tex>s</tex>)<br />
fill(<tex>\pi</tex>, 0)<br />
'''for''' i = 1 '''to''' s.length <br />
'''for''' k = 1 '''to''' i<br />
'''if''' s[1..k] == s[i - k + 1..i])<br />
<tex>\pi</tex>[i] = k<br />
'''return''' <tex>\pi</tex><br />
<br />
===Пример===<br />
Рассмотрим строку abcabcd, для которой значение префикс-функции равно <tex>[0,0,0,1,2,3,0]</tex>.<br />
{| class="wikitable"<br />
! Шаг || Строка || Значение функции<br />
|-<br />
| <tex>1</tex> || a || 0<br />
|-<br />
| <tex>2</tex> || ab || 0<br />
|-<br />
| <tex>3</tex> || abc || 0<br />
|-<br />
| <tex>4</tex> || abca || 1<br />
|-<br />
| <tex>5</tex> || abcab || 2<br />
|-<br />
| <tex>6</tex> || abcabc || 3<br />
|-<br />
| <tex>7</tex> || abcabcd || 0<br />
|}<br />
<br />
===Время работы===<br />
Всего <tex>O(n^2)</tex> итераций цикла, на каждой из который происходит сравнение строк за <tex>O(n)</tex>, что дает в итоге <tex>O(n^3)</tex>.<br />
<br />
==Эффективный алгоритм==<br />
Вносятся несколько важных замечаний:<br />
*Заметим, что <tex>\pi[i + 1] \le \pi[i] + 1</tex>. Чтобы показать это, рассмотрим суффикс,оканчивающийся на позиции <tex>i + 1</tex> и имеющий длину <tex>\pi[i + 1]</tex>, удалив из него последний символ, мы получим суффикс, оканчивающийся на позиции <tex>i</tex> и имеющий длину <tex>\pi[i + 1] - 1</tex>, следовательно неравенство <tex>\pi[i + 1] > \pi[i] + 1</tex> неверно.<br />
*Избавимся от явных сравнений строк. Пусть мы вычислили <tex>\pi[i]</tex>, тогда, если <tex>s[i + 1] = s[\pi[i]]</tex>, то <tex>\pi[i + 1] = \pi[i] + 1</tex>. Если окажется, что <tex>s[i + 1] \ne s[\pi[i]]</tex>, то нужно попытаться попробовать подстроку меньшей длины. Для этого подберем такое <tex>k</tex>, что <tex>k = \pi(i) - 1</tex>. Делаем это следующим образом. За исходное <tex>k</tex> необходимо взять <tex>\pi(i - 1)</tex>, что следует из первого пункта. В случае, когда символы <tex>s[k+1]</tex> и <tex>s[i]</tex> не совпадают, <tex>\pi(k)</tex> {{---}} следующее потенциальное наибольшее значение <tex>k</tex>, что видно из рисунка. Последнее утверждение верно, пока <tex>k>0</tex>, что позволит всегда найти его следующее значение. Если <tex>k=0</tex>, то <tex>\pi(i)=1</tex> при <tex>s[i] = s[1]</tex> , иначе <tex>\pi(i)=0</tex>.<br />
<br />
[[Файл:Prfx.jpg]]<br />
===Псевдокод===<br />
'''Prefix_function''' (<tex>s</tex>)<br />
<tex>\pi</tex>[1] = 0<br />
k = 0<br />
'''for''' (i = 2; i < s.length; i++) {<br />
'''while''' (k > 0 && s[i] != s[k + 1]) {<br />
k = <tex>\pi</tex>[k]<br />
}<br />
'''if''' (s[i] == s[k + 1]) {<br />
k++<br />
}<br />
<tex>\pi</tex>[i] = k<br />
}<br />
'''return''' <tex>\pi</tex><br />
<br />
===Время работы===<br />
Время работы алгоритма составит <tex>O(n)</tex>. Для доказательства этого нужно заметить, что итоговое количество итераций цикла <tex>while</tex> определяет асимптотику алгоритма. Теперь стоит отметить, что <tex>k</tex> увеличивается на каждом шаге не более чем на единицу, значит максимально возможное значение <tex>k = n - 1</tex>. Поскольку внутри цикла <tex>while</tex> значение <tex>k</tex> лишь уменьшается, получается, что <tex>k</tex> не может суммарно уменьшиться больше, чем <tex>n-1</tex> раз. Значит цикл <tex>while</tex> в итоге выполнится не более <tex>n</tex> раз, что дает итоговую оценку времени алгоритма <tex>O(n)</tex>.<br />
<br />
==Построение строки по префикс-функции==<br />
===Постановка задачи=== <br />
Восстановить строку по префикс-функции за <tex>O(N)</tex>, считая алфавит неограниченным.<br />
<br />
===Описание алгоритма===<br />
<br />
Пусть в массиве <tex>p</tex> хранятся значения префикс-функции, в <tex>s</tex> будет записан ответ. Пойдем по массиву <tex>p</tex> слева направо.<br />
<br />
Пусть мы хотим узнать значение <tex>s[i]</tex>. Для этого посмотрим на значение <tex>p[i]</tex>: если <tex>p[i] =0</tex> тогда в <tex>s[i]</tex> запишем новый символ, иначе <tex>s[i] = s[p[i]]</tex>. Обратим внимание, что <tex>s[p[i]]</tex> нам уже известно, так как <tex>p[i] < i</tex>.<br />
<br />
=== Реализация ===<br />
'''string''' buildFromPrefix('''int'''[] p):<br />
s = "" <br />
'''for''' i = 0 '''to''' p.length - 1<br />
'''if''' p[i] == 0 <br />
s += new character<br />
'''else'''<br />
s += s[p[i]]<br />
'''return''' s<br />
<br />
===Доказательство корректности алгоритма===<br />
Докажем, что если нам дали корректную префикс-функцию, то наш алгоритм построит строку с такой же префикс-функцией. Также заметим, что строк с такой префикс-функцией может быть много, и алгоритм строит только одну из них.<br />
<br />
Пусть <tex>p</tex> данная префикс-функция, <tex>s'</tex> правильная строка, строку <tex>s</tex> построил наш алгоритм, <tex> q </tex> массив значений префикс-функции для <tex>s</tex>.<br />
<br />
Докажем корректность индукцией по длине массива префикс-функции полученной строки. Для начала заметим, что на предыдущие значения массива <tex> q </tex> прибавление нового символа не влияет, так как при подсчёте префикс-функции на <tex> i </tex>-ой позиции рассматриваются символы на позициях не больше <tex> i </tex>. Поэтому достаточно показать, что очередное значение префикс-функции будет вычислено правильно.<br />
* База очевидна для строки длины <tex>1</tex>.<br />
* Переход: пусть до <tex>n</tex>-ой позиции мы построили строку, что <tex>p[1..n - 1] = q[1..n - 1]</tex>. Возможны два случая:<br />
** <tex>p[n] = 0</tex>. Тогда мы добавляем новый символ, поэтому <tex>q[n]</tex> тоже будет равно <tex>0</tex>. <br />
** <tex>p[n] > 0</tex>. По свойствам префикс-функции <tex> s'[p[n]] = s'[n] </tex> {{---}} суффикс и префикс строки <tex> s' </tex> длины <tex> p[n] </tex> продолжаются одним символом, значит, надо на текущую позицию строки <tex> s </tex> поставить символ <tex> s[p[n]] </tex>. Если значение префикс-функции увеличивается, значит, текущим символом продолжается префикс длины <tex> p[n - 1] </tex>, а из свойств следует, что <tex> p[n - 1] \geqslant p[n] - 1 </tex>. По предположению индукцию значение <tex> q[n - 1] </tex> будет вычислено верно. А если значение префикс-функции не увеличивается, значит, символ <tex> s[n] </tex> должен продолжить префикс меньшей длины, а в текущее значение префикс-функции запишется как раз длина нового бордера. Для этого будут использованы значения префикс-функции с меньшими индексами, которые посчитаны верно, опять же по препдположению индукции.<br />
<br />
== Литература ==<br />
* Кормен Т., Лейзерсон Ч., Ривест Р. Алгоритмы: построение и анализ. {{---}} 2-е изд. {{---}} М.: Издательский дом «Вильямс», 2007. {{---}} С. 1296 ISBN 978-5-8459-0857-5<br />
<br />
[[Категория: Алгоритмы и структуры данных]]<br />
[[Категория: Поиск подстроки в строке]]</div>217.197.6.98http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D1%80%D0%B5%D1%84%D0%B8%D0%BA%D1%81-%D1%84%D1%83%D0%BD%D0%BA%D1%86%D0%B8%D1%8F&diff=36980Префикс-функция2014-05-13T20:19:09Z<p>217.197.6.98: /* Псевдокод */</p>
<hr />
<div>{{Определение<br />
|definition = '''Префикс-функция''' ''(prefix-function)'' от строки(обозначается <tex>\pi(s,i)</tex>) - длина наибольшего префикса строки <tex>S[1..i]</tex>, который не совпадает с этой строкой и одновременно является ее суффиксом}}<br />
<br />
Префикс-функция строки <tex>s</tex> {{---}} функция <tex>\pi(s, i) = \max\limits_{k = 1..i - 1} \{ 0, k : </tex> <tex>s[1..k] = s[i - k + 1..i] \}</tex>.<br />
<br />
Здесь и далее считаем, что символы в строках нумеруются с <tex>1</tex>.<br />
==Наивный алгоритм==<br />
Наивный алгоритм вычисляет префикс функцию непосредственно по определению, сравнивая префиксы и суффиксы строк.<br />
<br />
===Псевдокод===<br />
'''Prefix_function''' (<tex>s</tex>)<br />
fill(<tex>\pi</tex>, 0)<br />
'''for''' (i = 1; i < s.length; i++) {<br />
'''for''' (k = 1; k < i; k++) {<br />
'''if''' (s[1..k] == s[i - k + 1..i]) {<br />
<tex>\pi</tex>[i] = k<br />
}<br />
}<br />
}<br />
'''return''' <tex>\pi</tex><br />
<br />
===Пример===<br />
Рассмотрим строку abcabcd, для которой значение префикс-функции равно <tex>[0,0,0,1,2,3,0]</tex>.<br />
{| class="wikitable"<br />
! Шаг || Строка || Значение функции<br />
|-<br />
| <tex>1</tex> || a || 0<br />
|-<br />
| <tex>2</tex> || ab || 0<br />
|-<br />
| <tex>3</tex> || abc || 0<br />
|-<br />
| <tex>4</tex> || abca || 1<br />
|-<br />
| <tex>5</tex> || abcab || 2<br />
|-<br />
| <tex>6</tex> || abcabc || 3<br />
|-<br />
| <tex>7</tex> || abcabcd || 0<br />
|}<br />
<br />
===Время работы===<br />
Всего <tex>O(n^2)</tex> итераций цикла, на каждой из который происходит сравнение строк за <tex>O(n)</tex>, что дает в итоге <tex>O(n^3)</tex>.<br />
<br />
==Эффективный алгоритм==<br />
Вносятся несколько важных замечаний:<br />
*Заметим, что <tex>\pi[i + 1] \le \pi[i] + 1</tex>. Чтобы показать это, рассмотрим суффикс,оканчивающийся на позиции <tex>i + 1</tex> и имеющий длину <tex>\pi[i + 1]</tex>, удалив из него последний символ, мы получим суффикс, оканчивающийся на позиции <tex>i</tex> и имеющий длину <tex>\pi[i + 1] - 1</tex>, следовательно неравенство <tex>\pi[i + 1] > \pi[i] + 1</tex> неверно.<br />
*Избавимся от явных сравнений строк. Пусть мы вычислили <tex>\pi[i]</tex>, тогда, если <tex>s[i + 1] = s[\pi[i]]</tex>, то <tex>\pi[i + 1] = \pi[i] + 1</tex>. Если окажется, что <tex>s[i + 1] \ne s[\pi[i]]</tex>, то нужно попытаться попробовать подстроку меньшей длины. Для этого подберем такое <tex>k</tex>, что <tex>k = \pi(i) - 1</tex>. Делаем это следующим образом. За исходное <tex>k</tex> необходимо взять <tex>\pi(i - 1)</tex>, что следует из первого пункта. В случае, когда символы <tex>s[k+1]</tex> и <tex>s[i]</tex> не совпадают, <tex>\pi(k)</tex> {{---}} следующее потенциальное наибольшее значение <tex>k</tex>, что видно из рисунка. Последнее утверждение верно, пока <tex>k>0</tex>, что позволит всегда найти его следующее значение. Если <tex>k=0</tex>, то <tex>\pi(i)=1</tex> при <tex>s[i] = s[1]</tex> , иначе <tex>\pi(i)=0</tex>.<br />
<br />
[[Файл:Prefix2.jpg]]<br />
===Псевдокод===<br />
'''Prefix_function''' (<tex>s</tex>)<br />
<tex>\pi</tex>[1] = 0<br />
k = 0<br />
'''for''' (i = 2; i < s.length; i++) {<br />
'''while''' (k > 0 && s[i] != s[k + 1]) {<br />
k = <tex>\pi</tex>[k]<br />
}<br />
'''if''' (s[i] == s[k + 1]) {<br />
k++<br />
}<br />
<tex>\pi</tex>[i] = k<br />
}<br />
'''return''' <tex>\pi</tex><br />
<br />
===Время работы===<br />
Время работы алгоритма составит <tex>O(n)</tex>. Для доказательства этого нужно заметить, что итоговое количество итераций цикла <tex>while</tex> определяет асимптотику алгоритма. Теперь стоит отметить, что <tex>k</tex> увеличивается на каждом шаге не более чем на единицу, значит максимально возможное значение <tex>k = n - 1</tex>. Поскольку внутри цикла <tex>while</tex> значение <tex>k</tex> лишь уменьшается, получается, что <tex>k</tex> не может суммарно уменьшиться больше, чем <tex>n-1</tex> раз. Значит цикл <tex>while</tex> в итоге выполнится не более <tex>n</tex> раз, что дает итоговую оценку времени алгоритма <tex>O(n)</tex>.<br />
<br />
==Построение строки по префикс-функции==<br />
===Постановка задачи=== <br />
Восстановить строку по префикс-функции за <tex>O(N)</tex>, считая алфавит неограниченным.<br />
<br />
===Описание алгоритма===<br />
<br />
Пусть в массиве <tex>p</tex> хранятся значения префикс-функции, в <tex>s</tex> будет записан ответ. Пойдем по массиву <tex>p</tex> слева направо.<br />
<br />
Пусть мы хотим узнать значение <tex>s[i]</tex>. Для этого посмотрим на значение <tex>p[i]</tex>: если <tex>p[i] =0</tex> тогда в <tex>s[i]</tex> запишем новый символ, иначе <tex>s[i] = s[p[i]]</tex>. Обратим внимание, что <tex>s[p[i]]</tex> нам уже известно, так как <tex>p[i] < i</tex>.<br />
<br />
=== Реализация ===<br />
'''string''' buildFromPrefix('''int'''[] p):<br />
s = "" <br />
'''for''' i = 0 '''to''' p.length - 1<br />
'''if''' p[i] == 0 <br />
s += new character<br />
'''else'''<br />
s += s[p[i]]<br />
'''return''' s<br />
<br />
===Доказательство корректности алгоритма===<br />
Докажем, что если нам дали корректную префикс-функцию, то наш алгоритм построит строку с такой же префикс-функцией. Также заметим, что строк с такой префикс-функцией может быть много, и алгоритм строит только одну из них.<br />
<br />
Пусть <tex>p</tex> данная префикс-функция, <tex>s'</tex> правильная строка, строку <tex>s</tex> построил наш алгоритм, <tex> q </tex> массив значений префикс-функции для <tex>s</tex>.<br />
<br />
Докажем корректность индукцией по длине массива префикс-функции полученной строки. Для начала заметим, что на предыдущие значения массива <tex> q </tex> прибавление нового символа не влияет, так как при подсчёте префикс-функции на <tex> i </tex>-ой позиции рассматриваются символы на позициях не больше <tex> i </tex>. Поэтому достаточно показать, что очередное значение префикс-функции будет вычислено правильно.<br />
* База очевидна для строки длины <tex>1</tex>.<br />
* Переход: пусть до <tex>n</tex>-ой позиции мы построили строку, что <tex>p[1..n - 1] = q[1..n - 1]</tex>. Возможны два случая:<br />
** <tex>p[n] = 0</tex>. Тогда мы добавляем новый символ, поэтому <tex>q[n]</tex> тоже будет равно <tex>0</tex>. <br />
** <tex>p[n] > 0</tex>. По свойствам префикс-функции <tex> s'[p[n]] = s'[n] </tex> {{---}} суффикс и префикс строки <tex> s' </tex> длины <tex> p[n] </tex> продолжаются одним символом, значит, надо на текущую позицию строки <tex> s </tex> поставить символ <tex> s[p[n]] </tex>. Если значение префикс-функции увеличивается, значит, текущим символом продолжается префикс длины <tex> p[n - 1] </tex>, а из свойств следует, что <tex> p[n - 1] \geqslant p[n] - 1 </tex>. По предположению индукцию значение <tex> q[n - 1] </tex> будет вычислено верно. А если значение префикс-функции не увеличивается, значит, символ <tex> s[n] </tex> должен продолжить префикс меньшей длины, а в текущее значение префикс-функции запишется как раз длина нового бордера. Для этого будут использованы значения префикс-функции с меньшими индексами, которые посчитаны верно, опять же по препдположению индукции.<br />
<br />
== Литература ==<br />
* Кормен Т., Лейзерсон Ч., Ривест Р. Алгоритмы: построение и анализ. {{---}} 2-е изд. {{---}} М.: Издательский дом «Вильямс», 2007. {{---}} С. 1296 ISBN 978-5-8459-0857-5<br />
<br />
[[Категория: Алгоритмы и структуры данных]]<br />
[[Категория: Поиск подстроки в строке]]</div>217.197.6.98http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D1%80%D0%B5%D1%84%D0%B8%D0%BA%D1%81-%D1%84%D1%83%D0%BD%D0%BA%D1%86%D0%B8%D1%8F&diff=36979Префикс-функция2014-05-13T20:18:18Z<p>217.197.6.98: /* Псевдокод */</p>
<hr />
<div>{{Определение<br />
|definition = '''Префикс-функция''' ''(prefix-function)'' от строки(обозначается <tex>\pi(s,i)</tex>) - длина наибольшего префикса строки <tex>S[1..i]</tex>, который не совпадает с этой строкой и одновременно является ее суффиксом}}<br />
<br />
Префикс-функция строки <tex>s</tex> {{---}} функция <tex>\pi(s, i) = \max\limits_{k = 1..i - 1} \{ 0, k : </tex> <tex>s[1..k] = s[i - k + 1..i] \}</tex>.<br />
<br />
Здесь и далее считаем, что символы в строках нумеруются с <tex>1</tex>.<br />
==Наивный алгоритм==<br />
Наивный алгоритм вычисляет префикс функцию непосредственно по определению, сравнивая префиксы и суффиксы строк.<br />
<br />
===Псевдокод===<br />
'''Prefix_function''' (<tex>s</tex>)<br />
<tex>\pi</tex> = [0,..,0]<br />
'''for''' (i = 1; i < s.length; i++) {<br />
'''for''' (k = 1; k < i; k++) {<br />
'''if''' (s[1..k] == s[i - k + 1..i]) {<br />
<tex>\pi</tex>[i] = k<br />
}<br />
}<br />
}<br />
'''return''' <tex>\pi</tex><br />
<br />
===Пример===<br />
Рассмотрим строку abcabcd, для которой значение префикс-функции равно <tex>[0,0,0,1,2,3,0]</tex>.<br />
{| class="wikitable"<br />
! Шаг || Строка || Значение функции<br />
|-<br />
| <tex>1</tex> || a || 0<br />
|-<br />
| <tex>2</tex> || ab || 0<br />
|-<br />
| <tex>3</tex> || abc || 0<br />
|-<br />
| <tex>4</tex> || abca || 1<br />
|-<br />
| <tex>5</tex> || abcab || 2<br />
|-<br />
| <tex>6</tex> || abcabc || 3<br />
|-<br />
| <tex>7</tex> || abcabcd || 0<br />
|}<br />
<br />
===Время работы===<br />
Всего <tex>O(n^2)</tex> итераций цикла, на каждой из который происходит сравнение строк за <tex>O(n)</tex>, что дает в итоге <tex>O(n^3)</tex>.<br />
<br />
==Эффективный алгоритм==<br />
Вносятся несколько важных замечаний:<br />
*Заметим, что <tex>\pi[i + 1] \le \pi[i] + 1</tex>. Чтобы показать это, рассмотрим суффикс,оканчивающийся на позиции <tex>i + 1</tex> и имеющий длину <tex>\pi[i + 1]</tex>, удалив из него последний символ, мы получим суффикс, оканчивающийся на позиции <tex>i</tex> и имеющий длину <tex>\pi[i + 1] - 1</tex>, следовательно неравенство <tex>\pi[i + 1] > \pi[i] + 1</tex> неверно.<br />
*Избавимся от явных сравнений строк. Пусть мы вычислили <tex>\pi[i]</tex>, тогда, если <tex>s[i + 1] = s[\pi[i]]</tex>, то <tex>\pi[i + 1] = \pi[i] + 1</tex>. Если окажется, что <tex>s[i + 1] \ne s[\pi[i]]</tex>, то нужно попытаться попробовать подстроку меньшей длины. Для этого подберем такое <tex>k</tex>, что <tex>k = \pi(i) - 1</tex>. Делаем это следующим образом. За исходное <tex>k</tex> необходимо взять <tex>\pi(i - 1)</tex>, что следует из первого пункта. В случае, когда символы <tex>s[k+1]</tex> и <tex>s[i]</tex> не совпадают, <tex>\pi(k)</tex> {{---}} следующее потенциальное наибольшее значение <tex>k</tex>, что видно из рисунка. Последнее утверждение верно, пока <tex>k>0</tex>, что позволит всегда найти его следующее значение. Если <tex>k=0</tex>, то <tex>\pi(i)=1</tex> при <tex>s[i] = s[1]</tex> , иначе <tex>\pi(i)=0</tex>.<br />
<br />
[[Файл:Prefix2.jpg]]<br />
===Псевдокод===<br />
'''Prefix_function''' (<tex>s</tex>)<br />
<tex>\pi</tex>[1] = 0<br />
k = 0<br />
'''for''' (i = 2; i < s.length; i++) {<br />
'''while''' (k > 0 && s[i] != s[k + 1]) {<br />
k = <tex>\pi</tex>[k]<br />
}<br />
'''if''' (s[i] == s[k + 1]) {<br />
k++<br />
}<br />
<tex>\pi</tex>[i] = k<br />
}<br />
'''return''' <tex>\pi</tex><br />
<br />
===Время работы===<br />
Время работы алгоритма составит <tex>O(n)</tex>. Для доказательства этого нужно заметить, что итоговое количество итераций цикла <tex>while</tex> определяет асимптотику алгоритма. Теперь стоит отметить, что <tex>k</tex> увеличивается на каждом шаге не более чем на единицу, значит максимально возможное значение <tex>k = n - 1</tex>. Поскольку внутри цикла <tex>while</tex> значение <tex>k</tex> лишь уменьшается, получается, что <tex>k</tex> не может суммарно уменьшиться больше, чем <tex>n-1</tex> раз. Значит цикл <tex>while</tex> в итоге выполнится не более <tex>n</tex> раз, что дает итоговую оценку времени алгоритма <tex>O(n)</tex>.<br />
<br />
==Построение строки по префикс-функции==<br />
===Постановка задачи=== <br />
Восстановить строку по префикс-функции за <tex>O(N)</tex>, считая алфавит неограниченным.<br />
<br />
===Описание алгоритма===<br />
<br />
Пусть в массиве <tex>p</tex> хранятся значения префикс-функции, в <tex>s</tex> будет записан ответ. Пойдем по массиву <tex>p</tex> слева направо.<br />
<br />
Пусть мы хотим узнать значение <tex>s[i]</tex>. Для этого посмотрим на значение <tex>p[i]</tex>: если <tex>p[i] =0</tex> тогда в <tex>s[i]</tex> запишем новый символ, иначе <tex>s[i] = s[p[i]]</tex>. Обратим внимание, что <tex>s[p[i]]</tex> нам уже известно, так как <tex>p[i] < i</tex>.<br />
<br />
=== Реализация ===<br />
'''string''' buildFromPrefix('''int'''[] p):<br />
s = "" <br />
'''for''' i = 0 '''to''' p.length - 1<br />
'''if''' p[i] == 0 <br />
s += new character<br />
'''else'''<br />
s += s[p[i]]<br />
'''return''' s<br />
<br />
===Доказательство корректности алгоритма===<br />
Докажем, что если нам дали корректную префикс-функцию, то наш алгоритм построит строку с такой же префикс-функцией. Также заметим, что строк с такой префикс-функцией может быть много, и алгоритм строит только одну из них.<br />
<br />
Пусть <tex>p</tex> данная префикс-функция, <tex>s'</tex> правильная строка, строку <tex>s</tex> построил наш алгоритм, <tex> q </tex> массив значений префикс-функции для <tex>s</tex>.<br />
<br />
Докажем корректность индукцией по длине массива префикс-функции полученной строки. Для начала заметим, что на предыдущие значения массива <tex> q </tex> прибавление нового символа не влияет, так как при подсчёте префикс-функции на <tex> i </tex>-ой позиции рассматриваются символы на позициях не больше <tex> i </tex>. Поэтому достаточно показать, что очередное значение префикс-функции будет вычислено правильно.<br />
* База очевидна для строки длины <tex>1</tex>.<br />
* Переход: пусть до <tex>n</tex>-ой позиции мы построили строку, что <tex>p[1..n - 1] = q[1..n - 1]</tex>. Возможны два случая:<br />
** <tex>p[n] = 0</tex>. Тогда мы добавляем новый символ, поэтому <tex>q[n]</tex> тоже будет равно <tex>0</tex>. <br />
** <tex>p[n] > 0</tex>. По свойствам префикс-функции <tex> s'[p[n]] = s'[n] </tex> {{---}} суффикс и префикс строки <tex> s' </tex> длины <tex> p[n] </tex> продолжаются одним символом, значит, надо на текущую позицию строки <tex> s </tex> поставить символ <tex> s[p[n]] </tex>. Если значение префикс-функции увеличивается, значит, текущим символом продолжается префикс длины <tex> p[n - 1] </tex>, а из свойств следует, что <tex> p[n - 1] \geqslant p[n] - 1 </tex>. По предположению индукцию значение <tex> q[n - 1] </tex> будет вычислено верно. А если значение префикс-функции не увеличивается, значит, символ <tex> s[n] </tex> должен продолжить префикс меньшей длины, а в текущее значение префикс-функции запишется как раз длина нового бордера. Для этого будут использованы значения префикс-функции с меньшими индексами, которые посчитаны верно, опять же по препдположению индукции.<br />
<br />
== Литература ==<br />
* Кормен Т., Лейзерсон Ч., Ривест Р. Алгоритмы: построение и анализ. {{---}} 2-е изд. {{---}} М.: Издательский дом «Вильямс», 2007. {{---}} С. 1296 ISBN 978-5-8459-0857-5<br />
<br />
[[Категория: Алгоритмы и структуры данных]]<br />
[[Категория: Поиск подстроки в строке]]</div>217.197.6.98http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D1%80%D0%B5%D1%84%D0%B8%D0%BA%D1%81-%D1%84%D1%83%D0%BD%D0%BA%D1%86%D0%B8%D1%8F&diff=36977Префикс-функция2014-05-13T20:16:50Z<p>217.197.6.98: /* Псевдокод */</p>
<hr />
<div>{{Определение<br />
|definition = '''Префикс-функция''' ''(prefix-function)'' от строки(обозначается <tex>\pi(s,i)</tex>) - длина наибольшего префикса строки <tex>S[1..i]</tex>, который не совпадает с этой строкой и одновременно является ее суффиксом}}<br />
<br />
Префикс-функция строки <tex>s</tex> {{---}} функция <tex>\pi(s, i) = \max\limits_{k = 1..i - 1} \{ 0, k : </tex> <tex>s[1..k] = s[i - k + 1..i] \}</tex>.<br />
<br />
Здесь и далее считаем, что символы в строках нумеруются с <tex>1</tex>.<br />
==Наивный алгоритм==<br />
Наивный алгоритм вычисляет префикс функцию непосредственно по определению, сравнивая префиксы и суффиксы строк.<br />
<br />
===Псевдокод===<br />
'''Prefix_function''' (<tex>s</tex>)<br />
<tex>\pi</tex> = [0,..,0]<br />
'''for''' i = 1 '''to''' n<br />
'''for''' k = 1 '''to''' i - 1<br />
'''if''' s[1..k] == s[i - k + 1..i]<br />
<tex>\pi</tex>[i] = k<br />
'''return''' <tex>\pi</tex><br />
<br />
===Пример===<br />
Рассмотрим строку abcabcd, для которой значение префикс-функции равно <tex>[0,0,0,1,2,3,0]</tex>.<br />
{| class="wikitable"<br />
! Шаг || Строка || Значение функции<br />
|-<br />
| <tex>1</tex> || a || 0<br />
|-<br />
| <tex>2</tex> || ab || 0<br />
|-<br />
| <tex>3</tex> || abc || 0<br />
|-<br />
| <tex>4</tex> || abca || 1<br />
|-<br />
| <tex>5</tex> || abcab || 2<br />
|-<br />
| <tex>6</tex> || abcabc || 3<br />
|-<br />
| <tex>7</tex> || abcabcd || 0<br />
|}<br />
<br />
===Время работы===<br />
Всего <tex>O(n^2)</tex> итераций цикла, на каждой из который происходит сравнение строк за <tex>O(n)</tex>, что дает в итоге <tex>O(n^3)</tex>.<br />
<br />
==Эффективный алгоритм==<br />
Вносятся несколько важных замечаний:<br />
*Заметим, что <tex>\pi[i + 1] \le \pi[i] + 1</tex>. Чтобы показать это, рассмотрим суффикс,оканчивающийся на позиции <tex>i + 1</tex> и имеющий длину <tex>\pi[i + 1]</tex>, удалив из него последний символ, мы получим суффикс, оканчивающийся на позиции <tex>i</tex> и имеющий длину <tex>\pi[i + 1] - 1</tex>, следовательно неравенство <tex>\pi[i + 1] > \pi[i] + 1</tex> неверно.<br />
*Избавимся от явных сравнений строк. Пусть мы вычислили <tex>\pi[i]</tex>, тогда, если <tex>s[i + 1] = s[\pi[i]]</tex>, то <tex>\pi[i + 1] = \pi[i] + 1</tex>. Если окажется, что <tex>s[i + 1] \ne s[\pi[i]]</tex>, то нужно попытаться попробовать подстроку меньшей длины. Для этого подберем такое <tex>k</tex>, что <tex>k = \pi(i) - 1</tex>. Делаем это следующим образом. За исходное <tex>k</tex> необходимо взять <tex>\pi(i - 1)</tex>, что следует из первого пункта. В случае, когда символы <tex>s[k+1]</tex> и <tex>s[i]</tex> не совпадают, <tex>\pi(k)</tex> {{---}} следующее потенциальное наибольшее значение <tex>k</tex>, что видно из рисунка. Последнее утверждение верно, пока <tex>k>0</tex>, что позволит всегда найти его следующее значение. Если <tex>k=0</tex>, то <tex>\pi(i)=1</tex> при <tex>s[i] = s[1]</tex> , иначе <tex>\pi(i)=0</tex>.<br />
<br />
[[Файл:Prefix2.jpg]]<br />
===Псевдокод===<br />
'''Prefix_function''' (<tex>s</tex>)<br />
<tex>\pi</tex>[1] = 0<br />
k = 0<br />
'''for''' (i = 2; i < s.length; i++) {<br />
'''while''' (k > 0 && s[i] != s[k + 1]) {<br />
k = <tex>\pi</tex>[k]<br />
}<br />
'''if''' (s[i] == s[k + 1]) {<br />
k++<br />
}<br />
<tex>\pi</tex>[i] = k<br />
}<br />
'''return''' <tex>\pi</tex><br />
<br />
===Время работы===<br />
Время работы алгоритма составит <tex>O(n)</tex>. Для доказательства этого нужно заметить, что итоговое количество итераций цикла <tex>while</tex> определяет асимптотику алгоритма. Теперь стоит отметить, что <tex>k</tex> увеличивается на каждом шаге не более чем на единицу, значит максимально возможное значение <tex>k = n - 1</tex>. Поскольку внутри цикла <tex>while</tex> значение <tex>k</tex> лишь уменьшается, получается, что <tex>k</tex> не может суммарно уменьшиться больше, чем <tex>n-1</tex> раз. Значит цикл <tex>while</tex> в итоге выполнится не более <tex>n</tex> раз, что дает итоговую оценку времени алгоритма <tex>O(n)</tex>.<br />
<br />
==Построение строки по префикс-функции==<br />
===Постановка задачи=== <br />
Восстановить строку по префикс-функции за <tex>O(N)</tex>, считая алфавит неограниченным.<br />
<br />
===Описание алгоритма===<br />
<br />
Пусть в массиве <tex>p</tex> хранятся значения префикс-функции, в <tex>s</tex> будет записан ответ. Пойдем по массиву <tex>p</tex> слева направо.<br />
<br />
Пусть мы хотим узнать значение <tex>s[i]</tex>. Для этого посмотрим на значение <tex>p[i]</tex>: если <tex>p[i] =0</tex> тогда в <tex>s[i]</tex> запишем новый символ, иначе <tex>s[i] = s[p[i]]</tex>. Обратим внимание, что <tex>s[p[i]]</tex> нам уже известно, так как <tex>p[i] < i</tex>.<br />
<br />
=== Реализация ===<br />
'''string''' buildFromPrefix('''int'''[] p):<br />
s = "" <br />
'''for''' i = 0 '''to''' p.length - 1<br />
'''if''' p[i] == 0 <br />
s += new character<br />
'''else'''<br />
s += s[p[i]]<br />
'''return''' s<br />
<br />
===Доказательство корректности алгоритма===<br />
Докажем, что если нам дали корректную префикс-функцию, то наш алгоритм построит строку с такой же префикс-функцией. Также заметим, что строк с такой префикс-функцией может быть много, и алгоритм строит только одну из них.<br />
<br />
Пусть <tex>p</tex> данная префикс-функция, <tex>s'</tex> правильная строка, строку <tex>s</tex> построил наш алгоритм, <tex> q </tex> массив значений префикс-функции для <tex>s</tex>.<br />
<br />
Докажем корректность индукцией по длине массива префикс-функции полученной строки. Для начала заметим, что на предыдущие значения массива <tex> q </tex> прибавление нового символа не влияет, так как при подсчёте префикс-функции на <tex> i </tex>-ой позиции рассматриваются символы на позициях не больше <tex> i </tex>. Поэтому достаточно показать, что очередное значение префикс-функции будет вычислено правильно.<br />
* База очевидна для строки длины <tex>1</tex>.<br />
* Переход: пусть до <tex>n</tex>-ой позиции мы построили строку, что <tex>p[1..n - 1] = q[1..n - 1]</tex>. Возможны два случая:<br />
** <tex>p[n] = 0</tex>. Тогда мы добавляем новый символ, поэтому <tex>q[n]</tex> тоже будет равно <tex>0</tex>. <br />
** <tex>p[n] > 0</tex>. По свойствам префикс-функции <tex> s'[p[n]] = s'[n] </tex> {{---}} суффикс и префикс строки <tex> s' </tex> длины <tex> p[n] </tex> продолжаются одним символом, значит, надо на текущую позицию строки <tex> s </tex> поставить символ <tex> s[p[n]] </tex>. Если значение префикс-функции увеличивается, значит, текущим символом продолжается префикс длины <tex> p[n - 1] </tex>, а из свойств следует, что <tex> p[n - 1] \geqslant p[n] - 1 </tex>. По предположению индукцию значение <tex> q[n - 1] </tex> будет вычислено верно. А если значение префикс-функции не увеличивается, значит, символ <tex> s[n] </tex> должен продолжить префикс меньшей длины, а в текущее значение префикс-функции запишется как раз длина нового бордера. Для этого будут использованы значения префикс-функции с меньшими индексами, которые посчитаны верно, опять же по препдположению индукции.<br />
<br />
== Литература ==<br />
* Кормен Т., Лейзерсон Ч., Ривест Р. Алгоритмы: построение и анализ. {{---}} 2-е изд. {{---}} М.: Издательский дом «Вильямс», 2007. {{---}} С. 1296 ISBN 978-5-8459-0857-5<br />
<br />
[[Категория: Алгоритмы и структуры данных]]<br />
[[Категория: Поиск подстроки в строке]]</div>217.197.6.98http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D1%80%D0%B5%D1%84%D0%B8%D0%BA%D1%81-%D1%84%D1%83%D0%BD%D0%BA%D1%86%D0%B8%D1%8F&diff=36971Префикс-функция2014-05-13T19:45:08Z<p>217.197.6.98: /* Эффективный алгоритм */</p>
<hr />
<div>{{Определение<br />
|definition = '''Префикс-функция''' ''(prefix-function)'' от строки(обозначается <tex>\pi(s,i)</tex>) - длина наибольшего префикса строки <tex>S[1..i]</tex>, который не совпадает с этой строкой и одновременно является ее суффиксом}}<br />
<br />
Префикс-функция строки <tex>s</tex> {{---}} функция <tex>\pi(s, i) = \max\limits_{k = 1..i - 1} \{ 0, k : </tex> <tex>s[1..k] = s[i - k + 1..i] \}</tex>.<br />
<br />
Здесь и далее считаем, что символы в строках нумеруются с <tex>1</tex>.<br />
==Наивный алгоритм==<br />
Наивный алгоритм вычисляет префикс функцию непосредственно по определению, сравнивая префиксы и суффиксы строк.<br />
<br />
===Псевдокод===<br />
'''Prefix_function''' (<tex>s</tex>)<br />
<tex>\pi</tex> = [0,..,0]<br />
'''for''' i = 1 '''to''' n<br />
'''for''' k = 1 '''to''' i - 1<br />
'''if''' s[1..k] == s[i - k + 1..i]<br />
<tex>\pi</tex>[i] = k<br />
'''return''' <tex>\pi</tex><br />
<br />
===Пример===<br />
Рассмотрим строку abcabcd, для которой значение префикс-функции равно <tex>[0,0,0,1,2,3,0]</tex>.<br />
{| class="wikitable"<br />
! Шаг || Строка || Значение функции<br />
|-<br />
| <tex>1</tex> || a || 0<br />
|-<br />
| <tex>2</tex> || ab || 0<br />
|-<br />
| <tex>3</tex> || abc || 0<br />
|-<br />
| <tex>4</tex> || abca || 1<br />
|-<br />
| <tex>5</tex> || abcab || 2<br />
|-<br />
| <tex>6</tex> || abcabc || 3<br />
|-<br />
| <tex>7</tex> || abcabcd || 0<br />
|}<br />
<br />
===Время работы===<br />
Всего <tex>O(n^2)</tex> итераций цикла, на каждой из который происходит сравнение строк за <tex>O(n)</tex>, что дает в итоге <tex>O(n^3)</tex>.<br />
<br />
==Эффективный алгоритм==<br />
Вносятся несколько важных замечаний:<br />
*Заметим, что <tex>\pi[i + 1] \le \pi[i] + 1</tex>. Чтобы показать это, рассмотрим суффикс,оканчивающийся на позиции <tex>i + 1</tex> и имеющий длину <tex>\pi[i + 1]</tex>, удалив из него последний символ, мы получим суффикс, оканчивающийся на позиции <tex>i</tex> и имеющий длину <tex>\pi[i + 1] - 1</tex>, следовательно неравенство <tex>\pi[i + 1] > \pi[i] + 1</tex> неверно.<br />
*Избавимся от явных сравнений строк. Пусть мы вычислили <tex>\pi[i]</tex>, тогда, если <tex>s[i + 1] = s[\pi[i]]</tex>, то <tex>\pi[i + 1] = \pi[i] + 1</tex>. Если окажется, что <tex>s[i + 1] \ne s[\pi[i]]</tex>, то нужно попытаться попробовать подстроку меньшей длины. Для этого подберем такое <tex>k</tex>, что <tex>k = \pi(i) - 1</tex>. Делаем это следующим образом. За исходное <tex>k</tex> необходимо взять <tex>\pi(i - 1)</tex>, что следует из первого пункта. В случае, когда символы <tex>s[k+1]</tex> и <tex>s[i]</tex> не совпадают, <tex>\pi(k)</tex> {{---}} следующее потенциальное наибольшее значение <tex>k</tex>, что видно из рисунка. Последнее утверждение верно, пока <tex>k>0</tex>, что позволит всегда найти его следующее значение. Если <tex>k=0</tex>, то <tex>\pi(i)=1</tex> при <tex>s[i] = s[1]</tex> , иначе <tex>\pi(i)=0</tex>.<br />
<br />
[[Файл:Prefix2.jpg]]<br />
===Псевдокод===<br />
'''Prefix_function''' (<tex>s</tex>)<br />
<tex>\pi</tex>[1] = 0<br />
k = 0<br />
'''for''' i = 2 '''to''' n<br />
'''while''' k > 0 && s[i] != s[k + 1]<br />
k = <tex>\pi</tex>[k]<br />
'''if''' s[i] == s[k + 1]<br />
k++<br />
<tex>\pi</tex>[i] = k<br />
'''return''' <tex>\pi</tex><br />
<br />
===Время работы===<br />
Время работы алгоритма составит <tex>O(n)</tex>. Для доказательства этого нужно заметить, что итоговое количество итераций цикла <tex>while</tex> определяет асимптотику алгоритма. Теперь стоит отметить, что <tex>k</tex> увеличивается на каждом шаге не более чем на единицу, значит максимально возможное значение <tex>k = n - 1</tex>. Поскольку внутри цикла <tex>while</tex> значение <tex>k</tex> лишь уменьшается, получается, что <tex>k</tex> не может суммарно уменьшиться больше, чем <tex>n-1</tex> раз. Значит цикл <tex>while</tex> в итоге выполнится не более <tex>n</tex> раз, что дает итоговую оценку времени алгоритма <tex>O(n)</tex>.<br />
<br />
==Построение строки по префикс-функции==<br />
===Постановка задачи=== <br />
Восстановить строку по префикс-функции за <tex>O(N)</tex>, считая алфавит неограниченным.<br />
<br />
===Описание алгоритма===<br />
<br />
Пусть в массиве <tex>p</tex> хранятся значения префикс-функции, в <tex>s</tex> будет записан ответ. Пойдем по массиву <tex>p</tex> слева направо.<br />
<br />
Пусть мы хотим узнать значение <tex>s[i]</tex>. Для этого посмотрим на значение <tex>p[i]</tex>: если <tex>p[i] =0</tex> тогда в <tex>s[i]</tex> запишем новый символ, иначе <tex>s[i] = s[p[i]]</tex>. Обратим внимание, что <tex>s[p[i]]</tex> нам уже известно, так как <tex>p[i] < i</tex>.<br />
<br />
=== Реализация ===<br />
'''string''' buildFromPrefix('''int'''[] p):<br />
s = "" <br />
'''for''' i = 0 '''to''' p.length - 1<br />
'''if''' p[i] == 0 <br />
s += new character<br />
'''else'''<br />
s += s[p[i]]<br />
'''return''' s<br />
<br />
===Доказательство корректности алгоритма===<br />
Докажем, что если нам дали корректную префикс-функцию, то наш алгоритм построит строку с такой же префикс-функцией. Также заметим, что строк с такой префикс-функцией может быть много, и алгоритм строит только одну из них.<br />
<br />
Пусть <tex>p</tex> данная префикс-функция, <tex>s'</tex> правильная строка, строку <tex>s</tex> построил наш алгоритм, <tex> q </tex> массив значений префикс-функции для <tex>s</tex>.<br />
<br />
Докажем корректность индукцией по длине массива префикс-функции полученной строки. Для начала заметим, что на предыдущие значения массива <tex> q </tex> прибавление нового символа не влияет, так как при подсчёте префикс-функции на <tex> i </tex>-ой позиции рассматриваются символы на позициях не больше <tex> i </tex>. Поэтому достаточно показать, что очередное значение префикс-функции будет вычислено правильно.<br />
* База очевидна для строки длины <tex>1</tex>.<br />
* Переход: пусть до <tex>n</tex>-ой позиции мы построили строку, что <tex>p[1..n - 1] = q[1..n - 1]</tex>. Возможны два случая:<br />
** <tex>p[n] = 0</tex>. Тогда мы добавляем новый символ, поэтому <tex>q[n]</tex> тоже будет равно <tex>0</tex>. <br />
** <tex>p[n] > 0</tex>. По свойствам префикс-функции <tex> s'[p[n]] = s'[n] </tex> {{---}} суффикс и префикс строки <tex> s' </tex> длины <tex> p[n] </tex> продолжаются одним символом, значит, надо на текущую позицию строки <tex> s </tex> поставить символ <tex> s[p[n]] </tex>. Если значение префикс-функции увеличивается, значит, текущим символом продолжается префикс длины <tex> p[n - 1] </tex>, а из свойств следует, что <tex> p[n - 1] \geqslant p[n] - 1 </tex>. По предположению индукцию значение <tex> q[n - 1] </tex> будет вычислено верно. А если значение префикс-функции не увеличивается, значит, символ <tex> s[n] </tex> должен продолжить префикс меньшей длины, а в текущее значение префикс-функции запишется как раз длина нового бордера. Для этого будут использованы значения префикс-функции с меньшими индексами, которые посчитаны верно, опять же по препдположению индукции.<br />
<br />
== Литература ==<br />
* Кормен Т., Лейзерсон Ч., Ривест Р. Алгоритмы: построение и анализ. {{---}} 2-е изд. {{---}} М.: Издательский дом «Вильямс», 2007. {{---}} С. 1296 ISBN 978-5-8459-0857-5<br />
<br />
[[Категория: Алгоритмы и структуры данных]]<br />
[[Категория: Поиск подстроки в строке]]</div>217.197.6.98http://neerc.ifmo.ru/wiki/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A8%D1%82%D0%BE%D1%80-%D0%92%D0%B0%D0%B3%D0%BD%D0%B5%D1%80%D0%B0_%D0%BD%D0%B0%D1%85%D0%BE%D0%B6%D0%B4%D0%B5%D0%BD%D0%B8%D1%8F_%D0%BC%D0%B8%D0%BD%D0%B8%D0%BC%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D0%B3%D0%BE_%D1%80%D0%B0%D0%B7%D1%80%D0%B5%D0%B7%D0%B0&diff=34981Алгоритм Штор-Вагнера нахождения минимального разреза2014-01-03T08:18:46Z<p>217.197.6.98: </p>
<hr />
<div><br />
== Необходимые определения ==<br />
<tex>G</tex> - неориентированный взвешенный граф с <tex>n</tex> вершинами и <tex>m</tex> ребрами.<br />
{{Определение |definition=<br />
'''Разрезом''' называется такое разбиение множества <tex>V</tex> на два подмножества <tex>A</tex> и <tex>B</tex>, что:<br />
* <tex>A, B \subset V</tex>;<br />
* <tex>A, B \neq \emptyset</tex>;<br />
* <tex>A \cap B = \emptyset</tex>;<br />
* <tex>A \cup B = V</tex>.<br />
}}<br />
<br />
{{Определение |definition=<br />
'''Весом разреза''' называется сумма весов рёбер, проходящих через разрез, т.е. таких рёбер, один конец которых принадлежит <tex>A</tex>, а второй конец - <tex>B</tex>.<br />
* <tex>w(A, B) =</tex> <tex dpi = "140">\sum\limits_{uv \in E, u \in A, v \in B} w(u, v)</tex><br />
}}<br />
<br />
Эту задачу называют "глобальным минимальным разрезом". Глобальный минимальный разрез равен минимуму среди разрезов минимальной стоимости по всевозможным парам исток-сток. Хотя эту задачу можно решить с помощью любого алгоритма нахождения максимального потока (запуская его <tex>O(n^2)</tex> раз для всевозможных пар истока и стока), однако ниже описан гораздо более простой и быстрый алгоритм, предложенный Матильдой Штор (Mechthild Stoer) и Франком Вагнером (Frank Wagner) в 1994 г.<br />
<br />
В общем случае допускаются петли и кратные рёбра, все кратные рёбра можно заменить одним ребром с их суммарным весом а петли не влияют на решение. Поэтому будем считать, что кратных ребер и петель во входном графе нет.<br />
<br />
== Алгоритм == <br />
Идея алгоритма довольно проста. Будем <tex>n-1</tex> раз повторять следующий процесс: находить минимальный разрез между какой-нибудь парой вершин <tex>s</tex> и <tex>t</tex>, а затем объединять эти две вершины в одну (создавать новую вершину, список смежности которой равен объединению списков смежности <tex>s</tex> и <tex>t</tex>). В конце концов, после <tex>n-1</tex> итерации, останется одна вершина. После этого ответом будет являться минимальный среди всех <tex>n-1</tex> найденных разрезов. Действительно, на каждой <tex>i</tex>-ой стадии найденный минимальный разрез <tex>\langle A,B \rangle</tex> между вершинами <tex>s_i</tex> и <tex>t_i</tex> либо окажется искомым глобальным минимальным разрезом, либо же, напротив, вершины <tex>s_i</tex> и <tex>t_i</tex> невыгодно относить к разным множествам, поэтому мы ничего не ухудшаем, объединяя эти две вершины в одну.<br />
<br />
Следовательно нам необходимо для данного графа найти минимальный разрез между какой-нибудь парой вершин <tex>s</tex> и <tex>t</tex>. Для этого вводим некоторое множество вершин <tex>A</tex>, которое изначально содержит единственную произвольную вершину <tex>s</tex>. На каждом шаге находится вершина, наиболее сильно связанная с множеством <tex>A</tex>, т.е. вершина <tex>v \not\in A</tex>, для которой следующая величина <tex dpi = "140">w(v,A) = \sum\limits_{(v,u) \in E, \atop u \in A} w(v,u)</tex> максимальна (максимальна сумма весов рёбер, один конец которых <tex>v</tex>, а другой принадлежит <tex>A</tex>). Этот процесс завершится, когда все вершины перейдут в множество <tex>A</tex>.<br />
<br />
<br />
<br />
minCut(граф G):<br />
v[i] - список вершин, которые были сжаты в i-тую (сначала заполняется i);<br />
for i = 1..n-1<br />
A = Ø;<br />
fill(w, 0);<br />
for j = 1..n-1<br />
s = {s <tex>\in</tex> V | s <tex>\notin</tex> A, w[s] - max};<br />
if (j != n-1)<br />
A += s;<br />
пересчитываем связность w[i] для остальных вершин; <br />
prev = s;<br />
else<br />
if (w[s] < minCost)<br />
minCost = w[s];<br />
minCut = v[s];<br />
s' = s <tex>\cup</tex> prev;<br />
return minCut - список вершин в минимальном разрезе;<br />
<br />
== Корректность алгоритма ==<br />
{{Теорема<br />
|statement=<br />
Если добавить в множество <tex>A</tex> по очереди все вершины, каждый раз добавляя вершину, наиболее сильно связанную с <tex>A</tex>, то пусть предпоследняя добавленная вершина {{---}} <tex>s</tex>, а последняя {{---}} <tex>t</tex>. Тогда минимальный <tex>s</tex>-<tex>t</tex> разрез состоит из единственной вершины {{---}} <tex>t</tex><br />
|proof=<br />
Рассмотрим произвольный <tex>s</tex>-<tex>t</tex> разрез <tex>C</tex> и покажем, что его вес не может быть меньше веса разреза, состоящего из единственной вершины <tex>t</tex>:<br />
<br />
: <tex dpi = '130'>w (\{t\}) \le w (C)</tex>. <br />
<br />
Пусть <tex>v</tex> - вершина, которую мы хотим добавить в <tex>A</tex>, тогда <tex>A_v</tex> - состояние множества <tex>A</tex> в этот момент. Пусть <tex>C_v</tex> - разрез множества <tex>A_v \cup v</tex>, индуцированный разрезом <tex>C</tex>. Вершина <tex>v</tex> - активная, если она и предыдущая добавленная вершина в <tex>A</tex> принадлежат разным частям разреза <tex>C</tex>, тогда для любой такой вершины:<br />
<br />
: <tex dpi = '130'>w (v, A_v) \le w (C_v)</tex>. <br />
<br />
<tex>t</tex> - активная вершина, для нее выполняется:<br />
<br />
: <tex dpi = '130'>w (t,A_t) \le w (C_t)</tex> <br />
: <tex dpi = '130'>w (t,A_t) = w (\{t\}), w (C_t) = w (C)</tex><br />
<br />
Получили утверждение теоремы.<br />
Для доказательства воспользуемся методом математической индукции.<br />
Для первой активной вершины <tex>v</tex> это неравенство верно, так как все вершины <tex>A_v</tex> принадлежат одной части разреза, а <tex>v</tex> - другой. Пусть неравенство выполнено для всех активных вершин до <tex>v</tex>, включая <tex>v</tex>, докажем его для следующей активной вершины <tex>u</tex>.<br />
<br />
: <tex dpi = '130'> w (u,A_u) \equiv w (u,A_v) + w (u,A_u \setminus A_v)</tex> (*)<br />
<br />
Заметим, что <br />
<br />
: <tex dpi = '130'>w (u,A_v) \le w (v,A_v)</tex> (**)<br />
<br />
вершина <tex>v</tex> имела большее значение <tex>w</tex>, чем <tex>u</tex>, так как была добавлена в <tex>A</tex> раньше.<br />
По предположению индукции:<br />
<br />
: <tex dpi = '130'>w (v,A_v) \le w (C_v)</tex><br />
<br />
Следовательно из (**):<br />
<br />
: <tex dpi = '130'>w(u,A_v) \le w(C_v)</tex><br />
<br />
А из (*) имеем:<br />
<br />
: <tex dpi = '130'>w (u,A_u) \le w (C_v) + w (u,A_u \setminus A_v)</tex> <br />
<br />
Вершина <tex>u</tex> и <tex>A_u \setminus A_v</tex> находятся в разных частях разреза <tex>C</tex>, значит <tex>w (u,A_u \setminus A_v)</tex> равна сумме весов ребер, которые не входят в <tex>C_v</tex>, но входят в <tex>C_u</tex>.<br />
<br />
: <tex dpi = '130'>w (u,A_u) \le w (C_v) + w (u,A_u \setminus A_v) \le w (C_u)</tex><br />
<br />
Что и требовалось доказать.<br />
}}<br />
<br />
== Асимптотика ==<br />
#Нахождение вершины с наибольшей <tex>w</tex> за <tex>O (n)</tex>, <tex>n-1</tex> фаза по <tex>n-1</tex> итерации в каждой. В итоге имеем <tex>O (n^3)</tex><br />
#Если использовать фибоначчиевы кучи для нахождения вершины с наибольшей <tex>w</tex>, то асимптотика составит <tex>O (nm + n^2 \log n)</tex><br />
#Если использовать двоичные кучи, то асимптотика составит <tex>O (nm \log n + n^2)</tex><br />
<br />
== Применение ==<br />
Нахождение разреза минимальной стоимости является основой в одном из методов сегментации изображений (сегментацией изображения называется разбиение его на некоторые области, непохожие по некоторому признаку). <br />
<br />
Изображение представляется в виде взвешенного графа, вершинами которого являются точки изображения (скорее всего пиксели но может и области чуть больше, от этого зависит качество сегментации, а также скорость ее построения). Вес ребра представляет отражает "разницу" между точками(расстояние по некоторой введенной метрике). Разбиение изображения на однородные области сводится к задаче поиска минимального разреза в графе. Специально для такого рода задач был предложен метод нахождения разреза минимальной стоимости [https://www.google.ru/url?sa=t&rct=j&q=&esrc=s&source=web&cd=2&ved=0CDQQFjAB&url=http%3A%2F%2Fwww.cs.berkeley.edu%2F~malik%2Fpapers%2FSM-ncut.pdf&ei=cP2-UuqhAuSJ4gTnhYCwAg&usg=AFQjCNFn9GZPlFjDUgDofCScu6Wm47qMWQ&sig2=Yufd8LreEQKHe3NGnFVm7A&bvm=bv.58187178,d.bGE&cad=rjt Метод Normalized Cut Normalized Cut(J. Shi, J. Malik (1997))]<br />
<br />
== Источники ==<br />
* [http://e-maxx.ru/bookz/files/stoer_wagner_mincut.pdf Mechthild Stoer, Frank Wagner. A Simple Min-Cut Algorithm]<br />
* [http://e-maxx.ru/algo/stoer_wagner_mincut Алгоритм Штор-Вагнера]<br />
* [http://cgm.computergraphics.ru/content/view/147 Методы сегментации изображения]<br />
<br />
== Ссылки ==<br />
*[[Алгоритм Каргера для нахождения минимального разреза]]<br />
*[https://www.google.ru/url?sa=t&rct=j&q=&esrc=s&source=web&cd=2&ved=0CDQQFjAB&url=http%3A%2F%2Fwww.cs.berkeley.edu%2F~malik%2Fpapers%2FSM-ncut.pdf&ei=cP2-UuqhAuSJ4gTnhYCwAg&usg=AFQjCNFn9GZPlFjDUgDofCScu6Wm47qMWQ&sig2=Yufd8LreEQKHe3NGnFVm7A&bvm=bv.58187178,d.bGE&cad=rjt Метод Normalized Cut]<br />
<br />
[[Категория: Алгоритмы и структуры данных]]<br />
[[Категория: Задача о максимальном потоке]]</div>217.197.6.98