Menu
Coddy logo textTech

Алгоритм Беллмана-Форда

Последнее обновление

Беллман-Форд находит кратчайший путь от исходной вершины до всех остальных вершин и, в отличие от Дейкстры, работает даже когда некоторые веса рёбер отрицательны. Он работает методом релаксации перебором: он многократно проходит по каждому ребру и, если путь через это ребро даёт меньшее расстояние, обновляет расстояние целевой вершины. Нажмите воспроизведение выше, чтобы увидеть, как предварительные расстояния уменьшаются проход за проходом.

После V - 1 проходов по всем рёбрам найден каждый кратчайший путь (который может содержать не более V - 1 рёбер). Это делает его O(V · E) - медленнее Дейкстры, но он обрабатывает отрицательные веса и может обнаружить цикл отрицательного веса (если V-й проход всё ещё релаксирует ребро, кратчайшего пути не существует).

Временная и пространственная сложность

ПоказательСложностьПримечания
ВремяO(V · E)V − 1 проходов по всем E рёбрам
ПамятьO(V)Одно расстояние на вершину
Отрицательные весаПоддерживаютсяЕго ключевое преимущество перед Дейкстрой
Отрицательные циклыОбнаруживаемыРелаксирующий V-й проход сигнализирует о нём

Пошагово

ШагЧто происходит
1Задайте расстояние источника равным 0, а всех остальных — бесконечности.
2Повторите следующий шаг V − 1 раз.
3Для каждого ребра (u → v, w) проверьте, выполняется ли dist[u] + w < dist[v].
4Если да, релаксируйте его: задайте dist[v] = dist[u] + w.
5Остановитесь раньше, если полный проход ничего не меняет.

Разобранный пример

Источник S на 4 вершинах S, A, B, C с рёбрами S→A (4), S→B (5), A→C (3), B→A (-3), релаксируемыми в этом порядке. Расстояния начинаются с S=0, а всё остальное — :

ПроходРасстояния [S, A, B, C]Действие
Init[0, ∞, ∞, ∞]Расстояние источника равно 0, все остальные — бесконечность.
1[0, 2, 5, 7]Релаксирует S→A до 4, S→B до 5, A→C до 7, затем B→A понижает A до 5 + (-3) = 2.
2[0, 2, 5, 5]A→C теперь улучшает C до 2 + 3 = 5; больше ни одно ребро не релаксируется.
3[0, 2, 5, 5]Ни одно ребро не релаксируется, поэтому расстояния окончательны: A стоит 2 через S→B→A.

Когда использовать Беллмана-Форда

Используйте, когдаИзбегайте, когда
Веса рёбер могут быть отрицательными.Все веса неотрицательны (Дейкстра быстрее).
Нужно обнаруживать циклы отрицательного веса.Граф огромный и плотный - O(V · E) слишком медленно.
Граф маленький или разреженный.Нужен лишь один запрос источник-цель на большом графе.
Нужна простая, легко реализуемая основа.Веса неотрицательны, и важна задержка.

Код Bellman-Ford Algorithm

Чистая, готовая к запуску реализация Bellman-Ford Algorithm на Python, JavaScript, Java, C++, C. Выберите язык, скопируйте код или откройте его уже загруженным в плейграунде Coddy.

Код Bellman-Ford Algorithm на Python

Python
1def bellman_ford(vertices, edges, start):2    dist = {v: float("inf") for v in vertices}3    dist[start] = 04    # Relax every edge V-1 times5    for _ in range(len(vertices) - 1):6        for u, v, w in edges:7            if dist[u] + w < dist[v]:8                dist[v] = dist[u] + w9    # One more pass: any improvement means a negative cycle10    for u, v, w in edges:11        if dist[u] + w < dist[v]:12            raise ValueError("Graph contains a negative-weight cycle")13    return dist14
15
16vertices = ["A", "B", "C", "D", "E"]17edges = [18    ("A", "B", 6), ("A", "C", 7), ("B", "D", 5), ("B", "E", -4),19    ("C", "D", -3), ("D", "B", -2), ("E", "D", 7),20]21
22for node, d in bellman_ford(vertices, edges, "A").items():23    print(f"A -> {node}: {d}")
Запустите этот код в плейграунде Python

Часто задаваемые вопросы о Беллмане-Форде

Какова временная сложность Беллмана-Форда?
Беллман-Форд работает за время O(V · E): он делает V - 1 проходов, и каждый проход релаксирует все E рёбер. Это медленнее, чем O((V + E) log V) у Дейкстры, что является платой за поддержку отрицательных весов рёбер.
Когда использовать Беллмана-Форда вместо Дейкстры?
Используйте Беллмана-Форда, когда граф может иметь отрицательные веса рёбер или когда нужно обнаруживать циклы отрицательного веса. Дейкстра быстрее, но корректен только при неотрицательных весах. Распространённое реальное применение — обнаружение арбитража при обмене валют, где важны отрицательные циклы.
Как Беллман-Форд обнаруживает отрицательный цикл?
После V - 1 проходов релаксации все кратчайшие пути завершены, если отрицательного цикла не существует. Если ещё один проход всё ещё может релаксировать ребро, значит достижим цикл отрицательного веса, и кратчайшие пути не определены (можно бесконечно ходить по циклу, снижая стоимость).
В чём разница между Беллманом-Фордом и Флойдом-Уоршеллом?
Беллман-Форд вычисляет кратчайшие пути из одного источника за O(V · E), тогда как Флойд-Уоршелл вычисляет кратчайшие пути между всеми парами за O(V³). На плотном графе запуск Беллмана-Форда из каждого источника стоит O(V² · E), поэтому Флойд-Уоршелл обычно лучший выбор, когда нужны все пары. Оба обрабатывают отрицательные рёбра и могут сигнализировать об отрицательных циклах.
Почему Беллману-Форду нужно ровно V - 1 проходов?
Любой кратчайший путь в графе без отрицательных циклов использует не более V - 1 рёбер, потому что путь, проходящий более V вершин, должен повторить одну из них. Каждый проход гарантирует, что корректно релаксируется хотя бы ещё одно ребро каждого кратчайшего пути, поэтому V - 1 проходов достаточно, чтобы зафиксировать их все. Распространённое заблуждение — что дополнительные проходы улучшают результат; они никогда не улучшают, если только не существует отрицательного цикла.
Может ли Беллман-Форд остановиться раньше?
Да. Если полный проход по всем рёбрам ничего не релаксирует, все расстояния уже оптимальны, и можно остановиться до достижения V - 1 проходов. Эта оптимизация раннего выхода часто делает его гораздо быстрее на графах, которые быстро сходятся, хотя граница худшего случая остаётся O(V · E).
Coddy programming languages illustration

Освойте алгоритмы с Coddy

НАЧАТЬ