forked from UNN/2026-rff_mp
183 lines
7.8 KiB
Markdown
183 lines
7.8 KiB
Markdown
## Описание работы
|
||
Схема реализованных классов:
|
||
|
||
```mermaid
|
||
classDiagram
|
||
class TextFileMazeBuilder {
|
||
+buildFromFile(filename): Maze
|
||
}
|
||
class Maze {
|
||
-cells: Cell[]
|
||
-width: int
|
||
-height: int
|
||
-start: Cell
|
||
-exit: Cell
|
||
+getCell(x,y): Cell
|
||
+getNeighbors(cell): List~Cell~
|
||
}
|
||
|
||
class Cell {
|
||
-x: int
|
||
-y: int
|
||
-isWall: bool
|
||
-isStart: bool
|
||
-isExit: bool
|
||
-value: int
|
||
+isPassable(): bool
|
||
+getXY(): tuple[int, int]
|
||
+toStr(): str
|
||
}
|
||
|
||
class MazeBuilder {
|
||
<<interface>>
|
||
+buildFromFile(filename): Maze
|
||
}
|
||
|
||
class PathFindingStrategy {
|
||
<<interface>>
|
||
+name(): str
|
||
+findPath(maze, start, exit): tuple[list[tuple[int, int]], int]
|
||
}
|
||
|
||
class BFS {
|
||
+findPath(maze, start, exit): tuple[list[tuple[int, int]], int]
|
||
}
|
||
class DFS {
|
||
+findPath(maze, start, exit): tuple[list[tuple[int, int]], int]
|
||
}
|
||
class AStar {
|
||
+findPath(maze, start, exit): tuple[list[tuple[int, int]], int]
|
||
+heuristic(a, b): int
|
||
}
|
||
class Dijkstra {
|
||
+findPath(maze, start, exit): tuple[list[tuple[int, int]], int]
|
||
}
|
||
|
||
class SearchStats {
|
||
-timeMs: float
|
||
-visitedCells: int
|
||
-pathLength: int
|
||
-path: list~Cell~
|
||
}
|
||
|
||
class MazeSolver {
|
||
-Maze maze
|
||
-PathFindingStrategy strategy
|
||
-Observer observer
|
||
+strategyName: str
|
||
+setStrategy(strategy)
|
||
+solve(): SearchStats
|
||
}
|
||
|
||
class Observer {
|
||
<<interface>>
|
||
+update(event)
|
||
}
|
||
|
||
class ConsoleView {
|
||
+update(event)
|
||
+render(maze, player_position, path)
|
||
}
|
||
|
||
class Event {
|
||
-event: str
|
||
-maze: Maze
|
||
-player_position: tuple[int,int]
|
||
-path: list~Cell~
|
||
}
|
||
|
||
MazeBuilder <|.. TextFileMazeBuilder
|
||
MazeBuilder --> Maze : creates
|
||
PathFindingStrategy <|.. BFS
|
||
PathFindingStrategy <|.. DFS
|
||
PathFindingStrategy <|.. AStar
|
||
PathFindingStrategy <|.. Dijkstra
|
||
MazeSolver --> PathFindingStrategy : uses
|
||
MazeSolver --> Maze : uses
|
||
Maze --> Cell : uses
|
||
MazeSolver --> SearchStats : return
|
||
Observer <|.. ConsoleView
|
||
ConsoleView --> Event : get
|
||
MazeSolver --> Observer : notifies
|
||
```
|
||
1. Листинги ключевых классов (можно выборочно) или ссылка на репозиторий.
|
||
- Классы `Cell` и `Maze` представлены в папке `source/classes/`
|
||
- Реализации интерфейса `Builder` и класса `TextFileMazeBuilder` находятся в `source/builder/`
|
||
- Реализации интерфейса `Observer` и класса `ConsoleView` находятся в `source/observer/`
|
||
- Интерфейс `strategy`, класс `MazeSolver` и реализации алгоритмов BFS, DFS, A*, Дейкстра находятся в папке `source/strategy/`
|
||
## Результаты экспериментов
|
||
Все результаты находятся в `/data/cvs/banchmark.csv`, тесты запускаются через файл `benchmark.ipynb`. Лабиринты, на которых проходили тесты, находятся в директори `mazes/benchmarks/`
|
||
Проведём 10 замеров и отобразим результаты на графиках (пунктиром отмечены среднее значение)
|
||
![[10x10.pdf]]
|
||
![[50x50.pdf]]
|
||
![[100x100.pdf]]
|
||
![[empty.pdf]]
|
||
![[no_path.pdf]]
|
||
|
||
Заполним таблицу для количества посещённых клеток для каждого алгоритма:
|
||
|
||
| Лабиринт | BFS | DFS | A* | Дейкстра |
|
||
| :------------: | :--: | :--: | :--: | :------: |
|
||
| $10\times10$ | 25 | 24 | 24 | 25 |
|
||
| $50\times50$ | 972 | 920 | 763 | 972 |
|
||
| $100\times100$ | 2345 | 2609 | 1194 | 2345 |
|
||
| Пустой | 5328 | 5328 | 5328 | 5328 |
|
||
| Без выхода | 1245 | 1245 | 1245 | 1245
|
||
|
||
## Анализ результатов
|
||
- **DFS** быстрее на большинстве лабиринтов, но путь может быть неоптимальным
|
||
В качестве демонстрации, сравним работу DFS и BFS на небольшом пустом лабиринте:
|
||
```
|
||
BFS
|
||
Путь найден:
|
||
#####################################
|
||
#S #
|
||
#. #
|
||
#. #
|
||
#. #
|
||
#. #
|
||
#. #
|
||
#. #
|
||
#. #
|
||
#..................................E#
|
||
#####################################
|
||
time: 0.8261000002676155 ms
|
||
visited cells: 315
|
||
path length: 43
|
||
```
|
||
```
|
||
DFS
|
||
Путь найден:
|
||
#####################################
|
||
#S..................................#
|
||
# .#
|
||
#...................................#
|
||
#. #
|
||
#...................................#
|
||
# .#
|
||
#...................................#
|
||
#. #
|
||
#..................................E#
|
||
#####################################
|
||
time: 0.6825999989814591 ms
|
||
visited cells: 315
|
||
path length: 179
|
||
```
|
||
Как видно по примеру DFS нашёл путь быстрее (0.68 против 0.82 мс), но длина найденного маршрута 179 клеток, в то время как путь, найденный BFS состоит из 43 клеток.
|
||
#### A*:
|
||
- По таблице видно, что A* проходит меньше всего клеток. Это происходит, так как идея алгоритма в том что он отдаёт приоритет клеткам, которые ближе к цели.
|
||
- На практике медленнее DFS из-за операций с кучей (O(log n) на каждый шаг)
|
||
|
||
#### Dijkstra:
|
||
- По сложности аналогичен BFS для лабиринтов без весов, но медленнее BFS из-за приоритетной очереди.
|
||
- Имеет смысл на взвешенных графах
|
||
|
||
## Выводы
|
||
Использование ООП и паттернов дало:
|
||
- расширяемость - лёгкость добавления нового алгоритма поиска без изменения текущей структуры и существующих классов
|
||
- гибкость - можно менять алгоритмы поиска, конструкторы лабиринтов и способы отображения так же без изменения уже существующих
|
||
- Лёгкость тестирования - можно тестировать каждый элемент независимо
|
||
Без этого было бы сложно внедрять новые реализации классов, способы отображения или создания лабиринта или изменять существующие алгоритмы.
|
||
Но реализация интерфейсов и унификация классов увеличили объём кода и так же наложили ограничения на обрабатываемые данные.
|
||
|
||
По скорости лучшим по большинству тестов стал DFS. Второй по скорости BFS, так же он находит самый короткий путь, но при усложнении лабиринта(увеличении развилок и размера) начинает проигрывать A*. |