You are on page 1of 28

Національний університет “Одеська політехніка”

Інститут комп’ютерних систем

Кафедра інформаційних систем

Розрахунково-графічна робота
з дисципліни «Теорія алгоритмів»
Тема: «Алгоритм визначення найкоротшого шляху»

Виконала:
Студентка групи АІ-226
Каракушан К.Г.

Прийняли:
Арсірій О. О.
Смик С. Ю.

Одеса 2023
Завдання: Розробіть та реалізуйте алгоритм визначення
найкоротшого шляху між будь-якими двома визначними пам’ятками
обраними на мапі.

Мета: Розробити та реалізувати визначення найкоротшого шляху


шляху між будь-якими двома визначними пам’ятками обраними на мапі.
Провести дослідження алгоритму у контексті вирішення конкретної задачі
на знаходження найкоротшого шляху.

1. Опис вхідних та вихідних даних:


Алгоритм Дейкстри:

Вхідні дані: граф, к-сть вершин, початкова вершина, кінцева


вершина, вага ребер графа.

Вихідні дані: найкоротший шлях, вага найкоротшого шляху.

2. Загальні відомості про алгоритм .

Призначення:

o Алгоритм Дейкстри використовується для пошуку


найкоротших шляхів в графі з неорієнтованими або
орієнтованими ребрами та невід'ємними вагами.

Властивості:

o Працює лише для графів без від'ємних циклів.


o Забезпечує точні та оптимальні результати для графів з
додатними вагами ребер.

Оптимальність:
o На кожному кроці вибирається вершина з найменшою
відстанню, тому гарантовано знаходить найкоротший шлях
для кожної вершини.

Складність:

o Часова складність: O((V + E) * log(V)), де V - кількість


вершин, E - кількість ребер.
o Просторова складність: O(V + E).

Дискретність:

o Може використовуватися для розв'язання проблем на графах з


дискретними даними (наприклад, мережі доріг, маршрутизація
пакетів).

Напрямок:

o Працює для орієнтованих і неорієнтованих графів.

Основні Кроки:

Ініціалізація:

o Встановлення початкової вершини та визначення початкових


відстаней до всіх інших вершин як нескінченність, крім
початкової, яка встановлюється на 0.

Основний Цикл:

o Повторення до тих пір, поки є невідвідані вершини.


o Вибір вершини з найменшою відстанню.
o Оновлення відстаней до всіх сусідніх вершин через обране
ребро, якщо нова відстань менша за поточну.

Формування Відповіді:

o Визначення найкоротших відстаней та шляхів від початкової


вершини до всіх інших вершин.

Вихідні Дані:

o Найкоротші відстані та шляхи виводяться як результат роботи


алгоритму.

Застосування:

 Маршрутизація в комп'ютерних мережах.


 GPS-навігація.
 Оптимізація транспортних маршрутів.
 Моделювання мережені електропередач.

Алгоритм Дейкстри є ефективним та широко використовується для


вирішення задач, пов'язаних із пошуком оптимальних шляхів в графах.

Псевдокод:

// Вхідні дані: G - граф (V, E), s - стартовий вузол

// Вихідні дані: pred - масив розміром |V|, де pred[i] - попередник вузла i в


найкоротшому шляху з s,

// dist - масив розміром |V|, де dist[i] - довжина найкоротшого шляху від s


до i
function Dijkstra(G, s):

pred = CreateArray(|V|)

dist = CreateArray(|V|)

pq = CreatePQ()

for each v in V

pred[v] = -1

if v ≠ s:

dist[v] = ∞

else:

dist[v] = 0

InsertInPQ(pq, v, dist[v])

while SizePQ(pq) ≠ 0:

u = ExtractMinFromPQ(pq)

for each v in AdjacencyList(G, u):

if dist[v] > dist[u] + Weight(G, u, v):


dist[v] = dist[u] + Weight(G, u, v)

pred[v] = u

UpdatePQ(pq, v, dist[v])

return (pred, dist)

3. Програмна реалізація алгоритму

Цей код є реалізацією алгоритму пошуку найкоротших шляхів в


орієнтованому графі за допомогою алгоритму Дейкстри. Граф
представлений класом Graph, який містить кількість вершин, список
суміжних вершин для кожної вершини та можливість додавання ребер.

Клас Edge представляє ребро графу і містить інформацію про кінцеву


вершину та вагу ребра.

Основна частина коду містить клас ShortestPathAlgorithm, який містить


статичний метод dijkstra, що реалізовує алгоритм Дейкстри для
знаходження найкоротших шляхів в графі. Результат виконання алгоритму
представлений класом Result, який містить вагу найкоротшого шляху та
список вершин цього шляху.

