Skip to content

Commit 4177902

Browse files
committed
Ещё немного про производные для регулярок: построение автомата по регулярке.
1 parent 2d13984 commit 4177902

File tree

2 files changed

+136
-15
lines changed

2 files changed

+136
-15
lines changed

tex/RegularLanguages.tex

Lines changed: 135 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ \section{Конечные автоматы}
118118
Заметим, что функцию переходов можно представить разными способами: это может быть множество троек вида $(q_i, t, q_j)$, матрица, или же граф с метками на рёбрах
119119
\[G = \langle Q, \{(q_i,t,q_j \mid q_j \in \delta(q_i,t))\}, \Sigma \rangle.\]
120120
В дальнейшем мы чаще всего будем использовать представление автомата в виде графа.
121-
Чтобы такое представление было полным, в графе отдельно обозначают стартовые и финальные состояния, как это показано на рисунке~\ref{fa:fa_example}.
121+
Чтобы такое представление было полным, в графе отдельно обозначают стартовые и финальные состояния, как это показано на рисунке~\ref{fig:nfa_example}.
122122

123123
Так как нас интересуют конечные автоматы в контексте языков, то будем говорить, что на ленте автомата записано какое-то слово (или строка).
124124
Иными словами, будем говорить, что автомат принимает на вход слово или строку.
@@ -138,7 +138,7 @@ \section{Конечные автоматы}
138138
\delta(0,\varepsilon) =& \varnothing & \delta(1,\varepsilon) =& \{0\} & \delta(2,\varepsilon) =& \varnothing.
139139
\end{alignat*}
140140
\end{itemize}
141-
Тогда его можно представить в виде графа как показано на рисунке~\ref{fa:fa_example}.
141+
Тогда его можно представить в виде графа как показано на рисунке~\ref{fig:nfa_example}.
142142
\begin{marginfigure}
143143
\begin{center}
144144
\begin{tikzpicture}
@@ -154,7 +154,7 @@ \section{Конечные автоматы}
154154
\end{tikzpicture}
155155
\end{center}
156156
\caption{Пример конечного автомата в котором состояние $0$~--- стартовое, а состояние $1$~--- финальное}
157-
\label{fa:fa_example}
157+
\label{fig:nfa_example}
158158
\end{marginfigure}
159159
\end{example}
160160

@@ -216,7 +216,7 @@ \section{Конечные автоматы}
216216
Это поможет при написании реального интерпретатора: будем отслеживать уже посещённые (обработанные) конфигурации\sidenote{Техника, аналогичная той, что применяется в обходах графов (обход в ширину, обход в глубину) для того, чтобы избежать повторного посещения вершин и, как следствие, зацикливания обхода. Более того, она типична для алгоритмов с рабочим множеством.}.
217217

