\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}. Соответствие вещественного числа разным форматам представления.

    В данной лабораторной работе рассматриваются следующие форматы представления вещественных чисел.
    \begin{itemize}
        \item Целые числа, например: \texttt{''123''}, \texttt{''-456''}, \texttt{''0''}, в т. ч. \texttt{''+0''}, \texttt{''-0''}.
        \item Десятичные числа с разделителем точка, например: \texttt{''123.456''}, \texttt{''-456.789''}, \texttt{''.5''}.
        \item Экспоненциальная форма (буква E может быть как в верхнем, так и в нижнем регистре), например: \texttt{''1.23E4''}, \texttt{''-4.56e-7''}, \texttt{''7e8''}.
    \end{itemize}
    
    Формальное определение синтаксиса предложенного формата вещественных чисел в БНФ нотации:
    \begin{verbatim}
    real           ::= [sign] (integer | float | exponentnumber)
    sign           ::= "+" | "-"
    
    integer        ::= nonzerodigit {digit} | "0" {"0"}
    nonzerodigit   ::= "1" | "2" | ... | "9"
    digit          ::= "0" | nonzerodigit
    
    float          ::= {digit} "." digitpart | digitpart "."
    digitpart      ::= digit {digit}
    
    exponentnumber ::= (digitpart | float) exponent
    exponent       ::= ("e" | "E") [sign] digitpart
    \end{verbatim}
    Где:
    \begin{itemize}
        \item \lbrack R\rbrack -- необязательный элемент (0 или 1 раз)
        \item \{R\} -- повторение элемента (0 или более раз)
        \item P|Q -- альтернатива (либо P, либо Q)
    \end{itemize}


    \newpage
    \section {Математическое описание}
    \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{[+-]?(0+|[1-9][0-9]*)}
        \item Десятичные числа: \\
        \texttt{[+-]?([0-9]*\textbackslash.[0-9]+|[0-9]+\textbackslash.)}
        \item Экспоненциальная форма: \\
        \texttt{[+-]?([0-9]+|[0-9]*\textbackslash.[0-9]+|[0-9]+\textbackslash.)[eE][+-]?[0-9]+}
    \end{itemize}

    Объединяя, получаем следующее регулярное выражение, которое распознает все форматы вещественных чисел в соответствии с БНФ нотацией:
    \begin{verbatim}
    [+-]?(
        0+|
        [1-9][0-9]*|
        [0-9]*\.[0-9]+|
        [0-9]+\.|
        ([0-9]+|[0-9]*\.[0-9]+|[0-9]+\.)[eE][+-]?[0-9]+
    )
    \end{verbatim}
    Разберём структуру этого выражения:
    \begin{itemize}
        \item \texttt{[+-]?} -- необязательный знак числа (плюс или минус)
        \item \texttt{0+|[1-9][0-9]*} -- целое число, которое может быть либо последовательностью нулей, либо цифрой от 1 до 9, за которой следует произвольное количество цифр.
        \item \texttt{[0-9]*\textbackslash.[0-9]+|[0-9]+\textbackslash.} -- десятичное число, которое может быть представлено произвольным количеством цифр до и как минимум одной цифрой после точки, либо произвольным количеством цифр и одной точкой в конце.
        \item \texttt{([0-9]+|[0-9]*\textbackslash.[0-9]+|[0-9]+\textbackslash.)[eE][+-]?[0-9]+} -- экспоненциальная форма числа, состоящая из произвольного количества цифр, либо десятичного числа, за которым следует буква 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_3$ & $S_2$ & $S_6$ & -- \\
        \hline
        $S_1$ & -- & $S_3$ & $S_2$ & $S_6$ & -- \\
        \hline
        $S_2$ & -- & $S_2$ & $S_2$ & $S_5$ & $S_8$ \\
        \hline
        $S_3$ & -- & $S_3$ & $S_4$ & $S_5$ & $S_8$ \\
        \hline
        $S_4$ & -- & $S_4$ & $S_4$ & $S_5$ & $S_8$ \\
        \hline
        $S_5$ & -- & $S_5$ & $S_5$ & -- & $S_8$ \\
        \hline
        $S_6$ & -- & $S_7$ & $S_7$ & -- & -- \\
        \hline
        $S_7$ & -- & $S_7$ & $S_7$ & -- & $S_8$ \\
        \hline
        $S_8$ & $S_9$ & $S_{10}$ & $S_{10}$ & -- & -- \\
        \hline
        $S_9$ & -- & $S_{10}$ & $S_{10}$ & -- & -- \\
        \hline
        $S_{10}$ & -- & $S_{10}$ & $S_{10}$ & -- & -- \\
        \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}


    \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_3$ & $S_2$ & $S_6$ & $S_E$ \\
        \hline
        $S_1$ & $S_E$ & $S_3$ & $S_2$ & $S_6$ & $S_E$ \\
        \hline
        $S_2$ & $S_E$ & $S_2$ & $S_2$ & $S_5$ & $S_8$ \\
        \hline
        $S_3$ & $S_E$ & $S_3$ & $S_4$ & $S_5$ & $S_8$ \\
        \hline
        $S_4$ & $S_E$ & $S_4$ & $S_4$ & $S_5$ & $S_8$ \\
        \hline
        $S_5$ & $S_E$ & $S_5$ & $S_5$ & $S_E$ & $S_8$ \\
        \hline
        $S_6$ & $S_E$ & $S_7$ & $S_7$ & $S_E$ & $S_E$ \\
        \hline
        $S_7$ & $S_E$ & $S_7$ & $S_7$ & $S_E$ & $S_8$ \\
        \hline
        $S_8$ & $S_9$ & $S_{10}$ & $S_{10}$ & $S_E$ & $S_E$ \\
        \hline
        $S_9$ & $S_E$ & $S_{10}$ & $S_{10}$ & $S_E$ & $S_E$ \\
        \hline
        $S_{10}$ & $S_E$ & $S_{10}$ & $S_{10}$ & $S_E$ & $S_E$ \\
        \hline
        \end{tabularx}
        \label{tab:ka}
    \end{table}

    
    \newpage
    Матрица переходов для данного автомата представлена в Таблице~\ref{tab:ka}.
    

    \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
) -> tuple[str, list[str]]:
    result = []
    current_state = self.initial_state
    path = [current_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
        path.append(current_state)

    return "".join(result), path
\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", "S3", "S5", "S7", "S10"}

    transitions = {
        "S0": [("+-", "S1"), ("123456789", "S2"), ("0", "S3"), (".", "S6")],
        "S1": [("123456789", "S2"), ("0", "S3"), (".", "S6")],
        "S2": [("0123456789", "S2"), (".", "S5"), ("eE", "S8")],
        "S3": [("0", "S3"), ("123456789", "S4"), (".", "S5"), ("eE", "S8")],
        "S4": [("0123456789", "S4"), (".", "S5"), ("eE", "S8")],
        "S5": [("0123456789", "S5"), ("eE", "S8")],
        "S6": [("0123456789", "S7")],
        "S7": [("0123456789", "S7"), ("eE", "S8")],
        "S8": [("+-", "S9"), ("0123456789", "S10")],
        "S9": [("0123456789", "S10")],
        "S10": [("0123456789", "S10")],
    }

    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, path = automaton.generate_random_string(stop_prob)
            print(f"Сгенерированная строка: {random_string}")
            print("Путь переходов:", " -> ".join(path))

        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}

    \newpage

    \begin{figure}[h!]
       \centering
       \includegraphics[width=0.8\linewidth]{img/wrong.png}
       \caption{Реакция программы на некорректный пользовательский ввод.}
       \label{fig:wrong}
    \end{figure}

    На Рис.~\ref{fig:wrong} представлена реакция программы на некорректный пользовательский ввод.


    \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}