Изменения

Перейти к: навигация, поиск
м
== Пустота ==Для различных операций с [[Регулярные языки: два определения и их эквивалентность|регулярными языками]] полезно знать некоторые их свойства. Как правило, в доказательствах этих свойств используется факт из теоремы Клини об эквивалентности [[Детерминированные_конечные_автоматы#Автоматные_языки | автоматных]] и [[Регулярные языки:_два определения_и_их_эквивалентность#REG1 | регулярных]] языков.
[[Регулярные языки: два определения и их эквивалентность|Регулярный язык]] является '''пустым''', если он не содержит ни одного слова. Язык, содержащий хотя бы одно слово, назовём '''непустым'''.== Пустота регулярного языка ==
{{УтверждениеОпределение|id=empty|definition=[[Регулярные языки: два определения и их эквивалентность|Регулярный язык]] называется '''пустым''' (англ. ''empty''), если он не содержит ни одного слова.}} {{Определение|id=nonempty|definition=[[Регулярные языки: два определения и их эквивалентность|Регулярный язык]] содержащий хотя бы одно слово, называется '''непустым''' (англ. ''nonempty'').}} {{Теорема
|id=
regEmpty
|proof=
Пусть язык содержит слово <tex>w\Rightarrow</tex>. Любой автомат <tex>A</tex>, задающий этот язык, должен допускать <tex>w</tex>. Тогда при переходе из стартового состояния <tex>A</tex> по символам <tex>w</tex> получится путь, оканчивающийся в одной из терминальных вершин.
:Пусть язык содержит слово <tex>w</tex>. Любой [[Детерминированные конечные автоматы| детерминированный конечный автомат]] <tex>A</tex>, задающий этот язык, должен допускать <tex>w</tex>. Тогда при переходе из стартового состояния <tex>A</tex> по символам <tex>w</tex> получится путь, оканчивающийся в одном из терминальных состояний.
<tex>\Leftarrow</tex>:Пусть в автомате существует путь из стартового состояния в одно из допускающих. Рассмотрим последовательность символов на рёбрахпереходах, образующих этот путь. Строка из этой последовательности допускается автоматом, а значит, принадлежит языку.
}}
Для определения пустоты языка по соответствующему ему автомату проще всего использовать алгоритм [[Обход в глубину, цвета вершин|обхода в глубину]]. Язык не является пустым тогда и только тогда, когда при поиске из стартового состояния автомата окажется достижимой хотя бы одна терминальная вершина.
==== Псевдокод ==Совпадение регулярных языков ==
boolean dfs(State v):{{Определение v.seen |id= trueequal if v.isFinal:|definition= return false for each State u in v.nextДва [[Регулярные языки: if !uдва определения и их эквивалентность|регулярных языка]] '''совпадают''' (англ.seen && !dfs(u''coincide''): return false, если любое слово или содержится в обоих языках, или не содержится ни в одном из них. return true}}
boolean isEmpty(Automaton a): for each State v in a: vДля проверки совпадения языков достаточно запустить алгоритм проверки [[Эквивалентность_состояний_ДКА|эквивалентности]] задающих их автоматов.seen = false return dfs(a.start)
=== Пример проверки на совпадение регулярных языков===
Для того, чтобы узнать равен ли язык своему замыканию Клини, нужно проверить на [[Эквивалентность_состояний_ДКА|эквивалентность]] автомат, задающий язык, и автомат, задающий замыкание Клини языка. Если автоматы эквивалентны, то язык равен своему замыканию Клини.
== Совпадение Включение одного регулярного языка в другой ==
Два {{Определение|id=inclusion|definition=[[Регулярные языки: два определения и их эквивалентность|регулярных языкаРегулярный язык]] '''совпадают''', если любое слово или содержится в обоих языках, или не содержится ни в одном из них. Пусть <tex>A_{1}</tex> и <tex>A_{2}</tex> - детерминированные конечные автоматы, соответствующие языкам <tex>L_{1}</tex> и <tex>L_{2}</tex> над одним алфавитом <tex>\Sigma</tex>, соответственно'''входит (включается)''' (англ. Совпадение языков на языке конечных автоматов (''эквивалентностьincluded'') означает, что любое слово, допустимое одним автоматом, допускается и другим. Назовём состояния в регулярный язык <tex>p_{1} \in A_{1}</tex> и <tex>p_{2} \in A_L_{2}</tex> '''различимыми''', если существует строка <tex>w</tex> из символов <tex>\Sigma</tex>любое слово, для которой выполняется  принадлежащее <tex>\langle p_{1}, w \rangle \rightarrow \langle t_L_{1}, \epsilon \rangle</tex>, принадлежит <tex>\langle p_L_{2}, w \rangle \rightarrow \langle u_{2}, \epsilon \rangle</tex>. или <tex>\langle p_{1}, w \rangle \rightarrow \langle u_{1}, \epsilon \rangle</tex>, <tex>\langle p_{2}, w \rangle \rightarrow \langle t_{2}, \epsilon \rangle</tex>,
где Пусть автомат <tex>s_{1}M_1</tex> задаёт язык <tex>L_1</tex>, а автомат <tex>M_2</tex>s_{2}— язык <tex>L_1 \cap L_2</tex> - стартовые состояния, . Для проверки включения <tex>t_{1}L_1</tex>, в <tex>t_{2}L_2</tex> - допускающие состояния, достаточно проверить [[Эквивалентность_состояний_ДКА|эквивалентность]] <tex>u_{1}M_1</tex>, и <tex>u_{2}M_2</tex> - недопускающие.
Все ''бесполезные'' состояния== Конечность регулярного языка, из которых не достигаются допускающие, не влияют на множество подсчёт числа слов, допускаемых автоматами, поэтому далее они рассматриваться не будут. Введём ''сток'' - специальное недопускающее состояние, переходы по всем символам из которого ведут в него самого. Все переходы исходного автомата, которые отсутствовали или вели в бесполезные состояния, направим в сток.==
{{Определение|id=== Алгоритм проверки языков на совпадение ==finite|definitionПервым шагом алгоритма является избавление автоматов от состояний, из которых недостижимы допускающие. Проще всего это реализовать обходом [[Обход в глубину, цвета вершин|в глубину]] или [[Обход в ширину|в ширину]] из допускающих состояний по обратным рёбрам. Все непосещённые состояния затем удаляются из автоматов, вместо них вводится описанный выше сток.  Пусть <tex>eq(u, v)</tex> - функция, принимающая пару состояний из первого и второго автоматов Регулярные языки: два определения и возвращающая некоторое значение булевского типа. Второй шаг алгоритма - установка <tex>eq(u, v)</tex> в <tex>false</tex> для всех пар <tex>\langle u, v \rangle</tex>, кроме <tex>\langle s_{1}, s_{2} \rangle</tex>. Также создаётся очередь, в которую помещается пара <tex>\langle s_{1}, s_{2} \rangle</tex>.  Третий шаг алгоритма - [[Обход в ширинуих эквивалентность|обход в ширинуРегулярный язык]]. Пусть на текущем шаге из очереди получена пара <tex>\langle u \in A_{1}, v \in A_{2} \rangle</tex>. Тогда для всех символов <tex>c \in \Sigma</tex> рассматриваются пары <tex>\langle uназывается '''конечным', v' \rangle : \delta_{1} (u, c) = u', \delta_{2} (v, c) = v'</tex>англ. Если <tex>eq(u', v')</tex> возвращает <tex>false</tex>, данное значение устанавливается в <tex>true</tex>, а в очередь добавляется пара <tex>\langle ufinite', v' \rangle</tex>.  {{Утверждение|id=regEqual|statement=Автоматы <tex>A_{1}</tex> и <tex>A_{2}</tex> эквивалентны тогда и только тогда, когда после окончания работы алгоритма не существует такой пары <tex>\langle u, v \rangle</tex>, что <tex>eq(u, v)</tex> возвращает <tex>true</tex> и ровно одно из <tex>\langle u, v \rangle</tex> допускающее. |proof=Пусть такой пары не существует. Возьмём произвольное слово <tex>w</tex> длины <tex>n</tex> и выпишем последовательность пар состояний <tex>\langle u_{i}, v_{i} \rangle</tex>: <tex>u_{0} = s_{1}, v_{0} = s_{2}</tex> и <tex>\forall i = 1 .. n</tex> справедливо <tex>\delta_{1} (u_{i-1}, s[i-1]) = u_{i}, \delta_{2} (v_{i-1}, s[i-1]) = v_{i}</tex>. Так как пара <tex>\langle u_{0}, v_{0} \rangle</tex> была в очереди, каждая из последующих пар в процессе алгоритма также побывала в очереди, значит, <tex>eq</tex> для них возвращает <tex>true</tex>. По предположению, или оба состояния <tex>\langle u_{n}, v_{n} \rangle</tex> допускающие в своих автоматах, или оба недопускающие. Таким образом, строка <tex>w</tex> или входит в оба языка, или не входит ни в один.  Пусть такая пара <tex>\langle u, v \rangle</tex> существует. Для определённости скажем, что <tex>u \in A_{1}</tex> - допускающее. Рассмотрим строку <tex>w</tex>, состоящую из символов, в результате переходов по которым из <tex>\langle s_{1}, s_{2} \rangle</tex> в процессе обхода в ширину <tex>eq(u, v)</tex> было установлено в <tex>true</tex>. Строка <tex>w</tex> допускается первым автоматом, но не допускается вторым, значит, автоматы не эквивалентныесли принадлежащее ему множество слов конечно.
}}
==== Псевдокод ====  void reverseDfs(State v): v.canReach = true for each State u in v.prev: if !u.canReach: reverseDfs(u)  void setSink(Automaton a): State sink = new State for each symbol c in a.alphabet: sink.next(c) = sink for each State v in a: if !v.canReach: v = sink  void bfs(Automaton a, Automaton b, boolean[][] eq) fill(eq, false) eq[a.start][b.start] = true Queue q = new Queue q.add((a.start, b.start)) while !q.isEmpty: (v, u) = q.remove() for each symbol c in a.alphabet: // a.alphabet == b.alphabet v' = v.next(c) u' = u.next(c) if !eq[v'][u']: eq[v'][u'] = true q.add((v', u'))  boolean areEqual(Automaton a, Automaton b) for each State v in a: v.canReach = false for each State v in a: if v.isFinal: reverseDfs(v) setSink(a) for each State v in b: v.canReach = false for each State v in b: if v.isFinal: reverseDfs(v) setSink(b) eq = new boolean[a.statesNumber][b.statesNumber] bfs(a, b, eq) for each State v in a: for each State u in b: if eq[v][u] && v.isFinal != u.isFinal: return false return true  == Включение == Регулярный язык <tex>L_{1}</tex> '''входит (включается)''' в регулярный язык <tex>L_{2}</tex>, если любое слово, принадлежащее <tex>L_{1}</tex>, принадлежит <tex>L_{2}</tex>. === Алгоритм проверки на включение === Алгоритм проверки <tex>L_{1}</tex> на включение в <tex>L_{2}</tex> идентичен алгоритму проверки их совпадения, кроме одной особенности. Могут существовать слова из <tex>L_{2}</tex>, не входящие в <tex>L_{1}</tex>, поэтому существование пар <tex>\langle v \in L_{1}, u \in L_{2} \rangle : eq(v, u) == true, v \notin T_{1}, u \in T_{2}</tex>, где <tex>T_{i}</tex> - множества допускающих состояний, не нарушает факт вхождения <tex>L_{1}</tex> в <tex>L_{2}</tex>. Таким образом, <tex>L_{1}</tex> не входит в <tex>L_{2}</tex> тогда и только тогда, когда после окончания работы алгоритма, идентичного алгоритму проверки на совпадение, не существует такой пары <tex>\langle v, u \rangle</tex>, что <tex>eq(v, u)</tex> возвращает <tex>true</tex>, <tex>v \in T_{1}, u \notin T_{2}</tex>. ==== Псевдокод ====  void reverseDfs(State v): v.canReach = true for each State u in v.prev: if !u.canReach: reverseDfs(u)  void setSink(Automaton a): State sink = new State for each symbol c in a.alphabet: sink.next(c) = sink for each State v in a: if !v.canReach: v = sink  void bfs(Automaton a, Automaton b, boolean[][] eq) fill(eq, false) eq[a.start][b.start] = true Queue q = new Queue q.add((a.start, b.start)) while !q.isEmpty: (v, u) = q.remove() for each symbol c in a.alphabet: // a.alphabet == b.alphabet v' = v.next(c) u' = u.next(c) if !eq[v'][u']: eq[v'][u'] = true q.add((v', u'))  boolean areEqual(Automaton a, Automaton b) for each State v in a: v.canReach = false for each State v in a: if v.isFinal: reverseDfs(v) setSink(a) for each State v in b: v.canReach = false for each State v in b: if v.isFinal: reverseDfs(v) setSink(b) eq = new boolean[a.statesNumber][b.statesNumber] bfs(a, b, eq) for each State v in a: for each State u in b: if eq[v][u] && v.isFinal && !u.isFinal: return false return true == Конечность языка, подсчёт числа слов == Язык называется '''конечным''', если принадлежащее ему множество слов конечно. {{УтверждениеТеорема
|id=
regFinite
|statement=
Автомат [[Детерминированные конечные автоматы|Детерминированный конечный автомат]] <tex>A_{1}</tex> задаёт конечный язык тогда и только тогда, когда в <tex>A_{1}</tex> не существует состояния <tex>v</tex>, для которого выполняются три условия:1) * <tex>v</tex> достижимо из стартового состояния <tex>s</tex>;2) * из <tex>v</tex> достижимо какое-либо из допускающих состояний;3) * из <tex>v</tex> по одному или более переходам достижимо <tex>v</tex>.
|proof=
Пусть такое состояние <tex>v</tex> существует, а строки <tex>x, y, z</tex> таковы, что <tex>\langle s, xyz \rangle \vdash ^{*} \langle v, yz \rangle \vdash ^{*} \langle v, z \rangle \vdash ^{*} \langle t, \epsilon \rangle</tex>, <tex>t</tex> - допускающее, <tex>y</tex> - непустая. Рассмотрим строки вида <tex>xy^{k}z, k \in \mathbb{N}</tex>. Их бесконечное количество, и все они, как легко увидеть, допускаются автоматом. Значит, язык бесконечен.
Пусть такого состояния не существует. Тогда любой путь из стартового состояния в какое-либо из допускающих является простым. Количество слов в языке равно количеству таких путей; количество путей, в свою очередь, ограничено <tex>n! (n-1)^{| \Sigma |^{n-1}</tex>, где <tex>n</tex> - количество состояний автомата: <tex>n!</tex> - количество перестановок состояний, <tex>(n-1)^{| \Sigma |^{n-1}</tex> - количество совокупностей переходов по символам между ними. Таким образом, язык конечен.
}}
=== Алгоритм нахождения числа слов в языке ===
Доказанное утверждение позволяет свести задачу поиска числа слов в языке к поиску количества различных путей в ациклическом графе. Сначала с помощью [[Обход в глубину, цвета вершин|обхода в глубину]] по обратным рёбрам определим '''полезные''' состояния, из которых достижимо хотя бы одно допускающее. Затем найдём любой цикл, состояния которого полезны, достижимый из старта; при нахождении констатируем бесконечность языка. Пусть язык конечен; тогда отсортируем автомат [[Использование обхода в глубину для топологической сортировки|топологически]]. Введём функцию <tex>\mathrm{paths} (v)</tex>, задающую число различных путей из <tex>s</tex> в <tex>v</tex>; <tex>\mathrm{paths} (s) = 1</tex>. Заметим, что если известны значения <tex>\mathrm{paths} (u)</tex> для всех <tex>u</tex>, из которых существует переход в <tex>v</tex>, то <tex>\mathrm{paths} (v) = \sum\limits_{u}\mathrm{paths} (u)</tex>. Количеством слов в языке будет сумма <tex>\mathrm{paths} (t)</tex> для всех допускающих <tex>t</tex>. Топологическую сортировку и поиск цикла можно объединить в один обход, но для наглядности они были разделены. ==== Псевдокод ====  Stack topSort(Automaton a): for each State v in a: v.seen = false Stack sorted = new Stack dfsSort(a.start, sorted) return sorted
void dfsSort(State v, Stack sorted): v== См.seen также == true for each State u in v.next: if !u.seen:* [[Замкнутость регулярных языков относительно различных операций]] dfsSort* [[Теорема Клини (u, sortedсовпадение классов автоматных и регулярных языков)]] sorted.push(v)* [[Доказательство нерегулярности языков: лемма о разрастании]]
void reverseDfs(State v): v.canReach = true for each State u in v.prev: if !u.canReach: reverseDfs(u)= Источики информации ==
boolean dfs(State v): // returns true iff there is a cycle v* ''Хопкрофт Д., Мотвани Р.color = GREY for each State u in v, Ульман Д.next: if u'' — Введение в теорию автоматов, языков и вычислений, 2-е изд.color == GREY: return true if uПер.canReach && uс англ.color == WHITE && dfs(u)— Москва: return true vИздательский дом «Вильямс», 2002. — с.color = BLACK return false 169-177 — ISBN 5-8459-0261-4
int words(Automaton a)[[Категория:Теория формальных языков]] for each State v in a: v.canReach = false for each State v in a: if v.isFinal: reverseDfs(v) for each State v in a: v.color = WHITE if dfs(a.start)[[Категория: return infinity Stack sorted = topSort(a) paths = new int[a.statesNumberАвтоматы и регулярные языки] fill(paths, 0) paths[0] = 1 while !sorted.isEmpty: State v = sorted.pop() for each State u in v.next: paths[u] += paths[vКатегория: Свойства конечных автоматов] int result = 0 for each State v in a: if v.isFinal: result += paths[v] return result

Навигация