2026-rff_mp/filippovavm/docs/laba2/отчет2.ipynb

376 lines
20 KiB
Plaintext
Raw Normal View History

2026-05-24 15:27:35 +00:00
{
"cells": [
{
"cell_type": "markdown",
"id": "204f9862-9a51-47be-bb5b-fcb099f9f707",
"metadata": {},
"source": [
"# Отчёт по лабораторной работе: Поиск выхода из лабиринта (ООП + паттерны)\n",
"\n",
"## 1. Описание задачи\n",
"\n",
"Разработать программу для загрузки лабиринта из файла, поиска пути от старта до выхода с возможностью выбора алгоритма и экспериментального сравнения алгоритмов. Требовалось применить минимум 3 паттерна проектирования GoF.\n",
"\n",
"**Исходные данные:** \n",
"- Лабиринты разной сложности (маленький, средний, большой, пустой, без выхода). \n",
"- Формат файла: `#` – стена, ` ` – проход, `S` – старт, `E` – выход. \n",
"- Алгоритмы поиска: BFS, DFS, A* (с манхэттенской эвристикой).\n",
"\n",
"**Цель эксперимента:** сравнить эффективность алгоритмов по времени, количеству посещённых клеток и длине найденного пути.\n",
"\n",
"---\n",
"\n",
"## 2. Выбранные паттерны проектирования\n",
"\n",
"### 2.1. Builder (строитель) – для загрузки лабиринта\n",
"**Проблема:** создание лабиринта из файла требует нескольких шагов (чтение, парсинг, установка флагов). \n",
"**Решение:** интерфейс `MazeBuilder` и конкретная реализация `TextFileMazeBuilder` скрывают детали построения. \n",
"**Преимущество:** легко добавить новый формат (JSON, XML) без изменения остального кода.\n",
"\n",
"### 2.2. Strategy (стратегия) – для алгоритмов поиска\n",
"**Проблема:** алгоритмы поиска (BFS, DFS, A*) взаимозаменяемы, но их реализация отличается. \n",
"**Решение:** интерфейс `PathFindingStrategy` и три класса-стратегии. \n",
"**Преимущество:** можно динамически менять алгоритм во время выполнения (например, через `MazeSolver.setStrategy()`).\n",
"\n",
"### 2.3. Observer (наблюдатель) – для логирования (опционально, но реализован)\n",
"**Проблема:** нужно оповещать внешние компоненты о событиях поиска (начало, найден путь, ошибка). \n",
"**Решение:** интерфейс `Observer` и класс `ConsoleLogger`. \n",
"**Преимущество:** слабая связность – легко добавить другие наблюдатели (GUI, файл лога).\n",
"\n",
"### 2.4. Command (команда) – для пошагового движения (опционально, в демо)\n",
"**Проблема:** требуется поддержка отмены ходов (undo). \n",
"**Решение:** интерфейс `Command`, класс `MoveCommand`, класс `Player`. \n",
"**Преимущество:** инкапсуляция запроса, возможность отмены, ведения истории.\n",
"\n",
"---\n",
"\n",
"## 3. Диаграмма классов (Mermaid)\n",
"\n",
"```mermaid\n",
"classDiagram\n",
" class Cell {\n",
" -int x\n",
" -int y\n",
" -bool isWall\n",
" -bool isStart\n",
" -bool isExit\n",
" +isPassable()\n",
" }\n",
" class Maze {\n",
" -int width\n",
" -int height\n",
" -List[List[Cell]] cells\n",
" -Cell start\n",
" -Cell exit\n",
" +getCell(x,y)\n",
" +getNeighbors(cell)\n",
" }\n",
" class MazeBuilder {\n",
" <<interface>>\n",
" +buildFromFile(filename)\n",
" }\n",
" class TextFileMazeBuilder {\n",
" +buildFromFile(filename)\n",
" }\n",
" class PathFindingStrategy {\n",
" <<interface>>\n",
" +findPath(maze, start, exit)\n",
" }\n",
" class BFSStrategy\n",
" class DFSStrategy\n",
" class AStarStrategy\n",
" class SearchStats {\n",
" +float time_ms\n",
" +int visited_cells\n",
" +int path_length\n",
" +string algorithm\n",
" }\n",
" class MazeSolver {\n",
" -Maze maze\n",
" -PathFindingStrategy strategy\n",
" +setStrategy(strategy)\n",
" +solve() (path, stats)\n",
" }\n",
" class Observer {\n",
" <<interface>>\n",
" +update(event_type, data)\n",
" }\n",
" class ConsoleLogger {\n",
" +update(event_type, data)\n",
" }\n",
" class Command {\n",
" <<interface>>\n",
" +execute()\n",
" +undo()\n",
" }\n",
" class MoveCommand {\n",
" -Player player\n",
" -tuple direction\n",
" -Maze maze\n",
" -Cell prev_pos\n",
" +execute()\n",
" +undo()\n",
" }\n",
" class Player {\n",
" -Cell current_cell\n",
" }\n",
"\n",
" MazeBuilder <|.. TextFileMazeBuilder\n",
" PathFindingStrategy <|.. BFSStrategy\n",
" PathFindingStrategy <|.. DFSStrategy\n",
" PathFindingStrategy <|.. AStarStrategy\n",
" MazeSolver --> PathFindingStrategy\n",
" MazeSolver --> Maze\n",
" MazeSolver --> SearchStats\n",
" Observer <|.. ConsoleLogger\n",
" MazeSolver --> Observer\n",
" Command <|.. MoveCommand\n",
" MoveCommand --> Player\n",
" MoveCommand --> Maze"
]
},
{
"cell_type": "markdown",
"id": "0e671083-627f-4940-970f-80f8668388fb",
"metadata": {},
"source": [
"## 4.1 Builder (загрузка лабиринта)"
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "bb983238-274f-4f19-b784-73be954a3aae",
"metadata": {},
"outputs": [
{
"ename": "NameError",
"evalue": "name 'ABC' is not defined",
"output_type": "error",
"traceback": [
"\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[1;31mNameError\u001b[0m Traceback (most recent call last)",
"Cell \u001b[1;32mIn[1], line 1\u001b[0m\n\u001b[1;32m----> 1\u001b[0m \u001b[38;5;28;01mclass\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;21;01mMazeBuilder\u001b[39;00m(ABC):\n\u001b[0;32m 2\u001b[0m \u001b[38;5;129m@abstractmethod\u001b[39m\n\u001b[0;32m 3\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;21mbuild_from_file\u001b[39m(\u001b[38;5;28mself\u001b[39m, filename):\n\u001b[0;32m 4\u001b[0m \u001b[38;5;28;01mpass\u001b[39;00m\n",
"\u001b[1;31mNameError\u001b[0m: name 'ABC' is not defined"
]
}
],
"source": [
"class MazeBuilder(ABC):\n",
" @abstractmethod\n",
" def build_from_file(self, filename):\n",
" pass\n",
"\n",
"class TextFileMazeBuilder(MazeBuilder):\n",
" def build_from_file(self, filename):\n",
" with open(filename, 'r') as f:\n",
" lines = [line.rstrip('\\n') for line in f]\n",
" height = len(lines)\n",
" width = max(len(line) for line in lines)\n",
" maze = Maze(width, height)\n",
" for y, line in enumerate(lines):\n",
" for x, ch in enumerate(line):\n",
" cell = maze.get_cell(x, y)\n",
" if ch == '#': cell.is_wall = True\n",
" elif ch == 'S': cell.is_start = True; maze.start = cell\n",
" elif ch == 'E': cell.is_exit = True; maze.exit = cell\n",
" else: cell.is_wall = False\n",
" return maze"
]
},
{
"cell_type": "markdown",
"id": "f20c327f-b8f7-40f4-a71f-eede64d5dedf",
"metadata": {},
"source": [
"## 4.2. Стратегии поиска (BFS, DFS, A*)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "b5fb0715-9749-404d-870d-0a83c0025eac",
"metadata": {},
"outputs": [],
"source": [
"class BFSStrategy(PathFindingStrategy):\n",
" def find_path(self, maze, start, exit):\n",
" queue = deque([start])\n",
" visited = {start}\n",
" parent = {start: None}\n",
" while queue:\n",
" cur = queue.popleft()\n",
" if cur == exit:\n",
" path = []\n",
" while cur:\n",
" path.append(cur)\n",
" cur = parent[cur]\n",
" return path[::-1], len(visited)\n",
" for nb in maze.get_neighbors(cur):\n",
" if nb not in visited:\n",
" visited.add(nb)\n",
" parent[nb] = cur\n",
" queue.append(nb)\n",
" return [], len(visited)"
]
},
{
"cell_type": "markdown",
"id": "bae1222c-07f6-487d-ac05-7d09d7086e67",
"metadata": {},
"source": [
"## 4.3. Оркестратор MazeSolver и статистика"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "247f19cf-4811-4dd8-8e35-03af6550d202",
"metadata": {},
"outputs": [],
"source": [
"class MazeSolver:\n",
" def solve(self):\n",
" start_time = time.perf_counter()\n",
" path, visited = self.strategy.find_path(self.maze, self.maze.start, self.maze.exit)\n",
" end_time = time.perf_counter()\n",
" stats = SearchStats(\n",
" time_ms=(end_time - start_time)*1000,\n",
" visited_cells=visited,\n",
" path_length=len(path),\n",
" algorithm=self.strategy.__class__.__name__\n",
" )\n",
" return path, stats"
]
},
{
"cell_type": "markdown",
"id": "8a7167d5-efd8-4b3c-a14e-5b6c1cba83ef",
"metadata": {},
"source": [
"## 5. Результаты экспериментов\n",
"\n",
"**Тестовые лабиринты:**\n",
"- `tiny.txt` – 10×10, простой путь\n",
"- `medium.txt` – 50×50, с тупиками\n",
"- `large.txt` – 100×100, запутанный\n",
"- `empty.txt` – 100×100, без стен (старт в левом верхнем углу, выход в правом нижнем)\n",
"- `no_exit.txt` – лабиринт без выхода\n",
"\n",
"> **Примечание:** средний лабиринт (`medium.txt`) не был включён в замеры из-за отсутствия корректного файла. Остальные четыре лабиринта соответствуют заданию.\n",
"\n",
"**Методика:** каждый алгоритм запущен 5 раз на каждом лабиринте, значения усреднены. Данные получены из `all_results.csv`.\n",
"\n",
"### 5.1. Таблица результатов\n",
"\n",
"| Лабиринт | Алгоритм | Время (мс) | Посещено клеток | Длина пути |\n",
"|----------------|----------------|------------|-----------------|------------|\n",
"| tiny.txt | BFSStrategy | 0.2854 | 72.0 | 16.0 |\n",
"| tiny.txt | DFSStrategy | 0.2665 | 71.0 | 72.0 |\n",
"| tiny.txt | AStarStrategy | 0.3941 | 72.0 | 16.0 |\n",
"| large.txt | BFSStrategy | 4.9520 | 1275.0 | 50.0 |\n",
"| large.txt | DFSStrategy | 0.2159 | 49.0 | 50.0 |\n",
"| large.txt | AStarStrategy | 0.3549 | 50.0 | 50.0 |\n",
"| empty.txt | BFSStrategy | 24.3337 | 5049.0 | 100.0 |\n",
"| empty.txt | DFSStrategy | 0.5570 | 99.0 | 100.0 |\n",
"| empty.txt | AStarStrategy | 0.7525 | 100.0 | 100.0 |\n",
"| no_exit.txt | BFSStrategy | 1.2649 | 324.0 | 0.0 |\n",
"| no_exit.txt | DFSStrategy | 3.2304 | 324.0 | 0.0 |\n",
"| no_exit.txt | AStarStrategy | 2.2239 | 324.0 | 0.0 |\n",
"\n",
"### 5.2. Графики\n",
"\n",
"Графики для каждого по отдельности сохранены в файле, тут предоставляю общее сравнение\n",
"![Сводный график](summary_comparison.png)\n",
"\n",
"---\n",
"\n",
"## 6. Анализ эффективности алгоритмов\n",
"\n",
"### 6.1. Время выполнения\n",
"- На **tiny.txt** все алгоритмы показали близкое время (~0.2–0.4 мс); DFS незначительно быстрее.\n",
"- На **large.txt** BFS значительно медленнее (4.95 мс) из-за равномерного обхода, а DFS и A* работают быстро (~0.2–0.35 мс).\n",
"- На **empty.txt** BFS крайне медленен (24.3 мс), поскольку вынужден обойти почти всё поле; DFS и A* справляются за ~0.5–0.75 мс.\n",
"- В лабиринте **no_exit.txt** BFS быстрее (1.26 мс), а DFS и A* медленнее (2.2–3.2 мс) из-за разных порядков обхода.\n",
"\n",
"### 6.2. Количество посещённых клеток\n",
"- **BFS** на `empty.txt` посещает 5049 клеток (почти половину поля), тогда как DFS и A* – всего ~100 клеток.\n",
"- В `large.txt` BFS посещает 1275 клеток, DFS – 49, A* – 50. Это показывает, что A* (как и DFS) находит путь, исследуя лишь узкую область.\n",
"- В `tiny.txt` все алгоритмы посещают около 70 клеток (различия незначительны).\n",
"\n",
"### 6.3. Длина найденного пути\n",
"- **BFS** и **A*** находят кратчайший путь (16 шагов в tiny.txt, 50 в large.txt, 100 в empty.txt).\n",
"- **DFS** в tiny.txt даёт очень длинный путь (72 шага вместо 16), в large.txt – 50 (совпал с оптимальным), в empty.txt – 100 (оптимально).\n",
"- Таким образом, DFS не гарантирует кратчайший путь, хотя в некоторых случаях может его найти.\n",
"\n",
"### 6.4. Лабиринт без выхода\n",
"- Все алгоритмы исследуют всю достижимую область (324 клетки). \n",
"- Время различается: BFS – 1.26 мс, DFS – 3.23 мс, A* – 2.22 мс. Это связано с тем, что DFS «закапывается» в глубину, а A* тратит время на поддержание очереди с приоритетами.\n",
"\n",
"---\n",
"\n",
"## 7. Применимость паттернов и гибкость архитектуры\n",
"\n",
"### 7.1. Паттерн Builder\n",
"- **Без него:** код загрузки был бы прямо в `main` или в конструкторе `Maze`. Пришлось бы переписывать при добавлении нового формата.\n",
"- **С ним:** легко добавить `JSONMazeBuilder`, заменив всего одну строку в клиентском коде.\n",
"\n",
"### 7.2. Паттерн Strategy\n",
"- **Без него:** пришлось бы использовать громоздкие `if-elif` для выбора алгоритма, дублировать код замера времени.\n",
"- **С ним:** алгоритмы полностью независимы, можно динамически менять стратегию (например, на основе размера лабиринта).\n",
"\n",
"### 7.3. Паттерн Observer (опционально)\n",
"- **Без него:** логирование и визуализация были бы вплетены в алгоритмы поиска.\n",
"- **С ним:** наблюдатели подписываются на события, и логирование можно отключить или заменить на другой вывод без изменения `MazeSolver`.\n",
"\n",
"### 7.4. Паттерн Command (для пошагового движения)\n",
"- **Без него:** отмена хода пришлось бы реализовывать вручную, что привело бы к дублированию кода.\n",
"- **С ним:** команды легко складывать в историю, реализовать `undo` и `redo`.\n",
"\n",
"---\n",
"\n",
"## 8. Выводы\n",
"\n",
"- **ООП и паттерны проектирования** позволили создать гибкую, расширяемую и легко тестируемую программу.\n",
"- **Builder** упростил добавление новых форматов лабиринтов.\n",
"- **Strategy** сделал алгоритмы поиска взаимозаменяемыми и позволил проводить честное сравнение.\n",
"- **Observer** и **Command** добавили возможности логирования и отмены действий без «засорения» основной логики.\n",
"- Экспериментально подтверждены теоретические свойства алгоритмов: A* – лучший выбор для большинства случаев (оптимальный путь и высокая скорость), BFS – оптимален по длине пути, но медленен на больших картах, DFS – прост, но не даёт гарантий кратчайшего пути.\n",
"\n",
"**Итог:** использование паттернов повысило качество кода, уменьшило связанность и облегчило поддержку. Без них любое изменение (добавление нового алгоритма или формата файла) потребовало бы правки многих классов.\n",
"\n",
"--- \n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "5e634d95-e0c0-4b8a-a330-02463cd085c8",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.13.5"
}
},
"nbformat": 4,
"nbformat_minor": 5
}