У основному методі main створюється граф з фіксованою кількістю вершин


та ребер, заданих вручну. Визначаються початкова та кінцева вершини для
пошуку найкоротшого шляху. Викликається метод dijkstra, і результат
виводиться у консоль, включаючи інформацію про граф, початкову та
кінцеву вершини, найкоротший шлях та час виконання алгоритму.
import java.util.*;

public class Main {


public static void main(String[] args) {
// Кількість вершин у графі
int numberOfVertices = 8;
// Створення графу
ShortestPathAlgorithm.Graph graph = new
ShortestPathAlgorithm.Graph(numberOfVertices);

// Додавання ребер у граф


graph.addEdge(1, 2, 50);
graph.addEdge(1, 4, 18);
graph.addEdge(2, 3, 78);
graph.addEdge(2, 5, 45);
graph.addEdge(5, 3, 51);
graph.addEdge(3, 4, 85);
graph.addEdge(3, 7, 78);
graph.addEdge(4, 6, 45);
graph.addEdge(6, 8, 23);
graph.addEdge(7, 8, 6);

// Початкова та кінцева вершини для знаходження найкоротшого


шляху
int source = 1;
int destination = 8;

// Отримання часу до виконання алгоритму


long startTime = System.currentTimeMillis();
// Виклик алгоритму та отримання результату
ShortestPathAlgorithm.Result result =
ShortestPathAlgorithm.dijkstra(graph, source, destination);

// Отримання часу після виконання алгоритму


long endTime = System.currentTimeMillis();

// Виведення інформації у консоль


System.out.println("Граф з " + numberOfVertices + " вершинами та
ребрами:");
graph.printGraph();
System.out.println("\nПочаткова вершина: " + source);
System.out.println("Кінцева вершина: " + destination);
System.out.println("Найкоротший шлях: " + result.shortestDistance);
System.out.println("Шлях до вершини " + destination + ": " +
result.shortestPath);

// Виведення часу виконання алгоритму


System.out.println("Час виконання алгоритму: " + (endTime - startTime)
+ " мс");
}
}
import java.util.*;

public class ShortestPathAlgorithm {

// Клас, який представляє результат виконання алгоритму


static class Result {
int shortestDistance; // Вага найкоротшого шляху
List<Integer> shortestPath; // Список вершин найкоротшого шляху

// Конструктор результату
public Result(int shortestDistance, List<Integer> shortestPath) {
this.shortestDistance = shortestDistance;
this.shortestPath = shortestPath;
}
}

// Клас, який представляє граф


static class Graph {
int vertices; // Кількість вершин у графі
Map<Integer, List<Edge>> adjacencyList; // Список суміжних вершин
для кожної вершини

// Конструктор графу
public Graph(int vertices) {
this.vertices = vertices;
this.adjacencyList = new HashMap<>();
for (int i = 1; i <= vertices; i++) {
this.adjacencyList.put(i, new ArrayList<>());
}
}

// Додавання ребра в граф

public void addEdge(int source, int destination, int weight) {


this.adjacencyList.get(source).add(new Edge(destination, weight));
this.adjacencyList.get(destination).add(new Edge(source, weight));
}

// Виведення графу у консоль


public void printGraph() {
for (int vertex : adjacencyList.keySet()) {
System.out.print("Вершина " + vertex + ": ");
for (Edge edge : adjacencyList.get(vertex)) {
System.out.print("(" + edge.destination + ", " + edge.weight + ") ");
}
System.out.println();
}
}
}

// Клас, який представляє ребро графу


static class Edge {
int destination; // Кінцева вершина ребра
int weight; // Вага ребра

// Конструктор ребра
public Edge(int destination, int weight) {
this.destination = destination;
this.weight = weight;
}
}

// Реалізація алгоритму Дейкстри


public static Result dijkstra(Graph graph, int source, int destination) {
int[] distances = new int[graph.vertices + 1];
int[] previousVertices = new int[graph.vertices + 1];
Arrays.fill(distances, Integer.MAX_VALUE);

// Пріоритетна черга для вибору вершини з найменшою відстанню


PriorityQueue<Edge> priorityQueue = new
PriorityQueue<>(Comparator.comparingInt(e -> e.weight));
priorityQueue.add(new Edge(source, 0));
distances[source] = 0;

while (!priorityQueue.isEmpty()) {
Edge currentEdge = priorityQueue.poll();
int currentVertex = currentEdge.destination;

for (Edge neighbor : graph.adjacencyList.get(currentVertex)) {


int newDistance = distances[currentVertex] + neighbor.weight;

if (newDistance < distances[neighbor.destination]) {


distances[neighbor.destination] = newDistance;
previousVertices[neighbor.destination] = currentVertex;
priorityQueue.add(new Edge(neighbor.destination, newDistance));
}
}
}

// Побудова найкоротшого шляху


List<Integer> path = buildPath(previousVertices, source, destination);
// Повернення результату
return new Result(distances[destination], path);
}

// Метод для побудови найкоротшого шляху


private static List<Integer> buildPath(int[] previousVertices, int source, int
destination) {
List<Integer> path = new ArrayList<>();
int currentVertex = destination;

while (currentVertex != source) {


path.add(currentVertex);
currentVertex = previousVertices[currentVertex];
}

path.add(source);
Collections.reverse(path);

return path;
}
}
4. Приклади та покрокова візуалізація роботи коду

Для наглядності прикладу я використаю зменшений граф з 8


вершинами, щоб доступніше пояснити виконання алгоритму Дейкстри.
Сам граф представлений нижче (рис. 4.1.)
Рис. 4.1. – Граф з вагами.

Вхідні дані в коді:


- Змінна кількості вершин (рис. 4.2.)

Рис. 4.2. – Змінна, яка визначає к-сть вершин в графі

- Додавання ребер в граф (рис. 4.3.)

Рис. 4.3. – Виклики методу graph.addEdge для додавання ребер


в граф
- Визначення початкової і кінцевої точки графу для пошуку
найкоротшого шляху (рис. 4.4.)
-

Рис. 4.4. – Змінні, які визначають початкову і кінцеву точку в графі.

Вихідні дані:
Результат рішення алгоритму в коді виглядає так (рис. 4.5.):

Рис. 4.5. – Результат компіляції програми.

Проведення візуального рішення алгоритму на основі графу (рис. 4.6.):


Рис. 4.6. – Граф з 8 вершинами.

Перший крок:
- Знаходимо невідвідану вершину з найменшою міткою і це
вершина 1. Переглянемо суміжні їй вершини – 2 і 4. Змінюємо
мітки цих вершин на відповідно 50 і 18 (рис. 4.7.)

Рис. 4.7. – Визначення вершин, суміжніх для 1 вершини.


Другий крок:
- Знаходимо невідвідану вершину з найменшою міткою і це 4
вершина з міткою 18. Переглядаємо суміжні їй вершини
1,3,7,6. З 1 вершиною нічого не змінюєтся, 3 вершина приймає
мітку 103, 7 вершина - мітку 92, 6 вершина – мітку 63. (рис.4.8)

Рис. 4.8. – Визначення вершин, суміжніх для 4 вершини.

Третій крок:
- Знаходимо невідвідану вершину з найменшою міткою і це
вершина 2. Переглянемо суміжні їй вершини – 1, 3 і 5. Мітка 1
вершини незмінна, вона початкова, мітка 3 вершини не
змінюється так як 128(50+78)>103, при такій умові значення не
змінюється, мітка 5 вершини змінюється на 95. (рис. 4.9.)
Рис. 4. 9. – Визначення вершин, які суміжні для вершини 2.

Четвертий крок:
- Знаходимо невідвідану вершину з найменшою міткою і це
вершина 6. Переглянемо суміжні їй вершини – 4 і 8. В вершині 4
мітка не змінюється, в вершині 8 мітка приймає значення –
86(18+45+23) (рис.4.10.)

Рис. 4.10. - Визначення вершин, які суміжні для вершини 6


П’ятий крок:
- Знаходимо невідвідану вершину з найменшою міткою і це
вершина 8. Переглянемо суміжні їй вершини – 6 і 7. В 6
вершині мітка не змінюється і в 7 також, так як
18+45+23+6=92, тоді 92=92. (рис. 4.11.)

Рис. 4.11. - Визначення вершин, які суміжні для вершини 8.

Шостий крок:
- Знаходимо невідвідану вершину з найменшою міткою і це
вершина 7. Переглянемо суміжні їй вершини – 8, 4 і 3.
Вершини не змінюються (рис. 4.12.)
Рис. 4.12. - Визначення вершин, які суміжні для вершини 7

Сьомий крок:
- Знаходимо невідвідану вершину з найменшою міткою і це
вершина 5. Переглянемо суміжні їй вершини – 2 і 3. Вершини
не змінюються (рис. 4.13.)

Рис. 4.13. - Визначення вершин, які суміжні для вершини 5


Восьмий крок:
- Знаходимо невідвідану вершину з найменшою міткою і це
вершина 3. Переглянемо суміжні їй вершини – 2, 3, 4, 7.
Вершини не змінюються (рис. 4.14.)

Рис.4.14. - Визначення вершин, які суміжні для вершини 3.

Готовий граф виглядає так (рис. 4.15.):

Рис. 4.15. – Граф, у якому всі вершини мають свої відповідні мітки.
Тепер знаходимо найкоротший шлях в графі від вершини 1 до 8
(рис. 4.16.):

Рис. 4.16 – Знайдений найкоротший шлях.

Можемо зробити висновок, що алгоритм приведений в коді


виконаний вірно, так як відповідь з візуальним рішенням збігається.
Найкоротший шлях в цьому графі – [1, 4, 6, 8], а вага шляху складає 86.

Тепер, дізнавшись, що код написаний правильно, можемо


змоделювати граф, як представлення реальних вулиць, а 2 вершини
(початкова і кінцева) як 2 пам’ятки в Івано-Франківську.

Шукаємо такі пам’ятки і знаходимо (рис 4.17.):


Рис. 4.17. – Представлення 2 пам’яток (Пам’ятник і Меморіал) на
ресурсі Google Maps

Знайдемо, для наглядності, найкоротший шлях для пішої ходи від 1


пам’ятки до 2 на самому ресурсі (рис. 4.18.):

Рис. 4.18. – Представлення найкоротшого шляху на ресурсі.


Тепер формуємо граф, вершини це початкова і кінцева точка, а також
всі перехрестя. Ребра це вулиці, яким ми надамо вагу відповідно до їх
довжини в метрах. (рис. 4.19.)

Рис.4.19. – Граф в вигляді мапи с заданими вагами в метрах.

Тепер вводимо вхідні дані в код нашого графу:



// Кількість вершин у графі
int numberOfVertices = 21;

// Додавання ребер у граф
graph.addEdge(1, 2, 28);
graph.addEdge(2, 3, 70);
graph.addEdge(3, 4, 142);
graph.addEdge(4, 5, 40);
graph.addEdge(5, 6, 170);
graph.addEdge(5, 8, 217);
graph.addEdge(6, 7, 20);
graph.addEdge(7, 9, 111);
graph.addEdge(9, 10, 59);
graph.addEdge(10, 11, 35);
graph.addEdge(11, 13, 69);
graph.addEdge(9, 10, 59);
graph.addEdge(8, 12, 110);
graph.addEdge(12, 13, 92);
graph.addEdge(13, 17, 322);
graph.addEdge(7, 14, 268);
graph.addEdge(9, 15, 286);
graph.addEdge(11, 16, 295);
graph.addEdge(14, 15, 119);
graph.addEdge(15, 16, 100);
graph.addEdge(16, 17, 107);
graph.addEdge(17, 21, 23);
graph.addEdge(16, 18, 128);
graph.addEdge(18, 19, 87);
graph.addEdge(19, 20, 51);
graph.addEdge(21, 20, 65);

// Початкова та кінцева вершини для знаходження найкоротшого шляху
int source = 1;
int destination = 20;

І отримуємо результат (рис. 4.20.):

Рис. 4.20. – Результат знаходження найкоротшого шляху графу з 21


вершиною.

Візуально представимо знайдений найкоротший шлях, на


побудованому графі(рис.4.21):
Рис.4.21 – Найкоротший шлях на графі.

Дивлячись на рисунок 4.18. ми можемо побачити, що найкоротший


шлях знайдений на ресурсі Google Maps співпадає з знайденим
найкоротшим шляхом самостійно, за допомогою алгоритму Дейкстри,
реалізованому в коді.

5. Аналіз отриманих результатів та висновки щодо досягнення


мети РГР.
Результати:

Функціональність: Алгоритм визначення найкоротшого шляху


працює ефективно та швидко. Час виконання алгоритму залежить від
кількості вершин та ребер і це ми можемо наглядно побачити на рисунках
4.5 (граф з 8 вершинами – час виконання 5 мс) і 4.20 (граф з 21 вершиною
– час виконання 7 мс)

Точність: Розрахунки відстаней та маршрутів базуються на точних


даних географічних координат пам'яток, що забезпечує точність
результатів.

Графічне відображення: Створене графічне відображення


найкоротшого шляху на мапі чітко індикації шлях між пам'ятками, що
робить результати легкими для розуміння.

Висновки:
Проект успішно досягнув своєї мети: був розроблений та
реалізований алгоритм для визначення найкоротшого шляху між
визначними пам'ятками. Результати свідчать про високу функціональність
та точність алгоритму. Графічне відображення робить отримані дані
доступними та зрозумілими для користувачів. Проект може бути
використаний як основа для розширення функціональності або
впровадження в реальне використання, наприклад, для туристичних
додатків або навігації.

You might also like