Взаимосвязь регулярных множеств, регулярных грамматик и конечных автоматов.

Три способа задания регулярных языков

Три основных способа, с помощью которых можно задавать регу­лярные языки – это:

· регулярные (праволинейные и леволинейные) грамматики,

· конечные автоматы (КА) и

· регулярные множества (равно как и обозначающие их регулярные выра­жения).

Регулярные языки в принципе можно определять и другими способами, но именно три указанных способа представляют наибольший инте­рес.

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

Утверждение 2.1. Язык является регулярным множеством тогда и только тогда, когда он задан леволинейной (праволинейной) грамматикой.

Утверждение 2.2. Язык может быть задан леволинейной (праволинейной) грам­матикой тогда и только тогда, когда он является регулярным множеством.

Утверждение 23. Язык является регулярным множеством тогда и только тогда, когда он задан с помощью конечного автомата.

Утверждение 2.4. Язык распознается с помощью конечного автомата тогда и только тогда, когда он является регулярным множеством.

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

Связь регулярных выражений и регулярных грамматик

Регулярные выражения и регулярные грамматики связаны между собой следую­щим образом:

· для любого регулярного языка, заданного регулярным выражением, можно по­строить регулярную грамматику, определяющую тот же язык;

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

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

Связь регулярных выражений и конечных автоматов

Регулярные выражения и недетерминированные конечные автоматы связаны между собой следующим образом:

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

· для любого регулярного языка, заданного конечным автоматом, можно полу­чить регулярное выражение, определяющее тот же язык.

Известен алгоритм, реализующий построение конечного автомата по регулярному выражению [3]. Алгоритм построения регулярного выражения по конечному автомату здесь не представляет интереса, поскольку проще построить грамматику, эквивалент­ную заданному конечному автомату, а потом уже найти регулярное выражение для заданного грамматикой языка.

Связь регулярных грамматик и конечных автоматов

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

Это очень важное утверждение, поскольку регулярные грамматики используют­ся для определения лексических конструкций языков программирования. Соз­дав автомат на основе известной грамматики, мы получаем распознаватель для лексических конструкций данного языка. Таким образом, удается решить задачу разбора для лексических конструкций языка, заданных произвольной регуляр­ной грамматикой. Обратное утверждение также полезно, поскольку позволяет узнать грамматику, цепочки языка которой допускает заданный автомат.

Для построения конечного автомата на основании известной грамматики и для построения грамматики на основании данного конечного автомата используются достаточно простые алгоритмы.

Все языки программирования определяют нотацию записи «слева направо». В той же нотации работают и компиляторы. Поэтому далее рассмотрены алгоритмы для леволинейных грамматик.

Построение конечного автомата на основе леволинейной грамматики

Пусть имеется леволинейная грамматика G(VT,VN,P,S), необходимо построить экви­валентный ей конечный автомат M(Q,V,d,qo,F).

Прежде всего для построения автомата исходную грамматику G необходимо привести к автоматному виду. Известно, что такое преобразование можно вы­полнить для любой регулярной грамматики. Алгоритм преобразования к авто­матному виду был рассмотрен выше, поэтому здесь на данном вопросе останав­ливаться нет смысла. Можно считать, что исходная грамматика G уже является леволинейной автоматной грамматикой.

Тогда построение конечного автомата M(Q,V,d,qo,F) на основе грамматики G(VT, VN, P, S) выполняется по следующему алгоритму.

Шаг 1. Строим множество состояний автомата Q, Состояния автомата строятся таким образом, чтобы каждому нетерминальному символу из множества VN грам­матики G соответствовало одно состояние из множества Q автомата М. Кроме того, во множество состояний автомата добавляется еще одно дополнительное состояние, которое будем обозначать Н. Сохраняя обозначения нетерминальных символов грамматики G, для множества состояний автомата М можно записать:

Q=VNÈ{H}.

