Лекция 11

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

(Конспект: В. Вялов)

11.1 Бесконтекстные языки

11.1.1 Нормальные формы бесконтекстных языков

Определение 11.1. Будем говорить, что грамматика — в нормальной форме Хомского (НФХ ), если она содержит только правила одного из следующих типов.

1. A → BC.

2. A → a.

3. S → ϵ.

Причем

4. Cтартовый символ S в правых частях не встречается (т.е. B, C ≠ S в пункте 1).

Теорема 11.1. Для любой бесконтекстной грамматики G существует грамматика G^{∗} в НФХ, такая, что L(G) = L(G^{∗}).

Доказательство. 1. Добавим новый стартовый символ S^{∗} и правило S^{∗} → S. Тем самым мы заработаем пункт 4.

2. Для каждого терминала a добавим нетерминал A_{a} и правило A_{a} → a. Также заменим a на A_{a} в остальных правилах. Теперь в правых частях правил у нас встречается либо только один терминал (как в пункте 2), либо цепочка нетерминалов (почти как в пунктах 1 и 3). Следующим шагом мы уменьшим длину этой цепочки до не более, чем двух символов.

3. Пусть у нас есть правило A → BCα, где α — непустая цепочка из нетерминалов. Добавим новый нетерминал Z, а правило заменим на два других: A → BZ и Z → Cα. Тем самым мы сократим правую часть правила на один символ. Продолжая действовать таким образом, добьемся нужной длины правой части.

4. Остается избавиться от правил вида A → ϵ (где A ≠ S^{∗}) и A → B. Аналогично лемме о праволинейных грамматиках построим множества S(B) = {A | A →^{∗} B} и S(ϵ) = {A | A →^{∗} ϵ}. Заметим, что A ∈ S(B) означает, что выполняется хотя бы одно из трех условий:

(1) A = B;

(2) A → C, где C ∈ S(B);

(3) A → CD, где C ∈ S(B), а D ∈ S(ϵ) (или наоборот).

Аналогично для S(ϵ). Отсюда видно, что множества S(…) можно построить за конечное число шагов: на каждом шаге мы будем для каждой пары нетерминалов (A, B) проверять условия (1)–(3) и добавлять A в S(B), если одно из условий выполнится (и то же самое — для S(ϵ)). Если на очередном шаге мы ничего не добавим ни в одно из множеств, то множества S(…) построены.

Теперь для каждого A ∈ S(B) и правила B → α добавим правило A → α. Также, если S^{∗} ∈ S(ϵ), то добавим правило S^{∗} → ϵ. После этого удалим все �плохие� правила, т.е. правила вида A → ϵ (где A ≠ S^{∗}) и A → B.

Заметим, что в результате наших действий язык не изменился.

Определение 11.2. Будем говорить, что грамматика — в нормальной форме Грейбах (НФГ ), если она содержит только правила одного из следующих типов.

1. A → a.

2. A → aB.

3. A → aBC.

4. S → ϵ.

Причем

5. Cтартовый символ S в правых частях не встречается.

Задача 11.1. Доказать аналогичную теорему для НФГ.

11.1.2 Автоматы с магазинной памятью

Как уже было доказано, любой праволинейной грамматике соответствует конечный автомат. Для бесконтекстной грамматики конечного автомата мало, поэтому мы усложним конструкцию автомата.

Наш автомат будет обладать памятью, а именно, магазином. Магазин представляет из себя стек, с которым мы можем делать две вещи:

(1) достать верхний элемент;

(2) положить наверх данный элемент.

Определение 11.3. Недетерминированный автомат с магазинной памятью состоит из тех же частей, что и недетерминированный конечный автомат, но надо добавить еще магазинный алфавит M и начальный символ магазина Z ∈ M. Функция перехода теперь такая:

δ : Q × (Σ ∪ {ϵ}) × M → 2^{Q×M^{∗}},

при этом требуется, чтобы значения δ были конечными множествами.

Переход такого автомата мы будем записывать следующим образом: q ⟶^{a,A/α} p. Это значит, что (p, α) ∈ δ(q, a, A). Фактически это означает следующее: мы переходим из состояния q в состояние p, считывая из строки символ a (возможно, никакого символа вовсе не считываем: a = ϵ). При этом мы снимаем с магазина символ A, а на его место помещаем строку α.

Конфигурацией нашего автомата назовем тройку (q, u, γ), где q ∈ Q — текущее состояние, u — оставшаяся (непрочитанная) часть строки, γ — содержимое магазина (верхний символ записывается слева). Такт работы нашего автомата выглядит так:

(q, au, Aβ) ⊢ (p, u, αβ),

если q ⟶^{a,A/α} p (здесь a ∈ Σ ∪ {ϵ}). Будем говорить, что строка x принимается автоматом, если мы сможем из состояния (q_{s}, x, Z) за конечное число шагов попасть в конечное состояние, исчерпав всю строку, т.е.

(q_{s}, x, Z) ⊢^{∗} (q, ϵ, α) (где q ∈ F).

При этом нам неважно, что именно у нас осталось в магазине.

Пример 11.1. Построим автомат, задающий язык {0^{n}1^{n} | n ∈ ℕ ∪ {0}}. Он будет иметь четыре состояния: начальное q_{s}, конечное q_{f}, и два промежуточных q_{1}, q_{2}; а также следующие правила:

(1) q_{s} ⟶^{ϵ,Z/} q_{f} (чтобы не забыть о случае n = 0);

(2) q_{s} ⟶^{0,Z/0Z} q_{1};

(3) q_{1} ⟶^{0,0/00} q_{1};

(4) q_{1} ⟶^{1,0/} q_{2};

(5) q_{2} ⟶^{1,0/} q_{2};

(6) q_{2} ⟶^{ϵ,Z/} q_{f}.

Наш автомат действует следующим образом: считывая на очередном шаге 0, мы кладем его в магазин; считывая 1, вынимаем 0 из магазина. При этом, если единиц будет больше, чем нулей, то мы не сможем дочитать строку до конца; а если наоборот, то мы не увидим дна магазина и не попадем в конечное состояние.

Теорема 11.2. По любой бесконтекстной грамматике можно построить недетерминированный магазинный автомат, задающий тот же язык.

Доказательство. 1. Приведем грамматику к НФХ.

2. В магазин автомата мы будем складывать нетерминалы; наши состояния — q_{s} (стартовое), q (�рабочее�) и q_{f} (конечное); а правилам вида (1)–(3) сопоставим, соответственно, такие переходы:

(1) q ⟶^{ϵ,A/BC} q;

(2) q ⟶^{a,A/ϵ} q;

(3) q ⟶^{ϵ,S/ϵ} q.

А также добавим еще два правила: q_{s} ⟶^{ϵ,Z/SZ} q (�начинаем вывод со

стартового символа�) и q ⟶^{ϵ,Z/ϵ} q_{f} (�увидев дно магазина, следует завершить вывод�). Для того, чтобы убедиться, что полученный автомат задает тот же самый язык, достаточно доказать по индукции (упражнение) следующую лемму.

Лемма 11.1. (q, uv, αβ) ⊢^{∗} (q, v, β) для нашего автомата выполняется тогда и только тогда, когда α ⇒^{∗} u для исходной грамматики (в НФХ).

Задача 11.2. Доказать обратную теорему.

11.1.3 Алгоритмические проблемы, связанные с бесконтекстными языками

Алгоритм проверки принадлежности слова языку. Цель — проверить, принадлежит ли слово a_{1}a_{2} … a_{n} языку, задаваемому грамматикой G (очевидно, можно считать, что она — в НФХ).

Решать эту задачу мы будем методом динамического программирования. Обозначим t_{ij} множество тех нетерминалов, из которых можно получить строку a_{i} … a_{i+j−1}. Множество t_{i1} состоит из терминалов, для которых в G имеется правило A → a_{i}. Для того, чтобы построить множество t_{ij} будем разбивать строку по позиции i + k − 1 и смотреть, из чего выводится каждая из половинок. Иначе говоря, A ∈ t_{ij}, если существует правило A → BC, где B ∈ t_{ik} и C ∈ t_{i+k,j−k}. Отсюда видно, что все множества t_{ij} можно построить за кубическое от длины строки количество шагов. При этом исходная строка принадлежит языку, если стартовый символ лежит в множестве t_{1n}.

Проверка пустоты языка. Для решения этой задачи докажем следующую лемму:

Лемма 11.2 (лемма о разрастании для бесконтекстных языков). Для любого бесконтекстного языка L существует число k, такое, что, если длина слова α больше чем k, то его можно представить в виде α = xuvwy, где длина слова uvw — менее k, слово uw не пусто, и xu^{i}vw^{i}y ∈ L для всех i ⩾ 0.

Доказательство. Рассмотрим грамматику в НФХ, порождающую этот язык. В качестве k возьмем 2^{|N|+2}.

Построим дерево вывода строки α. В корень поместим стартовый символ S. Потомками вершины, помеченной символом X, будут вершины, помеченные символами правой части правила, которое было применено для X при выводе строки α. (Упорядочим их так же, как они были расположены в правой части правила.) Листьями будут терминалы (символы строки α).

По условию, в дереве ⩾ 2^{|N|+2} листьев, поэтому его высота — не менее |N| + 2. Значит, существует путь из корня в лист такой длины. Тогда на этом пути какой-то нетерминал B встретится дважды.

Рассмотрим основное дерево, а также получившиеся два поддерева с корнями в вершинах, где сидит этот символ. В результате слово разобьется на необходимые нам пять частей (см. рис. 11.1). По построению, B ⇒^{∗} uBw и B ⇒^{∗} v. Слово uw непусто, поскольку грамматика была в НФХ.

S /|\ / | \ / | \ / B \ / /|\ \ / / | \ \ / / | \ \ / / B \ \ / / / \ \ \

/ / / \ \ \ _{x} _{u} _{v} _{w} _{y}

Рис. 11.1: Разбиение α = xuvwy. Пунктирная черта обозначает �границы� соответствующего поддерева.

Осталось добиться того, чтобы выполнялось |uvw| < k.

Задача 11.3. Сделать это.

Теперь легко проверить пустоту языка. Если язык не является пустым, то в нем должна существовать строка длины меньшей, чем k (пока строка удовлетворяет лемме, ее можно при помощи леммы заменять на другую строку, меньшую по длине), а количество таких строк конечно. Итого нам надо будет лишь конечное число раз запустить алгоритм, распознающий принадлежность строки нашему языку.