2026-rff_mp/semyanovra/docs/report2.md
2026-05-24 15:10:38 +00:00

15 KiB
Raw Blame History

Отчет по лабораторной работе: Поиск выхода из лабиринта

1. Описание задачи

Разработать программу для загрузки лабиринта из текстового файла, поиска пути от стартовой клетки до выхода с возможностью выбора алгоритма поиска, визуализации процесса и экспериментального сравнения эффективности алгоритмов.

Основные требования:

  • Реализовать модель лабиринта (классы Cell, Maze)
  • Реализовать загрузку лабиринта из файла с символами # (стена), S (старт), E (выход)
  • Реализовать три алгоритма поиска пути: BFS, DFS, A*
  • Реализовать класс-оркестратор MazeSolver с возможностью смены стратегии
  • Собрать статистику: время выполнения, количество посещенных клеток, длина пути
  • Провести эксперименты на лабиринтах разной сложности

Использованные паттерны проектирования GoF:

1. Builder

  • Где используется: Классы LabyrinthBuilder и TxtLabyrinthBuilder
  • Почему выбран: Создание лабиринта из файла включает сложную логику парсинга, валидации и установки старта и выхода. Builder скрывает эти детали от клиента и позволяет легко добавлять новые форматы файлов
  • Преимущества: При добавлении нового формата достаточно создать новый класс-строитель, не меняя существующие классы Labyrinth и алгоритмы поиска

2. Strategy

  • Где используется: Классы SearchAlgorithm, BFS, DFS, AStar
  • Почему выбран: Алгоритмы поиска пути взаимозаменяемы и решают одну задачу разными способами. Strategy позволяет динамически менять алгоритм во время выполнения и легко добавлять новые алгоритмы
  • Преимущества: Класс Pathfinder может использовать любую стратегию через метод set_algorithm. Добавление нового алгоритма требует только создания нового класса

3. Observer

  • Где используется: Классы EventListener и ConsoleDisplay
  • Почему выбран: Приложение должно обновлять консольный интерфейс при различных событиях. Observer отделяет логику отображения от логики приложения
  • Преимущества: Легко добавить новые виды отображения без изменения основной логики

4. Command

  • Где используется: Классы Action и MoveAction
  • Почему выбран: Для реализации пошагового перемещения игрока с возможностью отмены действий. Command инкапсулирует действие в объект и позволяет реализовать undo и redo
  • Преимущества: Хранение истории действий и возможность отмены последних ходов без изменения логики класса Walker

2. Архитектура приложения

Приложение состоит из следующих основных компонентов:

Компонент Назначение
GridCell Модель клетки лабиринта (координаты, стена, старт, выход)
Labyrinth Модель лабиринта (сетка клеток, методы доступа)
TxtLabyrinthBuilder Загрузка лабиринта из текстового файла
BFS, DFS, AStar Алгоритмы поиска пути
Pathfinder Оркестратор, управляющий поиском
ConsoleDisplay Визуализация лабиринта и игрока
Walker Управление позицией игрока
MoveAction Команда перемещения с поддержкой Undo

3. Реализация алгоритмов поиска пути

BFS (Поиск в ширину)

Алгоритм использует очередь для обхода лабиринта. Начинает со стартовой клетки, помещает её в очередь. Затем циклически извлекает клетку из начала очереди, проверяет не является ли она выходом, и добавляет всех непосещенных соседей в конец очереди. Гарантирует нахождение кратчайшего пути по количеству шагов.

DFS (Поиск в глубину)

Алгоритм использует стек для обхода лабиринта. Начинает со стартовой клетки, помещает её в стек. Затем циклически извлекает клетку из конца стека, проверяет не является ли она выходом, и добавляет всех непосещенных соседей в стек. Не гарантирует нахождение кратчайшего пути, но обычно быстрее по времени.

A* (A звездочка)

Алгоритм использует приоритетную очередь с эвристической функцией. Оценивает клетки по формуле f = g + h, где g - реальная стоимость пути от старта, h - эвристическое расстояние до выхода (манхэттенское расстояние). Всегда находит кратчайший путь при допустимой эвристике и обычно быстрее BFS.

4. Экспериментальная часть

Тестовые лабиринты

Имя файла Размер Описание
level1.txt 10x6 Простой лабиринт
medium10x10.txt 10x10 Лабиринт среднего размера
large20x20.txt 20x20 Большой запутанный лабиринт
empty15x15.txt 15x15 Пустой лабиринт без стен
no_exit10x10.txt 10x10 Лабиринт без достижимого выхода

Результаты замеров

Каждый эксперимент проводился 3 раза с усреднением результатов.