Шаг 2. Входным алфавитом автомата М является множество терминальных сим­волов грамматики G: V = VT.

Шаг 3. Просматриваем все множество правил исходной грамматики.

Если встречается правило вида A®tÎP, где AÎVN, tÎVT, то в функцию перехо­дов d(H,t) автомата М добавляем состояние A: AÎd(H,t).

Если встречается правило вида A®BtÎP, где A,BÎVN, tÎVT, то в функцию пе­реходов d(B,t) автомата М добавляем состояние A: AÎd(B,t).

Шаг 4. Начальным состоянием автомата М является состояние Н: qo = Н.

Шаг 5. Множество конечных состояний автомата М состоит из одного состоя­ния. Этим состоянием является состояние, соответствующее целевому символу грамматики G: F = {S}.

На этом построение автомата заканчивается.

Построение леволинейной грамматики на основе конечного автомата

Имеется конечный автомат M(Q, V, d, qo, F), необходимо построить эквивалент­ную ему леволинейную грамматику G(VT, VN, P, S).

Построение выполняется по следующему алгоритму.

Шаг 1. Множество терминальных символов грамматики G строится из алфавита входных символов автомата М: VT = V.

Шаг 2. Множество нетерминальных символов грамматики G строится на основа­нии множества состояний автомата М таким образом, чтобы каждому состоянию автомата, за исключением начального состояния, соответствовал один нетерми­нальный символ грамматики: VN = Q\{qo}.

Шаг 3. Просматриваем функцию переходов автомата М для всех возможных со­стояний из множества Q для всех возможных входных символов из множества V.

Если имеем d(A,t) = Æ, то ничего не выполняем.

Если имеем d(A,t) = {B1,B2,...,Bn}, n >0, где AÎQ, "n³i³0: BiÎQ, tÎV, тогда для всех состояний Вi выполняем следующее:

· добавляем правило Bi®t во множество Р правил грамматики G, если А = qo;

· добавляем правило Bi®At во множество Р правил грамматики G, если A ¹ qo.

Шаг 4. Если множество конечных состояний F автомата М содержит только одно состояние F = {F0}, то целевым символом S грамматики G становится символ мно­жества VN, соответствующий этому состоянию: S = Fo; иначе, если множество конечных состояний F автомата М содержит более одного состояния F = {F1, F2,...,Fn}, n>1, тогда во множество нетерминальных символов VN грамматики G добавляется новый нетерминальный символ S: VN = VNÈ{S}, а во множество пра­вил Р грамматики G добавляются правила: S®F1 | F2 | ... | Fn.

На этом построение грамматики заканчивается.

Пример построения конечного автомата на основе заданной грамматики

Рассмотрим грамматику G({"a", "(", "*", ")", "{", "}"}, {S.C.K}, Р, S) (символы а, (, *, ), {, } из множества терминальных символов грамматики взяты в кавычки, чтобы выделить их среди фигурных скобок, обозначающих само множество):

Р: S ® С*) | К}

