Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
ТАФЯ. Лабораторная работа 2.docx
Скачиваний:
0
Добавлен:
15.04.2026
Размер:
91.32 Кб
Скачать

МИНИСТЕРСТВО ЦИФРОВОГО РАЗВИТИЯ,

Связи и массовых коммуникаций российской федерации

Федеральное государственное бюджетное образовательное учреждение высшего образования «Санкт-Петербургский государственный университет

телекоммуникаций им. проф. М. А. Бонч-Бруевича»

(СПбГУТ)

Факультет инфокоммуникационных сетей и систем

Кафедра программной инженерии и вычислительной техники

Лабораторная работа №2

по дисциплине «Теория алгоритмов и формальных языков»

студенты гр. ИКПИ-93

_______________

Смирнов Д.А.

Тюришев М.А.

Цыганков М.А.

Козлов Н.С.

ассистент каф. ПИиВТ

_______________

Марочкина А. В.

Санкт-Петербург

2022

Постановка задачи

Придумать и нарисовать диаграмму детерминированного КА, распознающего заданный язык.

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

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

Ход работы

Код разработанной программы на языке Python приведен в таблице 1.

Таблица 1. Код разработанной программы

main.py

import re import sys import collections

from typing import Any, Union, Dict, Tuple, List, Set

def to_flatten_tuple(any_arg: Any) -> Tuple[str]: """

Преобразует аргумент any_arg в кортеж (для использования в качестве ключа сортировки состояний и пр.)

42 -> ('42',) | (42, 24) -> ('42', '24') | [4, ['2', '4'], 2] -> ('4', '2', '4', '2')

([6, [[5]]], ([4], 3), 2, ['one', ['0']]) -> ('6', '5', '4', '3', '2', 'one', '0')

:param any_arg: число / строка / кортеж / список / множество

:return: кортеж

"""

return tuple(re.findall(pattern=r'\w+', string=str(any_arg)))

def fsm_to_deterministic(alphabet: Union[str, Tuple[str], List[str], Set[str]],

fsm: Dict[Union[str, Tuple[str]],

Dict[str, Union[str, Tuple[str], Set[Union[str, Tuple[str]]]]]], initial_states: Union[str, Tuple[str],

List[Union[str, Tuple[str]]], Set[Union[str, Tuple[str]]]], final_states: Union[str, Tuple[str],

List[Union[str, Tuple[str]]], Set[Union[str, Tuple[str]]]], is_gen_new_states: bool = True) -> (Dict[Union[str, Tuple[str]],

Dict[str, Union[str, Tuple[str]]]],

Set[Union[str, Tuple[str]]], Set[Union[str, Tuple[str]]]):

"""

Преобразует входной конечный автомат fsm в детерминированный (алгоритм Томпсона)

Множество {,}:: 'a' : {1, 2, 3} -- это переходы в состояния 1, 2, 3 с меткой 'a' (недетерминированность)

Кортеж (,):: 'a' : (1, 2, 3) -- это переход в одно состояние (1, 2, 3) с меткой 'a' (детерминированность)

Примечание: эта функция не выясняет, насколько корректны входные параметры :param alphabet: алфавит языка

:param fsm: конечный автомат, например, {'1': {'a': {'1', '2'}}, '2': {'a': '1'}}

:param initial_states: множество начальных состояний КА (элементы множества -- строки / кортежи) либо одно начальное состояние (строка / кортеж)

:param final_states: множество заключительных состояний КА (элементы множества -- строки / кортежи) либо одно заключительное состояние (строка / кортеж)

:param is_gen_new_states: выдавать (1, 2) и т.п. (False) либо создавать новые состояния (True): (1, 2) -> 3 :return: (ДКА, множество начальных состояний, множество заключительных состояний) """

# если FSM пуст или множество начальных состояний пусто if not fsm or not initial_states and not isinstance(initial_states, (str, tuple)):

return dict(), set(), set()

# преобразуем в множества

initial_states = set(initial_states) if isinstance(initial_states, (set, list)) else {initial_states} final_states = set(final_states) if isinstance(final_states, (set, list)) else {final_states}