218218
\begin{example}
219-
Рассмотрим пример работы конечного автомата, представленного на рисунке~\ref{fa:fa_example}, на входной цепочке \texttt{abb}.
219+
Рассмотрим пример работы конечного автомата, представленного на рисунке~\ref{fig:nfa_example}, на входной цепочке \texttt{abb}.
220220
В данном автомате стартовое состояние одно, потому множество стартовых конфигураций состоит из одной конфигурации:
221221
\[
222222
C_S = \{(0,\texttt{abb})\}.
@@ -262,7 +262,7 @@ \section{Конечные автоматы}
262262
Во-первых, стартовое состояние ровно одно.
263263
Во-вторых, функция переходов не допускает переходов по $\varepsilon$ и из любого состояния существует не более одного перехода по конкретному символу.
264264

265-
\begin{example}
265+
\begin{example}\label{example:DFA}
266266
Пусть задан детерминированный автомат $M=\langle Q, q_S, Q_F, \delta, \Sigma\rangle$, где
267267
\begin{itemize}
268268
\item $Q = \{0,1,2\}$;
@@ -409,22 +409,113 @@ \section{Производные для регулярных языков}
409409

410410
\section{Построение конечного автомата по регулярному выражению}
411411

412-
На производных%
412+
Конечные автоматы и регулярные выражения --- два способа задать оди и тот же класс языков.
413+
Для того, чтобы убедиться в их равной выразительной силе, для начала, научимся строить конечный автомат по регулярному выражению.
414+
Использование производных позволит нам сразу построить полный детерминированный автомат%
413415
\sidenote{Существуют и другие алгоритмы построения автомата по регулярному выражению. Например, алгоритм Томпсона~\cite{10.1145/363347.363387} или алгоритм Глушкова~\cite{Glushkov1961}.
414416
Как правило, каждый алгоритм строит автомат со специфичными свойствами: детерминированный или нет, с $\varepsilon$-переходами или без них.}.
415417

416-
Каждое состояние соответствует языку, распозноваемому из этого состояния.
418+
Состояниям автомата будут соответствовать регулярные выражения: если (в результирующем автомате) состояние выбрать стартовым, то получившийся автомат будет распознавать язык, задаваемый соответствующим регулярным выражением.
419+
Чтобы автомат был минимальным нам будет необходимо проверять на эквивалентность языки, задаваемые регулярными выражениями и не дублировать состояния.
420+
Хотя задача проверки эквивалентности регулярных языков разрешима, на практике, в рамках данного алгоритма, чаще пользуются более быстро проверяемой структурной эквивалентностью~\sidenote{На деле, при проверке структурной эквивалентности дополнительно пользуются знаниями об ассоциативности некоторых операций и другимим свойствами выражений.} регулярных выражений~\sidecite{OWENS_REPPY_TURON_2009}.
421+
417422

418-
Стартовое --- исходное регулярное выражение. Далее в цикле.
423+
Таким образом, получится алгоритм с рабочим множеством, который состоит в следующем.
424+
В качестве стартового состояния возьмём состояние, соответствующее исходному регулярному выражению.
425+
В рабочее множество добавим стартовое состояние.
426+
Далее, пока рабочее множество не пусто, в цикле повторяем следующие шаги.
427+
\btgin{enumerate}
428+
\item Берём из множества очередное состояние $q$, соответствующее регулярному выражению $R_q$.
429+
\item Для всех символов из алфавита вычисляем производные $R_q$:.
430+
\item Для всех построенных состояний проверяем, существует ли уже
431+
\end{enumerate}
419432
Берём очередное необработанное состояние, по каждому символу из алфавита берём производную соответствующего ему регулярного выражения.
420433
Добавляем получившееся состояние (если ещё не было такого).
421434
Если состояние isNullable, то помечаем его финальным.
422435
Добавляем ребро.
423436

424-
В результате получим полный детерминированный конечный автомат\sidenote{Формально можно получить минимальный, но на практике это сложно. Но часто всё же достаточно близко к минимальному получается.}.
437+
В результате получим полный детерминированный конечный автомат\sidenote{Формально, если честно проверять равенство языков, можно получить минимальный, но на практике это сложно. Но часто всё же достаточно близко к минимальному получается.}.
425438

426-
Примеры.
439+
\begin{example}
440+
Построим автомат по регулярному выражению $a(a \mid (b \ b))^*$.
441+
Состояние, помеченное данным выражением назначим стартовым и вычислим производные по символам.
442+
Так как $\emph{IsNull}(a(a \mid (b \ b))^*) = \emph{false}$, финальным данное состояние не является.
443+
Вычислим производные исходного выражения по всем символам из алфавита.
444+
Проверим, что получившиеся выражения задают различные языки и добавим соответствующие переходы.
445+
Одно из состояний соответствует пустому языку.
446+
Данное состояние будет <<дьявольским>> или <<стоком>>.
447+
Второе, помеченное $(a \mid (b \ b))^*$, соответствует языку, содержащему пустую цепочку.
448+
То есть это состояние должно быть финальным.
449+
В результате данного шага мы получим автомат, представленный на рисунке~\ref{fig:regexp_to_dfa_example_step_1}.
450+
451+
Продолжаем работу.
452+
Возьмём производные для двух новых состояний и проделаем все соответствующие процедуры.
453+
Уже видно, почему одно из состояний (соответствующее пустому языку), является <<дьявольским>>: попав в него однажды, остаёмся в нём навсегда.
454+
Обработаем аналогичным образом новые состояния. Итог~\ref{fig:regexp_to_dfa_example_step_2}
455+
456+
Обработаем аналогичным образом новые состояния. Итог~\ref{fig:regexp_to_dfa_example_step_3}
457+
\begin{marginfigure}
458+
\begin{center}
459+
\scalebox{0.64}{
460+
\begin{tikzpicture}
461+
\node[elliptic state,initial] (q_0) {$a(a \mid (b \ b))^*$};
462+
\node[elliptic state,accepting] (q_1) [right = of q_0] {$(a \mid (b \ b))^*$};
463+
\node[state] (q_2) [above = of q_0] {$\varnothing$};
464+
\path[->]
465+
(q_0) edge[bend right, below] node {$a$} (q_1)
466+
(q_0) edge[bend left, left] node {$b$} (q_2);
467+
\end{tikzpicture}}
468+
\end{center}
469+
\caption{Пример детерминированного конечного автомата в котором состояние $0$}
470+
\label{fig:regexp_to_dfa_example_step_1}
471+
\end{marginfigure}
472+
\begin{marginfigure}
473+
\begin{center}
474+
\scalebox{0.64}{
475+
\begin{tikzpicture}
476+
\node[elliptic state,initial] (q_0) {$a(a \mid (b \ b))^*$};а
477+
\node[elliptic state,accepting] (q_1) [right = of q_0] {$(a \mid (b \ b))^*$};
478+
\node[state] (q_2) [above = of q_0] {$\varnothing$};
479+
\node[elliptic state] (q_3) [above = of q_1] {$b(a \mid (b \ b))^*$};
480+
\path[->]
481+
(q_0) edge[bend right, below] node {$a$} (q_1)
482+
(q_0) edge[bend left, left] node {$b$} (q_2)
483+
(q_1) edge[bend left, left] node {$b$} (q_3)
484+
(q_2) edge[loop left, left] node {$b,a$} (q_2)
485+
(q_1) edge[loop below, above] node {$a$} (q_1);
486+
\end{tikzpicture}
487+
}
488+
\end{center}
489+
\caption{Пример детерминированного конечного автомата в котором состояние}
490+
\label{fig:regexp_to_dfa_example_step_2}
491+
\end{marginfigure}
492+
\begin{marginfigure}
493+
\begin{center}
494+
\scalebox{0.64}{
495+
\begin{tikzpicture}
496+
\node[elliptic state,initial] (q_0) {$a(a \mid (b \ b))^*$};
497+
\node[elliptic state,accepting] (q_1) [right = of q_0] {$(a \mid (b \ b))^*$};
498+
\node[state] (q_2) [above = of q_0] {$\varnothing$};
499+
\node[elliptic state] (q_3) [above = of q_1] {$b(a \mid (b \ b))^*$};
500+
\path[->]
501+
(q_0) edge[bend right, below] node {$a$} (q_1)
502+
(q_0) edge[bend left, left] node {$b$} (q_2)
503+
(q_1) edge[bend left, left] node {$b$} (q_3)
504+
(q_2) edge[loop left, left] node {$b,a$} (q_2)
505+
(q_1) edge[loop below, above] node {$a$} (q_1)
506+
(q_3) edge[bend left, right] node {$b$} (q_1)
507+
(q_3) edge[bend left, below] node {$a$} (q_2);
508+
\end{tikzpicture}
509+
}
510+
\end{center}
511+
\caption{Пример детерминированного конечного автомата в }
512+
\label{fig:regexp_to_dfa_example_step_3}
513+
\end{marginfigure}
514+
\end{example}
427515

516+
В результате построения мы получили автомат, очень похожий на автомат из примера~\ref{example:DFA}.
517+
Единственная разница заключается в том, что автомат из примера не является полным.
518+
Само же сходство не случайно: автоматы действительно задают один и тот же регулярный язык.
428519

429520
\section{Построение регулярного выражения по конечному автомату}
430521

@@ -518,7 +609,6 @@ \section{Построение регулярного выражения по к
518609

519610
\end{tikzpicture}
520611
\end{center}
521-
522612
\caption{Автомат перед устранением состояния $v$}
523613
\label{fa:fa3}
524614

@@ -559,8 +649,8 @@ \section{Построение регулярного выражения по к
559649
\end{figure}
560650

561651
После завершения основного цикла (когда в автомате осталось ровно два состояния), необходимо ещё раз объединить параллельные рёбра.
562-
Общий вид получившегося автомата представлен на рисунке~\ref{fa:fa5}.
563-
По такому автомату строим выражение вида \[(R_1^* \cdot (R_2 \cdot R_3^* \cdot R_4 )^* \cdot R_2 \cdot R_3^*,\] которое и будет ответом.
652+
Общий вид получившегося автомата представлен на рисунке~\ref{fig:fa_to_regexp_final}.
653+
По такому автомату строим выражение вида \[(R_1^* \cdot (R_2 \cdot R_3^* \cdot R_4 \cdot R_1^*)^* \cdot R_2 \cdot R_3^*,\] которое и будет ответом.
564654
\begin{marginfigure}
565655

566656
\begin{center}
@@ -579,12 +669,42 @@ \section{Построение регулярного выражения по к
579669
\end{center}
580670

581671
\caption{Общий вид автомата после завершения основного цикла}
582-
\label{fa:fa5}
672+
\label{fig:fa_to_regexp_final}
583673

584674
\end{marginfigure}
585675

586676
\begin{example}
587-
Примеры.
677+
Построим регулярное выражение по автомату, представленному на рисунке~\ref{fig:nfa_example}.
678+
В автомате уже одно финальное и одно стартовое состояние.
679+
При этом, существует всего одно состояние, которое не является ни финальным, ни стартовым.
680+
Это состояние \circled{2}.
681+
Его и устраним.
682+
683+
После устранения состояния \circled{2} получим автомат, представленный на рисунке~\ref{fig:nfa_to_regexp}.
684+
Теперь наш автомат принял вид, соответствующий представленному на рисунке~\ref{fig:fa_to_regexp_final}, и можно выписать результирующее регулярное выражение, которое будет иметь следующий вид%
685+
\sidenote{Конесно, получившесея выражение не <<минимально>>: дослаточно легко можно придумать более простое и компактное регулярное выражение, задающее тот же самый язык.
686+
Но алгоритм нам и не обещал ни каких дополнительных свойств получившегося выражения.}:
687+
\[
688+
a^*(a \ (b \ b)^* \ \varepsilon \ a^*)^* \ a \ (b \ b)^* =
689+
a^*(a \ (b \ b)^* \ a^*)^* \ a \ (b \ b)^*.
690+
\]
691+
692+
\begin{marginfigure}
693+
\begin{center}
694+
\begin{tikzpicture}
695+
\node[state,initial] (q_0) {$0$};
696+
\node[state,accepting] (q_1) [right = of q_0] {$1$};
697+
\path[->]
698+
(q_0) edge[bend left, above] node {$a$} (q_1)
699+
(q_0) edge[loop above, above] node {$a$} (q_0)
700+
(q_1) edge[loop above, above] node {$bb$} (q_1)
701+
(q_1) edge[bend left, below] node {$\varepsilon$} (q_0);
702+
\end{tikzpicture}
703+
\end{center}
704+
\caption{Пример построения регулярного выражения по конечному автомату: финальное состояние автомата}
705+
\label{fig:nfa_to_regexp}
706+
\end{marginfigure}
707+
588708
\end{example}
589709

590710

tex/styles/tikz.tex

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,4 @@
2626
}
2727

2828
\tikzstyle{r_state} = [shape=rectangle, draw, minimum size=0.2cm]
29+
\tikzset{elliptic state/.style={draw,ellipse}}

0 commit comments

Comments
 (0)