2026-rff_mp/filippovavm/docs/laba2/отчет2.ipynb
2026-05-24 18:27:35 +03:00

376 lines
20 KiB
Plaintext
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.

{
"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.20.4 мс); DFS незначительно быстрее.\n",
"- На **large.txt** BFS значительно медленнее (4.95 мс) из-за равномерного обхода, а DFS и A* работают быстро (~0.20.35 мс).\n",
"- На **empty.txt** BFS крайне медленен (24.3 мс), поскольку вынужден обойти почти всё поле; DFS и A* справляются за ~0.50.75 мс.\n",
"- В лабиринте **no_exit.txt** BFS быстрее (1.26 мс), а DFS и A* медленнее (2.23.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
}