new_fsm = dict() # новый конечный автомат

new_initial_states = initial_states.copy() # множество новых начальных состояний КА new_final_states = final_states.copy() # множество новых заключительных состояний КА seen = set() # множество уже обработанных состояний q = collections.deque(initial_states) # очередь состояний while q: # пока очередь не пуста

state = q.popleft() # достаем первый элемент из очереди if state in seen: # если состояние уже было обработано continue # переходим к следующему состоянию в очереди

seen.add(state) # или добавляем текущее состояние в список обработанных и обрабатываем его

# если состояние -- это строка или единичный кортеж if isinstance(state, str) or isinstance(state, tuple) and len(state) <= 1:

# преобразуем в строку if len(state) == 1:

if state in new_final_states:

new_final_states.add(state[0])

state = state[0]

new_fsm.setdefault(state, dict()) # добавляем соответствие # просматриваем все переходы из текущего состояния for alpha, alpha_states in fsm.get(state, dict()).items():

# если есть несколько переходов с одной меткой (недетерминированность) if isinstance(alpha_states, set) and len(alpha_states) > 1: # удаляем из множества отсутствующие в FSM состояния alpha_states = alpha_states.intersection(fsm) if not alpha_states: # если все состояния отсутствовали continue # пропускаем переход по метке alpha

# формируем состояние в виде отсортированного кортежа состояний sorted_alpha_states = tuple(sorted(alpha_states, key=to_flatten_tuple)) # добавляем переход по метке alpha из state в новое состояние new_fsm[state][alpha] = sorted_alpha_states # добавляем в очередь новое состояние

q.append(sorted_alpha_states)

else: # если есть ровно один переход или их нет совсем (детерминированность) if isinstance(alpha_states, (set, tuple)): if not alpha_states: # если нет переходов continue # пропускаем переход по метке alpha

# если ровно один переход if isinstance(alpha_states, set) or alpha_states not in fsm and len(alpha_states) == 1: alpha_states = next(iter(alpha_states)) # преобразуем в сам элемент

# если состояние или его составляющие отсутствуют if alpha_states not in fsm and (isinstance(alpha_states, str) or all(x not in fsm for x in alpha_states)): continue # пропускаем переход по метке alpha

# добавляем переход по метке alpha из state в новое состояние new_fsm[state][alpha] = alpha_states # добавляем в очередь новое состояние

q.append(alpha_states)

elif isinstance(state, tuple): # если состояние -- это кортеж из двух или более элементов sorted_alpha_states = tuple(sorted(state, key=to_flatten_tuple)) # если хотя бы одно состояние в кортеже является заключительным if not new_final_states.isdisjoint(sorted_alpha_states):

# добавляем состояние sorted_alpha_states в множество новых заключительных состояний new_final_states.add(sorted_alpha_states) d = dict() # словарь переходов для нового состояния

for alpha in alphabet: # просматриваем все возможные переходы по меткам

states_for_alpha = set() # множество состояний для текущего перехода с меткой alpha

# для каждого состояния в кортеже, включая сам кортеж for alpha_state in [*sorted_alpha_states, sorted_alpha_states]: if alpha_state in fsm and alpha in fsm[alpha_state]: # определяем, есть ли состояние в КА elem = fsm[alpha_state][alpha] if isinstance(elem, str): # в случае строки if elem in fsm: # если есть целевое состояние в FSM states_for_alpha.add(elem) # добавляем как элемент elif isinstance(elem, tuple): # в случае кортежа if elem in fsm: # если есть целевое состояние в FSM states_for_alpha.add(elem) # добавляем как элемент else: # если есть составляющие

states_for_alpha.update(x for x in elem if x in fsm) # дополняем else: # в случае множества

states_for_alpha.update(elem.intersection(fsm)) # дополняем # сортируем множество состояний для текущего перехода с меткой alpha sorted_states_for_alpha = tuple(sorted(states_for_alpha, key=to_flatten_tuple)) if sorted_states_for_alpha: # если множество не пусто

