results and report

This commit is contained in:
kirill 2026-05-24 21:34:23 +03:00
parent f26da8a6cc
commit 653af1f2f6
11 changed files with 433 additions and 0 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 125 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 115 KiB

View File

@ -0,0 +1,207 @@
# Отчёт: Поиск выхода из лабиринта
## 1. Описание задачи и выбранных паттернов
### Задача
Разработать программу для загрузки лабиринта из текстового файла, поиска пути от старта до выхода тремя алгоритмами (BFS, DFS, A*), визуализации найденного пути и экспериментального сравнения алгоритмов по времени, числу посещённых клеток и длине пути.
### Структура файлов
```
02/
main.py - точка запуска
codes/
maze.py - все классы (Cell, Maze, Builder, Strategy, Solver)
maze_generator.py - генерация тестовых лабиринтов
mazes/ - текстовые файлы лабиринтов
results/
results_maze.csv - результаты экспериментов
benchmark_plot.png - графики
docs/
report1.md - отчёт
mermaid.png - диаграмма классов
```
### Применённые паттерны проектирования
**1. Builder** - класс `TextFileMazeBuilder` реализует интерфейс `MazeBuilder`.
Построение лабиринта из файла включает несколько шагов: чтение строк, обход символов, создание объектов `Cell`, поиск стартовой и конечной клетки. Без Builder вся эта логика оказалась бы в `main.py` или в конструкторе `Maze`. Builder скрывает детали создания от клиента. Если понадобится загружать лабиринт из JSON или бинарного файла - достаточно написать новый класс, реализующий тот же интерфейс `MazeBuilder`.
**2. Strategy** - классы `BFSStrategy`, `DFSStrategy`, `AStarStrategy` реализуют интерфейс `PathFindingStrategy`.
Алгоритм поиска можно менять во время работы программы через `MazeSolver.set_strategy()`, не трогая остальной код. Добавление нового алгоритма - это написание одного нового класса с методом `find_path()`. Без Strategy в `solve()` пришлось бы писать if/elif для каждого алгоритма.
**3. Observer** - интерфейс `Observer` с методом `update(event)`.
`MazeSolver` хранит список наблюдателей и уведомляет их при событиях `search_started`, `path_found`, `path_not_found`. Это позволяет добавлять отображение в консоль, запись в лог или GUI-уведомления, не меняя код солвера. Слабая связанность: солвер не знает, кто его слушает.
### Диаграмма классов
![Диаграмма классов](mermaid.png)
---
## 2. Листинги ключевых классов
### Cell и Maze
```python
class Cell:
def __init__(self, x, y, is_wall=False, is_start=False, is_exit=False):
self.x = x
self.y = y
self.is_wall = is_wall
self.is_start = is_start
self.is_exit = is_exit
def is_passable(self):
return not self.is_wall
class Maze:
def get_neighbors(self, cell):
result = []
for dx, dy in [(0,-1),(0,1),(-1,0),(1,0)]:
n = self.get_cell(cell.x + dx, cell.y + dy)
if n and n.is_passable():
result.append(n)
return result
```
### Паттерн Builder
```python
class MazeBuilder(ABC):
@abstractmethod
def build_from_file(self, filename) -> Maze:
pass
class TextFileMazeBuilder(MazeBuilder):
def build_from_file(self, filename) -> Maze:
with open(filename, encoding="utf-8") as f:
lines = [l.rstrip("\n") for l in f]
# ... парсинг символов, создание Cell, поиск S и E
return Maze(cells, width, height, start, exit_cell)
```
### Паттерн Strategy - алгоритм A*
```python
class AStarStrategy(PathFindingStrategy):
@staticmethod
def _h(a, b):
return abs(a.x - b.x) + abs(a.y - b.y)
def find_path(self, maze, start, end):
heap = [(0, 0, start)]
parent = {start: None}
g = {start: 0}
closed = set()
while heap:
_, _, cur = heapq.heappop(heap)
if cur in closed:
continue
closed.add(cur)
if cur == end:
return self._build_path(parent, start, end)
for nb in maze.get_neighbors(cur):
ng = g[cur] + 1
if ng < g.get(nb, float("inf")):
g[nb] = ng
heapq.heappush(heap, (ng + self._h(nb, end), id(nb), nb))
parent[nb] = cur
return []
```
### MazeSolver
```python
class MazeSolver:
def __init__(self, maze, strategy):
self.maze = maze
self.strategy = strategy
def set_strategy(self, strategy):
self.strategy = strategy
def solve(self) -> SearchStats:
t0 = time.perf_counter()
path = self.strategy.find_path(self.maze, self.maze.start, self.maze.exit)
t1 = time.perf_counter()
return SearchStats(
strategy=self.strategy.name,
time_ms=(t1 - t0) * 1000,
visited=self.strategy._visited,
path_length=len(path),
path=path,
)
```
---
## 3. Результаты экспериментов
Каждый алгоритм запускался 7 раз на каждом лабиринте, результаты усреднялись.
### Таблица результатов
| Лабиринт | Алгоритм | Время (мс) | Посещено клеток | Длина пути |
|----------|----------|-----------|----------------|------------|
| small (11x11) | BFS | 0.070 | 39 | 33 |
| small (11x11) | DFS | 0.055 | 33 | 33 |
| small (11x11) | A* | 0.112 | 35 | 33 |
| medium (51x51) | BFS | 1.391 | 793 | 497 |
| medium (51x51) | DFS | 0.949 | 515 | 497 |
| medium (51x51) | A* | 2.271 | 707 | 497 |
| large (101x101) | BFS | 6.231 | 3533 | 1613 |
| large (101x101) | DFS | 3.341 | 1957 | 1613 |
| large (101x101) | A* | 11.27 | 3379 | 1613 |
| empty (51x21) | BFS | 1.992 | 931 | 67 |
| empty (51x21) | DFS | 1.021 | 451 | 451 |
| empty (51x21) | A* | 3.527 | 931 | 67 |
| no_exit (11x11) | BFS | 0.079 | 40 | - |
| no_exit (11x11) | DFS | 0.077 | 40 | - |
| no_exit (11x11) | A* | 0.140 | 40 | - |
### Графики
![Графики](../results/benchmark_plot.png)
---
## 4. Анализ эффективности алгоритмов и применимости паттернов
### Алгоритмы
**BFS** гарантирует кратчайший путь по числу шагов. Расширяет узлы слой за слоем во всех направлениях, поэтому посещает наибольшее число клеток. На практике это надёжный выбор когда нужен точно кратчайший маршрут.
**DFS** посещает меньше клеток и выполняется быстрее - на large лабиринте в 1.8 раза быстрее BFS. Однако путь может быть далеко не кратчайшим. На пустом лабиринте DFS нашёл путь длиной 451 шаг, тогда как BFS и A* - 67. Это связано с тем, что DFS уходит в первое попавшееся направление и возвращается только в тупике.
**A*** использует манхэттенскую эвристику h = |x1-x2| + |y1-y2| и должен в теории посещать меньше клеток чем BFS. На лабиринтах, сгенерированных алгоритмом recursive backtracker, выигрыш небольшой (примерно 5%). Причина: backtracker строит дерево - между любыми двумя клетками ровно один путь, тупиков нет, эвристика не помогает их обходить. На лабиринтах с циклами A* посещает заметно меньше клеток. Накладные расходы на работу с heap и closed-set делают A* медленнее по времени, чем DFS.
На пустом лабиринте (без стен) A* ведёт себя как BFS. Математически: f(x,y) = g + h = (x-1+y-1) + (W-x+H-y) = const для всех клеток. Все узлы неразличимы по приоритету.
На лабиринте без выхода все три алгоритма посещают одинаковое число клеток и корректно возвращают пустой путь.
### Паттерны
**Builder** оказался полезным при добавлении нового типа лабиринта (взвешенного, с символами s и m). Изменения были внесены только в `TextFileMazeBuilder`, клиентский код не менялся.
**Strategy** позволил в одном цикле запустить все три алгоритма через `solver.set_strategy(strategy)`. Без паттерна пришлось бы либо дублировать код запуска для каждого алгоритма, либо писать условные ветки.
**Observer** полезен при расширении: чтобы добавить вывод в лог или консоль, достаточно написать новый Observer и подписать его на solver, не меняя `MazeSolver`.
---
## 5. Выводы
ООП и паттерны позволили сделать код гибким в нескольких направлениях.
Добавление нового алгоритма поиска сводится к написанию одного класса, реализующего `find_path()`. Без Strategy пришлось бы добавлять ветку в `solve()` и во все места, где запускается поиск.
Добавление нового формата лабиринта - только новый класс Builder. Без паттерна логика парсинга была бы перемешана с логикой работы программы.
Добавление нового способа отображения (GUI, запись в файл) - только новый Observer. Без него MazeSolver пришлось бы напрямую вызывать функции отображения, что создало бы зависимость от конкретной реализации.
Без применения паттернов код решал бы задачу, но любое изменение требовало бы правки в нескольких местах сразу. С паттернами каждый класс отвечает за одну задачу и не знает о деталях реализации соседних классов.