Лабиринт Алгоритм Время (мс) Посещено клеток Длина пути
Small 10x6 BFS 0.032 24 11
Small 10x6 DFS 0.017 17 11
Small 10x6 A* 0.064 24 11
Medium 10x10 BFS 0.044 42 16
Medium 10x10 DFS 0.024 26 16
Medium 10x10 A* 0.060 30 16
Large 20x20 BFS 0.245 211 36
Large 20x20 DFS 0.211 170 100
Large 20x20 A* 0.264 103 36
Empty 15x15 BFS 0.199 169 25
Empty 15x15 DFS 0.122 169 97
Empty 15x15 A* 0.411 169 25
No exit 10x10 BFS 0.054 45 18
No exit 10x10 DFS 0.030 28 18
No exit 10x10 A* 0.083 35 18

Графики

Сравнение производительности алгоритмов

На графике представлено сравнение трех алгоритмов по трем метрикам: время выполнения (мс), количество посещенных клеток и длина найденного пути.

5. Анализ результатов

Сравнение характеристик алгоритмов

Характеристика BFS DFS A*
Гарантия кратчайшего пути Да Нет Да
Скорость на малых лабиринтах Средняя Быстрая Средняя
Скорость на больших лабиринтах Средняя Быстрая Средняя
Потребление памяти Высокое Низкое Среднее
Количество посещенных клеток Много (211) Среднее (170) Мало (103)

Детальный анализ по лабиринтам

Small 10x6:

  • Все алгоритмы нашли оптимальный путь длиной 11 шагов
  • DFS оказался самым быстрым (0.017 мс) и посетил меньше всего клеток (17)
  • A* посетил больше клеток (24), но нашел оптимальный путь

Medium 10x10:

  • Оптимальный путь - 16 шагов (BFS и A*)
  • DFS нашел путь длиной 16 (в данном случае совпал с оптимальным)
  • DFS снова самый быстрый (0.024 мс) и посетил 26 клеток против 42 у BFS

Large 20x20:

  • BFS и A* нашли оптимальный путь (36 шагов)
  • DFS нашел неоптимальный путь (100 шагов), что на 64 шага длиннее!
  • A* посетил значительно меньше клеток (103 против 211 у BFS)
  • Это показывает преимущество эвристики A* на больших лабиринтах

Empty 15x15:

  • Оптимальный путь - 25 шагов (по прямой)
  • DFS нашел путь длиной 97 шагов, что в 3.8 раза длиннее!
  • Все алгоритмы посетили одинаковое количество клеток (169) - весь лабиринт
  • A* показал самое большое время из-за накладных расходов на эвристику

No exit 10x10:

  • Все алгоритмы обошли весь достижимый лабиринт
  • DFS посетил меньше клеток (28 против 45 у BFS)
  • Длина пути 18 показывает, что алгоритмы прошли до тупика

Ключевые выводы

  1. BFS - надежный выбор, когда гарантия кратчайшего пути критична. Работает предсказуемо, но на больших лабиринтах посещает много клеток (211 против 103 у A*)

  2. DFS - самый быстрый алгоритм в большинстве тестов (0.017-0.211 мс), но ненадежен для поиска оптимального пути. В большом лабиринте путь оказался на 64% длиннее оптимального!

  3. A* - лучший баланс. Находит кратчайший путь (как BFS), но посещает на 51% меньше клеток в большом лабиринте. Немного медленнее DFS из-за вычисления эвристики.

Рекомендации по выбору алгоритма

Ситуация Рекомендуемый алгоритм Обоснование
Небольшой лабиринт (< 100 клеток) Любой Разница в производительности незначительна
Большой лабиринт, нужен кратчайший путь A* Быстрее BFS, посещает меньше клеток
Максимальная скорость, путь не важен DFS Самый быстрый, но может найти длинный путь
Лабиринт неизвестной структуры A* Лучшее соотношение скорость/качество

6. Заключение

Преимущества использованных паттернов

Builder позволил легко реализовать загрузку лабиринтов из текстовых файлов и оставил возможность для добавления других форматов без изменения основного кода.

Strategy сделал алгоритмы поиска взаимозаменяемыми. Добавление нового алгоритма (например, Дейкстры) потребовало бы только создания нового класса.

Observer отделил логику отображения от логики приложения, что упростило добавление новых видов визуализации.

Command позволил реализовать пошаговое управление игроком с возможностью отмены действий без усложнения класса Walker.

Итог

Разработанная программа демонстрирует преимущества объектно-ориентированного подхода и использования паттернов проектирования. Код является гибким, расширяемым и легко поддерживаемым.

Эксперименты показали, что A* является наиболее сбалансированным алгоритмом для поиска пути в лабиринте, обеспечивая оптимальный путь при приемлемой скорости работы и минимальном количестве посещенных клеток. DFS может быть полезен только когда скорость критична, а оптимальность пути не важна. BFS остается надежным выбором для небольших лабиринтов, где простота реализации важнее производительности.