# добавляем переход по метке alpha

d[alpha] = sorted_states_for_alpha if len(sorted_states_for_alpha) > 1 \ else next(iter(sorted_states_for_alpha)) new_fsm[sorted_alpha_states] = d # добавляем переходы

# каждый добавленный переход нужно будет обработать отдельно (добавляем переходы в очередь q) for state in d.values():

q.append(state) if is_gen_new_states: # если нужно создавать новые состояния: (1, 2) -> 4, (1, 2, 3) -> 5...

# сортируем множество переходов конечного автомата

sorted_new_states = sorted(new_fsm.items(), key=to_flatten_tuple) new_fsm = dict() # новый КА transformation_dict = dict() # словарь преобразований: (1, 2) -> 5 и т. п. # предварительно обрабатываем те состояния, которые представлены в виде строк for state, alphas in sorted_new_states:

# если состояния ещё не было в словаре и оно является строкой if state not in transformation_dict and isinstance(state, str): transformation_dict[state] = state # добавляем # обрабатываем те состояния, которые представлены в виде кортежей for state, alphas in sorted_new_states: if state not in transformation_dict: # если состояния ещё не было в словаре

# находим незанятый строковый номер состояния (лексикографически минимальное состояние) min_key = min(fsm.keys(), key=to_flatten_tuple) if fsm else 1 i = int(min_key) if isinstance(min_key, str) and min_key.isdigit() else 1 while str(i) in transformation_dict:

i += 1 si = str(i)

transformation_dict[si] = si # добавляем номер в словарь, чтобы снова на него не выйти transformation_dict[state] = si # добавляем соответствие состояния номеру в словарь if si in new_final_states: # если номер нового состояния совпал со старым в

new_final_states.remove(si) # множестве заключительных состояний, то убираем его оттуда

if state in new_final_states: # заменяем старое заключительное состояние на новое new_final_states.remove(state) new_final_states.add(si) # обрабатываем переходы по состояниям for state, alphas in sorted_new_states: d = dict() # словарь переходов

for alpha, alpha_states in alphas.items(): # просматриваем все переходы из состояния state

# если найдено соответствие, выполняем замену

d[alpha] = transformation_dict.get(alpha_states, alpha_states)

new_fsm[transformation_dict[state]] = d # добавляем словарь переходов по номеру состояния

# удаление отсутствующих состояний в множествах начальных и заключительных состояний for state in new_initial_states.union(new_final_states): if state not in new_fsm: if state in new_initial_states:

new_initial_states.remove(state) if state in new_final_states:

new_final_states.remove(state) return new_fsm, new_initial_states, new_final_states

def determine_alphabet(fsm: Dict[Any, Dict[Union[str, Tuple[str]], Any]]) -> Set[str]:

"""

Возвращает алфавит языка, распознаваемого конечным автоматом fsm

:param fsm: конечный автомат, например, {'1': {'a': {'1', '2'}}, '2': {'a': '1'}}

:return: алфавит языка

""" alphabet = set() for alphas in fsm.values():

alphabet.update(alphas.keys()) return alphabet

def fsm_eliminate_epsilon_transitions(fsm: Dict[Union[str, Tuple[str]],

Dict[str, Union[str, Tuple[str], Set[Union[str, Tuple[str]]]]]], initial_states: Union[str, Tuple[str],

List[Union[str, Tuple[str]]], Set[Union[str, Tuple[str]]]], final_states: Union[str, Tuple[str],

List[Union[str, Tuple[str]]], Set[Union[str, Tuple[str]]]], epsilon: str = 'εϵΕ'

) -> (Dict[Union[str, Tuple[str]],

Dict[str, Union[str, Tuple[str], Set[Union[str, Tuple[str]]]]]],

Union[str, Tuple[str],

List[Union[str, Tuple[str]]], Set[Union[str, Tuple[str]]]],

Union[str, Tuple[str],

List[Union[str, Tuple[str]]], Set[Union[str, Tuple[str]]]]):

"""

Устраняет epsilon переходы конечного автомата

:param fsm: конечный автомат, например, {'1': {'a': {'1', '2'}}, '2': {'a': '1'}}

:param initial_states: множество начальных состояний КА (элементы множества -- строки / кортежи) либо одно начальное состояние (строка / кортеж)

:param final_states: множество заключительных состояний КА (элементы множества -- строки / кортежи) либо одно заключительное состояние (строка / кортеж)

:param epsilon: символы epsilon переходов ('', 'ε', 'ϵ', 'Ε')

:return: (КА без epsilon переходов, множество начальных состояний, множество заключительных состояний) """

# если FSM пуст или множество начальных состояний пусто if not fsm or not initial_states and not isinstance(initial_states, (str, tuple)):

return dict(), set(), set()

# преобразуем в множества

new_initial_states = set(initial_states) if isinstance(initial_states, (set, list)) else {initial_states} new_final_states = set(final_states) if isinstance(final_states, (set, list)) else {final_states}

# составляем новый конечный автомат new_fsm = dict()

# состояние -> множество состояний, в которые можно попасть из исходного по epsilon переходу from_state_to_state_by_epsilon = dict()

for state, alphas in fsm.items(): # для каждого состояния new_fsm.setdefault(state, dict()) # добавляем в новый КА

from_state_to_state_by_epsilon.setdefault(state, set()) # добавляем в словарь соответствий for alpha, alpha_states in alphas.items(): # для каждой метки

if alpha in epsilon: # если метка epsilon, то добавляем новое соответствие all_states = {state} if isinstance(alpha_states, set):

all_states.update(x for x in alpha_states if x in fsm) elif alpha_states in fsm:

all_states.add(alpha_states)

from_state_to_state_by_epsilon[state].update(all_states) else: # если метка не epsilon, то добавляем то, что было new_fsm[state].setdefault(alpha, set()) if isinstance(alpha_states, set):

new_fsm[state][alpha].update(x for x in alpha_states if x in fsm) elif alpha_states in fsm:

new_fsm[state][alpha].add(alpha_states) b = True

while b: # пока есть изменения b = False

# для каждого состояния, из которого есть переход по epsilon

for state, next_states in from_state_to_state_by_epsilon.items():

states_by_epsilon = from_state_to_state_by_epsilon.get(state, set())

for next_state in next_states.copy(): # дополняем множество состояний по каждому элементу этого множества

previous_length = len(states_by_epsilon)

states_by_epsilon.update(from_state_to_state_by_epsilon.get(next_state, set())) b |= (previous_length != len(states_by_epsilon)) # было ли изменение

for state, next_states in from_state_to_state_by_epsilon.items(): # для каждого состояния в словаре for next_state in next_states: # добавляем переходы из state во входящие из next_state for alpha, alpha_states in new_fsm[next_state].items(): # alpha != epsilon new_fsm[state].setdefault(alpha, set()).update(alpha_states) if state in new_initial_states: # дополняем множество начальных состояний new_initial_states.update(next_states)

if not new_final_states.isdisjoint(next_states): # дополняем множество заключительных состояний new_final_states.add(state)

return new_fsm, new_initial_states, new_final_states

def fsm_convert_and_minimize(fsm: Dict[Union[str, Tuple[str]],

Dict[str, Union[str, Tuple[str], Set[Union[str, Tuple[str]]]]]], initial_states: Union[str, Tuple[str],

List[Union[str, Tuple[str]]], Set[Union[str, Tuple[str]]]], final_states: Union[str, Tuple[str],

List[Union[str, Tuple[str]]], Set[Union[str, Tuple[str]]]] ) -> (Dict[str, Dict[str, str]], Set[str], Set[str]):

"""

Преобразует входной конечный автомат fsm в минимальный детерминированный

(алгоритм Томпсона преобразования КА в ДКА и алгоритм Хопкрофта минимизации ДКА)

:param fsm: конечный автомат, например, {'1': {'a': {'1', '2'}}, '2': {'a': '1'}}

:param initial_states: множество начальных состояний КА (элементы множества -- строки / кортежи) либо одно начальное состояние (строка / кортеж)

:param final_states: множество заключительных состояний КА (элементы множества -- строки / кортежи) либо одно заключительное состояние (строка / кортеж)

:return: (минимальный ДКА, множество начальных состояний, множество заключительных состояний) """

# если FSM пуст или множество начальных состояний пусто if not fsm or not initial_states and not isinstance(initial_states, (str, tuple)): return dict(), set(), set()

# устраняем epsilon переходы

fsm, initial_states, final_states = fsm_eliminate_epsilon_transitions(fsm, initial_states, final_states)

# определяем алфавит языка alphabet = determine_alphabet(fsm)

# добавляем фиктивное состояние fictive_state = 'fictive_state' while fictive_state in fsm: fictive_state += "'"

# добавляем фиктивные переходы из фиктивного состояния в начальные fictive_alpha = 'fictive_alpha' while fictive_alpha in alphabet: fictive_alpha += "'"

fsm[fictive_state] = {fictive_alpha: initial_states} initial_states = {fictive_state}

# преобразуем в ДКА

fsm, initial_states, final_states = fsm_to_deterministic(alphabet, fsm, initial_states, final_states, True)

# строим обратный конечный автомат inversed_fsm = dict() for state, alphas in fsm.items():

inversed_fsm.setdefault(state, dict()) for alpha, alpha_states in alphas.items(): if isinstance(alpha_states, (str, tuple)):

alpha_states = {alpha_states} for alpha_state in alpha_states:

inversed_fsm.setdefault(alpha_state, dict()).setdefault(alpha, set()).add(state)

# множество из одного фиктивного состояния fictive_state_class = frozenset({fictive_state})

# множество заключительных состояний only_final_states = frozenset(final_states)

# множество состояний, исключая заключительные и фиктивное

without_final_states = frozenset(fsm).difference(final_states).difference(fictive_state_class) classes = {fictive_state_class, only_final_states, without_final_states} # классы эквивалентности q = classes.copy() # очередь из классов while q: # пока очередь не пуста

some_class = q.pop() # достаем первый элемент из очереди for alpha in alphabet: # для каждой буквы языка

path_sources = set() # состояния, из которых можно попасть в состояния из some_class for state in some_class: if state in inversed_fsm and alpha in inversed_fsm[state]: path_sources.update(inversed_fsm[state][alpha]) # inversed_fsm[state][alpha] -- set for class_to_split in classes.copy(): # для каждого класса # находим общие состояния path_sources и класса p_with_class = class_to_split.intersection(path_sources) if p_with_class: # если есть общее

# находим состояния в классе, отсутствующие в path_sources p_without_class = class_to_split.difference(path_sources) if p_without_class: # если есть разница

classes.remove(class_to_split) # разбиваем класс на два новых classes.add(p_with_class) classes.add(p_without_class)

if class_to_split in q: # если класс есть в очереди

q.remove(class_to_split) # также разбиваем его

q.add(p_with_class)

q.add(p_without_class) else: # иначе добавляем наименьший if len(p_with_class) <= len(p_without_class):

q.add(p_with_class) else:

q.add(p_without_class)

# формируем new_states, new_initial_states и new_final_states new_states = dict() # словарь соответствий старых состояний новым new_initial_states = set() # множество новых начальных состояний new_final_states = set() # множество новых заключительных состояний n_class = -1 # номер класса (не изменять)

classes = {some_class for some_class in classes if some_class} # рассматриваем не пустые классы

# сначала рассматриваем классы с начальными состояниями new_non_initial_classes = set()

for some_class in classes: # для каждого класса

if not initial_states.isdisjoint(some_class): # если есть хотя бы одно начальное состояние n_class += 1 n_class_str = str(n_class) new_initial_states.add(n_class_str) for state in some_class: new_states[state] = n_class_str

# начальное состояние может быть заключительным -- нужно учесть

if not final_states.isdisjoint(some_class): # если есть хотя бы одно заключительное состояние new_final_states.add(n_class_str) else: new_non_initial_classes.add(some_class)