View File

@ -0,0 +1,21 @@
###################################################
#S #
# #
# #
# #
# #
# #
# #
# #
# #
# #
# #
# #
# #
# #
# #
# #
# #
# #
# E#
###################################################

View File

@ -0,0 +1,101 @@
#####################################################################################################
#S # # # # # # # # # # # # #
### # # ##### # ### # # ##### ### ### ############# # # # # ##### # ######### ### # # ##### ### # ###
# # # # # # # # # # # # # # # # # # # # # # # # # #
# # # ##### ### ### # # ########### ####### # ####### ### ### # # # ####### ##### # # ### # # ##### #
# # # # # # # # # # # # # # # # # # # # # # # # # # # #
# ##### # ### ### # ### # ### # # ##### # ##### ####### ### ### # ### ### ### # ### ### # ##### # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # ### # # ### # ##### ##### # ### # # ##### # # ####### ######### ### # # ######### # # # #####
# # # # # # # # # # # # # # # # # # # # # # # # # #
# # ##### ### # ##### # ##### ### # ##### ######### # # ####### ####### # ### # # ########### ##### #
# # # # # # # # # # # # # # # # # # # # # #
##### ##### ########### ### # # ########### # # # # ##### ####### # ####### # ##### ### ### ### ### #
# # # # # # # # # # # # # # # # # # # # # # # #
####### # ### # # ####### ##### ### ##### # # ##### # ##### ### # # # ######### # ### ### ### ### ###
# # # # # # # # # # # # # # # # # # # # # # # # # # #
# ### # ########### # # # ### ### ##### ##### # # ##### # # # ##### # # ##### ##### # ##### ### ### #
# # # # # # # # # # # # # # # # # # # # # # # #
# # ### # ### # ### ### ### ####### # ########### ####### # # ##### # # ### ########### ### ### # # #
# # # # # # # # # # # # # # # # # # # # # # # #
# # ####### # # # # ##### ### # ####### # ##### ##### ##### # # # ### ### # # ##### ##### ### #######
# # # # # # # # # # # # # # # # # # # # # # # # # # #
########### # # ##### # # # ####### # ### # ##### # ##### ### # # # # # ####### # ##### ### # # ### #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# ### # # ### # # ##### # # # # # ##### # # # ############# # ##### # ############### ### # # ### # #
# # # # # # # # # # # # # # # # # # # # # # #
# # ####### ##### # ########### # # # ######### ### ############# # ####### ####################### #
# # # # # # # # # # # # # # # # # # # # #
# # ### ### # # # # # ### # ##### # ### # ####### # ##### # ######### ####### # ##### # ########### #
# # # # # # # # # # # # # # # # # # # # # # # # # # #
# ##### ##### ######### ####### ##### # # # # # ### # ##### # ### # ### # ##### # # # # # ### # # ###
# # # # # # # # # # # # # # # # # # # # # # # # # # #
# ### # # # ### ##### ### # # ### ##### ##### ####### # # ##### # ### ##### ####### # # # ####### # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # ### ####### # ### # # ### ### ##### ##### # # # # ### # # # ####### # # ### ##### # # ####### #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
##### # # # # # ### # ### ### ### ### # ### # ### ##### # # ########### # # ### # # ##### ##### # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # #
# ### ##### ### ### # # ### ### # # ##### ### # ##### ########### ### ### ### # # # # ### ######### #
# # # # # # # # # # # # # # # # # # # # # # # # #
# # ### # ### ### # # ####### # # ######### ### # ########### # ### ### ##### ### # ### # # ### ### #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # ######### # ####### ##### ######### # ### ### # ##### # ### ### ### # ### ### # ##### ##### # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # ### ### # # # # # ##### # # # # ####### # # # ### # ### # ### ### ##### ### # # ### # # # ### #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# ### # ### ######### # ##### # ##### # # ############# # # ### ####### # # ##### # # # # # # ### # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# ##### ####### ### # ### ##### # # # ##### # ####### ### # # ### # # # # ### ### # ########### # ###
# # # # # # # # # # # # # # # # # # # # # # # # # #
# ####### # ##### # ### # ##### # # ##### # ##### # ### ### ######### ##### ### # ##### # ######### #
# # # # # # # # # # # # # # # # # # # # # # # # # #
### # # # ### ####### # # # # ##### # ############### # # ##### ####### ##### # ### # # # # ### # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # #
# ### ########### ### # ############# # # # ### # ##### ##### ### ####### # ### # # # ##### ### # ###
# # # # # # # # # # # # # # # # # # # # # # # # #
### ##### ##### ### ### ### # # # # ##### # # ####### # # # ####### # # ### # ### # # # # ### ##### #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# ### # ### # # # ####### # # # # ### ####### # # # ##### ### # # ##### # ##### # # ### ### # # ### #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # ### # ### # # # # ### ### ### # # ### ### # ##### ### # # ##### ####### # ##### # ##### ### # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# ### # # ### ####### ### # # ### ##### # # ######### ### ### # # # # # ### # ### # # ### ### # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # #
### # # ### ### ##### ######### ######### # # # # # ########### ####### # # ### ########### ##### # #
# # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # ### ### ### # ### # # ######### # # # # ##### # # # ##### # ####### ####### # ##### # # ##### #
# # # # # # # # # # # # # # # # # # # # # # # # # # #
# # ### ### ##### ####### # # ### ######### ### ### # # ########### # ##### # ### ##### ##### # ### #
# # # # # # # # # # # # # # # # # # # # # # # #
# ####### # # ##### ### # ### # ####### # # # ### ### # # ### # ####### # ##### ### # ######### #####
# # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # ##### # ####### ####### # ########### ####### ######### # ### # ### # ### # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # #
# ### # # ####### # ### ########### # # ######### ### # # # # # # ### # ######### # ### ########### #
# # # # # # # # # # # # # # # # # # # # # # #
### ####### # # # # # ### # # # ######### # # # ########################### # # ##### ####### # # # #
# # # # # # # # # # # # # # # # # # # # # # # # #
# ### ### ############### ####### ##### # ########### # ### # # ##### ### # ##### # ##### # ### # ###
# # # # # # # # # # # # # # # # # # # # # # # # #
# ########### ##### ### ####### # # # ##### # # # # # ### # # ### ### # ### ### # ##### # # # # ### #
# # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # ##### # ### # ### # # ### # # ####### ####### # ### ##### ##### ##### ### ##### # ####### #######
# # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # ##### ####### ### # # # ############# # # # # ##### ### ######### # ######### # # ### #
# # # # # # # # # # # # # # # # # # # # # # # # #
### # ##### ##### # ### # ### ####### # ####### # ### ##### # ######### ##### # # ####### ### ##### #
# # # # # # # # # # # # # # # # # # # # # # # # #
# ### # # # # ##### # ##### # ### # ### # ### ##### ### ##### ########### ### # ########### # # #####
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # ##### # # # # # # # # ######### ### # ### # # ### # # # ### # # ### # # # # ### # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # ### # ### ######### ### ### ######### # ### # # ### # ### # # ##### # # ######### ### ### ##### #
# # # # # # # # # # # # # # # # # # # # # # # #
# ##### ##### # # ####### ### ### # ##### ##### ##### # # ##### ##### ############# # # ### ### ### #
# # # # # # # # # # # # # # # # # # # # # # # # # # #
# ### # # # ### ### # # ### # # ####### ##### # # # # # ### ##### ##### # ### ##### # # ######### ###
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # ##### ### ### # # ### ##### ##### ##### # ##### ### # ### # ### ##### ##### # ##### # # # # # # #
# # # # # # # # # # # # # E#
#####################################################################################################

