1. Введение
В современной информатике теория графов (Graph Theory) предоставляет мощную математическую основу для моделирования сетевых структур. В нашей повседневной жизни технологии вычисления «кратчайшего пути» используются в различных ситуациях, таких как автомобильная навигация, информация о пересадках на поездах, интернет-маршрутизация и даже поиск пути ИИ в играх.
В этой статье представлено всестороннее объяснение, начиная с математического определения теории графов, которая является основой этого поиска пути, и заканчивая механизмами типичных алгоритмов поиска: алгоритма Дейкстры (Dijkstra’s Algorithm) и его дальнейшего развития, алгоритма A* (A-Star Algorithm), их математическими доказательствами и практическими методами реализации с использованием Python.
2. Основы теории графов
Перед тем как перейти к объяснению алгоритмов, давайте сначала математически определим граф, который является целевой структурой данных.
2.1 Математическое определение графа
Граф \( G \) определяется парой из множества вершин (Vertex/Node) \( V \) и множества ребер (Edge) \( E \).
\[ G = (V, E) \]Здесь элемент \( e \) множества ребер \( E \) соединяет две вершины \( u, v \in V \) и представляется как \( e = (u, v) \).
- Неориентированный граф (Undirected Graph): Граф без направления ребер. Если \( (u, v) \in E \), то \( (v, u) \in E \).
- Ориентированный граф (Directed Graph): Граф с направлением ребер. \( (u, v) \) и \( (v, u) \) различаются.
2.2 Взвешенный граф (Weighted Graph)
При реальном поиске пути необходимо учитывать расстояние, время, стоимость и т. д. Поэтому мы рассмотрим взвешенный граф, в котором каждому ребру назначен «вес» (Weight). При введении весовой функции \( w: E \rightarrow \mathbb{R} \) граф определяется как \( G = (V, E, w) \).
\[ w(u, v) \ge 0 \]Во многих случаях, поскольку расстояние и время не могут быть отрицательными, предполагается, что веса ребер неотрицательны.
graph LR
A(("A")) -- "4" --> B(("B"))
A -- "2" --> C(("C"))
B -- "5" --> D(("D"))
C -- "1" --> B
C -- "8" --> D
C -- "10" --> E(("E"))
D -- "2" --> E
D -- "6" --> Z(("Z"))
E -- "3" --> Z
Приведенный выше рисунок представляет собой пример взвешенного ориентированного графа от вершины \( A \) до \( Z \). Числа на ребрах обозначают стоимость (вес).
2.3 Формулировка задачи о кратчайшем пути
Пусть путь (Path) \( P \) от начальной точки (Source) \( s \in V \) до конечной точки (Target) \( t \in V \) представляет собой последовательность вершин \( (v_0, v_1, \dots, v_k) \) (где \( v_0 = s, v_k = t \)), и для каждого \( i \) выполняется \( (v_i, v_{i+1}) \in E \). Общая стоимость \( W(P) \) этого пути \( P \) выражается суммой весов ребер на пути.
\[ W(P) = \sum_{i=0}^{k-1} w(v_i, v_{i+1}) \]Задача о кратчайшем пути (Shortest Path Problem) — это задача нахождения пути \( P^* \), минимизирующего \( W(P) \) среди всех возможных путей \( P \).
3. Алгоритм Дейкстры (Dijkstra’s Algorithm)
Алгоритм Дейкстры, разработанный Эдсгером Дейкстрой, представляет собой алгоритм для нахождения кратчайших путей от одной начальной вершины до всех остальных вершин в графе с неотрицательными весами.
3.1 Интуитивное понимание алгоритма
Алгоритм Дейкстры основан на жадном алгоритме (Greedy Algorithm) с подходом «последовательного определения ближайшей неподтвержденной вершины от начальной точки».
- Подготовьте массив для хранения предварительного расстояния от начальной точки, инициализируйте начальную точку как
0, а остальные — какбесконечность( \( \infty \) ). - Среди неподтвержденных вершин выберите вершину \( u \) с минимальным предварительным расстоянием и отметьте её как «подтвержденную».
- Для всех смежных с вершиной \( u \) вершин \( v \), если путь через \( u \) дает меньшее предварительное расстояние, обновите расстояние (эта операция называется релаксацией (Relaxation)).
- Повторяйте шаги 2 и 3, пока все вершины не будут подтверждены или пока не будет подтверждена целевая вершина.
3.2 Математическое выражение релаксации (Relaxation)
Операция релаксации ребра от вершины \( u \) к \( v \) математически выражается следующим образом. Здесь \( d[v] \) обозначает текущее предварительное кратчайшее расстояние от начальной точки до \( v \).
\[ \text{если } d[u] + w(u, v) < d[v]: \\ d[v] = d[u] + w(u, v) \]3.3 Реализация алгоритма Дейкстры на Python
Для эффективной реализации в качестве структуры данных для получения минимального значения используется очередь с приоритетом (Priority Queue). В Python можно использовать модуль heapq.
| |
3.4 О вычислительной сложности
Если в качестве очереди с приоритетом используется бинарная куча (Binary Heap), каждая вершина извлекается из очереди один раз, а каждое ребро релаксируется один раз. Следовательно, временная сложность составляет \( O((|V| + |E|) \log |V|) \). При использовании фибоначчиевой кучи теоретически она улучшается до \( O(|E| + |V| \log |V|) \), но на практике часто применяется бинарная куча.
4. Алгоритм A* (A-Star Algorithm)
Алгоритм Дейкстры надежен, но может привести к множеству ненужных поисков, поскольку расширяет поиск во всех направлениях без учета направления цели. Эту проблему решает алгоритм A*.
4.1 Введение эвристической функции
Алгоритм A* приоритетно продвигает поиск в направлении цели, используя «оценочное расстояние» от текущего узла до цели. Функция, возвращающая это оценочное расстояние, называется эвристической функцией (Heuristic Function) \( h(n) \).
В A* функция \( f(n) \) для оценки узла \( n \) определяется следующим образом.
\[ f(n) = g(n) + h(n) \]Где,
- \( g(n) \): Фактическая стоимость от начальной точки до узла \( n \) (то же самое, что расстояние в алгоритме Дейкстры)
- \( h(n) \): Оценочная стоимость от узла \( n \) до конечной точки (эвристика)
- \( f(n) \): Оценочная общая стоимость пути от начальной точки до конечной через \( n \)
4.2 Условия эвристики
Чтобы алгоритм A* всегда находил кратчайший путь (оптимальность), эвристическая функция \( h(n) \) должна удовлетворять следующим условиям.
- \[
h(n) \le h^*(n)
\]
(где \( h^*(n) \) — истинная кратчайшая стоимость от \( n \) до конечной точки)
- \[
h(m) \le c(m, n) + h(n)
\]
Здесь \( c(m, n) \) — стоимость ребра от \( m \) к \( n \). Согласованная эвристика автоматически становится допустимой.
4.3 Типичные эвристические функции
Для поиска пути на сетке часто используются следующие функции расстояния.
- Манхэттенское расстояние (Manhattan Distance): Когда движение возможно только вверх, вниз, влево и вправо \( h(n) = |x_n - x_{goal}| + |y_n - y_{goal}| \)
- Евклидово расстояние (Euclidean Distance): Когда возможно прямолинейное движение в любом направлении \( h(n) = \sqrt{(x_n - x_{goal})^2 + (y_n - y_{goal})^2} \)
4.4 Реализация алгоритма A* на Python
Реализация A* очень похожа на алгоритм Дейкстры, за исключением того, что ключом для очереди с приоритетом становится \( f(n) \).
| |
4.5 Сравнение алгоритма Дейкстры и A*
Следующая диаграмма Mermaid представляет собой концептуальное сравнение диапазонов поиска алгоритма Дейкстры и A*. В то время как алгоритм Дейкстры расширяет поиск концентрическими кругами, A* продвигает поиск в форме эллипса, вытянутого в направлении цели.
graph TD
subgraph "Дейкстра"
S1(("Начало")) --> A1((" "))
S1 --> B1((" "))
S1 --> C1((" "))
A1 --> D1((" "))
B1 --> Goal1(("Цель"))
C1 --> E1((" "))
style S1 fill:#4a9,stroke:#333
style Goal1 fill:#f94,stroke:#333
end
subgraph "A_Звезда"
S2(("Начало")) --> B2((" "))
B2 --> Goal2(("Цель"))
style S2 fill:#4a9,stroke:#333
style Goal2 fill:#f94,stroke:#333
end
5. Применение поиска пути и перспективы
Алгоритм Дейкстры и алгоритм A* являются базовыми методами, но они служат основой для многих прикладных технологий.
- Двунаправленный поиск (Bidirectional Search): Метод, который радикально сокращает пространство поиска путем одновременного продвижения поиска как от начальной, так и от конечной точки, объединяя их посередине.
- Алгоритм D* (Dynamic A*): Метод эффективного пересчета пути в средах, где динамически появляются неизвестные препятствия (например, автономное вождение роботов).
- JPS (Jump Point Search): Метод дальнейшего ускорения поиска A* на однородных картах-сетках. Он использует симметрию для пропуска ненужных узлов.
Алгоритмы поиска пути — это область, в которой прекрасно сочетаются математическая красота теории графов и алгоритмическая эффективность информатики.
6. Заключение
В этой статье мы начали с базовых определений теории графов и объяснили математическую подоплеку, конкретные механизмы и примеры реализации на Python алгоритма Дейкстры и алгоритма A*.
- Алгоритм Дейкстры оценивает все узлы одинаково и гарантирует нахождение надежного кратчайшего пути.
- Алгоритм A* реализует эффективный поиск к цели путем введения эвристической функции \( h(n) \).
Эти знания не ограничиваются простым пониманием алгоритмов, но станут мощным инструментом мышления для сведения сложных проблем реального мира к математической модели под названием «граф» и нахождения оптимального решения. Обязательно попробуйте запустить реальный код и ощутить его мощь.