# затем рассматриваем классы с не заключительными состояниями new_final_classes = set() for some_class in new_non_initial_classes: if final_states.isdisjoint(some_class): # если заключительные состояния отсутствуют n_class += 1 n_class_str = str(n_class) for state in some_class: new_states[state] = n_class_str else: new_final_classes.add(some_class)

# далее рассматриваем классы с заключительными (и не начальными) состояниями for some_class in new_final_classes: n_class += 1 n_class_str = str(n_class) new_final_states.add(n_class_str) for state in some_class: new_states[state] = n_class_str

# формируем минимальный ДКА по имеющимся классам эквивалентности new_fsm = dict() for some_class in classes: new_state = dict() for state in some_class:

for alpha, alpha_states in fsm.get(state, dict()).items():

if alpha_states in new_states:

new_state[alpha] = new_states[alpha_states] # alpha_states -- str any_state = next(iter(some_class)) # пустые классы ранее были удалены new_fsm[new_states[any_state]] = new_state

# удаляем фиктивное состояние и фиктивные переходы из него if '0' in new_fsm:

new_initial_states = new_fsm['0'].pop(fictive_alpha)

new_initial_states = set(new_initial_states) if isinstance(new_initial_states, (set, list)) else

{new_initial_states} new_fsm.pop('0') if '0' in new_initial_states:

new_initial_states.remove('0')

return new_fsm, new_initial_states, new_final_states

def fsm_plot(filename: str,

fsm: Dict[Union[str, Tuple[str]],

Dict[str, Union[str, Tuple[str], Set[Union[str, Tuple[str]]]]]], initial_states: Union[str, Tuple[str],

List[Union[str, Tuple[str]]], Set[Union[str, Tuple[str]]]], final_states: Union[str, Tuple[str],

List[Union[str, Tuple[str]]], Set[Union[str, Tuple[str]]]]) -> None:

"""

Генерирует диаграмму конечного автомата и сохраняет результат в файл <filename>.png

:param filename: имя файла изображения без расширения

:param fsm: конечный автомат, например, {'1': {'a': {'1', '2'}}, '2': {'a': '1'}}

:param initial_states: множество начальных состояний КА (элементы множества -- строки / кортежи) либо одно начальное состояние (строка / кортеж)

:param final_states: множество заключительных состояний КА (элементы множества -- строки / кортежи) либо одно заключительное состояние (строка / кортеж)

:return: None

"""

import graphviz G = graphviz.Digraph()

G.attr('graph', rankdir="TB") # направление -- сверху вниз

G.node('start', None, {'shape': 'point'}) # начальная точка, из которой идут дуги в начальные состояния initial_states = set(initial_states) if isinstance(initial_states, (set, list)) else {initial_states} final_states = set(final_states) if isinstance(final_states, (set, list)) else {final_states} for state in fsm:

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

G.attr('node', shape='doublecircle' if state in final_states else 'circle')

G.node(','.join(state) if isinstance(state, tuple) else state) G.attr('node', shape='point') for state, alphas in fsm.items(): if state in initial_states:

G.edge(tail_name='start', head_name=state, label='') # из начальной точки в начальное состояние # формируем словарь для соответствий "целевое состояние" -> "метки дуг, идущих в целевое состояние" arcs = dict()

for alpha, alpha_states in alphas.items(): # просматриваем все метки переходов из state if isinstance(alpha_states, (str, tuple)): # если alpha_states -- это одно состояние alpha_states = {alpha_states} # заводим множество с одним состоянием

for alpha_state in alpha_states: # просматриваем множество if state != alpha_state: # петли обрабатываем в конце

arcs.setdefault(alpha_state, set()) # по умолчанию -- пустое множество

arcs[alpha_state].add(alpha) # добавляем метку в словарь

# обрабатываем добавленные в arcs состояния for alpha_state, alpha_state_alphas in arcs.items():

G.edge(tail_name=','.join(state) if isinstance(state, tuple) else state,

head_name=','.join(alpha_state) if isinstance(alpha_state, tuple) else alpha_state, label=','.join(sorted(alpha_state_alphas, key=to_flatten_tuple))) # рисуем дугу # обрабатываем петли (чтобы не было много дуг: делаем одну с нужными метками через запятую)

