задание 2: экспериментальная часть, визуализация, выводы и главное меню

This commit is contained in:
Sorokin Fedor 2026-05-25 02:57:25 +03:00
parent f04f36c12e
commit 2ff3ae7e2a

View File

@ -6,6 +6,10 @@ from collections import defaultdict, deque
import pandas as pd import pandas as pd
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
import os
current_dir = os.path.dirname(os.path.abspath(__file__))
os.chdir(current_dir)
# увеличиваем лимит рекурсии # увеличиваем лимит рекурсии
sys.setrecursionlimit(25000) sys.setrecursionlimit(25000)
@ -342,12 +346,11 @@ def print_report():
""" """
print(report) print(report)
if __name__ == "__main__": def run_task_1():
run_experiments() run_experiments()
plot_results() plot_results()
print_report() print_report()
# ЗАДАНИЕ 2: ПОИСК ВЫХОДА ИЗ ЛАБИРИНТА # ЗАДАНИЕ 2: ПОИСК ВЫХОДА ИЗ ЛАБИРИНТА
# поиск выхода из лабиринта # поиск выхода из лабиринта
@ -379,7 +382,7 @@ class BFSStrategy(PathfindingStrategy):
# проверка входных данных # проверка входных данных
if not maze.start or not maze.end: if not maze.start or not maze.end:
return None return None, 0
# инициализация структур данных # инициализация структур данных
queue = deque([(maze.start, [maze.start])]) queue = deque([(maze.start, [maze.start])])
@ -443,8 +446,12 @@ class MazeFactory:
for c in range(width): for c in range(width):
if random.random() < wall_chance: grid[r][c] = "#" if random.random() < wall_chance: grid[r][c] = "#"
grid[0][0] = "S" grid[0][0] = "S"
if has_exit: grid[height-1][width-1] = "E" if has_exit:
else: grid[height-1][width-1] = "#" grid[height-1][width-1] = "E"
else:
grid[height-1][width-1] = "E"
if height > 1: grid[height-1][width-2] = "#"
if width > 1: grid[height-2][width-1] = "#"
return grid return grid
@staticmethod @staticmethod
@ -468,4 +475,98 @@ class MazeFactory:
def run_maze_experiments(): def run_maze_experiments():
maze_types = ["маленький (10x10)", "средний (50x50)", "большой (100x100)", "пустой", "без выхода"] maze_types = ["маленький (10x10)", "средний (50x50)", "большой (100x100)", "пустой", "без выхода"]
strategies = [("BFS", BFSStrategy()), ("DFS", DFSStrategy())] strategies = [("BFS", BFSStrategy()), ("DFS", DFSStrategy())]
csv_rows = [["лабиринт", "стратегия", "время_мс", "посещено_клеток", "длина_пути"]]
print("\nзапуск экспериментов(5 повторений)")
for m_type in maze_types:
maze_obj = MazeFactory.create_maze(m_type)
for strat_name, strategy in strategies:
solver = MazeSolver(strategy)
times = []
visited_cells = 0
path_len = 0
# запускаем по 5 раз для усреднения времени
for rep in range(5):
t_start = time.perf_counter()
path, visited = solver.solve_maze(maze_obj)
t_end = time.perf_counter()
times.append((t_end - t_start) * 1000) # переводим в миллисекунды
visited_cells = visited
path_len = len(path) if path else 0
avg_time_ms = sum(times) / len(times)
csv_rows.append([m_type, strat_name, round(avg_time_ms, 4), visited_cells, path_len])
# сохраняем в CSV
with open("maze_results.csv", "w", newline="", encoding="utf-8") as f:
writer = csv.writer(f)
writer.writerows(csv_rows)
print("Результаты сохранены в 'maze_results.csv'")
df = pd.DataFrame(csv_rows[1:], columns=csv_rows[0])
print("\nТАБЛИЦА СРАВНЕНИЯ АЛГОРИТМОВ ПОИСКА:")
print(df.to_string(index=False))
# визуализация графиков
print("\nпостроение графиков эффективности")
for m_type in maze_types:
sub_df = df[df["лабиринт"] == m_type]
fig, ax1 = plt.subplots(figsize=(7, 4))
ax2 = ax1.twinx()
sub_df.plot(kind="bar", x="стратегия", y="время_мс", ax=ax1, position=0, width=0.2, color="blue", legend=False)
sub_df.plot(kind="bar", x="стратегия", y="посещено_клеток", ax=ax2, position=1, width=0.2, color="orange", legend=False)
ax1.set_ylabel("время выполнения (мс)", color="blue")
ax2.set_ylabel("посещено клеток (ед)", color="orange")
plt.title(f"эффективность на лабиринте: {m_type}")
ax1.set_xticklabels(sub_df["стратегия"], rotation=0)
lines1, labels1 = ax1.get_legend_handles_labels()
lines2, labels2 = ax2.get_legend_handles_labels()
ax1.legend(lines1 + lines2, ["время (мс)", "посещено клеток"], loc="upper center")
plt.tight_layout()
filename = f"maze_chart_{m_type.replace(' ', '_').replace('(', '').replace(')', '')}.png"
plt.savefig(filename)
plt.close()
print("графики для каждого типа лабиринта сохранены в текущую папку")
def print_maze_report():
print("""
ВЫВОДЫ ПО ИТОГАМ АНАЛИЗА ЛАБИРИНТОВ:
1. Маленький и Пустой лабиринты: Оба алгоритма работают мгновенно. Однако в пустом
пространстве BFS проверяет почти все клетки «волной» до достижения цели, в то время
как DFS может случайно угадать прямую траекторию быстрее, но выдать неоптимальный путь.
2. Средний и Большой лабиринты (с тупиками): BFS стабильно находит самый КОРОТКИЙ путь,
однако тратит много памяти и времени на посещение клеток. DFS работает непредсказуемо,
его путь часто длиннее в разы, так как он «плутает» по тупикам.
3. Лабиринт без выхода: Оба алгоритма вынуждены совершить полный перебор графа.
Количество посещенных клеток у них совпадает и равняется общему числу доступных клеток.
""")
def run_task_2():
run_maze_experiments()
print_maze_report()
# главное меню
def main():
while True:
print("МЕНЮ ЛАБОРАТОРНЫХ РАБОТ")
print("1. Задание 1: структуры данных")
print("2. Задание 2: эксперименты с Лабиринтами")
print("0. Выход")
choice = input("Введите номер задания: ")
if choice == '1': run_task_1()
elif choice == '2': run_task_2()
elif choice == '0': break
else: print("Ошибка ввода.")
if __name__ == "__main__":
main()