Лекция 10

Теория формальных языков (II)

(Конспект: К. Ефремова)

10.1 Конечные автоматы (продолжение)

Определение 10.1. Детерминированный конечный автомат — это недетерминированный конечный автомат, у которого δ(q, ϵ) = ∅ и |δ(q, a)| ⩽ 1. Полностью определенным детерминированным конечным автоматом называется автомат, у которого |δ(q, a)| = 1. (Иначе говоря, можно считать, что у него δ : Q × Σ → Q.)

Определение 10.2. Обозначение: q_{1} →^{a} q_{2} означает q_{2} ∈ δ(q_{1}, a).

Теорема 10.1. Для любого недетерминированного конечного автомата A = (Q, Σ, q_{s}, F, δ) можно построить полностью определенный детерминированный конечный автомат A′ = (Q′, Σ, q′_{s}, F′, δ′), такой, что L(A) = L(A′).

Доказательство. 1. Избавимся от ϵ-переходов. Найдем все пары состояний (q_{1}, q_{2}), такие, что q_{2} достижимо из q_{1} по ϵ-переходам: q_{1} →^{ϵ} q′ →^{ϵ} … →^{ϵ} q_{2}. Для каждого не-ϵ-перехода q_{2} →^{a} q_{3} добавим переход q_{1} →^{a} q_{3}. Кроме того, если q_{2} ∈ F, добавим и q_{1} в F.

После этого удалим все ϵ-переходы. Заметим, что язык при этом не изменится. Действительно, раньше мы шли по пути q_{1} → … → q_{2} → q_{3}, считывая a, а теперь по другому пути, но все равно считываем a; и наоборот.

2. Пусть Q′ = 2^{Q}; q′_{s} = {q_{s}}; F′ = {q′ ∈ Q′ | q′∩F ≠ ∅}. Напишем новые переходы вида q′_{1} →^{a} q′_{2}, где q′_{2} = {q ∈ Q | ∃r ∈ q′_{1} : r →^{a} q}. Построенный новый автомат, очевидно, детерминированный.

Докажем, что язык не меняется.

(a) Пусть старый автомат принимает строчку. Покажем,что новый автомат тоже примет эту строчку. Раньше мы совершали переход q_{s} →^{a_{1}} q_{1}, теперь — переход {q_{s}} →^{a_{1}}{q_{1}, …}. Последующие переходы q_{i−1} →^{a_{i}} q_{i} превращаются в {q_{i−1}, …} →^{a_{i}}{q_{i}, …}. Последнее состояние будет принадлежать F′, ибо оно содержит конечное состояние старого автомата.

(b) Пусть строчка принимается новым автоматом. По определению, в состоянии, в котором он завершает работу, содержится некоторое конечное состояние старого автомата. Пусть последний переход нового автомата — q′ →^{a}{p, …} (где p ∈ F). По определению новой функции перехода, существует q ∈ q′, такое, что q →^{a} p в старом автомате. Далее рассмотрим предыдущий переход r′ →^{b}{q, …} в новом автомате, и т. д., пока не дойдем до стартового состояния.

Теорема 10.2. Множества языков, задаваемых

(1) конечными автоматами,

(2) регулярными выражениями,

(3) праволинейными грамматиками

в одном и том же алфавите Σ, совпадают.

Доказательство. (2)⇒(1). Доказательство по индукции. База:

1. Пустой язык. Автомат, принимающий пустой язык — автомат с пустым F.

2. Язык {ϵ}. В автомате, принимающем его, F = {q_{s}}, а переходов в нем нет.

3. Язык {a}. Единственный переход такого автомата: q_{s} →^{a} q; F = {q}.

Индукционный переход:

1. Рассмотрим конкатенацию языков L_{1} и L_{2}. Пусть есть автомат, принимающий язык L_{1}; и другой автомат, принимающий L_{2}. Мы хотим построить автомат,принимающий L_{1} · L_{2}.

Объединим эти два автомата в один, и слегка модифицируем его. Новым стартовым состоянием будет стартовое состояние первого автомата. Новым множеством конечных состояний будет множество конечных состояний второго автомата. Также добавим ϵ-переходы из (бывших) конечных состояний первого автомата в (бывшее) начальное состояние второго автомата.

2. L_{1} ∪ L_{2}. Снова объединим два автомата. Добавим новое стартовое состояние, а из него — ϵ-переходы в два старых стартовых состояния. На сей раз конечными состояниями построенного автомата будут конечные состояния обоих автоматов.

3. L^{∗}. Добавим ϵ-переходы из конечных состояний — в начальное, а также добавим ϵ-переход из стартового состояния в новое (дополнительное) конечное состояние.

(1)⇒(3). Нетерминалы грамматики — состояния автомата. Если q →^{a} p, то добавим правило q → ap. Если еще и p ∈ F, то добавим еще правило q → a.

(3)⇒(2). Можно построить систему уравнений

A_{1} = α_{10} ∪ (α_{11}A_{1}) ∪ (α_{12}A_{2}) … ∪ (α_{1n}A_{n})
A_{2} = α_{20} ∪ (α_{21}A_{1}) ∪ (α_{22}A_{2}) … ∪ (α_{2n}A_{n})
…
A_{n} = α_{n0} ∪ (α_{n1}A_{1}) ∪ (α_{n2}A_{2}) … ∪ (α_{nn}A_{n}),