state_alpha_state = set() # множество всех меток петель for alpha, alpha_states in alphas.items(): if isinstance(alpha_states, (str, tuple)): # если alpha_states -- это одно состояние alpha_states = {alpha_states} # заводим множество с одним состоянием

for alpha_state in alpha_states: # просматриваем множество if state == alpha_state: # если петля state_alpha_state.add(alpha) # добавляем метку

break # искать дальше по той же метке смысла нет -- метку уже добавили if state_alpha_state: # если петли есть

G.edge(tail_name=','.join(state) if isinstance(state, tuple) else state, head_name=','.join(state) if isinstance(state, tuple) else state,

label=','.join(sorted(state_alpha_state, key=to_flatten_tuple))) # рисуем петлю G.render(filename, format='png', cleanup=True)

def fsm_int_states_to_str(fsm: Dict[Any, Dict[Any, Any]]) -> Dict[str, Dict[str, Union[str, Set[str]]]]:

"""

Преобразует числовые состояния КА в строковые: 1 -> '1', 2 -> '2' и т. д.

:param fsm: конечный автомат

:return: конечный автомат со строковыми состояниями

"""

return {str(state): {str(alpha): set((str(x) for x in sorted(alpha_states)))

if isinstance(alpha_states, (set, tuple, list)) and len(alpha_states) != 1 else str(next(iter(alpha_states)))

if isinstance(alpha_states, (set, tuple, list)) and len(alpha_states) == 1 else str(alpha_states) for alpha, alpha_states in alphas.items()} for state, alphas in fsm.items()} def fsm_startswith(alphabet: str, prefix: str) -> (Dict[str, Dict[str, str]], Set[str], Set[str]):

"""

Формирует конечный автомат для проверки принадлежности слова языку над алфавитом alphabet, такому что все слова языка начинаются на prefix

:param alphabet: алфавит языка

:param prefix: первые буквы всех слов языка

:return: (минимальный детерминированный конечный автомат, множество начальных состояний, множество заключительных состояний)

"""

if not set(prefix).issubset(alphabet): # если в prefix есть символ не из алфавита alphabet raise Exception('input prefix contains invalid symbol') initial_state = 1 # начальное состояние

last_state = initial_state + len(prefix) # конечное состояние fsm = dict() # конечный автомат

fsm[last_state] = {x: {last_state, } for x in alphabet} # для последнего -- все переходы указывают на него for i, x in enumerate(prefix, initial_state): # обработка остальных состояний fsm[i] = {x: {i + 1, }} # переход в следующее состояние из текущего fsm = fsm_int_states_to_str(fsm) # преобразование числовых состояний в строковые

# КА получается детерминированным и минимальным return fsm, {str(initial_state)}, {str(last_state)} def fsm_endswith(alphabet: str, suffix: str) -> (Dict[str, Dict[str, str]], Set[str], Set[str]):

"""

Формирует конечный автомат для проверки принадлежности слова языку над алфавитом alphabet, такому что все слова языка заканчиваются на suffix

:param alphabet: алфавит языка

:param suffix: последние буквы всех слов языка

:return: (минимальный детерминированный конечный автомат, множество начальных состояний, множество заключительных состояний)

"""

if not set(suffix).issubset(alphabet): # если в prefix есть символ не из алфавита alphabet raise Exception('input suffix contains invalid symbol') initial_state = 1 # начальное состояние

last_state = initial_state + len(suffix) # конечное состояние fsm = dict() # конечный автомат

fsm[initial_state] = {x: {initial_state, } for x in

alphabet} # для первого -- все переходы указывают на него самого if suffix: # если имеется суффикс

fsm[initial_state][suffix[0]].add(initial_state + 1) # добавляем переход из нач. состояния во второе

for i, x in enumerate(suffix[1:], initial_state + 1): # обработка остальных состояний fsm[i] = {x: {i + 1, }} # переход в следующее состояние из текущего fsm[last_state] = {} # из последнего состояния нет перехода