View File

@ -0,0 +1,51 @@
###################################################
#S# # # # # # # #
# ##### # # # # # # # ####### # ### # ### ##### # #
# # # # # # # # # # # # # # # # # #
### # ### # # ####### # ### # # # # ### # ### ### #
# # # # # # # # # # # # # #
# # # # # # ####### ####### # ### ######### ##### #
# # # # # # # # # # # # # #
# # # ##### # # # ### ####### ##### ##### # ### ###
# # # # # # # # # # # # # # # # #
# # ##### ### # ### ### # # ### # ##### # ### # # #
# # # # # # # # # # # # # # # # #
# # # # # # ### # ### ### ##### # # # ####### # # #
# # # # # # # # # # # # # # #
# ######### # # # # ### # # # ### # ####### # ### #
# # # # # # # # # # # # # # # # # #
# # ### # ### # # ### ### ### # ### # # # ### # # #
# # # # # # # # # # # # # # #
# ### ### # ### ####### ######### # ### ####### ###
# # # # # # # # # # # # # # #
# # ### ### # ### # ####### # # # # # ####### # # #
# # # # # # # # # # # # # #
# ####### # ############# ##### # # ####### # ### #
# # # # # # # # # # #
# # # # # ################# ########### # # ##### #
# # # # # # # # # # #
# ####### # ### ##### # # ### ### ######### # #####
# # # # # # # # # #
####### # ####### ##### ### ##### # ############# #
# # # # # # # # # # # #
##### ####### # # ### # ##### # # ##### ##### # # #
# # # # # # # # # # # # # # #
# ##### # # # ##### ##### # ### ### # # # ##### # #
# # # # # # # # # # # # # #
# ####### # ### # ### # # # ################# ### #
# # # # # # # # # #
### ##### ########### # # ############# ### ### ###
# # # # # # # # # # # #
# ### ### # ####### # # ### # ####### # # ### ### #
# # # # # # # # # # # # # # #
# # ######### ### # # # # # ### ### ####### # ### #
# # # # # # # # # # # # # #
# ##### # ##### # ##### ##### ### ####### ##### ###
# # # # # # # # # # # #
# # # # # # ##### ### ########### # # ####### ### #
# # # # # # # # # # # # # # #
# # ### ##### # ### ##### ##### ##### # # ### # # #
# # # # # # # # # # # # #
### # ### ##### ####### ##### ##### ####### # # # #
# # # # # #E#
###################################################

