[2] #344
123
novikovsd/отчет_лабороторная2.txt
Normal file
123
novikovsd/отчет_лабороторная2.txt
Normal 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, добавляя в него вызовы отрисовки.
|
||||
Loading…
Reference in New Issue
Block a user