2026-rff_mp/soninrv/docs/report2.md
2026-05-25 03:26:25 +03:00

106 lines
9.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Отчёт по лабораторной работе «Поиск выхода из лабиринта»
## 1. Цель работы
Разработать гибкую расширяемую программу для загрузки лабиринта из текстового файла, поиска пути от старта до выхода с возможностью выбора алгоритма и экспериментального сравнения алгоритмов. В ходе работы применены паттерны проектирования GoF: **Builder**, **Strategy**, **Observer**, **Command**.
Реализованные алгоритмы поиска пути:
- **BFS** (поиск в ширину) — гарантирует кратчайший путь по числу шагов.
- **DFS** (поиск в глубину) — не гарантирует кратчайший путь, но быстрее при удачном порядке соседей.
- **A\*** (с манхэттенской эвристикой) — направленный поиск, учитывает веса клеток.
- **Dijkstra** — оптимален для взвешенных графов, без эвристики.
Тестовые лабиринты:
- Маленький 10×10 — простой путь.
- Средний 50×50 — с тупиками (алгоритм Прима).
- Большой 100×100 — запутанная структура.
- Пустой 30×30 — без стен, демонстрирует максимальную нагрузку.
- Без выхода 20×20 — старт и выход разделены глухой стеной.
- Взвешенный 40×40 — клетки с разным весом: асфальт (1), песок (2), болото (3).
Каждый эксперимент повторялся 7 раз, результаты усреднены.
## 2. Описание паттернов
| Паттерн | Классы | Назначение |
|---|---|---|
| Builder | `MazeBuilder`, `TextFileMazeBuilder` | Скрывает парсинг файла; новый формат = новый класс |
| Strategy | `PathFindingStrategy`, BFS/DFS/A\*/Dijkstra | Смена алгоритма одной строкой без изменения остального кода |
| Observer | `Observer`, `ConsoleView` | Визуализация отделена от логики поиска |
| Command | `Command`, `MoveCommand`, `CommandHistory` | Пошаговое управление игроком с поддержкой undo |
## 3. Результаты экспериментов
Усреднённые значения (7 повторений) представлены в таблице:
| Лабиринт | Алгоритм | Время, мс | Посещено клеток | Длина пути |
|---|---|---|---|---|
| 10×10 | BFS | 0.081 | 28 | 21 |
| 10×10 | DFS | 0.053 | 22 | 21 |
| 10×10 | A\* | 0.088 | 24 | 21 |
| 10×10 | Dijkstra | 0.672 | 28 | 21 |
| 50×50 | BFS | 1.150 | 493 | 257 |
| 50×50 | DFS | 0.614 | 263 | 257 |
| 50×50 | A\* | 1.220 | 357 | 257 |
| 50×50 | Dijkstra | 1.685 | 493 | 257 |
| 100×100 | BFS | 11.378 | 4783 | 1953 |
| 100×100 | DFS | 5.141 | 2161 | 1953 |
| 100×100 | A\* | 18.019 | 4741 | 1953 |
| 100×100 | Dijkstra | 17.489 | 4783 | 1953 |
| 30×30 пустой | BFS | 1.832 | 784 | 55 |
| 30×30 пустой | DFS | 1.151 | 433 | 379 |
| 30×30 пустой | A\* | 3.748 | 784 | 55 |
| 30×30 пустой | Dijkstra | 3.945 | 784 | 55 |
| 20×20 без выхода | BFS | 0.370 | 162 | — |
| 20×20 без выхода | DFS | 0.373 | 162 | — |
| 20×20 без выхода | A\* | 0.708 | 162 | — |
| 20×20 без выхода | Dijkstra | 0.677 | 162 | — |
| 40×40 взвешенный | BFS | 1.104 | 533 | 321 |
| 40×40 взвешенный | DFS | 0.774 | 361 | 321 |
| 40×40 взвешенный | A\* | 1.516 | 452 | 321 |
| 40×40 взвешенный | Dijkstra | 1.725 | 533 | 321 |
Графическое представление результатов приведено на рисунке ниже.
![Сравнение производительности](performance_comparison.png)
## 4. Анализ результатов
### 4.1. BFS
Гарантирует кратчайший путь по числу шагов. Исследует все клетки на расстоянии d перед переходом к d+1, поэтому число посещённых клеток максимально среди всех алгоритмов — на лабиринте 100×100 это 4783 клетки. На пустом лабиринте 30×30 BFS находит оптимальный путь длиной 55 клеток, тогда как DFS даёт 379. Время растёт линейно с размером: от 0.08 мс (10×10) до 11.4 мс (100×100).
### 4.2. DFS
Самый быстрый алгоритм по времени: на лабиринте 100×100 — 5.1 мс против 11.4 мс у BFS. Посещает вдвое меньше клеток (2161 против 4783), так как уходит глубоко в одном направлении. Однако на пустом лабиринте DFS даёт путь в 7 раз длиннее оптимального (379 против 55) — алгоритм уходит в угол и обходит весь лабиринт по периметру. Оптимальная длина пути при этом совпадает с BFS только в лабиринтах-лабиринтах (50×50 и 100×100), где единственный путь — сам.
### 4.3. A\*
На невзвешенных лабиринтах находит тот же кратчайший путь, что BFS, но исследует на 2030% меньше клеток благодаря манхэттенской эвристике (на 50×50: 357 против 493). Однако на большом лабиринте 100×100 оказывается медленнее BFS (18 мс против 11 мс) из-за накладных расходов на `heapq`. На взвешенном лабиринте A\* корректно учитывает стоимость клеток, в отличие от BFS.
### 4.4. Dijkstra
На невзвешенных лабиринтах полностью совпадает с BFS по посещённым клеткам и длине пути, но медленнее из-за `heapq` вместо `deque`. На взвешенном лабиринте 40×40 корректно минимизирует суммарный вес пути. Практически вытесняется A\* везде, где цель заранее известна.
### 4.5. Лабиринт «без выхода»
Все алгоритмы обходят все 162 доступные клетки левой секции и возвращают пустой путь. Реализация корректно обрабатывает этот случай через событие `no_path` для Observer. A\* и Dijkstra работают медленнее (0.7 мс против 0.37 мс у BFS/DFS) из-за накладных расходов `heapq` при полном обходе без нахождения цели.
## 5. Выводы и рекомендации
На основе полученных результатов можно сформулировать следующие рекомендации:
- **Кратчайший путь в невзвешенном лабиринте → BFS.** Гарантированный результат, простая реализация на `deque`, линейное масштабирование.
- **Максимальная скорость, длина пути не критична → DFS.** В 2 раза быстрее BFS на больших лабиринтах, посещает вдвое меньше клеток. Не использовать на открытых пространствах — путь может быть многократно длиннее оптимального.
- **Взвешенный граф, цель известна → A\*.** Направленный поиск + учёт весов. На небольших и средних лабиринтах быстрее и экономнее BFS. На очень больших (100×100+) накладные расходы `heapq` могут перевесить выигрыш от эвристики.
- **Взвешенный граф, нужны все кратчайшие расстояния → Dijkstra.** Оптимален без целевой точки. С целевой точкой предпочтительнее A\*.
**Итог:** для навигации в лабиринте с одним выходом оптимален BFS (гарантия) или DFS (скорость). A\* предпочтителен при взвешенных клетках. Паттерн Strategy позволяет переключать алгоритмы без изменения остального кода — `solver.set_strategy(AStarStrategy())`.