diff --git a/lab2/.gitignore b/lab2/.gitignore new file mode 100644 index 0000000..96dc833 --- /dev/null +++ b/lab2/.gitignore @@ -0,0 +1,8 @@ +**/* +!.gitignore +!report.tex +!img +!img/** +!programm +!programm/*.py +!programm/*.txt \ No newline at end of file diff --git a/lab2/img/ka.png b/lab2/img/ka.png new file mode 100644 index 0000000..de717da Binary files /dev/null and b/lab2/img/ka.png differ diff --git a/lab2/img/nka.png b/lab2/img/nka.png new file mode 100644 index 0000000..e37ebe7 Binary files /dev/null and b/lab2/img/nka.png differ diff --git a/lab2/img/result1.png b/lab2/img/result1.png new file mode 100644 index 0000000..e4e9573 Binary files /dev/null and b/lab2/img/result1.png differ diff --git a/lab2/img/wrong.png b/lab2/img/wrong.png new file mode 100644 index 0000000..45c793d Binary files /dev/null and b/lab2/img/wrong.png differ diff --git a/lab2/programm/finite_automaton.py b/lab2/programm/finite_automaton.py new file mode 100644 index 0000000..46fce4c --- /dev/null +++ b/lab2/programm/finite_automaton.py @@ -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) diff --git a/lab2/programm/main.py b/lab2/programm/main.py new file mode 100644 index 0000000..143fd83 --- /dev/null +++ b/lab2/programm/main.py @@ -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() diff --git a/lab2/report.tex b/lab2/report.tex new file mode 100644 index 0000000..fd2bb4e --- /dev/null +++ b/lab2/report.tex @@ -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} \ No newline at end of file