Добавил:
baraevaliza
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз:
Предмет:
Файл:Лабы по ПиАА 2024 / Baraeva_Elizaveta_lb2
.pdf
МИНОБРНАУКИ РОССИИ
САНКТ-ПЕТЕРБУРГСКИЙ ГОСУДАРСТВЕННЫЙ
ЭЛЕКТРОТЕХНИЧЕСКИЙ УНИВЕРСИТЕТ
«ЛЭТИ» ИМ. В. И. УЛЬЯНОВА (ЛЕНИНА)
Кафедра МО ЭВМ
ОТЧЕТ
по лабораторной работе №2
по дисциплине «Построение и анализ алгоритмов»
Тема: Жадный алгоритм и А*
Студентка гр. 2383
Бараева Е. Н.
Преподаватель
Шевелева А. М.
Санкт-Петербург
2024

2
Цель работы.
Изучить информацию о жадном алгоритме и об алгоритме А*.
Используя эти алгоритмы, написать код, для нахождения кратчайшего пути в
графе. Провести исследование по времени работы жадного алгоритма, А* и
точного алгоритма (ветвей и границ).
Задание.
1) Разработайте программу, которая решает задачу построения пути в
ориентированном графе при помощи жадного алгоритма. Жадность в данном
случае понимается следующим образом: на каждом шаге выбирается
последняя посещённая вершина. Переместиться необходимо в ту вершину,
путь до которой является самым дешёвым из последней посещённой
вершины. Каждая вершина в графе имеет буквенное обозначение ("a", "b",
"c"...), каждое ребро имеет неотрицательный вес.
Пример входных данных:
a e
a b 3.0
b c 1.0
c d 1.0
a d 5.0
d e 1.0
В первой строке через пробел указываются начальная и конечная
вершины
Далее в каждой строке указываются ребра графа и их вес
В качестве выходных данных необходимо представить строку, в которой
перечислены вершины, по которым необходимо пройти от начальной
вершины до конечной. Для приведённых в примере входных данных ответом
будет
abcde

3
2) Разработайте программу, которая решает задачу построения
кратчайшего пути в ориентированном графе методом А*. Каждая вершина в
графе имеет буквенное обозначение ("a", "b", "c"...), каждое ребро имеет
неотрицательный вес. В качестве эвристической функции следует взять
близость символов, обозначающих вершины графа, в таблице ASCII.
Пример входных данных:
a e
a b 3.0
b c 1.0
c d 1.0
a d 5.0
d e 1.0
В первой строке через пробел указываются начальная и конечная
вершины
Далее в каждой строке указываются ребра графа и их вес
В качестве выходных данных необходимо представить строку, в которой
перечислены вершины, по которым необходимо пройти от начальной
вершины до конечной. Для приведённых в примере входных данных ответом
будет
ade
Исследование: проанализировать время работы жадного алгоритма, А* и
точного алгоритма (ветвей и границ) в зависимости от размера графа,
заполненного на 75%.
Основные теоретические положения.
Жадный алгоритм – алгоритм, заключающийся в принятии локально
оптимальных решений на каждом этапе, допуская, что конечное решение
также окажется оптимальным.

4
Поиск A* – в информатике и математике, алгоритм поиска по первому
наилучшему совпадению на графе, который находит маршрут с наименьшей
стоимостью от одной вершины (начальной) к другой (целевой, конечной).
Метод ветвей и границ – общий алгоритмический метод для нахождения
оптимальных решений различных задач оптимизации. Для него необходимы
две процедуры: ветвление и нахождение оценок (границ).
Выполнение работы.
• greedy_algorithm() – функция жадного алгоритма, которая принимает
начальную вершину start, конечную вершину end и граф graph в виде
словаря. Внутри функции объявляются переменные way, current_vertex,
которые представляют путь и текущую вершину соответственно. Затем
цикл while продолжается до тех пор, пока текущая вершина не станет
равной конечной вершине. Внутри цикла производится поиск
следующей вершины с минимальным весом из соседних относительно
текущей. Если не удалось найти следующую вершину, и текущая не
является конечной, удаляется последняя вершина из пути и
продолжается цикл. Если же следующая вершина найдена, она
добавляется в путь, и текущая обновляется. По завершении цикла
возвращается путь в виде строки.
• heuristic() – функция, которая принимает текущую вершину vertex и
конечную вершину end. Она возвращает эвристическую оценку
расстояния между текущей вершиной и конечной вершиной.
• get_a_star_path() – функция алгорима A*, которая принимает
начальную вершину start, конечную вершину end и граф graph в виде
словаря. Внутри функции объявляются переменные
weight_and_vertex_list, passed, way_length, которые представляют
список весов и вершин, пройденные вершины и длины пути
соответственно. Затем цикл while продолжается до тех пор, пока список
weight_and_vertex_list не будет пустым. На каждой итерации

5
извлекается вершина с минимальным весом из списка. Если текущая
вершина равна конечной, то строится кратчайший путь от начальной до
конечной вершины и возвращается в виде строки. Если же нет, то для
каждой соседней вершины вычисляется предполагаемая длина пути
через текущую вершину. Далее если предполагаемая длина пути
меньше текущей длины пути до соседней вершины, то обновляется
информация о прохождении, длине пути, и соседняя вершина
добавляется в список с учетом эвристической оценки.
• input_graph() – функция для считывания начальная и конечная вершины,
а также ребер графа с их весами в словарь.
• В блоке if name == "main" сначала вызывается функция input_graph(), а
потом соответствующая функция алгоритма.
Тестирование.
Результаты тестирования представлены в табл. 1.
Таблица 1 – Результаты тестирования
№ п/п
Входные данные
Выходные данные
Комментарии
1.
a e
a b 3.0
b c 1.0
c d 1.0
a d 5.0
d e 1.0
abcde
Жадный алгоритм.
Верно.
2.
a g
a b 3.0
a c 1.0
b d 2.0
b e 3.0
d e 4.0
e a 1.0
e f 2.0
a g 8.0
f g 1.0
abdefg
Жадный алгоритм.
Верно.

