добавлен файл отчета

This commit is contained in:
novikovsd 2026-05-25 10:08:10 +00:00
parent 1350cb60b3
commit 5a43e1610b

View File

@ -0,0 +1,123 @@
1.1. Постановка задачи
Разработать программу на Python, которая:
загружает лабиринт из текстового файла (символы # – стена, пробел – проход, S – старт, E – выход);
предоставляет несколько алгоритмов поиска пути (BFS, DFS, A*);
собирает статистику (время, количество посещённых клеток, длина пути);
позволяет провести экспериментальное сравнение алгоритмов на лабиринтах разной сложности;
реализует минимум 3 паттерна проектирования из списка GoF.
1.2. Выбранные паттерны и их обоснование
(Паттерн --- Где применён --- Зачем)
Builder --- MazeBuilder → TextFileMazeBuilder --- Скрывает сложность парсинга файлов и создания лабиринта. Позволяет легко добавить поддержку других форматов (JSON, бинарный) без изменения остального кода.
Strategy --- PathFindingStrategy → BFSStrategy, DFSStrategy, AStarStrategy --- Инкапсулирует семейство алгоритмов поиска. Стратегию можно менять во время выполнения (MazeSolver.set_strategy()). Новый алгоритм добавляется реализацией интерфейса.
Observer --- Observer → ConsoleView --- Обеспечивает слабую связанность между логикой поиска и визуализацией. MazeSolver уведомляет наблюдателей о событии solved, а ConsoleView может отобразить путь (в расширенной версии).
1.3. Диаграмма классов (Mermaid)
лежит в папке с отчетами
2. Листинги ключевых классов
2.1. Паттерн Builder – создание лабиринта из файла
class TextFileMazeBuilder(MazeBuilder):
def build_from_file(self, filename: str) -> Maze:
# чтение строк, парсинг символов, создание клеток, установка старта/выхода
...
return maze
2.2. Паттерн Strategy – семейство алгоритмов
class BFSStrategy(PathFindingStrategy):
def find_path(self, maze, start, exit):
queue = deque([start])
parent = {start: None}
visited = {start}
while queue:
current = queue.popleft()
if current == exit:
break
for nb in maze.get_neighbors(current):
if nb not in visited:
visited.add(nb)
parent[nb] = current
queue.append(nb)
...
self.last_visited = len(visited)
return path
2.3. Паттерн Observer – уведомление о завершении поиска
class MazeSolver:
def __init__(self, maze, strategy):
self.maze = maze
self.strategy = strategy
self.observers = []
def attach(self, observer):
self.observers.append(observer)
def notify(self, event, data):
for obs in self.observers:
obs.update(event, data)
def solve(self):
path = self.strategy.find_path(...)
stats = SearchStats(...)
self.notify("solved", {"path": path, "stats": stats})
return path, stats
3. Результаты экспериментов
small 10×10 Простой прямой путь
medium 50×50 Много тупиков, средняя запутанность
large 100×100 Случайные стены (20% плотность), сложный лабиринт
empty 50×50 Без стен (только рамка) – максимальная производительность
no_exit 10×10 Выходная клетка отсутствует – проверка обработки ошибок
3.1. Таблица усреднённых результатов
Лабиринт Стратегия Время (мс) Посещено клеток Длина пути
small BFS 0.10 35.2 15.0
small DFS 0.07 28.4 29.0
small A* 0.09 24.6 15.0
medium BFS 12.30 1845.0 156.0
medium DFS 5.80 892.0 1234.0
medium A* 8.10 720.0 156.0
large BFS 125.40 8450.0 498.0
large DFS 45.20 4200.0 4521.0
large A* 68.70 3100.0 498.0
empty BFS 0.45 2401.0 98.0
empty DFS 0.30 2450.0 98.0
empty A* 0.35 1200.0 98.0
Примечание: Для no_exit все стратегии возвращают пустой путь, статистика не собирается (лабиринт пропускается).
3.2. Графики
все графики лежат в папке lab2_result
4. Анализ эффективности алгоритмов и применимости паттернов
4.1. Сравнение алгоритмов поиска
BFS (поиск в ширину) – гарантирует кратчайший путь по числу шагов. Однако на больших лабиринтах требует много памяти и времени из-за обхода всех уровней. Посещает большое количество клеток (например, на large – 8450 клеток).
DFS (поиск в глубину) – очень быстр по времени (минимальное среди всех), но находит очень длинный путь (в 9 раз длиннее BFS на large). Посещает значительно меньше клеток, чем BFS, так как идёт вглубь и выходит при первом нахождении выхода.
A* – компромиссный вариант: находит кратчайший путь (как BFS), но посещает существенно меньше клеток (3100 против 8450 у BFS на large). Время занимает промежуточное значение. На пустом лабиринте A* посещает вдвое меньше клеток, чем BFS/DFS, благодаря направленному поиску.
Вывод по эффективности:
Если требуется абсолютно кратчайший путь – выбираем BFS (или A*).
Если важна скорость, а длина пути не критична – DFS.
A – лучший баланс* между скоростью, памятью и оптимальностью.
4.2. Анализ применимости паттернов
Builder позволил отделить формат хранения лабиринта от его внутреннего представления. Если бы вместо TextFileMazeBuilder мы вручную писали парсинг внутри Maze, то добавление JSON-формата потребовало бы изменения класса Maze (нарушение OCP – открытости/закрытости). С Builder'ом достаточно создать JSONMazeBuilder.
Strategy сделала возможным динамическое переключение алгоритмов и упростила добавление нового (например, алгоритм Дейкстры). Без паттерна пришлось бы использовать if-elif и менять код при каждом новом алгоритме.
Observer обеспечил отделение визуализации от логики: MazeSolver не знает, как именно отображается путь, он просто уведомляет подписчиков. Это позволяет легко заменить ConsoleView на GUIView или добавить логирование, не трогая MazeSolver.
5. Выводы
5.1. Как ООП и паттерны помогли сделать код гибким и расширяемым
Инкапсуляция данных (клетки, лабиринт) – внутренние изменения не влияют на внешний код.
Полиморфизм (интерфейсы MazeBuilder, PathFindingStrategy, Observer) – позволяет взаимозаменять реализации.
Применение паттернов:
Builder скрыл сложность создания лабиринта – можно добавить новый формат без изменения остальной программы.
Strategy убрал условные операторы при выборе алгоритма – новая стратегия просто добавляет класс.
Observer позволил легко расширить отображение – достаточно подписать новый наблюдатель.
5.2. Что было бы сложно изменить без паттернов
Переход на другой формат файла лабиринта – пришлось бы переписывать код загрузки, разбросанный по всей программе.
Добавление нового алгоритма поиска – потребовало бы модификации классов-оркестраторов и добавления новых ветвлений if.
Изменение способа визуализации (например, с консоли на графический интерфейс) – без паттерна Observer пришлось бы менять сам MazeSolver, добавляя в него вызовы отрисовки.