С ® (* | Са | С{ | С} | С( | С* | С)

К ®. { | Ка | К( | К* | К) | К{

Это леволинейная регулярная грамматика. Она описывает множество комментариев языка Паскаль. Как было показано выше, ее можно преобразовать к автоматному виду.

После преобразования получим леволинейную автоматную грамматику следующего вида: G'({"a", "(", "*",.")", "{", "}"}, {S, D, C, E, K}, P', S):

Р': S ® E) | К}

D ®C*

С ® D* I Са | C{ | C} | C( | C* | C)

E ® (

К ® { | Ка | K( | K* | K) | K{

Построим конечный автомат M(Q,V,d,qo,F), эквивалентный указанной грамма­тике.

Шаг 1. Построим множество состояний автомата: Q = VNÈ{H}= {S, E, C, D, K, H}.

Шаг 2. В качестве алфавита входных символов автомата берем множество тер­минальных символов грамматики. Получаем: V = {"а", "(", "*", ")", "{", "}"}.

Шаг 3. Рассматриваем множество правил грамматики.

Для правил S ® Е) | К} имеем d(Е,")") = {S}; d(K,"}") = {S}.

Для правила Е ® С* имеем d(С,"*") = {Е}.

Для правил С ® D* | Са | С{ | С} | С( | С* | С) имеем d(D,"*") = {С}; d(С,"а") = {С}; d(С,"{") = {С}; d(С, "}") = {С}; d(С,"(") = {С}; d(С, “*”) = {Е.С}; d(С, ")") = {С}.

Для правила D ® ( имеем d(Н, "(") = {D}.

Для правил К ® { | Ка | К( | К* | К) | К{ имеем d(Н, "{") = {К}; d(К, "а") = {К}; d(К, “(") = {К}; d(К,"*") = {К}; d(К, ")") = {К}; d(К, "{") = {К}.

Шаг 4. Начальным состоянием автомата является состояние qo = Н.

Шаг 5. Множеством конечных состояний автомата является множество F = {S}.

Выполнение алгоритма закончено.

В итоге получаем автомат M({S.E,C,D,K,H}, {"а", "(", "*", ")",."{", "}"}. d, Н, {S}) с функцией переходов:

d(Н, "{") = {K} d(H, "(") = {D} d(К, "а") = {К} d(К, "(") = {К}

d(К, "*") = {К} d(К, ")") = {К} d(К, "{“) = {К} d(К, ")") = {S}

d(D, "*") = {С} d(С, "а") = {С} d(С, "{") = {С} d(C, "}") = {С}

d(С, "(") = {С} d(С, "*") = {Е, С} d(С, ")") = (С) d(Е, “)”) = {S}

Граф переходов этого автомата изображен на рис. 4.1.

clip_image002

a,(,*,),{,}

Рис. 4.1. Недетерминированный КА для языка комментариев в Borland Pascal

Это недетерминированный конечный автомат, поскольку существует состояние, в котором множество, получаемое с помощью функции переходов по одному и тому же символу, имеет более одного следующего состояния. Это состояние С и функция d(С,"*") = {Е,С}.

Моделировать поведение недетерминированного КА - непростая задача, поэто­му можно построить эквивалентный ему детерминированный КА. Полученный таким путем КА можно затем минимизировать.

В результате всех преобразований получаем детерминированный конечный ав­томат М'({S.E,С,D, К,Н}, {"а", "(", "*", ")", "{", "}"} .d', H, {S}) с функцией переходов:

d'(Н, "{") = {К} d'(Н, “(") = {D} d'(К, "а") = {К} d'(К, "(") = {К} d'(К, "*") = {К} d'(К, ")") = {К}

d'(К, "{") = {К} d'(К, "}") = {S) d'(D, "*") = {С} d'(С, "а") = {С} d'(С, “{“) = {С} d'(С, "}") = {С}

d'(С, "(") = {С} d'(С, ")") = {С} d'(С, "*") = {Е} d'(Е, “а") = {С} d'(E, “{“) = {С} d'(Е, "}") = {С}

d'(Е, "(") = {С} d'(Е, "*") = {Е} d'(Е, ")") = {S}

Граф переходов этого автомата изображен на рис. 4.2.

clip_image004

а, (,),{,}

Рис. 4.2. Детерминированный КА для языка комментариев в Borland Pascal

На основании этого автомата можно легко построить распознаватель. В данном случае мы можем получить распознаватель для двух типов комментариев языка Программирования Borland Pascal, если учесть, что а может означать любой ал­фавитно-цифровой символ, кроме символов (, *, ), {, }.

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

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

Например, множество целых чисел замкнуто относительно операций сложения, умножения и вычитания, но оно не замкнуто относительно операции деления — при делении двух целых чисел не всегда получается целое число.

Регулярные множества (и однозначно связанные с ними регулярные языки) замк­нуты относительно многих операций, которые применимы к цепочкам символов.

Например, регулярные языки замкнуты относительно следующих операций:

· пересечения;

· объединения;

· дополнения;

· итерации;

· конкатенации;

· гомоморфизма (изменения имен символов и подстановки цепочек вместо сим­волов).

Поскольку регулярные множества замкнуты относительно операций пересечения, объединения и дополнения, то они представляют булеву алгебру множеств. Су­ществуют и другие операции, относительно которых замкнуты регулярные мно­жества. Вообще говоря, таких операций достаточно много.

Регулярные языки представляют собой очень удобный тип языков. Для них раз­решимы многие проблемы, неразрешимые для других типов языков.

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

· Проблема эквивалентности. Даны два регулярных языка L1(V) и L2(V). Необхо­димо проверить, являются ли эти два языка эквивалентными.

· Проблема принадлежности цепочки языку. Дан регулярный язык L(V) и цепочка символов aÎV*. Необходимо проверить, принадлежит ли цепочка данному языку.

· Проблема пустоты языка. Дан регулярный язык L(V). Необходимо проверить, является ли этот язык пустым, то есть найти хотя бы одну цепочку a¹l, такую что aÎL(V).

Эти проблемы разрешимы вне зависимости от того, каким из трех способов за­дан регулярный язык. Следовательно, эти проблемы разрешимы для всех спосо­бов представления регулярных языков: регулярных множеств, регулярных грам­матик и конечных автоматов. На самом деле достаточно доказать разрешимость любой из этих проблем хотя бы для одного из способов представления языка, то­гда для остальных способов можно воспользоваться алгоритмами преобразова­ния, рассмотренными выше. (Возможны и другие способы представления регулярных множеств, а для них разреши­мость указанных проблем будет уже не очевидна.)

Для регулярных грамматик также разрешима проблема однозначности — доказа­но, что для любой регулярной грамматики можно построить эквивалентную ей однозначную регулярную грамматику. Это очевидно, поскольку для любой регу­лярной грамматики можно однозначно построить регулярное выражение, опре­деляющее заданный этой грамматикой язык.

Лемма о разрастании для регулярных языков

Существует простой метод проверки, является или нет заданный язык регулярным. Этот метод основан на проверке так называемой леммы о разраста­нии языка. Доказано, что если для некоторого заданного языка выполняется лемма о разрастании регулярного языка, то этот язык является регулярным; если же лемма не выполняется, то и язык регулярным не является [6, т. 1].

Лемма о разрастании для регулярных языков формулируется следующим обра­зом: если дан регулярный язык и достаточно длинная цепочка символов, при­надлежащая этому языку, то в этой цепочке можно найти непустую подцепочку, которую можно повторить сколь угодно много раз, и все полученные таким спо­собом новые цепочки будут принадлежать тому же регулярному языку. (Если найденную подцепочку повторять несколько раз, то исходная цепочка как бы «раз­растается» — отсюда и название «лемма о разрастании языков»).

Формально эту лемму можно записать так: если дан язык L, то $ константа р > 0, такая, что если aÎL и |a| ³ р, то цепочку a можно записать в виде a = dbe, где 0 < |b| < р, и тогда a' = dbie, a'ÎL "i ³ 0.

Используя лемму о разрастании регулярных языков, докажем, что язык L = {аnЬn | n > 0} не является регулярным.

Предположим, что этот язык регулярный, тогда для него должна выполняться лемма о разрастании. Возьмем некоторую цепочку этого языка a = аnЬn и запи­шем ее в виде a = dbe. Если bÎa+ или bÎb+ то тогда для i = 0 цепочка db0e = de не принадлежит языку L, что противоречит условиям леммы; если же bÎa+b+, тогда для i = 2 цепочка db2e = dbbe не принадлежит языку L. Таким образом, язык L не может быть регулярным языком.

Предлагаю ознакомиться с аналогичными статьями: