\documentclass[bachelor, och, coursework]{SCWorks} % параметр - тип обучения - одно из значений: % spec - специальность % bachelor - бакалавриат (по умолчанию) % master - магистратура % параметр - форма обучения - одно из значений: % och - очное (по умолчанию) % zaoch - заочное % параметр - тип работы - одно из значений: % referat - реферат % coursework - курсовая работа (по умолчанию) % diploma - дипломная работа % pract - отчет по практике % pract - отчет о научно-исследовательской работе % autoref - автореферат выпускной работы % assignment - задание на выпускную квалификационную работу % review - отзыв руководителя % critique - рецензия на выпускную работу % параметр - включение шрифта % times - включение шрифта Times New Roman (если установлен) % по умолчанию выключен \usepackage[T2A]{fontenc} \usepackage[cp1251]{inputenc} \usepackage{graphicx} \usepackage{minted} \usepackage{float} \usepackage[sort,compress]{cite} \usepackage{amsmath} \usepackage{amssymb} \usepackage{amsthm} \usepackage{fancyvrb} \usepackage{longtable} \usepackage{array} \usepackage[english,russian]{babel} \usepackage[colorlinks=true]{hyperref} %\newcommand{\eqdef}{\stackrel {\rm def}{=}} \newtheorem{lem}{Лемма} \begin{document} % Кафедра (в родительном падеже) \chair{математической кибернетики и компьютерных наук} % Тема работы \title{Создание приложения для отрисовки графов и алгоритмов для работы с ними} % Курс \course{2} % Группа \group{211} % Факультет (в родительном падеже) (по умолчанию "факультета КНиИТ") %\department{факультета КНиИТ} % Специальность/направление код - наименование \napravlenie{02.03.02 "--- Фундаментальная информатика и информационные технологии} %\napravlenie{02.03.01 "--- Математическое обеспечение и администрирование информационных систем} %\napravlenie{09.03.01 "--- Информатика и вычислительная техника} %\napravlenie{09.03.04 "--- Программная инженерия} %\napravlenie{10.05.01 "--- Компьютерная безопасность} % Для студентки. Для работы студента следующая команда не нужна. %\studenttitle{Студентки} % Фамилия, имя, отчество в родительном падеже \author{Морозова Андрея Денисовича} % Заведующий кафедрой \chtitle{к.\,ф.-м.\,н., доцент} % степень, звание \chname{С.\,В.\,Миронов} %Научный руководитель (для реферата преподаватель проверяющий работу) \satitle{ст. преподаватель} %должность, степень, звание \saname{М.\,И.\,Сафрончик} % Семестр (только для практики, для остальных % типов работ не используется) \term{2} % Год выполнения отчета \date{2021} \maketitle % Включение нумерации рисунков, формул и таблиц по разделам % (по умолчанию - нумерация сквозная) % (допускается оба вида нумерации) %\secNumbering \tableofcontents % Раздел "Обозначения и сокращения". Может отсутствовать в работе %\abbreviations %\begin{description} % \item SQL "--- англ. structured query language — «язык структурированных запросов; % \item $\det B$ "--- определитель матрицы $B$; % \item ИНС "--- Искусственная нейронная сеть; % \item FANN "--- Feedforward Artifitial Neural Network %\end{description} % Раздел "Определения". Может отсутствовать в работе %\definitions % Раздел "Определения, обозначения и сокращения". Может отсутствовать в работе. % Если присутствует, то заменяет собой разделы "Обозначения и сокращения" и "Определения" %\defabbr % Раздел "Введение" \intro Целью настоящей работы является изучение работы фреймворка для кроссплатформенной разработки ''Flutter'' и разработка приложения для создания графов и взаимодействия с ними. Поставлены задачи: \begin{itemize} \item разбор алгоритмов на графах \item разбор работы с Flutter \item построение приложения \end{itemize} \section{Введение} \subsection{Графы} Граф --- математический объект, состоящий из двух множеств. Одно из них --- любое конечное множество, его элементы называются \textit{вершинами} графа. Другое множество состоит из пар вершин, эти пары называются \textit{ребрами} графа~\cite{IITMMM_2017}. \subsection{Основные определения} \textbf{Ориентированный граф} определяется как пара \textit{(V, E)}, где \textit{V} --- конечное множество, а \textit{E} --- бинарное отношение на \textit{V}, т.~е. подмножество множества ${V \times V}$. Ориентированный граф для краткости называют \textbf{орграфом}. Множество $V$ называют \textbf{множеством вершин графа}, а его элемент называют \textbf{вершиной} графа. Множество $E$ называют \textbf{множеством рёбер}, а его элементы называют \textbf{рёбрами}. Граф может содержать \textbf{рёбра-циклы}, соединяющие вершину с собой. На рисунке~\ref{fig:orgrapf_example} изображен ориентированный граф с множеством вершин \{0, 1, 2, 3, 4\}.~\cite{Algo_2013} \begin{figure}[!ht] \centering \includegraphics[width=9cm]{./pic/orgraph.png} \caption{\label{fig:orgrapf_example} Пример орграфа} \end{figure} В \textbf{неориентированном} графе $G = (V, E)$ множество ребер состоит из \textbf{неупорядоченных} пар вершин: парами являются множества $\{u, v\}$, где $u, v \in V$ и $u \neq v$. Для неориентированного графа $\{u, v\}$ и $\{v, u\}$ обозначают одно и то же ребро. Неориентированный граф не может содержать рёбер-циклов, и каждое ребро состоит из двух различных вершин. На рисунке~\ref{fig:grapf_example} изображен неориентированный граф с множеством вершин \{0, 1, 2, 2, 4\} \begin{figure}[!ht] \centering \includegraphics[width=9cm]{./pic/graph.png} \caption{\label{fig:grapf_example} Пример неориентированного графа} \end{figure} Вершина \textit{v} \textbf{смежна} с вершиной \textit{u}, если в графе имеется ребро $\{u, v\}$. Для неориентированных графов отношение смежности является симметричным, но для ориентированных графов это не обязательно. \textbf{Степенью} вершины в неориентированном графе называется число инцидентных ей рёбер. Для ориентированного графа различают исходящую степень, определяемую как число выходящих из неё рёбер, и входящую степень, определяемую как число входящих в неё рёбер. Сумма исходящей и входящей степеней называется степенью вершины. \textbf{Маршрутом} в графе называется конечная чередующаяся последовательность смежных вершин и ребер, соединяющих эти вершины. Маршрут называется открытым, если его начальная и конечная вершины различны, в противном случае он называется замкнутым. Маршрут называется \textbf{цепью}, если все его ребра различны. Открытая цепь называется \textbf{путем}, если все ее вершины различны. Замкнутая цепь называется \textbf{циклом}, если различны все ее вершины, за исключением концевых. Граф называется \textbf{связным}, если для любой пары вершин существует соединяющий их путь.~\cite{intuit} \subsection{Представление графа} Граф можно описать несколькими способами. \begin{enumerate} \item Перечисление элементов:~\ref{ref:list}. \item Матрица смежности:~\ref{ref:smej}. \item Матрица инцидентности:~\ref{ref:incident}. \item Список смежности:~\ref{ref:spisok}. \end{enumerate} Рассмотрим на примере графа на рисунке~\ref{fig:graph_prim1}. \begin{figure} \centering \includegraphics[width=9cm]{./pic/prim1.png} \caption{\label{fig:graph_prim1} Рассматриваемый граф} \end{figure} \subsubsection{Перечисление элементов}\label{ref:list} Исходя из определения, для того, чтобы задать граф, достаточно перечислить его вершины и ребра (т.е. пары вершин). Пример: $$ V = \{a, b, c, d, e\} $$ $$ E = \{(a, b), (a, c), (a, e), (b, c), (b, d), (c, e), (d, e)\}$$ \subsubsection{Матрица смежности}\label{ref:smej} Пусть $G$ --- граф с $n$ вершинами, пронумерованными числами от 1 до $n$. \textbf{Матрица смежности} --- это таблица с $n$ строками и $n$ столбцами, в которой элемент, стоящий на пересечении строки с номером $i$ и столбца с номером $j$, равен 1, если вершины с номерами $i$ и $j$ смежны, и 0 в противоположном случае. \begin{table}[!ht] %\centering \caption{Пример матрицы смежности} \begin{tabular}{|l|c|c|c|c|} \hline 0 & 1 & 1 & 0 & 1\\ \hline 1 & 0 & 1 & 1 & 0\\ \hline 1 & 1 & 0 & 0 & 1\\ \hline 0 & 1 & 0 & 0 & 1\\ \hline 1 & 0 & 1 & 1 & 0\\ \hline \end{tabular} \end{table} \subsubsection{Матрица инцидентности}\label{ref:incident} Пусть $G$ --- граф с вершинами, пронумерованными числами от 1 до $n$, и ребрами, пронумерованными от 1 до $m$. В матрице инцидентности строки соответствуют вершинам, а столбцы рёбрам. На пересечении строки с номером $i$ и столбца с номером $j$ стоит 1, если вершина с номером $i$ инцидентна ребру с номером $j$, и 0 в противном случае. \begin{table}[!ht] %\centering \caption{Пример матрицы инцидентности} \begin{tabular}{|c|c|c|c|c|c|c|} \hline 1 & 1 & 1 & 0 & 0 & 0 & 0\\ \hline 1 & 0 & 0 & 1 & 1 & 0 & 0\\ \hline 0 & 1 & 0 & 1 & 0 & 1 & 0\\ \hline 0 & 0 & 0 & 0 & 1 & 0 & 1\\ \hline 0 & 0 & 1 & 0 & 0 & 1 & 1\\ \hline \end{tabular} \end{table} \subsubsection{Списки смежности}\label{ref:spisok} Списки смежности часто используются для компьютерного представления графов. Для каждой вершины задается список всех смежных с ней вершин. В структурах данных, применяемых в программировании, списки смежности могут быть реализованы как массив линейных списков. Пример: \begin{itemize} \item[1] : 2, 3, 5 \item[2] : 1, 3, 4 \item[3] : 1, 2, 5 \item[4] : 2, 5 \item[5] : 1, 3, 4 \end{itemize} \subsection{Алгоритмы} \subsubsection{Обход в ширину} Обход в ширину --- один из основных алгоритмов на графах. Пусть задан граф $G = (V, E)$ и выделена исходная вершина $s$. Алгоритм поиска в ширину систематически обходит все ребра $G$ для «открытия» всех вершин, достижимых из $s$, вычисляя минимальное количество рёбер от $s$ s до каждой достижимой из $s$ вершины. Поиск в ширину имеет такое название потому, что в процессе обхода мы идём вширь, то есть перед тем как приступить к поиску вершин на расстоянии $k + 1$, выполняется обход вершин на расстоянии $k$~\cite{Algo_2013}. Приведенная ниже процедура поиска в ширину BFS предполагает, что входной граф $G = (V, E)$ представлен при помощи списков смежности. Псевдокод алгоритма: \inputminted[fontsize=\footnotesize, linenos]{python}{./dart/pseudoBFS.txt} Воспользуемся групповым анализом. Операции внесения в очередь и удаление из нее требует $O(1)$ времени. Следовательно на очередь потребуется $O(V)$ времени. Т.к. максимальная длина списков смежности $\theta(E)$, то время, необходимое для сканирования списков, равно $O(E)$. Расходы на инициализацию: $O(V)$. Таким образом, общее время работы алгоритма: $O(V + E)$. Время поиска в ширину линейно зависит от размера представления графа. \subsubsection{Обход в глубину} Стратегия обхода в ширину состоит в том, чтобы идти ''вглубь'' графа. Сначала исследуются ребра, выходящие из вершины, открытой последней, и покидаем вершину, когда не остается неисследованных ребер --- при этом происходит возврат в вершину, из которой была открыта вершина $v$. Процесс продолжается, пока не будут открыты все вершины, достижимые из исходной. Псевдо код алгоритма: \inputminted[fontsize=\footnotesize, linenos]{python}{./dart/pseudoDFS.txt} \subsubsection{Алгоритм Дейкстры} Алгоритм был предложен голландским исследователем Эдсгером Дейкстрой в 1959 году. Используется для поиска кратчайшего пути от одной вершины до всех остальных. Используется только если вес ребер неотрицательный. В алгоритме поддерживается множество вершин $U$, для которых уже вычислены длины кратчайших путей до них из $s$. На каждой итерации основного цикла выбирается вершина, которой на текущий момент соответствует минимальная оценка кратчайшего пути. Вершина $u$ добавляется в множество $U$ и производится релаксация всех исходящих из неё рёбер. Псевдокод алгоритма: \inputminted[fontsize=\footnotesize, linenos]{python}{./dart/pseudoDijkstra.txt} \subsubsection{Алгоритм Краскала} Алгоритм Краскала — алгоритм поиска минимального остовного дерева во взвешенном неориентированном связном графе. Был описан Джозефом Краскалом в 1956 году. Алгоритм Крускала изначально помещает каждую вершину в своё дерево, а затем постепенно объединяет эти деревья, объединяя на каждой итерации два некоторых дерева некоторым ребром. Перед началом выполнения алгоритма, все рёбра сортируются по весу (в порядке неубывания). Затем начинается процесс объединения: перебираются все рёбра от первого до последнего (в порядке сортировки), и если у текущего ребра его концы принадлежат разным поддеревьям, то эти поддеревья объединяются, а ребро добавляется к ответу. По окончании перебора всех рёбер, все вершины окажутся принадлежащими одному поддереву.\cite{krusc} \inputminted[fontsize=\footnotesize, linenos]{python}{./dart/pseudoKruscal.txt} \section{Инструменты} Рассмотрим используемый язык и библиотеку для отрисовки. \subsection{Dart} В качестве основы используется язык \textbf{Dart}, разработанный компанией Google, и широко используемый для кросс-платформенной разработки~\cite{dart_web}. Dart позиционируется в качестве замены/альтернативы JavaScript. Один из разработчиков языка Марк Миллер (Mark S. Miller) писал, что JavaScript «имеет фундаментальные изъяны»\cite{futureOfJavascript}, которые невозможно исправить. Версия 1.0 вышла в 14 ноября 2013 года. Вторая версия была выпущена в августе 2018 года. В языке появилась надежная система типов, т.~е. во время выполнении программы все переменные будут гарантированно указанному типу~\cite{dart_sound}. В Dart используется сборщик мусора. Синтаксис похож на языки: JavaScript, C\#, Java~\cite{dartInAction}. Пример HelloWorld на Dart: \inputminted[fontsize=\footnotesize, linenos]{dart}{./dart/helloWorld.dart} Dart поддерживает сокращенную запись. Код примера HelloWorld можно записать так: \inputminted[fontsize=\footnotesize, linenos]{dart}{./dart/hW.dart} Концепты языка~\cite{dart_tour}: \begin{itemize} \item Все, что можно поместить в переменную, является объектом, а каждый объект является частью класса; \item Dart -- язык со строгой типизацией, но объявления типов опциональны, т.к. поддерживается определение типа при компиляции; \item В версии языка 2.12 появилась Null безопасность. Каждый объект не может быть Null, если не указано обратное; \item поддерживаются дженерики; \item для объявления локальных функций и переменных необходимо начать имя со знака ''\_'' \end{itemize} . \subsection{Flutter} Для отрисовки информации используется фреймворк c открытым исходным кодом ''Flutter'', разработанный компанией Google. Flutter не использует нативные компоненты для отрисовки интерфейса. В его основе лежит графический движок ''Skia'', написанный преимущественно на С++. Skia --- библиотека для работы с 2D графикой с открытым исходным кодом, поддерживающая работу на большом количестве платформ~\cite{skia_docs} Первая версия выпущена в 2015 году под названием ''Sky'', работала только для Android-приложений. Основная заявленная особенность --- высокая графическая производительность (возможность отображения 120 кадров в секунду). Полная поддержка создания веб-приложений появилась в версии 2.0 (март 2021 года), также разрабатывается поддержка создания настольных приложений для Windows, macOS и Linux и Google Fuchsia. Простейший пример приложения с использованием Flutter: \inputminted[fontsize=\footnotesize, linenos]{dart}{./dart/flutter.dart} Из кода видно, что интерфейс описывается с помощью виджетов. Результат приложения изображен на рисунке~\ref{fig:flutter_example}. \newpage \begin{figure}[!ht] \centering \includegraphics[width=11cm]{./pic/flutter0.png} \caption{\label{fig:flutter_example} Простейшее приложение на Flutter} \end{figure} \section{Реализация} Структура программы разбита на 4 файла: \begin{itemize} \item main.dart --- точка входа в программу; \item drawing\_page.dart --- страница с описанием работы кнопок; \item curve\_painter.dart --- функционал для отрисовки графа; \item graph.dart --- класс для хранения графа и манипуляции с ним. \end{itemize} \subsection{Графы} Информация хранится в виде списков смежности. Полный код можно посмотреть в приложении~\ref{graph}. \subsubsection{Класс для хранения вершины} Класс \textbf{Dot} используется для хранения информации о ребрах, исходящих из данной вершины. Основная информация: \inputminted[fontsize=\footnotesize, linenos]{dart}{./dart/graph/dot.dart} \textit{String \_name} используется для хранения имени, \textit{int num} используется для хранения номера вершины. Информация о ребрах задается в виде \textit{Map \_ln}, где каждой вершине можно присвоить вес ребра. Создать вершину можно тремя способами: \begin{enumerate} \item Пустая точка; \item из двух списков, где в первом список вершин, а во втором - длины пути; \item из \textit{Map}. \end{enumerate} \inputminted[fontsize=\footnotesize, linenos]{dart}{./dart/graph/dot_constr.dart} Для манипуляций с информацией доступно: \begin{enumerate} \item изменение имени; \item добавление пути; \item удаление пути. \end{enumerate} \inputminted[fontsize=\footnotesize, linenos]{dart}{./dart/graph/dot_manip.dart} \subsubsection{Класс для графов} Класс \textbf{Graphs} используется для хранения точек и манипуляции с ними. Основная информация: \inputminted[fontsize=\footnotesize, linenos]{dart}{./dart/graph/graph.dart} \textit{String \_name} исользуется для имени графа, \textit{int \_amount} --- количество вершин, \textit{List \_dots} --- массив точек, \textit{Map \_nameTable} --- отслеживание имени каждой вершины, \textit{bool \_useLength} --- отслеживание взвешенности графа, \textit{bool \_oriented} --- отслеживание ориентированности графа. Для работы графом доступны функции: \begin{enumerate} \item \textbf{String? addDot(Dot a)} --- добавление вершины; \item \textbf{String? addIsolated(String name)} --- добавление изолированной точки; \item \textbf{String? addPath(int from, int to, [int len = 0])} --- добавление пути; \item \textbf{String? delPath(int from, int to)} --- удаление пути; \item \textbf{String? delDot(int inn)} --- удаление вершины; \item \textbf{void flushData()} --- удаление информации из графа; \item \textbf{bool checkDots([bool verbose = false])} --- проверка графа на ошибки; \item \textbf{void \_fixAfterDel(int inn}) --- испралвение нумерации после удаления; \item \textbf{void \_syncNameTable()} --- синхронизация таблицы имен вершин; \item \textbf{void setName(String name)} --- изменение имени; \item \textbf{String? replaceDataFromFile(String path)} --- заменяет информацию графа на информацию из файла; \item \textbf{List getSortedPathList()} --- возвращает список всех путей, отсортированный в порядке неубывания; \item \textbf{void printG()} --- выводит информацию о графе в консоль; \item \textbf{void printToFile(String name)} --- выводит информацию о графе в файл; \end{enumerate} Создать граф можно тремя способами: \begin{enumerate} \item пустой граф; \item из списка смежности; \item из файла. \end{enumerate} \subsubsection{Алгоритмы} Алгоритмы описаны в классе графа в приложении~\ref{graph} .\newline Обход в ширину: \inputminted[fontsize=\footnotesize, linenos]{dart}{./dart/graph/bfs.dart} Обход в глубину: \inputminted[fontsize=\footnotesize, linenos]{dart}{./dart/graph/dfs.dart} Алгоритм Дейкстры: \inputminted[fontsize=\footnotesize, linenos]{dart}{./dart/graph/dijkstra.dart} Алгоритм Краскала: \inputminted[fontsize=\footnotesize, linenos]{dart}{./dart/graph/kruscal.dart} \subsubsection{Интерфейс} Интерфейс описан в файле drawing\_page.dart. Полный код доступен в приложении~\ref{buttons}. Интерфейс программы изображен на рисунке~\ref{fig:programm}. \begin{figure}[!ht] \centering \includegraphics[width=13cm]{./pic/grafs.png} \caption{\label{fig:programm} Интерфейс программы} \end{figure} \newpage В качестве основы выбран виджет, сохраняющий состояние. Построение интерфейса: \inputminted[fontsize=\footnotesize, linenos]{dart}{./dart/page/build.dart} Запуск работы алгоритмов происходит по нажатию соответствующих кнопок, затем страница отрисовки, используя полученные данные, выводит на экране информацию. \subsubsection{Отрисовка графа} Отрисовка элементов графа описана в файле curve\_painter.dart. Код доступен в приложении~\ref{painter}. Для отрисовки информации используется виджет CustomPaint~\cite{custompaint}. Для передачи информации графа используется конструктор CurvePainter. \inputminted[fontsize=\footnotesize, linenos]{dart}{./dart/painter/constr.dart} Отрисовка соединений происходит с помощью пакета ''arrow\_path''. \inputminted[fontsize=\footnotesize, linenos]{dart}{./dart/painter/arrow.dart} Для упрощения отрисовки, точки располагаются на окружности с центом, совпадающим с центром экрана. Сначала вычисляется их местоположение на экране, точка с максимальным количеством соединений располагается в центре: \inputminted[fontsize=\footnotesize, linenos]{dart}{./dart/painter/pos.dart} Процесс вывода на экран: \begin{enumerate} \item вывод вершин на экран; \item определение текущей операции и вывод работы текущего алгоритма; \item вывод ребер; \item вывод длин ребер; \item вывод имен вершин. \end{enumerate} \subsubsection{Итоговый вид приложения} Итоговый вид приложения отображен на картинке~\ref{fig:result} \begin{figure}[!ht] \centering \includegraphics[width=15cm]{./pic/result.png} \caption{\label{fig:result} Интерфейс программы} \end{figure} \newpage % Раздел "Заключение" \conclusion Было разработано простое приложение для создания и работы с графами с использованием Dart и Flutter. В приложении возможно с помощью графического интерфейса: \begin{itemize} \item создать пустой граф; \item добавить вершину; \item удалить вершину; \item добавить/изменить путь; \item удалить путь; \item сохранить граф; \item загрузить граф; \item построить минимальное остовное дерево с помощью алгоритма Краскала; \item найти минимальный путь из выбранной вершины в другие с помощью алгоритма Дейкстры; \item проверить доступность вершин с помощью обхода в глубину; \item построить путь из одной вершины в другую с помощью обхода в ширину. \end{itemize} %Библиографический список, составленный с помощью BibTeX % \bibliographystyle{gost780uv} \bibliography{thesis} % Окончание основного документа и начало приложений % Каждая последующая секция документа будет являться приложением \appendix \section{Код main.dart}\label{mainD} Точка входа в программу. \inputminted[fontsize=\footnotesize, linenos]{dart}{./lib/main.dart} \section{Код страницы отрисовки}\label{buttons} Описание интерфейса и базовые функции для взаимодействия с информацией. \inputminted[fontsize=\footnotesize, linenos]{dart}{./lib/pages/drawing_page.dart} \section{Код отрисовки графа}\label{painter} Вывод на экран информации из графа. \inputminted[fontsize=\footnotesize, linenos]{dart}{./lib/src/curve_painter.dart} \section{Код класса для работы с графом}\label{graph} Основной класс для хранения и взаимодействия с информацией. \inputminted[fontsize=\footnotesize, linenos]{dart}{./lib/src/graph.dart} \noindent \end{document}