где A_{1}, A_{2}, …, A_{n} — все нетерминалы, они же неизвестные этой системы уравнений; α_{ij} — регулярные выражения в алфавите Σ, коэффициенты системы, строятся они следующим образом. Посмотрим все правила для нетерминала A_{i}, у которых в правой части A_{j}; пусть это A_{i} → a_{1}A_{j}, …, A_{i} → a_{k}A_{j}. Тогда α_{ij} := {a_{1}}∪{a_{2}}∪…∪{a_{k}}. (Если j = 0, рассматриваем правила, не содержащие нетерминала в правой части.)

Исключим A_{1}: перепишем первое неравенство в виде A_{1} = (α_{11}A_{1})∪β; теперь A_{1} = α_{11}^{∗}β (где β ∈ (Σ ∪ N)^{∗}) подставляем везде в последующие уравнения системы. Аналогично поступаем с A_{2}, …, A_{n}, находя A_{i} из i-го уравнения. Найдя, таким образом, регулярное выражение (уже в алфавите Σ, а не Σ∪N) для A_{n}, подставим его в предыдущие уравнения, и т. д. В результате будет получено решение, которое будет минимальным^{1}, т.е. не будет содержать других решений; значения для нетерминалов будут, очевидно, будет регулярными выражениями. Значение для стартового нетерминала — искомое^{2} регулярное выражение.

Задача 10.1. Завершить доказательство теоремы 10.2: доказать, что найденное так �решение� действительно будет минимальным решением, а также, что минимальное решение (точнее, соответствующее выражение для стартового нетерминала) действительно является языком, порождаемым данной грамматикой.

1См. задачу 10.1.

2См. задачу 10.1.

Лемма 10.1 (лемма о разрастании для регулярных языков (pumping lemma)). Пусть L — регулярный язык. Тогда существует константа c, такая, что любую строку x ∈ L длины не менее c можно разбить на три части x = u · v · w, такие что 0 < |v| ⩽ c и ∀i ⩾ 0 u · v^{i} · w ∈ L.

Доказательство. Рассмотрим детерминированный конечный полностью определенный автомат для языка L. Пусть c = |Q| + 1. Посмотрим, как он работает на цепочке x: q_{s} → q_{1} → … → q_{k} ∈ F. На каждом шаге считывается некоторый символ. Поскольку |x| ⩾ c, мы должны были побывать в каком-то состоянии дважды, и в нашем пути есть циклы. Выберем несамопересекающийся цикл; пусть до первого прохождения по нему считывалась строка u, при прохождении по нему считывалась строка v (ее длина не превосходит c, поскольку цикл — несамопересекающийся), а после прохождения по нему (в том числе, если по нему пошли еще раз) — строка w. Очевидно, наш автомат примет любую строку вида uv^{i}w.

С помощью этой леммы можно доказывать, что какой-нибудь язык не является регулярным.

Пример 10.1. L = {0^{n}1^{n}|n ∈ ℕ ∪ 0} не является регулярным. Доказательство. Пусть регулярный. Рассмотрим варианты подстроки v из леммы.

1. В строку попадают только нули ⇒ в uv^{2}w количество 0 увеличится, а количество 1 останется неизменным ⇒ строка не будет принадлежать L.

2. В строку попадают только единицы — аналогично.

3. v = 0^{i}1^{j} ⇒ в uv^{2}w после 1 будет идти 0 ⇒ строка опять не будет принадлежать языку.

(Замечание: тем не менее, этот язык может быть порожден бесконтекстной грамматикой S → 0S1, S → ϵ.)

Полезные свойства регулярных языков:

1. Множество всех регулярных языков в данном алфавите замкнуто относительно операций, которые их порождают: ∪, ·, ^{∗}.

2.

Лемма 10.2. Множество всех регулярных языков в данном алфавите замкнуто относительно дополнения.

Доказательство. Рассмотрим детерминированный конечный полностью определенный автомат, задающий язык L. Поменяем местами его конечные состояния с остальными: F′ := Q\F. Полученный автомат задает язык \overline{L}.

3.

Следствие 10.1…. и замкнуто относительно пересечения.

Разрешимые проблемы, связанные с регулярными языками:

1. Принадлежность. Рассмотрим детерминированный конечный полностью определенный автомат, принимающий данный язык. Чтобы узнать, принадлежит ли этому языку некоторая строка, запустим наш автомат на этой строке x. После |x| переходов мы поймем, принадлежит ли она языку.

2. Пустота языка. Чтобы решить эту проблему, достаточно проверить, достижимо ли какое-нибудь конечное состояние автомата из начального. Эта задача, очевидно, алгоритмически разрешима.

3. Равенство языков. Чтобы построить алгоритм для этой задачи, достаточно заметить, что

L_{1} = L_{2} ⇔ L_{1} ∩ \overline{L_{2}} = ∅ ∧ L_{2} ∩ \overline{L_{1}} = ∅ (или: (L_{1} ∩ \overline{L_{2}}) ∪ (L_{2} ∩ \overline{L_{1}}) = ∅).

4. Включение языков. Чтобы построить алгоритм для этой задачи, достаточно заметить, что

L_{1} ⊆ L_{2} ⇔ (L_{1} ∩ \overline{L_{2}} = ∅) (или: (L_{1} ∩ L_{2} = L_{1})).