Алгоритм Дейкстры — различия между версиями
| м | м (rollbackEdits.php mass rollback) | ||
| (не показано 65 промежуточных версий 9 участников) | |||
| Строка 1: | Строка 1: | ||
| − | + | {{Задача | |
| + | |definition=Для заданного взвешенного графа <tex>G = (V, E)</tex> найти кратчайшие пути из заданной вершины <tex> s </tex> до всех остальных вершин. Веса всех рёбер неотрицательны. | ||
| + | }} | ||
| == Алгоритм == | == Алгоритм == | ||
| − | В алгоритме поддерживается множество вершин <tex> | + | В [[Ориентированный граф|ориентированном]] взвешенном [[Основные определения: граф, ребро, вершина, степень, петля, путь, цикл|графе]] <tex>G = (V, E)</tex>, вес [[Основные определения: граф, ребро, вершина, степень, петля, путь, цикл|рёбер]] которого неотрицателен и определяется весовой функцией <tex>w : E \to \mathbb{R}</tex>, алгоритм Дейкстры находит длины кратчайших [[Основные определения: граф, ребро, вершина, степень, петля, путь, цикл|путей]] из заданной [[Основные определения: граф, ребро, вершина, степень, петля, путь, цикл|вершины]] <tex>s</tex> до всех остальных.<br> | 
| + | В алгоритме поддерживается множество вершин <tex>U</tex>, для которых уже вычислены длины кратчайших путей до них из <tex>s</tex>. На каждой итерации основного цикла выбирается вершина <tex> u \notin U</tex>, которой на текущий момент соответствует минимальная оценка  кратчайшего пути. Вершина <tex>u</tex> добавляется в множество <tex>U</tex> и производится релаксация всех исходящих из неё рёбер. | ||
| == Псевдокод == | == Псевдокод == | ||
| − |   ''' | + |   '''func''' dijkstra(s)''':''' | 
| − | + |      '''for''' <tex>v \in V</tex>                     | |
| − | + |          d[v] = <tex>\infty</tex> | |
| − | + |          used[v] = ''false'' | |
| − | + |      d[s] = 0 | |
| − | + |      '''for''' <tex>i \in V</tex> | |
| − | + |          v = ''null'' | |
| − | + |          '''for''' <tex>j \in V</tex>                        <font color="green">// найдём вершину с минимальным расстоянием</font> | |
| − | + |              '''if''' !used[j] '''and''' (v == ''null'' '''or''' d[j] < d[v]) | |
| + |                  v = j | ||
| + |          '''if''' d[v] == <tex>\infty</tex> | ||
| + |              '''break''' | ||
| + |          used[v] = ''true'' | ||
| + |          '''for''' e : исходящие из ''v'' рёбра     <font color="green">// произведём релаксацию по всем рёбрам, исходящим из ''v''</font> | ||
| + |              '''if''' d[v] + e.len < d[e.to] | ||
| + |                  d[e.to] = d[v] + e.len | ||
| − | == Обоснование корректности  | + | == Обоснование корректности == | 
| {{Теорема | {{Теорема | ||
| − | |statement= | + | |statement= | 
| + | Пусть <tex>G = (V, E)</tex> {{---}} ориентированный взвешенный граф, вес рёбер которого неотрицателен, <tex>s</tex> {{---}} стартовая вершина. | ||
| + | Тогда после выполнения алгоритма Дейкстры <tex>d(u) = \rho(s, u)</tex> для всех <tex>u</tex>, где <tex>\rho(s, u)</tex> {{---}} длина кратчайшего пути из вершины <tex>s</tex> в вершину <tex>u</tex> | ||
| |proof= | |proof= | ||
| − | + | Докажем по индукции, что в момент посещения любой вершины <tex>u</tex>, <tex>d(u) = \rho(s, u)</tex>. | |
| − | + | * На первом шаге выбирается <tex>s</tex>, для неё выполнено: <tex>d(s) = \rho(s, s) = 0</tex> | |
| − | + | * Пусть для <tex>n</tex> первых шагов алгоритм сработал верно и на <tex>n + 1</tex> шагу выбрана вершина <tex>u</tex>. Докажем, что в этот момент <tex>d(u) = \rho(s, u)</tex>. Для начала отметим, что для любой вершины <tex>v</tex>, всегда выполняется <tex>d(v) \geqslant \rho(s, v)</tex> (алгоритм не может найти путь короче, чем кратчайший из всех существующих). Пусть <tex>P</tex> — кратчайший путь из <tex>s</tex> в <tex>u</tex>, <tex>v</tex> {{---}} первая непосещённая вершина на <tex>P</tex>, <tex>z</tex> {{---}} предшествующая ей (следовательно, посещённая). Поскольку путь <tex>P</tex> кратчайший, его часть, ведущая из <tex>s</tex> через <tex>z</tex> в <tex>v</tex>, тоже кратчайшая, следовательно <tex>\rho(s, v) = \rho(s, z) + w(zv)</tex>. По предположению индукции, в момент посещения вершины <tex>z</tex> выполнялось <tex>d(z) = \rho(s, z)</tex>, следовательно, вершина <tex>v</tex> тогда получила метку не больше чем <tex>d(z) + w(zv) = \rho(s, z) + w(zv) = \rho(s, v)</tex>, следовательно, <tex>d(v) = \rho(s, v)</tex>. С другой стороны, поскольку сейчас мы выбрали вершину <tex>u</tex>, её метка минимальна среди непосещённых, то есть <tex>d(u) \leqslant d(v) = \rho(s, v) \leqslant \rho(s, u)</tex>, где второе неравенсто верно из-за ранее упомянутого определения вершины <tex>v</tex> в качестве первой непосещённой вершины на <tex>P</tex>, то есть вес пути до промежуточной вершины не превосходит веса пути до конечной вершины вследствие неотрицательности весовой функции. Комбинируя это с <tex>d(u) \geqslant \rho(s, u)</tex>, имеем <tex>d(u) = \rho(s, u)</tex>, что и требовалось доказать. | |
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | + | *Поскольку алгоритм заканчивает работу, когда все вершины посещены, в этот момент <tex>d(u) = \rho(s, u)</tex> для всех <tex>u</tex>. | |
| }} | }} | ||
| == Оценка сложности == | == Оценка сложности == | ||
| − | + | В реализации алгоритма присутствует функция выбора вершины с минимальным значением <tex>d</tex> и релаксация по всем рёбрам для данной вершины. Асимптотика работы зависит от реализации. | |
| − | + | Пусть <tex>n</tex> {{---}} количество вершин в графе, <tex>m</tex> {{---}} количество рёбер в графе. | |
| − | { | + | |
| − | + | {| class="wikitable" | |
| − | |||
| |- | |- | ||
| − | | | + | ! rowspan="2" | | 
| − | + | ! colspan="3" | Время работы | |
| + | ! rowspan="2" | Описание | ||
| |- | |- | ||
| − | + | ! Поиск минимума | |
| − | + | ! Релаксация | |
| + | ! Общее | ||
| |- | |- | ||
| − | | | + | | align="center" | Наивная реализация | 
| − | | | + | | align="center" | <tex>O(n)</tex> | 
| + | | align="center" | <tex>O(1)</tex> | ||
| + | | align="center" | <tex>O(n^2 + m)</tex> | ||
| + | | align="center" | <tex>n</tex> раз осуществляем поиск вершины с минимальной величиной <tex>d</tex> среди <tex>O(n)</tex> непомеченных вершин и <tex>m</tex> раз проводим релаксацию за <tex>O(1)</tex>. Для плотных графов (<tex>m \approx n^2</tex>) данная асимптотика является оптимальной. | ||
| + | |- | ||
| + | | align="center" | [[Двоичная куча]] | ||
| + | | align="center" | <tex>O(\log{n})</tex> | ||
| + | | align="center" | <tex>O(\log{n})</tex> | ||
| + | | align="center" | <tex>O(m\log{n})</tex> | ||
| + | | align="center" | Используя двоичную кучу можно выполнять операции извлечения минимума и обновления элемента за <tex>O(\log{n})</tex>. Тогда время работы алгоритма Дейкстры составит <tex>O(n\log{n} + m\log{n}) = O(m\log{n})</tex>.  | ||
| + | |- | ||
| + | | align="center" | [[Фибоначчиевы кучи|Фибоначчиева куча]] | ||
| + | | align="center" | <tex>O(\log{n})</tex> | ||
| + | | align="center" | <tex>O(1)</tex> | ||
| + | | align="center" | <tex>O(n\log{n} + m)</tex> | ||
| + | | align="center" | Используя Фибоначчиевы кучи можно выполнять операции извлечения минимума за <tex>O(\log{n})</tex> и обновления элемента за <tex>O(1)</tex>. Таким образом, время работы алгоритма составит <tex>O(n\log{n} + m)</tex>. | ||
| |} | |} | ||
| + | На практике удобно использовать стандартные контейнеры (например, '''std::set''' или '''std::priority_queue''' в C++). <br>При реализации необходимо хранить вершины, которые упорядочены по величине <tex>d</tex>, для этого в контейнер можно помещать пару {{---}} расстояние-вершина. В результате будут храниться пары, упорядоченные по расстоянию.  | ||
| + | |||
| + | Изначально поместим в контейнер стартовую вершину <tex>s</tex>. Основной цикл будет выполняться, пока в контейнере есть хотя бы одна вершина. На каждой итерации извлекается вершина с наименьшим расстоянием <tex>d</tex> и выполняются релаксации по рёбрам из неё. При выполнении успешной релаксации нужно удалить из контейнера вершину, до которой обновляем расстояние, а затем добавить её же, но с новым расстоянием. | ||
| + | <br>В обычных кучах нет операции удаления произвольного элемента. При релаксации можно не удалять старые пары, в результате чего в куче может находиться одновременно несколько пар расстояние-вершина для одной вершины (с разными расстояниями). Для корректной работы при извлечении из кучи будем проверять расстояние: пары, в которых расстояние отлично от <tex>d[v]</tex> будем игнорировать. При этом асимптотика будет <tex>O(m\log{m})</tex> вместо <tex>O(m\log{n})</tex>. | ||
| − | ==  | + | == Источники информации == | 
| − | *  | + | * Томас Х. Кормен, Чарльз И. Лейзерсон, Рональд Л. Ривест, Клиффорд Штайн Алгоритмы: построение и анализ — 2-е изд. — М.: «Вильямс», 2007. — с. 459. — ISBN 5-8489-0857-4 | 
| + | * [http://e-maxx.ru/algo/dijkstra MAXimal :: algo :: Нахождение кратчайших путей от заданной вершины до всех остальных вершин алгоритмом Дейкстры] | ||
| + | * [https://ru.wikipedia.org/wiki/Алгоритм_Дейкстры Википедия — Алгоритм Дейкстры] | ||
| + | * [https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm Wikipedia — Dijkstra's algorithm] | ||
| [[Категория: Алгоритмы и структуры данных]] | [[Категория: Алгоритмы и структуры данных]] | ||
| [[Категория: Кратчайшие пути в графах ]] | [[Категория: Кратчайшие пути в графах ]] | ||
Текущая версия на 19:30, 4 сентября 2022
| Задача: | 
| Для заданного взвешенного графа найти кратчайшие пути из заданной вершины до всех остальных вершин. Веса всех рёбер неотрицательны. | 
Содержание
Алгоритм
В ориентированном взвешенном графе , вес рёбер которого неотрицателен и определяется весовой функцией , алгоритм Дейкстры находит длины кратчайших путей из заданной вершины  до всех остальных.
В алгоритме поддерживается множество вершин , для которых уже вычислены длины кратчайших путей до них из . На каждой итерации основного цикла выбирается вершина , которой на текущий момент соответствует минимальная оценка  кратчайшего пути. Вершина  добавляется в множество  и производится релаксация всех исходящих из неё рёбер.
Псевдокод
func dijkstra(s):
    for                     
        d[v] = 
        used[v] = false
    d[s] = 0
    for 
        v = null
        for                         // найдём вершину с минимальным расстоянием
            if !used[j] and (v == null or d[j] < d[v])
                v = j
        if d[v] == 
            break
        used[v] = true
        for e : исходящие из v рёбра     // произведём релаксацию по всем рёбрам, исходящим из v
            if d[v] + e.len < d[e.to]
                d[e.to] = d[v] + e.len
Обоснование корректности
| Теорема: | 
| Пусть  — ориентированный взвешенный граф, вес рёбер которого неотрицателен,  — стартовая вершина.
Тогда после выполнения алгоритма Дейкстры  для всех , где  — длина кратчайшего пути из вершины  в вершину  | 
| Доказательство: | 
| Докажем по индукции, что в момент посещения любой вершины , . 
 
 | 
Оценка сложности
В реализации алгоритма присутствует функция выбора вершины с минимальным значением и релаксация по всем рёбрам для данной вершины. Асимптотика работы зависит от реализации.
Пусть — количество вершин в графе, — количество рёбер в графе.
| Время работы | Описание | |||
|---|---|---|---|---|
| Поиск минимума | Релаксация | Общее | ||
| Наивная реализация | раз осуществляем поиск вершины с минимальной величиной среди непомеченных вершин и раз проводим релаксацию за . Для плотных графов () данная асимптотика является оптимальной. | |||
| Двоичная куча | Используя двоичную кучу можно выполнять операции извлечения минимума и обновления элемента за . Тогда время работы алгоритма Дейкстры составит . | |||
| Фибоначчиева куча | Используя Фибоначчиевы кучи можно выполнять операции извлечения минимума за и обновления элемента за . Таким образом, время работы алгоритма составит . | |||
На практике удобно использовать стандартные контейнеры (например, std::set или std::priority_queue в C++). 
При реализации необходимо хранить вершины, которые упорядочены по величине , для этого в контейнер можно помещать пару — расстояние-вершина. В результате будут храниться пары, упорядоченные по расстоянию. 
Изначально поместим в контейнер стартовую вершину . Основной цикл будет выполняться, пока в контейнере есть хотя бы одна вершина. На каждой итерации извлекается вершина с наименьшим расстоянием  и выполняются релаксации по рёбрам из неё. При выполнении успешной релаксации нужно удалить из контейнера вершину, до которой обновляем расстояние, а затем добавить её же, но с новым расстоянием.
В обычных кучах нет операции удаления произвольного элемента. При релаксации можно не удалять старые пары, в результате чего в куче может находиться одновременно несколько пар расстояние-вершина для одной вершины (с разными расстояниями). Для корректной работы при извлечении из кучи будем проверять расстояние: пары, в которых расстояние отлично от  будем игнорировать. При этом асимптотика будет  вместо .
Источники информации
- Томас Х. Кормен, Чарльз И. Лейзерсон, Рональд Л. Ривест, Клиффорд Штайн Алгоритмы: построение и анализ — 2-е изд. — М.: «Вильямс», 2007. — с. 459. — ISBN 5-8489-0857-4
- MAXimal :: algo :: Нахождение кратчайших путей от заданной вершины до всех остальных вершин алгоритмом Дейкстры
- Википедия — Алгоритм Дейкстры
- Wikipedia — Dijkstra's algorithm
