УДК 519.688
DOI: 10.24412/2071-6168-2024-2-127-128
ВИЗУАЛИЗАЦИЯ АЛГОРИТМА ДЕЙКСТРЫ
А.О. Гюллинг, Н.В. Воронцова
Статья посвящена рассмотрению разработанного алгоритма и программы решения задачи визуализации алгоритма Дейстры.
Ключевые слова: графы, визуализация, язык программирования С#, элемент WPF
Решается задача нахождения кратчайшего пути, при использовании алгоритма Дейкстры для каждой отдельно взятой вершины графа [1,2].
Алгоритм Дейкстры находит кратчайшие пути от одной из вершин графа до всех остальных. Алгоритм работает только для графов без рёбер отрицательного веса.
Формальное описание алгоритма выглядит следующим образом:
Дан взвешенный ориентированный граф G(V,E) без друг отрицательного веса. Найти кратчайшие пути от некоторой вершины A графа G до всех остальных вершин этого графа.
Каждой вершине из V сопоставим метку — минимальное известное расстояние от этой вершины до A. Алгоритм работает пошагово — на каждом шаге он «посещает» одну вершину и пытается уменьшать метки. Работа алгоритма завершается, когда все вершины посещены [3].
Инициализация - метка самой вершины A полагается равной 0, метки остальных вершин — бесконечности. Это отражает то, что расстояния от A до других вершин пока неизвестны. Все вершины графа помечаются как не являющиеся посещенными.
Шаг алгоритма - если все вершины посещены, алгоритм завершается. В противном случае, из ещё не посещённых вершин выбирается вершина U, имеющая минимальную метку. Мы рассматриваем всевозможные маршруты, в которых U является предпоследним пунктом. Вершины, в которые ведут рёбра из U, назовём соседями этой вершины. Для каждого соседа вершины U, кроме отмеченных как посещённые, рассмотрим новую длину пути, равную сумме значений текущей метки U и длины ребра, соединяющего U с этим соседом. Если полученное значение длины меньше значения метки соседа, заменим значение метки полученным значением длины. Рассмотрев всех соседей, пометим вершину U как посещённую и повторим шаг алгоритма.
Алгоритм Дейкстры достаточно прост в реализации. Под конец его работы все вершины должны быть помечены бирками с минимальным расстоянием до них из первоначальной вершины. Первоначальная вершина, что логично, должна выбираться самим пользователем.
В программной реализации большую роль сыграет тип данных List, используемый в C#. Это строго типизированный список объектов, позволяющий удалять или добавлять элементы, и обращаться к ним по индексу. Это упростит задачу для составления «подграфа» с уже посещенными вершинами, а также упростит способ проверки вершины на её фактическую принадлежность посещенным вершинам.
Ниже демонстрируется непосредственно программный код алгоритма Дейкстры, используемый в программе-прототипе с пояснениями в виде комментариев [4,5]:
public int[] GetShortPatch(int vertex_swopped) { /*Выполнение алгоритма Дейкстры*/
vertex_swopped--; int min;
var index_min = 0;
var short_path_vertex = new int[Size];//Индекс - порядковый номер вершины, содержимое - подсчитанное расстояние
for (int i = 0; i < Size; i++) {
short_path_vertex[i] = int.MaxValue;//Устанавливаем бесконечно большое число, чтобы пометить
вершины как непосещенные }
short_path_vertex[vertex_swopped] = 0;//Начальную вершину указываем с оптимальным маршрутом равным 0
var list_cheked_vertex = new List<int>();//Список уже посещенных вершин
list_cheked_vertex.Add(vertex_swopped);//Добавляем стартовую вершину, для которой надо найти все кратчайшие пути
while (list_cheked_vertex.Count != Size) {//Работа алгоритма до тех пор, пока все вершины не будут
посещены
min = int.MaxValue;
for (int i = 0; i < list_cheked_vertex.Count; i++) {//Перебор посещенных вершин с целью найти кратчайшие пути к неизвестным вершинам
for (int j = 0; j < Size; j++) {//Перебор расстояний отдельно взятой посещенной вершины /*Не входит ли рассматриваемая вершина в уже посещенный список*/
if (!list_cheked_vertex.Contains(j) && Matrix[list_cheked_vertex[i], j] != 0) { /*Перепись кратчайшего пути к еще не посещённым вершинам*/
if (short_path_vertex[list_cheked_vertex[i]] + Matrix[list_cheked_vertex[i], j] < short_path_vertex[j]) {
short_path_vertex[j] = short_path_vertex[list_cheked_vertex[i]] + Matrix[list_cheked_vertex[i], j];
}
/*Подборка минимального пути, ведущего к вершине, которую надо будет добавить следующей в список посещенных*/
if (min > short_path_vertex[j]) {
min = short_path_vertex[j];
index_min = j;
} } } }
list_cheked_vertex.Add(index_min);//Добавляем вершину, к которой ведет минимальный путь }
return short_path_vertex;
}
Данная реализация алгоритма была протестирована и успешно работает. Однако, фактическая её ценность будет лишь после интегрирования полученного ответа в методы отрисовки конечного графа. Необходимо провести приготовления в блок программы класса GraphVisible, отвечающего за визуализацию. Так как главной ценностью полученных результатов является стоимость маршрута, распределенная по вершинам графа [6].
Задачей кратчайшего пути, будет ввод пользователем в специальное окно номера вершины, расстояние до которой нужно будет просчитать, показано на рис.1.
Настройка алгоритмов
Тип исполняемого алгоритма: Нахождение кратчайшего пути "
(®) Ввод матрицы вручную О Рандомизатор
Касгройка размеров графа
Вершина поиска:
Количество вершин:
Обнулить матрицу
Рис. 1. Новый блок входных данных в программе
Проверка входных данных матрицы смежности на связность будет использоваться - специально написанной рекурсивной функцией для поиска в глубину графа. Код функции и её вспомогательной функции приведён для наглядности ниже [7,8]:
public bool Graph_Connec_Chek() {
/*Проверка графа на связность для исполнения алгоритма минимального остовного дерева, кратчайшего пути и коммивояжера*/
var list_vert = new List<int>(); list_vert.Add(0);
Graph_Connec_Chek_Rec(ref list_vert, 0); if (list_vert.Count == Size) {
return true; }
return false; }
private void Graph_Connec_Chek_Rec(ref List<int> list_vert, int i) {
/*Рекурсивный алгоритм проверки на связность*/
for (int j = 0; j < Size; j++) {
if (Matrix[i, j] > 0) {
if (!list_vert.Contains(j)) {
list_vert.Add(j);
if (list_vert.Count != Size) {
Graph_Connec_Chek_Rec(ref list_vert, j); } } } }
}
Поскольку защита от ввода ошибочных данных в программе почти полностью реализована в виде упреждения ошибок - пользователю просто не покажется кнопка отрисовки графа в случае ввода неверных данных, то данная методика должна распространяться и на новые входные данные. Данные изменения также необходимо зафиксировать в коде программы [9]:
private void AlgorithmComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e) { /*Событие, которое рандомизирует граф во время переключения алгоритма, если рандом был включен, регулирует видимость кнопки выбора вершины алгоритма Дейкстры, а также корректирует отображение главной кнопки построения графа в случае выбора алгоритма Дейкстры или прочих алгоритмов*/ if (AlgorithmComboBox.Selectedlndex != 3) {
vertex_Dijkstra_name.Visibility = Visibility.Hidden; vertex_box_Dijkstra.Visibility = Visibility.Hidden; var reg = new Regex(@"A[1-9]{1}[0-9]{0,1}$"); if (reg.Match(vertex_box.Text).Success) {
if (Convert.ToInt32(vertex_box.Text) <= 25 && Convert.ToInt32(vertex_box.Text) > 3) {
start_but.Visibility = Visibility.Visible; } else {
start_but.Visibility = Visibility.Hidden;
}
горитма
}
} else {
var reg = new Regex(@"A[1-9]{1}[0-9]{0,1}$"); if (reg.Match(vertex_box.Text). Success) {
if (Convert.ToInt32(vertex_box.Text) <= 25 && Convert.ToInt32(vertex_box.Text) > 3) { vertex_Dijkstra_name.Visibility = Visibility.Visible; vertex_box_Dijkstra.Visibility = Visibility.Visible;
vertex_box_Dijkstra_cheked();//Повторная проверка на отображение кнопки для запуска ал}
} }
if (Input_randomize.IsChecked == true) { Input_randomize_Checked(sender, e);
}
}
При тестовом запуске прототипа программы необходимо убедиться в корректности написанной проверки на ошибки. Элемент WPF TextBox, в котором хранится указанная вершина, не должен влиять на работу других алгоритмов, и должен блокировать кнопку запуска построения графа в случае ввода некорректных данных непосредственно при выборе задачи о нахождении кратчайшего пути. Результат проверки виден на рис.2.
Нахождение кратчайшего пути
Настройка алгоритмов
Тип исполняемого алгоритма: О Ввод матрицы вручную ® Рандомизатор
Настройка размеров графа
Количество вершин: I Повтор рандома |
I Обнулить матрицу |
Строка: не выбрана | Столбец: не выбран
-о алгоритма: Нахождение кратчайшего пути О Ввод матрицы вручную (•) Рандомизатор
Настройка размеров графа
Вершина поиска: ^
Количество вершин: ®
Повтор рандома
I Обнулит
Строка: не выбрана | Столбец: не выбран
Рис. 2. Работоспособность проверки - кнопка не отображается при неверном вводе вершины
Как можно заметить, при вводе вершины, превосходящей размерность матрицы смежности, программа не отображает кнопку построения графа - т.е. фактически кнопку запуска решения задачи по алгоритму.
Реализация самого алгоритма Дейкстры в блоке программы, состоящем из вычислительного класса
GraphCore.
Теперь, когда алгоритм был написан в виде программного кода, а в ходе его работы выдаются необходимые для решения задачи о кратчайшем пути данные, эти данные необходимо визуализировать, как и формальный разбор алгоритма на итерации [10].
Результирующие данные несколько специфичны для визуализации на графе - поскольку результат алгоритма - это подграф, расширяющийся в итоге до исходного графа, с подсчитанным расстоянием до каждой вершины, то непосредственно подсветки путей в результирующей отрисовке графа не будет - все пути уже были пройдены. Поэтому, в первую очередь необходимо добавить в метод отрисовки класса GraphVisible новые результирующие веса - расстояния до вершин от искомой вершины.
В программе-прототипе уже имеется элемент GroupBox - это блок, в котором расписана информация по выполненному алгоритму. Этот элемент показывается после нажатия кнопки «построить граф» и находится слева от визуализированных данных, как и аналогичный блок с разбором алгоритма на итерации. Необходимо добавить в блок об информации по выполненному алгоритму список вершин и расстояние до них.
Для этого подойдет такой элемент WPF, как ScrollViewer, потому как количество вершин у графа может быть большим, и такой текст не поместится в статичный контейнер, не способный регулировать свой размер. В ScrollViewer в свою очередь помещается контейнер StackPanel, который в автоматическом режиме будет укладывать все надписи с дистанцией вершин сверху-вниз. Свойство элемента ScrollViewer под названием VerticalScrollBarVisibility с выставленным параметром Auto позволит скрыть всю вложенную группу элементов, что сделает её невидимой в результирующей выдаче других алгоритмов.
Ниже представлен фрагмент кода, отвечающий непосредственно за добавление в контейнер StackPanel результатов алгоритма:
var answer_path = Graph.GetShortPatch(Convert.ToInt32(vertex_box_Dijkstra.Text)); path_vertex_name.Children.Add(new Label {
Content = "Нач. вершина " + Convert.ToInt32(vertex_box_Dijkstra.Text)
});
for (int i = 0; i < answer_path.Length; i++) { path_vertex_name.Children.Add(new Label {
Content = "Вершина " + Convert.ToString(i + 1) + " = " + Convert.ToString(answer_path[i])
});
}
Итак, текстовая информация о результатах выполнения алгоритма Дейкстры выведена пользователю -это базовый минимум, который приносит пользователю практическую пользу. Теперь необходимо отрисовать получившиеся «веса» вершин с дистанцией на самом графе. Добавим в класс отрисовки GraphVisible следующий метод, который затем вызовем в основном теле программы-прототипа:
public void Vertex_Redrawning(int[] answer_vertex) {
//Перерисовка вершин в результате исполнения алгоритма Дейкстры for (int i = 0; i < Vertex_List.Count; i++) { var vertex_weight = new OutlinedText { Width = 25, Height = 25, FontSize = 20,
HorizontalAlignment = HorizontalAlignment.Left, VerticalAlignment = VerticalAlignment.Top,
Margin = new Thickness(VertexCoord[i].X + 6, VertexCoord[i].Y + 8, 0, 0),
Text = Convert.ToString(answer_vertex[i]),
Fill = Brushes.Yellow,
Stroke = Brushes.Black,
Bold = true,
StrokeThickness = 1,
AllowDrop = true,
};
Vertex_Weight_List.Add(vertex_weight); Panel.Children.Add(Vertex_Weight_List[i]);
}
}
Затем, добавим получившийся массив надписей, расположенный рядом с вершинами, в приватный метод класса под названием MouseMove - этот метод отвечает непосредственно за функционирование переноса графа во время манипуляций пользователя с мышью. После этого, можно увидеть результат работ, представленный на изображении ниже. Как видно, теперь информация о решении отображается как в текстовом, так и в графическом виде на рис.3.
£ nöfiv - X
Часть главной задачи решена, алгоритм визуализирован и продемонстрирован пользователю. Однако, ему не хватает подробностей - виден лишь результат. В этом поможет разбитие алгоритма на итерации, блок итерирования расположен под информацией об использованном алгоритме. Для реализации итерирования необходимо взять
искомый алгоритм Дейкстры с его исполнением в виде кода, и добавить туда дополнительные массивы данных, сохраняемые с каждой итерацией алгоритма - в данном случае, законченной итерацией вполне логично можно считать добавление вершины в подграф посещённых вершин.
Для реализации пошагового разбора алгоритма Дейкстры, необходимо сохранять отдельно каждую итерацию, и отправлять её в дочерний класс GraphCore, носящий название AlghoritmGraph. После чего все массивы поэтапных данных необходимо загрузить в блок пошагового разбора алгоритма, являющегося контейнером WPF под названием GroupBox. Отличие данного функционала от двух других алгоритмов заключается только в несколько большем количестве итераций и сохранении дополнительного массива путей вершин, ранее не существовавшего в классе AlghoritmGraph.
Также необходимо написать новый метод в класс GraphVisible, потому как при переключении итераций необходимо перекрашивать и переписывать веса вершин, которые могут меняться в процессе работы алгоритма. Бесконечный путь ранее не посещённой вершины будет именоваться как "INF" и окрашиваться в красный оттенок. Проведя все необходимые работы в коде, можно запустить программу и убедиться в работе итерационного модуля, что продемонстрировано на рис.4, 5, 6
Я нееу
Файл Информация Информация о построенном графе
Тип графа: неориентированный Количество вершин: 7
Задача о кратчайшем пути, Способ: Алгоритм Дейкстры
- X
Нач. верши! Вершина 4 -
11
Построить еще граф
иаговый разбор алгоритма Итерации:
Опис
е итераци>
Итерация 0
Итерация 1
Итерация 2
Итерация 3
Итерация 4 V
Вершиной, от которой планируется считать кратчайший путь, равна 1. Эта вершина помечается дистанцией О, и от неё начнется строительство подграфа-
И И
INF
Рис. 3. Результат работы итерационного модуля, в котором заметен процесс вычисления путей вершин
К RGGV
Файл Информация Информация о построенном графе
Тип графа: неориентированный Количество вершин: 7
Задача о кратчайшем пути, Способ: Алгоритм Дейкстры Нач, вершина 1 Вершина 1 = О Вершина 2 = 35 Вершина 3 = 57 Вершина 4 = 86
Построить еще граф
й разбор алгоритма Итерации: Описание итерации:
Итерация 0 *
Итерация 1
Итерация 2
Итерация 3
Итерация 4 V
GZH
Происходит перебор путей из вершин подграфа. Используются пути, ведущие только в непосещённые вершины. Из этих путей выбирается путь с минимально возможным весом. Непосёщенная вершина, к которой ведет путь, будет добавлена в подграф посещенных вершин. Это стала вершина под номером 3.
а: не выбрана | Количество себео: не существует
Рис. 4. Результат работы итерационного модуля, в котором заметен процесс вычисления путей вершин
и перебора необходимых путей
131
оптимизированными путями вершин
Проведя все необходимые тесты, можно подвести итог: алгоритм Дейкстры был успешно реализован
Список литературы
1. Акимов О.Е. Дискретная математика: логика, группы, графы. Лаборатория Базовых знаний, 2003.
2. Фляйшнер Г. Эйлеровы графы и смежные вопросы. Мир, 2002.
3. Дистель Р. Теория графов. Новосибирск: Институт математики, 2002.
4. Харари Ф. Теория графов. Едиториал УРСС, 2003.
5. Братищенко В.В. Проектирование информационных систем. Иркутск: Изд-во БГУЭП, 2004.
6. Мудров В.И. Задача о коммивояжере. Знание, 1969.
7. Левитин А.В. Алгоритмы. Введение в разработку и анализ. М. Вильямс, 2006.
8. Мак-Доналд, М. WPF: Windows Presentation Foundation в .NET 4.5 с примерами на C# 5.0 для профессионалов / М. Мак-Доналд, Ч. Лейзерсон, Р. Ривест. М. Вильямс, 2013.
9. Скит Д. C# для профессионалов: тонкости программирования, 3-е издание, новый перевод / Д. Скит, Ч. Лейзерсон, Р. Ривест. М. Вильямс, 2014.
10. Албахари Д. C# 6.0. Справочник. Полное описание языка = C# 6.0 in a Nutshell: The Definitive Reference. М. Вильямс, 2018.
Гюллинг Андрей Олегович, магистрант, [email protected], Россия, Тула, Тульский государственный университет им. Л.Н. Толстого,
Воронцова Наталья Вадимовна, канд. техн.наук, доцент, [email protected], Россия, Тула, Тульский государственный университет им. Л.Н. Толстого
VISUALIZATION OF THE DEISTRA ALGORITHM A.O. Gulling, N.V. Vorontsova
The article is devoted to the consideration of the developed algorithm and the program for solving the problem of visualization of the Deistra algorithm.
Key words: graphs, visualization, C# programming language, WPF element.
Gulling Andrey Olegovich, undergraduate, gulling@mail. ru, Russia, Tula, Tula State University named after L.N. Tolstoy,
Vorontsova Natalia Vadimovna, candidate of technical sciences, docent, [email protected], Russia, Tula, Tula State University named after L.N. Tolstoy