Лабы по ПиАА 2024 / Baraeva_Elizaveta_lb3
.docМИНОБРНАУКИ РОССИИ
Санкт-Петербургский государственный
электротехнический университет
«ЛЭТИ» им. В. И. Ульянова (Ленина)
Кафедра МО ЭВМ
отчет
по лабораторной работе №3
по дисциплине «Построение и анализ алгоритмов»
Тема: Задача коммивояжёра
Студентка гр. 2383 |
|
Бараева Е. Н. |
Преподаватель |
|
Шевелева А. М. |
Санкт-Петербург
2024
Цель работы.
Решить задачу коммивояжёра при помощи метода ветвей и границ, а также провести исследование точности работы ветвей и границ по сравнению с жадным алгоритмом на полном графе.
Задание.
Дано: дан полный граф G=(V, E), где V (|V| <= 20) – это вершины графа; E – ребра графа. W(G) – весовая, ассиметричная матрица графа, где wij > 0, wij – натуральные числа, i ≠ j и wij=inf, если i=j.
Требуется:
Найти минимальный гамильтонов цикл и вывести матрицу пути и вес получившегося пути.
Решить задачу методом ветвей и границ.
В отчете должна быть таблица с тестированием вашего кода. Обязательна возможность считывания матрицы из файла: первая строчка размер матрицы, последующие – сама матрица. Бесконечные ребра отмечайте «-1».
Пример:
Ввод:
3
-1 1 3
3 -1 1
1 2 -1
Вывод:
[0, 1, 2], вес – 3.
Исследование: вещественные положительные числа в интервале [0,1], анализ точности работы ветвей и границ по сравнению с жадным алгоритмом на полном графе.
Выполнение работы.
get_bnb_path() – функция, в которой создается очередь queue и пустой список best_path, в котором будет храниться лучший найденный путь. vertexes_amount – количество вершин в графе, min_cost инициализируется как бесконечность. В очередь добавляется кортеж (текущая стоимость, путь), начиная с вершины 0. Пока очередь не пуста, извлекается кортеж с наименьшей стоимостью. Если текущий путь образует гамильтонов цикл и имеет минимальную стоимость, то обновляются min_cost и best_path. Для каждой вершины, кроме последней в текущем пути, рассматриваются возможные следующие вершины и добавляются в очередь с учетом стоимости перехода. В конце функции возвращаются минимальная стоимость и лучший путь.
read_data() – функция, которая запрашивает у пользователя способ ввода данных (из файла или с консоли). Если выбран ввод из файла, то запрашивается его имя, затем считываются количество вершин n и матрица смежности из файла. Если выбран ввод с консоли, то от пользователя запрашиваются такие же параметры. В конце функция возвращает количество вершин n.
В блоке if name == "main" создается пустая матрица matrix, затем вызываются функции read_data для чтения данных и get_bnb_path для нахождения минимального гамильтонова цикла. В конце выводится найденный цикл и его вес.
Тестирование.
Результаты тестирования представлены в табл. 1.
Таблица 1 – Результаты тестирования
№ п/п |
Входные данные |
Выходные данные |
Комментарии |
1. |
3 -1 1 3 3 -1 1 1 2 -1 |
[0, 1, 2], вес - 3.0 |
Граф с 3-я вершинами. Верно. |
2. |
4 -1 0.774 0.926 0.15 0.192 -1 0.054 0.137 0.658 0.469 -1 0.388 0.708 0.738 0.206 -1 |
[0, 3, 2, 1], вес - 1.017 |
Граф с 4-я вершинами. Верно. |
3. |
5 -1 0.465 0.878 0.011 0.22 0.285 -1 0.36 0.469 0.403 0.536 0.839 -1 0.813 0.17 0.679 0.767 0.785 -1 0.623 0.476 0.754 0.77 0.626 -1 |
[0, 3, 1, 2, 4], вес - 1.784 |
Граф с 5-ю вершинами. Верно. |
4. |
6 -1 0.283 0.729 0.256 0.585 0.533 0.103 -1 0.3 0.751 0.037 0.933 0.35 0.841 -1 0.676 0.467 0.701 0.418 0.941 0.252 -1 0.42 0.509 0.854 0.431 0.16 0.504 -1 0.341 0.319 0.588 0.188 0.219 0.278 -1 |
[0, 1, 4, 5, 3, 2], вес - 1.482 |
Граф с 6-ю вершинами. Верно. |
Исследование.
Для проведения исследования был создан генератор матриц смежности полных графов с вершинами от 3 до 11, была написана функция жадного алгоритма. По графику (см. рис. 1) можно сделать вывод о том, что при количестве вершин равном 3 и 4 алгоритмы будут работать одинаково. Однако, если вершин больше 4, то точность жадного алгоритма начинает меняться. Это обусловлено тем, что жадный алгоритм принимает оптимальное решение отталкиваясь от той вершины, где он находится, а метод ветвей и границ обходит все варианты и выбирает лучший. Погрешность жадного алгоритма не столь большая, потому что ячейки матрицы смежности принимают значения от 0 до 1. Если множество значений не было бы ограниченно, то точность алгоритма заметно снизилась.
Рисунок 1 – График исследования точности алгоритмов
Вывод.
Была решена задача коммивояжёра при помощи метода ветвей и границ.
Для этого в ходе выполнения лабораторной работы была написана программа для поиска минимального гамильтонова цикла, считывающая данные с консоли или из файла, зависящая от выбора пользователя. Также был создан генератор тестов для тестирования написанной программы.
В ходе исследования выяснилось, что при количестве вершин меньшем 5 оба алгоритма работают одинаково. Но, если вершин больше 4, то точность жадного алгоритма начинает уступать методу ветвей и границ. Это связано с тем, что жадный алгоритм принимает оптимальное решение отталкиваясь от той вершины, где он находится, а метод ветвей и границ обходит все варианты и выбирает лучший. Именно поэтому метод ветвей и границ работает точнее, но дольше жадного алгоритма.
ПРИЛОЖЕНИЕ А ИСХОДНЫЙ КОД ПРОГРАММЫ
Название файла: branch_and_bound.py
import heapq
INF = float('inf')
def get_bnb_path():
queue, best_path = [], []
vertexes_amount, min_cost = n, INF
heapq.heappush(queue, (0, [0]))
while queue:
current_cost, path = heapq.heappop(queue)
if path[-1] == path[0] and len(path) == vertexes_amount + 1:
if current_cost < min_cost:
min_cost = current_cost
best_path = path
continue
last_vertex = path[-1]
for next_vertex in range(vertexes_amount):
if next_vertex not in path or (next_vertex == path[0] and len(path) == vertexes_amount):
heapq.heappush(queue, (current_cost + matrix[last_vertex][next_vertex], path + [next_vertex]))
return min_cost, best_path[:-1]
def read_data(graph):
print("Enter 0 to enter from file or 1 to enter from console:")
input_type = input()
if input_type == '0':
print("Enter file name:")
file_name = input()
with open(file_name, 'r') as file:
n = int(file.readline())
for i in range(n):
graph.append([float(j) for j in file.readline().split()])
elif input_type == '1':
print("Enter data:")
n = int(input())
for i in range(n):
graph.append([float(j) for j in input().split()])
else:
print("Incorrect input type!")
exit(0)
return n
if __name__ == "__main__":
matrix = []
n = read_data(matrix)
final_cost, final_cycle = get_bnb_path()
print(f'{final_cycle}, вес - {round(final_cost, 3)}')
Название файла: greedy_algorithm.py
from branch_and_bound import read_data
def get_path_using_greedy_algorithm():
min_path, min_cost = [], float('inf')
for start in range(n):
visited, path = [False] * n, [start]
cost, current = 0, start
visited[current] = True
for _ in range(n - 1):
next_node = min((i for i in range(n) if not visited[i]), key=lambda x: matrix[current][x])
visited[next_node] = True
cost += matrix[current][next_node]
path.append(next_node)
current = next_node
cost += matrix[current][start]
if cost < min_cost:
min_cost = cost
min_path = path
return min_cost, min_path
if __name__ == "__main__":
matrix = []
n = read_data(matrix)
final_cost, final_cycle = get_path_using_greedy_algorithm()
print(f'{final_cycle}, вес - {round(final_cost, 3)}')