fsm = fsm_int_states_to_str(fsm) # преобразование числовых состояний в строковые # в этой функции автомат получается недетерминированным в случае len(suffix) > 0,

# поэтому его нужно преобразовать в детерминированный

return fsm_convert_and_minimize(fsm, str(initial_state), str(last_state))

def fsm_check(string: str,

fsm: Dict[str, Dict[str, str]], initial_state: Union[str, Tuple[str]], final_states: Union[str, Tuple[str],

List[Union[str, Tuple[str]]], Set[Union[str, Tuple[str]]]]) -> bool:

"""

Определяет, принадлежит ли слово string языку, который распознает конечный автомат fsm с начальным состоянием initial_state и заключительными состояниями final_states

:param string: слово

:param fsm: детерминированный конечный автомат

:param initial_state: начальное состояние

:param final_states: множество заключительных состояний КА (элементы множества -- строки / кортежи) либо одно заключительное состояние (строка / кортеж) :return: True, если слово string принадлежит языку, и False, если не принадлежит

"""

final_states = set(final_states) if isinstance(final_states, (set, list)) else {final_states} state = initial_state # начинаем с начального состояния for x in string: # для каждой буквы слова

transition = fsm.get(state) # находим возможные переходы из текущего состояния if transition is None: # если переходов у состояния нет return False # значит не принадлежит

state = transition.get(x) # пробуем перейти по метке x в следующее состояние if state is None: # если метка перехода не найдена return False # значит не принадлежит

return state in final_states # слово принадлежит языку, если последнее состояние -- заключительное

def main(): def print_fsm(fsm: Any, final_states: Any) -> None: import pprint

print('Детерминированный конечный автомат:') pprint.pprint(fsm)

print(f'Заключительные состояния: {final_states}')

def print_examples(fsm: Dict[str, Dict[str, str]], initial_state: Union[str, Tuple[str]], final_states: Union[str, Tuple[str],

List[Union[str, Tuple[str]]], Set[Union[str, Tuple[str]]]]) -> None:

examples = ['', '0', '1', '00', '01', '10', '11', '0001', '1000', '0110', '1100', '0011', '001100', '110011'] for example in examples:

result = fsm_check(string=example, fsm=fsm, initial_state=initial_state, final_states=final_states) print(f"'{example}': {result}" + (', ' if example != examples[-1] else ''), end='') print(end='\n\n')

def print_fsm_and_examples(alphabet: str, ix: str, task_type: str) -> None: if task_type == 'startswith':

print(f'Слова языка начинаются на {ix}')

fsm, initial_states, final_states = fsm_startswith(alphabet=alphabet, prefix=ix) elif task_type == 'endswith': print(f'Слова языка заканчиваются на {ix}')

fsm, initial_states, final_states = fsm_endswith(alphabet=alphabet, suffix=ix) else:

print('Task is missing') return None print_fsm(fsm, final_states)

print_examples(fsm, next(iter(initial_states)), final_states) try:

fsm_plot(f'{task_type}_{ix}', fsm, initial_states, final_states) except Exception as e:

print(e, file=sys.stderr) return None

alphabet = '01'

print(f'Алфавит: {alphabet}')

# Вариант 1

print_fsm_and_examples(alphabet, '00', 'endswith')

if __name__ == "__main__": main()

Вывод программы и диаграммы состояний представлены в таблице 2.

Таблица 2. Вывод программы и соответствующие диаграммы состояний

Вывод программы

Диаграмма состояний

Алфавит: 01

Слова языка заканчиваются на 00 Детерминированный конечный автомат:

{'1': {'0': '2', '1': '1'},

'2': {'0': '3', '1': '1'},

'3': {'0': '3', '1': '1'}}

Заключительные состояния: {'3'}

'': False, '0': False, '1': False,

'00': True, '01': False, '10': False,

'11': False, '0001': False, '1000': True,

'0110': False, '1100': True,

'0011': False, '001100': True,

'110011': False