1 Commits
lab1 ... lab2

Author SHA1 Message Date
7030059bbb lab2 2025-04-23 20:24:23 +03:00
8 changed files with 817 additions and 0 deletions

8
lab2/.gitignore vendored Normal file
View File

@@ -0,0 +1,8 @@
**/*
!.gitignore
!report.tex
!img
!img/**
!programm
!programm/*.py
!programm/*.txt

BIN
lab2/img/ka.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 111 KiB

BIN
lab2/img/nka.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

BIN
lab2/img/result1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

BIN
lab2/img/wrong.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

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