Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7030059bbb |
8
lab2/.gitignore
vendored
Normal file
8
lab2/.gitignore
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
**/*
|
||||
!.gitignore
|
||||
!report.tex
|
||||
!img
|
||||
!img/**
|
||||
!programm
|
||||
!programm/*.py
|
||||
!programm/*.txt
|
||||
BIN
lab2/img/ka.png
Normal file
BIN
lab2/img/ka.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 111 KiB |
BIN
lab2/img/nka.png
Normal file
BIN
lab2/img/nka.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 64 KiB |
BIN
lab2/img/result1.png
Normal file
BIN
lab2/img/result1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 37 KiB |
BIN
lab2/img/wrong.png
Normal file
BIN
lab2/img/wrong.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
63
lab2/programm/finite_automaton.py
Normal file
63
lab2/programm/finite_automaton.py
Normal file
@@ -0,0 +1,63 @@
|
||||
import random
|
||||
|
||||
|
||||
class FiniteAutomaton:
|
||||
def __init__(
|
||||
self,
|
||||
transitions: dict[str, list[tuple[str, str]]],
|
||||
initial_state: str,
|
||||
final_states: set[str],
|
||||
alphabet: set[str],
|
||||
):
|
||||
self.transitions = transitions
|
||||
self.initial_state = initial_state
|
||||
self.final_states = final_states
|
||||
self.alphabet = alphabet
|
||||
|
||||
def _get_next_state(self, current_state: str, char: str) -> str | None:
|
||||
if current_state in self.transitions:
|
||||
for transition, next_state in self.transitions[current_state]:
|
||||
if char in transition:
|
||||
return next_state
|
||||
return None
|
||||
|
||||
def process_input(self, input_string: str) -> tuple[str, list[str]]:
|
||||
current_state = self.initial_state
|
||||
transitions_path = [current_state]
|
||||
|
||||
for char in input_string:
|
||||
if char not in self.alphabet:
|
||||
return f"Символ '{char}' не из алфавита", transitions_path
|
||||
|
||||
next_state = self._get_next_state(current_state, char)
|
||||
|
||||
if next_state is None:
|
||||
return "Строка не соответствует", transitions_path
|
||||
|
||||
transitions_path.append(next_state)
|
||||
current_state = next_state
|
||||
|
||||
return (
|
||||
"Строка соответствует"
|
||||
if current_state in self.final_states
|
||||
else "Строка не соответствует"
|
||||
), transitions_path
|
||||
|
||||
def generate_random_string(self, stop_probability: float = 0.3) -> str:
|
||||
result = []
|
||||
current_state = self.initial_state
|
||||
|
||||
while True:
|
||||
if (
|
||||
current_state in self.final_states
|
||||
and random.random() < stop_probability
|
||||
):
|
||||
break
|
||||
|
||||
transition, next_state = random.choice(self.transitions[current_state])
|
||||
|
||||
char = random.choice(transition)
|
||||
result.append(char)
|
||||
current_state = next_state
|
||||
|
||||
return "".join(result)
|
||||
82
lab2/programm/main.py
Normal file
82
lab2/programm/main.py
Normal file
@@ -0,0 +1,82 @@
|
||||
from finite_automaton import FiniteAutomaton
|
||||
|
||||
|
||||
def main():
|
||||
alphabet = set("+-0123456789.,eE")
|
||||
initial_state = "S0"
|
||||
final_states = {"S2", "S4", "S7", "S8", "S9"}
|
||||
|
||||
transitions = {
|
||||
"S0": [("+-", "S1"), ("123456789", "S2"), ("0", "S8")],
|
||||
"S1": [("123456789", "S2"), ("0", "S8")],
|
||||
"S2": [("0123456789", "S2"), (".,", "S3"), ("eE", "S5")],
|
||||
"S3": [("0123456789", "S4")],
|
||||
"S4": [("0123456789", "S4"), ("eE", "S5")],
|
||||
"S5": [("+-", "S6"), ("123456789", "S7"), ("0", "S9")],
|
||||
"S6": [("123456789", "S7"), ("0", "S9")],
|
||||
"S7": [("0123456789", "S7")],
|
||||
"S8": [(".,", "S3")],
|
||||
}
|
||||
|
||||
automaton = FiniteAutomaton(
|
||||
transitions=transitions,
|
||||
initial_state=initial_state,
|
||||
final_states=final_states,
|
||||
alphabet=alphabet,
|
||||
)
|
||||
|
||||
print("Конечный автомат для распознавания форматов вещественных чисел")
|
||||
print("=" * 60)
|
||||
print("Варианты команд:")
|
||||
print(" - check <строка> - проверить, соответствует ли строка автомату")
|
||||
print(
|
||||
" - gen [<вероятность_остановки>] - сгенерировать случайную строку (по умолчанию 0.3)"
|
||||
)
|
||||
print(" - q - выход из программы")
|
||||
print("=" * 60)
|
||||
|
||||
while True:
|
||||
command = input("\nВведите команду: ").strip()
|
||||
|
||||
if not command:
|
||||
continue
|
||||
|
||||
parts = command.split()
|
||||
cmd = parts[0].lower()
|
||||
|
||||
if cmd == "q":
|
||||
print("Выход из программы.")
|
||||
break
|
||||
|
||||
elif cmd == "check":
|
||||
input_string = ""
|
||||
if len(parts) > 1:
|
||||
input_string = " ".join(parts[1:]).strip()
|
||||
|
||||
message, transitions = automaton.process_input(input_string)
|
||||
|
||||
print(f"Результат: {message}")
|
||||
print("Путь переходов:", " -> ".join(transitions))
|
||||
|
||||
elif cmd == "gen":
|
||||
stop_prob = 0.3
|
||||
if len(parts) > 1:
|
||||
try:
|
||||
stop_prob = float(parts[1])
|
||||
if not (0 < stop_prob <= 1):
|
||||
raise ValueError(
|
||||
"Вероятность должна быть больше 0 и меньше либо равна 1"
|
||||
)
|
||||
except ValueError as e:
|
||||
print(f"Ошибка: {e}")
|
||||
continue
|
||||
|
||||
random_string = automaton.generate_random_string(stop_prob)
|
||||
print(f"Сгенерированная строка: {random_string}")
|
||||
|
||||
else:
|
||||
print(f"Неизвестная команда: {cmd}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
664
lab2/report.tex
Normal file
664
lab2/report.tex
Normal file
@@ -0,0 +1,664 @@
|
||||
\documentclass[a4paper, final]{article}
|
||||
%\usepackage{literat} % Нормальные шрифты
|
||||
\usepackage[14pt]{extsizes} % для того чтобы задать нестандартный 14-ый размер шрифта
|
||||
\usepackage{tabularx}
|
||||
\usepackage[T2A]{fontenc}
|
||||
\usepackage[utf8]{inputenc}
|
||||
\usepackage[russian]{babel}
|
||||
\usepackage{amsmath}
|
||||
\usepackage[left=25mm, top=20mm, right=20mm, bottom=20mm, footskip=10mm]{geometry}
|
||||
\usepackage{ragged2e} %для растягивания по ширине
|
||||
\usepackage{setspace} %для межстрочно го интервала
|
||||
\usepackage{moreverb} %для работы с листингами
|
||||
\usepackage{indentfirst} % для абзацного отступа
|
||||
\usepackage{moreverb} %для печати в листинге исходного кода программ
|
||||
\usepackage{pdfpages} %для вставки других pdf файлов
|
||||
\usepackage{tikz}
|
||||
\usepackage{graphicx}
|
||||
\usepackage{afterpage}
|
||||
\usepackage{longtable}
|
||||
\usepackage{float}
|
||||
|
||||
|
||||
|
||||
% \usepackage[paper=A4,DIV=12]{typearea}
|
||||
\usepackage{pdflscape}
|
||||
% \usepackage{lscape}
|
||||
|
||||
\usepackage{array}
|
||||
\usepackage{multirow}
|
||||
|
||||
\renewcommand\verbatimtabsize{4\relax}
|
||||
\renewcommand\listingoffset{0.2em} %отступ от номеров строк в листинге
|
||||
\renewcommand{\arraystretch}{1.4} % изменяю высоту строки в таблице
|
||||
\usepackage[font=small, singlelinecheck=false, justification=centering, format=plain, labelsep=period]{caption} %для настройки заголовка таблицы
|
||||
\usepackage{listings} %листинги
|
||||
\usepackage{xcolor} % цвета
|
||||
\usepackage{hyperref}% для гиперссылок
|
||||
\usepackage{enumitem} %для перечислений
|
||||
|
||||
\newcommand{\specialcell}[2][l]{\begin{tabular}[#1]{@{}l@{}}#2\end{tabular}}
|
||||
|
||||
|
||||
\setlist[enumerate,itemize]{leftmargin=1.2cm} %отступ в перечислениях
|
||||
|
||||
\hypersetup{colorlinks,
|
||||
allcolors=[RGB]{010 090 200}} %красивые гиперссылки (не красные)
|
||||
|
||||
% подгружаемые языки — подробнее в документации listings (это всё для листингов)
|
||||
\lstloadlanguages{ SQL}
|
||||
% включаем кириллицу и добавляем кое−какие опции
|
||||
\lstset{tabsize=2,
|
||||
breaklines,
|
||||
basicstyle=\footnotesize,
|
||||
columns=fullflexible,
|
||||
flexiblecolumns,
|
||||
numbers=left,
|
||||
numberstyle={\footnotesize},
|
||||
keywordstyle=\color{blue},
|
||||
inputencoding=cp1251,
|
||||
extendedchars=true
|
||||
}
|
||||
\lstdefinelanguage{MyC}{
|
||||
language=SQL,
|
||||
% ndkeywordstyle=\color{darkgray}\bfseries,
|
||||
% identifierstyle=\color{black},
|
||||
% morecomment=[n]{/**}{*/},
|
||||
% commentstyle=\color{blue}\ttfamily,
|
||||
% stringstyle=\color{red}\ttfamily,
|
||||
% morestring=[b]",
|
||||
% showstringspaces=false,
|
||||
% morecomment=[l][\color{gray}]{//},
|
||||
keepspaces=true,
|
||||
escapechar=\%,
|
||||
texcl=true
|
||||
}
|
||||
|
||||
\textheight=24cm % высота текста
|
||||
\textwidth=16cm % ширина текста
|
||||
\oddsidemargin=0pt % отступ от левого края
|
||||
\topmargin=-1.5cm % отступ от верхнего края
|
||||
\parindent=24pt % абзацный отступ
|
||||
\parskip=5pt % интервал между абзацами
|
||||
\tolerance=2000 % терпимость к "жидким" строкам
|
||||
\flushbottom % выравнивание высоты страниц
|
||||
|
||||
|
||||
% Настройка листингов
|
||||
\lstset{
|
||||
language=python,
|
||||
extendedchars=\true,
|
||||
inputencoding=utf8,
|
||||
keepspaces=true,
|
||||
% captionpos=b, % подписи листингов снизу
|
||||
}
|
||||
|
||||
\begin{document} % начало документа
|
||||
|
||||
|
||||
|
||||
% НАЧАЛО ТИТУЛЬНОГО ЛИСТА
|
||||
\begin{center}
|
||||
\hfill \break
|
||||
\hfill \break
|
||||
\normalsize{МИНИСТЕРСТВО НАУКИ И ВЫСШЕГО ОБРАЗОВАНИЯ РОССИЙСКОЙ ФЕДЕРАЦИИ\\
|
||||
федеральное государственное автономное образовательное учреждение высшего образования «Санкт-Петербургский политехнический университет Петра Великого»\\[10pt]}
|
||||
\normalsize{Институт компьютерных наук и кибербезопасности}\\[10pt]
|
||||
\normalsize{Высшая школа технологий искусственного интеллекта}\\[10pt]
|
||||
\normalsize{Направление: 02.03.01 <<Математика и компьютерные науки>>}\\
|
||||
|
||||
\hfill \break
|
||||
\hfill \break
|
||||
\hfill \break
|
||||
\hfill \break
|
||||
\large{Лабораторная работа №2}\\
|
||||
\large{<<Конечный автомат и регулярное выражение для распознавания форматов вещественных чисел>>}\\
|
||||
\large{по дисциплине}\\
|
||||
\large{<<Математическая логика>>}\\
|
||||
\large{Вариант 15}\\
|
||||
|
||||
% \hfill \break
|
||||
\hfill \break
|
||||
\end{center}
|
||||
|
||||
\small{
|
||||
\begin{tabular}{lrrl}
|
||||
\!\!\!Студент, & \hspace{2cm} & & \\
|
||||
\!\!\!группы 5130201/20102 & \hspace{2cm} & \underline{\hspace{3cm}} &Тищенко А. А. \\\\
|
||||
\!\!\!Преподаватель & \hspace{2cm} & \underline{\hspace{3cm}} & Востров А. В. \\\\
|
||||
&&\hspace{4cm}
|
||||
\end{tabular}
|
||||
\begin{flushright}
|
||||
<<\underline{\hspace{1cm}}>>\underline{\hspace{2.5cm}} 2025г.
|
||||
\end{flushright}
|
||||
}
|
||||
|
||||
\hfill \break
|
||||
% \hfill \break
|
||||
\begin{center} \small{Санкт-Петербург, 2025} \end{center}
|
||||
\thispagestyle{empty} % выключаем отображение номера для этой страницы
|
||||
|
||||
% КОНЕЦ ТИТУЛЬНОГО ЛИСТА
|
||||
\newpage
|
||||
|
||||
\tableofcontents
|
||||
|
||||
|
||||
\newpage
|
||||
|
||||
\section*{Введение}
|
||||
\addcontentsline{toc}{section}{Введение}
|
||||
Лабораторная №2 по дисциплине <<Математическая логика>> заключается в следующем. Необходимо построить регулярное выражение для заданного варианта, затем создать недетерминированный конечный автомат и детерминировать его. Реализовать программу, которая проверяет введённый текст через реализацию конечного автомата, с вариантами вывода: строка соответствует, не соответствует, символы не из алфавита. Также необходимо реализовать функцию случайной генерации верной строки по полученному конечному автомату.
|
||||
|
||||
\textit{Вариант 15}. Соответствие вещественного числа разным форматам представления.
|
||||
|
||||
\newpage
|
||||
\section {Математическое описание}
|
||||
\subsection{Форматы представления вещественных чисел}
|
||||
|
||||
В данной лабораторной работе рассматриваются следующие форматы представления вещественных чисел.
|
||||
\begin{itemize}
|
||||
\item Целые числа, например: \texttt{''123''}, \texttt{''-456''}, \texttt{''0''}, в т. ч. \texttt{''+0''}, \texttt{''-0''}.
|
||||
\item Десятичные числа с двумя возможными разделителями (точка или запятая), например: \texttt{''123.456''}, \texttt{''-456,789''}.
|
||||
\item Экспоненциальная форма (буква E может быть как в верхнем, так и в нижнем регистре), например: \texttt{''1.23E4''}, \texttt{''-4,56e-7''}, \texttt{''7e8''}.
|
||||
\end{itemize}
|
||||
|
||||
Формальное определение синтаксиса предложенного формата вещественных чисел в БНФ нотации:
|
||||
\begin{verbatim}
|
||||
real ::= decimal | exponential
|
||||
decimal ::= integer [separator digit {digit}]
|
||||
integer ::= [sign] (nonzerodigit {digit} | "0")
|
||||
exponential ::= decimal ("e" | "E") integer
|
||||
sign ::= "+" | "-"
|
||||
nonzerodigit ::= "1" | "2" | ... | "9"
|
||||
digit ::= "0" | nonzerodigit
|
||||
separator ::= "." | ","
|
||||
\end{verbatim}
|
||||
Где:
|
||||
\begin{itemize}
|
||||
\item \lbrack R\rbrack -- необязательный элемент (0 или 1 раз)
|
||||
\item \{R\} -- повторение элемента (0 или более раз)
|
||||
\item P|Q -- альтернатива (либо P, либо Q)
|
||||
\end{itemize}
|
||||
|
||||
|
||||
|
||||
\subsection{Языки и грамматики}
|
||||
Языком над конечным словарем $\Sigma$ называется произвольное множество конечных цепочек над этим словарем.
|
||||
|
||||
\begin{itemize}
|
||||
\item Цепочки языка называются словами (предложениями).
|
||||
\item Над конечным непустым словарем можно определить бесконечное
|
||||
количество слов конечной длины (счетное множество).
|
||||
\item Над конечным
|
||||
непустым словарем можно определить бесконечное количество языков, т.е.
|
||||
подмножество множества всех возможных слов
|
||||
(континуум, как число подмножеств счетного множества).
|
||||
\end{itemize}
|
||||
|
||||
Языки могут быть конечными и бесконечными (содержать бесконечное число цепочек). Словарь всегда конечен.
|
||||
|
||||
Формальная грамматика – способ описания того, какие предложения возможны в языке. Существует два вида грамматик:
|
||||
\begin{itemize}
|
||||
\item порождающие грамматики – правила, позволяющие
|
||||
строить любое предложение языка,
|
||||
\item распознающие грамматики (алгоритмы) - позволяют
|
||||
определить, принадлежит ли данное предложение языку.
|
||||
\end{itemize}
|
||||
|
||||
Распознающая грамматика – это конечный набор
|
||||
правил, алгоритм, который по введенной цепочке
|
||||
определяет, принадлежит цепочка языку, или нет.
|
||||
|
||||
\subsection{Регулярные множества}
|
||||
Регулярные множества, как множества цепочек, построенные над конечным словарем (по определенным правилам) – это языки. Их называют регулярными языками.
|
||||
|
||||
Правила построения регулярных множеств:
|
||||
\begin{itemize}
|
||||
\item Объединение двух регулярных множеств $L_1$ и $L_2$ обозначается $L_1 \cup L_2$ и состоит из цепочек, которые принадлежат хотя бы одному из множеств $L_1$ или $L_2$.
|
||||
$$L_1 \cup L_2 = \{ \alpha \mid \alpha \in L_1 \text{ или } \alpha \in L_2 \}$$
|
||||
|
||||
\item Конкатенация (произведение) двух регулярных множеств $L_1$ и $L_2$ обозначается $L_1 \cdot L_2$ и состоит из цепочек, которые можно разбить на две части, одна из которых принадлежит $L_1$, а другая $L_2$.
|
||||
$$L_1 \cdot L_2 = \{ \alpha \beta \mid \alpha \in L_1 \text{ и } \beta \in L_2 \}$$
|
||||
|
||||
Обозначим $L^0 = \{ \varepsilon \}$, $L^1 = L$, $L^2 = L \cdot L$, $L^{k + 1} = L^k \cdot L$, и так далее. Обозначение $\varepsilon$ обозначает пустую цепочку.
|
||||
|
||||
\item Итерация регулярного множества $L$ обозначается $L^*$ и состоит из цепочек, которые можно разбить на произвольное количество повторений множества $L$.
|
||||
$$L^* = \{ \varepsilon \} \cup L \cup L^2 \cup L^3 \cup \ldots$$
|
||||
\end{itemize}
|
||||
|
||||
\subsection{Регулярные выражения}
|
||||
Регулярные выражения -- формальный язык шаблонов для поиска и выполнения манипуляций с подстроками в тексте. Регулярное выражение -- это формула (pattern, шаблон), задающая правило поиска подстрок в потоке символов.
|
||||
|
||||
Регулярное выражение показывает, как можно построить регулярное множество цепочек из одноэлементных множеств с использованием трех операций: конкатенации, объединения и итерации.
|
||||
|
||||
Примеры регулярных выражений:
|
||||
\begin{itemize}
|
||||
\item $ab + ba^*$ -- представляет регулярное множество:
|
||||
$$\{ a\} \{b \} \cup \{ b \} \{ a \}^*$$
|
||||
\item $(ac)^*b+c^*$ -- представляет регулярное множество:
|
||||
$$\{b, acb, acacb, acacacb, \ldots, \varepsilon, c, cc, ccc, \ldots \}$$
|
||||
\end{itemize}
|
||||
|
||||
В реальных программах и языках программирования используется расширенный синтаксис регулярных выражений, который добавляет множество удобных конструкций. Среди дополнительных возможностей: классы символов (например, \verb|[a-z0-9]|), квантификаторы (\verb|?|, \verb|+|, \verb|{n,m}|), группировка с помощью скобок, обратные ссылки, опережающие и ретроспективные проверки. Эти расширения делают регулярные выражения более компактными и удобными для использования, но не меняют их выразительную мощность с теоретической точки зрения.
|
||||
|
||||
Если регулярные множества это языки, то регулярные выражения -- это распознающие грамматики этих языков.
|
||||
|
||||
\subsection{Регулярные выражения для заданного варианта}
|
||||
|
||||
Для разных форматов представления вещественных чисел были построены следующие регулярные выражения в соответствии с определённой БНФ нотацией:
|
||||
\begin{itemize}
|
||||
\item Десятичные и целые числа: \\
|
||||
\texttt{[+-]?([1-9][0-9]*|0)([.,][0-9]+)?}
|
||||
\item Экспоненциальная форма: \\
|
||||
\texttt{[+-]?([1-9][0-9]*|0)([.,][0-9]+)?[eE][+-]?([1-9][0-9]*|0)}
|
||||
\end{itemize}
|
||||
|
||||
Объединяя все форматы в соответствии с нашей БНФ нотацией, получаем следующее регулярное выражение, которое распознает все форматы вещественных чисел:
|
||||
|
||||
\texttt{[+-]?([1-9][0-9]*|0)([.,][0-9]+)?([eE][+-]?([1-9][0-9]*|0))?}
|
||||
|
||||
Разберём структуру этого выражения:
|
||||
\begin{itemize}
|
||||
\item \texttt{[+-]?} -- необязательный знак числа (плюс или минус)
|
||||
\item \texttt{([1-9][0-9]*|0)} -- целая часть числа, которая может быть либо нулём, либо цифрой от 1 до 9, за которой следует произвольное количество цифр
|
||||
\item \texttt{([.,][0-9]+)?} -- необязательная десятичная часть, состоящая из разделителя (точка или запятая) и как минимум одной цифры
|
||||
\item \texttt{([eE][+-]?([1-9][0-9]*|0))?} -- необязательная экспоненциальная часть, состоящая из буквы E (в любом регистре), необязательного знака и целого числа
|
||||
\end{itemize}
|
||||
|
||||
Таким образом, полученное регулярное выражение распознаёт формат представления вещественных чисел, рассматриваемый в данной работе, и полностью соответствует формальному определению, представленному в БНФ нотации.
|
||||
|
||||
\subsection{Конечный автомат-распознаватель}
|
||||
Конечный автомат-распознаватель – это математическая модель, которая используется для распознавания цепочек символов в соответствии с заданным формальным языком.
|
||||
|
||||
Конечный автомат-распознаватель $A = (S, \Sigma, s_0, \delta, F)$, где:
|
||||
\begin{itemize}
|
||||
\item $S$ – конечное множество состояний
|
||||
\item $\Sigma$ – конечное множество входных символов
|
||||
\item $s_0 \in S$ – начальное состояние
|
||||
\item $\delta: S \times \Sigma \rightarrow S$ – функция переходов
|
||||
\item $F \subseteq S$ – множество финальных (допускающих) состояний
|
||||
\end{itemize}
|
||||
|
||||
Автомат $A$ допускает (распознает) цепочку, если эта цепочка переводит $A$ из начального в одно из финальных состояний. Автомат $A$ допускает язык $L$, если он допускает все цепочки этого языка – и только их.
|
||||
|
||||
Конечный автомат-распознаватель является распознающей грамматикой.
|
||||
|
||||
\subsection{Недетерминированный КА-распознаватель}
|
||||
Недетерминизм - очень удобное свойство формальной
|
||||
модели, его можно определенным образом трактовать,
|
||||
даже и не реализовывая, ограничиваясь только
|
||||
формальными аналитическими преобразованиями.
|
||||
|
||||
В недетерминированном конечном автомате-распознавателе могут быть следующие неоднозначности:
|
||||
\begin{itemize}
|
||||
\item несколько начальных состояний,
|
||||
\item несколько переходов, помеченных одним и тем
|
||||
же символом,
|
||||
\item переходы, помеченные пустым символом $\varepsilon$.
|
||||
\end{itemize}
|
||||
|
||||
Цепочка допускается конечным автоматом, если существует путь, по которому эта цепочка переводит автомат из какого-нибудь начального состояния в какое-нибудь финальное состояние.
|
||||
|
||||
Недетерминированный конечный автомат-распознаватель $A = (S, \Sigma, S_0, \delta, F)$, где:
|
||||
\begin{itemize}
|
||||
\item $S$ – конечное множество состояний
|
||||
\item $\Sigma$ – конечное множество входов
|
||||
\item $S_0 \subseteq S$ – множество начальных состояний
|
||||
\item $\delta: S \times \Sigma \rightarrow 2^S$ – функция переходов
|
||||
\item $F \subseteq S$ – множество финальных состояний
|
||||
\end{itemize}
|
||||
|
||||
\subsection{Теорема Клини}
|
||||
\textbf{Теорема Клини}. Классы регулярных множеств и автоматных языков совпадают. Это значит, что:
|
||||
\begin{itemize}
|
||||
\item Любой язык, распознаваемый конечным автоматом, может быть задан регулярным выражением.
|
||||
\item Для любого регулярного выражения существует конечный автомат, распознающий соответствующий язык.
|
||||
\end{itemize}
|
||||
|
||||
\subsection{Недетерминированный КА-распознаватель для заданного варианта}
|
||||
|
||||
На Рис.~\ref{fig:nka} представлен недетерминированный КА-распознаватель, соответствующий регулярному выражению для заданного варианта.
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=1\linewidth]{img/nka.png}
|
||||
\caption{Недетерминированный КА-распознаватель для заданного варианта.}
|
||||
\label{fig:nka}
|
||||
\end{figure}
|
||||
|
||||
Матрица переходов для данного автомата представлена в Таблице~\ref{tab:nka}.
|
||||
|
||||
\begin{table}[h!]
|
||||
\centering
|
||||
\caption{Таблица переходов для недетерминированного КА-распознавателя.}
|
||||
\footnotesize
|
||||
\begin{tabularx}{\textwidth}{|c|X|X|X|X|X|}
|
||||
\hline
|
||||
\textbf{Состояние\textbackslash Вход} & \textbf{+-} & \textbf{0} & \textbf{1-9} & \textbf{.,} & \textbf{eE} \\
|
||||
\hline
|
||||
$S_0$ & $S_1$ & $S_8$ & $S_2$ & -- & -- \\
|
||||
\hline
|
||||
$S_1$ & -- & $S_8$ & $S_2$ & -- & -- \\
|
||||
\hline
|
||||
$S_2$ & -- & $S_2$ & $S_2$ & $S_3$ & $S_5$ \\
|
||||
\hline
|
||||
$S_3$ & -- & $S_4$ & $S_4$ & -- & -- \\
|
||||
\hline
|
||||
$S_4$ & -- & $S_4$ & $S_4$ & -- & $S_5$ \\
|
||||
\hline
|
||||
$S_5$ & $S_6$ & $S_9$ & $S_7$ & -- & -- \\
|
||||
\hline
|
||||
$S_6$ & -- & $S_9$ & $S_7$ & -- & -- \\
|
||||
\hline
|
||||
$S_7$ & -- & -- & $S_7$ & -- & -- \\
|
||||
\hline
|
||||
$S_8$ & -- & -- & -- & $S_3$ & -- \\
|
||||
\hline
|
||||
$S_9$ & -- & -- & -- & -- & -- \\
|
||||
\hline
|
||||
\end{tabularx}
|
||||
\label{tab:nka}
|
||||
\end{table}
|
||||
|
||||
\subsection{Детерминированный КА-распознаватель для заданного варианта}
|
||||
|
||||
Для того, чтобы преобразовать недетерминированный КА-распознаватель (Рис.~\ref{fig:nka}) в детерминированный, достаточно добавить ещё одно состояние $S_E$, соответствующее недопустимой цепочке символов, и переходы в него из всех остальных состояний.
|
||||
|
||||
На Рис.~\ref{fig:ka} представлен детерминированный КА-распознаватель. Символом \textit{C} (от англ. Complement -- дополнение) обозначены переходы, соответствующие любым символам, кроме тех, по которым уже есть переходы в другие состояния. Символом \textit{A} (от англ. Any -- любой) обозначен переход, соответствующий любому символу.
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=1\linewidth]{img/ka.png}
|
||||
\caption{Детерминированный КА-распознаватель для заданного варианта.}
|
||||
\label{fig:ka}
|
||||
\end{figure}
|
||||
|
||||
Матрица переходов для данного автомата представлена в Таблице~\ref{tab:ka}.
|
||||
|
||||
\begin{table}[h!]
|
||||
\centering
|
||||
\caption{Таблица переходов для детерминированного КА-распознавателя.}
|
||||
\footnotesize
|
||||
\begin{tabularx}{\textwidth}{|c|X|X|X|X|X|}
|
||||
\hline
|
||||
\textbf{Состояние\textbackslash Вход} & \textbf{+-} & \textbf{0} & \textbf{1-9} & \textbf{.,} & \textbf{eE} \\
|
||||
\hline
|
||||
$S_0$ & $S_1$ & $S_8$ & $S_2$ & $S_E$ & $S_E$ \\
|
||||
\hline
|
||||
$S_1$ & $S_E$ & $S_8$ & $S_2$ & $S_E$ & $S_E$ \\
|
||||
\hline
|
||||
$S_2$ & $S_E$ & $S_2$ & $S_2$ & $S_3$ & $S_5$ \\
|
||||
\hline
|
||||
$S_3$ & $S_E$ & $S_4$ & $S_4$ & $S_E$ & $S_E$ \\
|
||||
\hline
|
||||
$S_4$ & $S_E$ & $S_4$ & $S_4$ & $S_E$ & $S_5$ \\
|
||||
\hline
|
||||
$S_5$ & $S_6$ & $S_9$ & $S_7$ & $S_E$ & $S_E$ \\
|
||||
\hline
|
||||
$S_6$ & $S_E$ & $S_9$ & $S_7$ & $S_E$ & $S_E$ \\
|
||||
\hline
|
||||
$S_7$ & $S_E$ & $S_E$ & $S_7$ & $S_E$ & $S_E$ \\
|
||||
\hline
|
||||
$S_8$ & $S_E$ & $S_E$ & $S_E$ & $S_3$ & $S_E$ \\
|
||||
\hline
|
||||
$S_9$ & $S_E$ & $S_E$ & $S_E$ & $S_E$ & $S_E$ \\
|
||||
\hline
|
||||
\end{tabularx}
|
||||
\label{tab:ka}
|
||||
\end{table}
|
||||
|
||||
|
||||
\newpage
|
||||
\phantom{text}
|
||||
\newpage
|
||||
\section{Особенности реализации}
|
||||
\subsection{Общая структура программы}
|
||||
Программа состоит из двух файлов:
|
||||
\begin{itemize}
|
||||
\item \texttt{finite\_automaton.py} -- содержит класс \texttt{FiniteAutomaton} для создания конечных автоматов.
|
||||
\item \texttt{main.py} -- содержит определение конечного автомата для распознавания вещественных чисел, а также функцию \texttt{main}, которая реализует интерактивный интерфейс для проверки и генерации строк.
|
||||
\end{itemize}
|
||||
|
||||
\subsection{Класс \texttt{FiniteAutomaton}}
|
||||
|
||||
Класс \texttt{FiniteAutomaton} это класс для представления конечных автоматов. Код конструктора класса представлен в листинге~\ref{lst:FiniteAutomaton}.
|
||||
|
||||
В классе определены четыре поля.
|
||||
\begin{itemize}
|
||||
\item \texttt{transitions} -- \texttt{dict[str, list[tuple[str, str]]]} -- словарь, определяющий переходы между состояниями. Ключами словаря являются состояния, значениями -- списки кортежей вида \texttt{(transition, next\_state)}, определяющие переходы из данного состояния. \texttt{transition} это строка, содержащая символы, по которым можно перейти из данного состояния в следующее состояние -- \texttt{next\_state}.
|
||||
\item \texttt{initial\_state} -- \texttt{str} -- начальное состояние.
|
||||
\item \texttt{final\_states} -- \texttt{set[str]} -- множество финальных состояний.
|
||||
\item \texttt{alphabet} -- \texttt{set[str]} -- множество символов, которые может содержать входная строка.
|
||||
\end{itemize}
|
||||
|
||||
\begin{lstlisting}[caption={Код конструктора класса FiniteAutomaton.}, label={lst:FiniteAutomaton}]
|
||||
class FiniteAutomaton:
|
||||
def __init__(
|
||||
self,
|
||||
transitions: dict[str, list[tuple[str, str]]],
|
||||
initial_state: str,
|
||||
final_states: set[str],
|
||||
alphabet: set[str],
|
||||
):
|
||||
self.transitions = transitions
|
||||
self.initial_state = initial_state
|
||||
self.final_states = final_states
|
||||
self.alphabet = alphabet
|
||||
\end{lstlisting}
|
||||
|
||||
Класс \texttt{FiniteAutomaton} содержит три метода: \texttt{\_get\_next\_state}, \texttt{process\_input} и \texttt{generate\_random\_string}.
|
||||
|
||||
\subsection{Метод \texttt{\_get\_next\_state}}
|
||||
Приватный метод \texttt{\_get\_next\_state} принимает три параметра: \texttt{self} -- ссылку на объект класса, \texttt{current\_state} (\texttt{str}) -- текущее состояние, и \texttt{char} (\texttt{str}) -- символ, по которому нужно перейти из текущего состояния. Возвращает следующее состояние (\texttt{str}), либо \texttt{None}, если переход невозможен.
|
||||
|
||||
\begin{lstlisting}[caption={Код метода \texttt{\_get\_next\_state}.}, label={lst:get_next_state}]
|
||||
def _get_next_state(self, current_state: str, char: str) -> str | None:
|
||||
if current_state in self.transitions:
|
||||
for transition, next_state in self.transitions[current_state]:
|
||||
if char in transition:
|
||||
return next_state
|
||||
return None
|
||||
\end{lstlisting}
|
||||
|
||||
|
||||
\subsection{Метод \texttt{process\_input}}
|
||||
Метод \texttt{process\_input} принимает два параметра: \texttt{self} -- ссылку на объект класса, и входную строку \texttt{input\_string}. Возвращает кортеж из двух элементов: строку с сообщением о результате проверки, и список пройденных состояний.
|
||||
|
||||
\begin{lstlisting}[caption={Код метода \texttt{process\_input}.}, label={lst:process_input}]
|
||||
def process_input(self, input_string: str) -> tuple[str, list[str]]:
|
||||
current_state = self.initial_state
|
||||
transitions_path = [current_state]
|
||||
|
||||
for char in input_string:
|
||||
if char not in self.alphabet:
|
||||
return f"Символ '{char}' не из алфавита", transitions_path
|
||||
|
||||
next_state = self._get_next_state(current_state, char)
|
||||
|
||||
if next_state is None:
|
||||
return "Строка не соответствует", transitions_path
|
||||
|
||||
transitions_path.append(next_state)
|
||||
current_state = next_state
|
||||
|
||||
return (
|
||||
"Строка соответствует"
|
||||
if current_state in self.final_states
|
||||
else "Строка не соответствует"
|
||||
), transitions_path
|
||||
\end{lstlisting}
|
||||
|
||||
|
||||
\subsection{Метод \texttt{generate\_random\_string}}
|
||||
Метод \texttt{generate\_random\_string} принимает два параметра: \texttt{self} -- ссылку на объект класса, и вероятность остановки в финальном состоянии -- \texttt{stop\_probability} (\texttt{float}). Возвращает сгенерированную строку.
|
||||
|
||||
Шаг алгоритма заключается в следующем:
|
||||
\begin{enumerate}
|
||||
\item Проверяем условие остановки: если текущее состояние является финальным и случайное число оказывается меньше заданной вероятности остановки, то генерация завершается.
|
||||
\item Случайным образом выбираем переход из текущего состояния в следующее.
|
||||
\item Случайным образом выбираем символ из множества символов выбранного перехода.
|
||||
\item Добавляем выбранный символ в результат и переходим в следующее состояние.
|
||||
\end{enumerate}
|
||||
|
||||
\begin{lstlisting}[caption={Код метода \texttt{generate\_random\_string}.}, label={lst:generate_random_string}]
|
||||
def generate_random_string(self, stop_probability: float = 0.3) -> str:
|
||||
result = []
|
||||
current_state = self.initial_state
|
||||
|
||||
while True:
|
||||
if (
|
||||
current_state in self.final_states
|
||||
and random.random() < stop_probability
|
||||
):
|
||||
break
|
||||
|
||||
transition, next_state = random.choice(self.transitions[current_state])
|
||||
|
||||
char = random.choice(transition)
|
||||
result.append(char)
|
||||
current_state = next_state
|
||||
|
||||
return "".join(result)
|
||||
\end{lstlisting}
|
||||
|
||||
|
||||
|
||||
\subsection{Функция \texttt{main}}
|
||||
В функции \texttt{main} (листинг~\ref{lst:Main}) заданы параметры конечного автомата и реализован интерактивный интерфейс пользователя. Функция не принимает параметров и ничего не возвращает.
|
||||
|
||||
Пользователю доступны следующие команды:
|
||||
|
||||
\begin{itemize}
|
||||
\item \texttt{check <строка>} -- проверяет, соответствует ли строка автомату.
|
||||
\item \texttt{gen [<вероятность\_остановки>]} -- сгенерировать случайную строку.
|
||||
\item \texttt{q} -- выход из программы.
|
||||
\end{itemize}
|
||||
|
||||
\begin{lstlisting}[caption={Функция main.}, label={lst:Main}]
|
||||
def main():
|
||||
alphabet = set("+-0123456789.,eE")
|
||||
initial_state = "S0"
|
||||
final_states = {"S2", "S4", "S7", "S8", "S9"}
|
||||
|
||||
transitions = {
|
||||
"S0": [("+-", "S1"), ("123456789", "S2"), ("0", "S8")],
|
||||
"S1": [("123456789", "S2"), ("0", "S8")],
|
||||
"S2": [("0123456789", "S2"), (".,", "S3"), ("eE", "S5")],
|
||||
"S3": [("0123456789", "S4")],
|
||||
"S4": [("0123456789", "S4"), ("eE", "S5")],
|
||||
"S5": [("+-", "S6"), ("123456789", "S7"), ("0", "S9")],
|
||||
"S6": [("123456789", "S7"), ("0", "S9")],
|
||||
"S7": [("0123456789", "S7")],
|
||||
"S8": [(".,", "S3")],
|
||||
}
|
||||
|
||||
automaton = FiniteAutomaton(
|
||||
transitions=transitions,
|
||||
initial_state=initial_state,
|
||||
final_states=final_states,
|
||||
alphabet=alphabet,
|
||||
)
|
||||
|
||||
print("Конечный автомат для распознавания форматов вещественных чисел")
|
||||
print("=" * 60)
|
||||
print("Варианты команд:")
|
||||
print(" - check <строка> - проверить, соответствует ли строка автомату")
|
||||
print(
|
||||
" - gen [<вероятность_остановки>] - сгенерировать случайную строку (по умолчанию 0.3)"
|
||||
)
|
||||
print(" - q - выход из программы")
|
||||
print("=" * 60)
|
||||
|
||||
while True:
|
||||
command = input("\nВведите команду: ").strip()
|
||||
|
||||
if not command:
|
||||
continue
|
||||
|
||||
parts = command.split()
|
||||
cmd = parts[0].lower()
|
||||
|
||||
if cmd == "q":
|
||||
print("Выход из программы.")
|
||||
break
|
||||
|
||||
elif cmd == "check":
|
||||
input_string = ""
|
||||
if len(parts) > 1:
|
||||
input_string = " ".join(parts[1:]).strip()
|
||||
|
||||
message, transitions = automaton.process_input(input_string)
|
||||
|
||||
print(f"Результат: {message}")
|
||||
print("Путь переходов:", " -> ".join(transitions))
|
||||
|
||||
elif cmd == "gen":
|
||||
stop_prob = 0.3
|
||||
if len(parts) > 1:
|
||||
try:
|
||||
stop_prob = float(parts[1])
|
||||
if not (0 < stop_prob <= 1):
|
||||
raise ValueError(
|
||||
"Вероятность должна быть больше 0 и меньше либо равна 1"
|
||||
)
|
||||
except ValueError as e:
|
||||
print(f"Ошибка: {e}")
|
||||
continue
|
||||
|
||||
random_string = automaton.generate_random_string(stop_prob)
|
||||
print(f"Сгенерированная строка: {random_string}")
|
||||
|
||||
else:
|
||||
print(f"Неизвестная команда: {cmd}")
|
||||
\end{lstlisting}
|
||||
|
||||
\newpage
|
||||
\section{Результаты работы программы}
|
||||
Результаты работы программы представлены на Рис.~\ref{fig:result1}.
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=1\linewidth]{img/result1.png}
|
||||
\caption{Результаты работы программы.}
|
||||
\label{fig:result1}
|
||||
\end{figure}
|
||||
|
||||
На Рис.~\ref{fig:wrong} представлена реакция программы на некорректный пользовательский ввод.
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=0.8\linewidth]{img/wrong.png}
|
||||
\caption{Реакция программы на некорректный пользовательский ввод.}
|
||||
\label{fig:wrong}
|
||||
\end{figure}
|
||||
|
||||
|
||||
\newpage
|
||||
\section*{Заключение}
|
||||
\addcontentsline{toc}{section}{Заключение}
|
||||
В ходе выполнения лабораторной работы было построено регулярное выражение для распознавания различных форматов вещественных чисел, созданы недетерминированный и детерминированный конечные автоматы-распознаватели. На основе разработанного автомата была реализована программа, которая проверяет соответствие входной строки заданному формату и генерирует случайные корректные строки.
|
||||
|
||||
Из достоинств выполнения лабораторной работы можно выделить структурирование кода за счёт использования ООП. Вся логика работы с конечными автоматами вынесена в отдельный класс \texttt{FiniteAutomaton} с четко разделенными методами для проверки строк и генерации случайных строк. Создана удобная интерактивная консольная оболочка для взаимодействия с пользователем, позволяющая выполнять различные команды.
|
||||
|
||||
К недостаткам текущей реализации можно отнести следующие аспекты. Во-первых, переходы в автомате представлены в виде строк, содержащих допустимые символы, такой способ представления переходов не является самым оптимальным с точки зрения производительности. Во-вторых, в реализации генерации случайных строк вероятность остановки одинакова для всех финальных состояний, что может приводить к неравномерному распределению различных форматов чисел в генерируемых строках.
|
||||
|
||||
Функционал программы несложно масштабировать. Класс \texttt{FiniteAutomaton} может быть использован для работы с различными конечными автоматами-распознавателями. Для изменения распознаваемого языка достаточно задать новые параметры для автомата, не меняя базовую логику программы. Однако, текущая реализация работает только с символьными переходами, поэтому задать строчные переходы в виде, например, регулярных выражений не представляется возможным. Однако подобный функционал также несложно реализовать, взяв за основу существующий код.
|
||||
|
||||
На выполнение лабораторной работы ушло около 10 часов. Работа была выполнена в среде разработки Visual Studio Code. Программа написана на Python версии 3.10.
|
||||
|
||||
\newpage
|
||||
\section*{Список литературы}
|
||||
\addcontentsline{toc}{section}{Список литературы}
|
||||
|
||||
\vspace{-1.5cm}
|
||||
\begin{thebibliography}{0}
|
||||
\bibitem{vostrov}
|
||||
Востров, А.В. Курс лекций по дисциплине <<Математическая логика>>. URL \url{https://tema.spbstu.ru/compiler/} (дата обращения 01.04.2025 г.)
|
||||
\bibitem{lutz}
|
||||
Лутц, М. Изучаем Python. 5-е изд. / М. Лутц. — СПб.: Питер, 2019. — 1216 с.
|
||||
\bibitem{friedl}
|
||||
Фридл, Дж. Регулярные выражения = Mastering Regular Expressions / Дж. Фридл. — СПб.: Питер, 2001. — 352 с. — (Библиотека программиста).
|
||||
\end{thebibliography}
|
||||
|
||||
\end{document}
|
||||
Reference in New Issue
Block a user