View File

@ -0,0 +1,11 @@
###########
#S# #E#
# #########
# # #
##### # ###
# # #
# ####### #
# # #
### ### # #
# ###
###########

View File

@ -0,0 +1,15 @@
###############
#S# # #
# ### # # ### #
# # # # # #
### ### ### # #
# # # # # #
# ### # # ### #
# # # # #
### ##### # # #
# # # #
# ### #########
# # # #
# # ##### # # #
# # #E#
###############

View File

@ -0,0 +1,11 @@
###########
#S # #
##### # # #
# # # #
# ####### #
# # # #
# ### # # #
# # # #
### # ### #
# # E#
###########

Binary file not shown.

After

Width:  |  Height:  |  Size: 125 KiB

View File

@ -0,0 +1,16 @@
maze,strategy,time_ms,visited_cells,path_length
small,BFS,0.0676,39.0,33.0
small,DFS,0.061,33.0,33.0
small,A*,0.1093,35.0,33.0
medium,BFS,1.4027,793.0,497.0
medium,DFS,0.8986,515.0,497.0
medium,A*,2.3001,707.0,497.0
large,BFS,6.1605,3533.0,1613.0
large,DFS,3.3919,1957.0,1613.0
large,A*,11.2172,3379.0,1613.0
empty,BFS,1.7583,931.0,67.0
empty,DFS,1.0076,451.0,451.0
empty,A*,3.4836,931.0,67.0
no_exit,BFS,0.067,40.0,0.0
no_exit,DFS,0.0599,40.0,0.0
no_exit,A*,0.1099,40.0,0.0
1 maze strategy time_ms visited_cells path_length
2 small BFS 0.0676 39.0 33.0
3 small DFS 0.061 33.0 33.0
4 small A* 0.1093 35.0 33.0
5 medium BFS 1.4027 793.0 497.0
6 medium DFS 0.8986 515.0 497.0
7 medium A* 2.3001 707.0 497.0
8 large BFS 6.1605 3533.0 1613.0
9 large DFS 3.3919 1957.0 1613.0
10 large A* 11.2172 3379.0 1613.0
11 empty BFS 1.7583 931.0 67.0
12 empty DFS 1.0076 451.0 451.0
13 empty A* 3.4836 931.0 67.0
14 no_exit BFS 0.067 40.0 0.0
15 no_exit DFS 0.0599 40.0 0.0
16 no_exit A* 0.1099 40.0 0.0