6
3.
a e
a b 3.0
b c 1.0
c d 1.0
a d 5.0
d e 1.0
ade
Алгоритм А*.
Верно.
4.
a d
a b 1.0
b c 1.0
c a 1.0
a d 8.0
ad
Алгоритм А*.
Верно.
Исследование.
Для проведения исследования был создан генератор графов с вершинами
от 3 до 52 и с заполненностью на 75%. По графику (см. рис. 1) можно сделать
вывод о том, что при количестве вершин больше 10 алгоритм ветвей и границ
начинает сильно проигрывать жадному алгоритму и А*. В то же время при
сравнении жадного алгоритма и А* (см. рис. 2) в среднем эффективнее
алгоритм А*. Стоит заметить, что жадный алгоритм при большем количестве
вершин перебирает существенно больше вершин, чем А*.
Рисунок 1 – График исследования трех алгоритмов

7
Рисунок 2 – График исследования двух алгоритмов
Вывод.
Были изучены жадный алгоритм и алгоритм А* для построения пути в
ориентированном графе.
В ходе выполнения лабораторной работы были реализованы жадный
алгоритм, алгоритм А*, точный алгоритм (ветвей и границ), а также для
исследования был написан генератор ориентированных графов.
Еще было проведено исследование времяни работы жадного алгоритма,
А* и точного алгоритма (ветвей и границ) в зависимости от размера графа,
заполненного на 75%. Анализ исследования показал, что жадный алгоритм
хоть и проходит больше вершин, но тем не менее работает практически также
быстро, как и А*, в то время как точный алгоритм существенно проигрывает
обоим алгоритмам. Это подтверждает, что для алгоритма А* была подобрана
хорошая эвристическая функция, которая помогает находить оптимальное
решение за наименьшее время.

8
ПРИЛОЖЕНИЕ А
ИСХОДНЫЙ КОД ПРОГРАММЫ
Название файла: greedy_algorithm.py
def greedy_algorithm(start, end, graph):
way = [start]
current_vertex = start
while current_vertex != end:
min_weight = float('inf')
next_vertex = None
for near_vertex, weight in graph[current_vertex].items():
if near_vertex not in way and weight < min_weight:
min_weight = weight
next_vertex = near_vertex
if next_vertex is None and current_vertex != end:
graph[way[-2]][current_vertex] = float('inf')
way.remove(current_vertex)
current_vertex = way[-1]
continue
way.append(next_vertex)
current_vertex = next_vertex
return ''.join(way)
def input_graph():
start_vertex, end_vertex = input().split()
graph = {}
while True:
try:
from_vertex, in_vertex, weight = input().split()
if from_vertex not in graph:
graph[from_vertex] = {}
if in_vertex not in graph:
graph[in_vertex] = {}
graph[from_vertex][in_vertex] = float(weight)
except EOFError:
break
return start_vertex, end_vertex, graph
if __name__ == "__main__":
start_vertex, end_vertex, graph = input_graph()
print(greedy_algorithm(start_vertex, end_vertex, graph))
Название файла: get_a_star_path.py
from greedy_algorithm import input_graph
import heapq

9
def heuristic(vertex, end):
return abs(ord(end) - ord(vertex))
def get_a_star_path(start, end, graph):
weight_and_vertex_list = [(0, start)]
passed = {}
way_length = {vertex: float('inf') for vertex in graph}
way_length[start] = 0
while weight_and_vertex_list:
current_weight, current_vertex =
heapq.heappop(weight_and_vertex_list)
if current_vertex == end:
way = []
while current_vertex in passed:
way.append(current_vertex)
current_vertex = passed[current_vertex]
way.append(start)
return ''.join(reversed(way))
for near_vertex, weight in graph[current_vertex].items():
tentative_way_length = way_length[current_vertex] +
weight
if tentative_way_length < way_length[near_vertex]:
passed[near_vertex] = current_vertex
way_length[near_vertex] = tentative_way_length
heapq.heappush(weight_and_vertex_list,
(tentative_way_length + heuristic(near_vertex, end), near_vertex))
if __name__ == "__main__":
start_vertex, end_vertex, graph = input_graph()
print(get_a_star_path(start_vertex, end_vertex, graph))
Название файла: branch_and_bound.py
from greedy_algorithm import input_graph
import heapq
def branch_and_bound(start, end, graph):
way_cost, way = float('infinity'), None
queue = []
heapq.heappush(queue, (0, start, [start]))
while queue:
cost, current, way = heapq.heappop(queue)
if current == end and cost < way_cost:
way_cost = cost
way = way
elif current in graph:

10
for near_vertex, edge_cost in graph[current].items():
new_cost = cost + edge_cost
if new_cost < way_cost:
new_way = way + [near_vertex]
heapq.heappush(queue, (new_cost, near_vertex,
new_way))
return ''.join(way)
if __name__ == "__main__":
start_vertex, end_vertex, graph = input_graph()
print(branch_and_bound(start_vertex, end_vertex, graph))
Соседние файлы в папке Лабы по ПиАА 2024
