Вы находитесь на странице: 1из 10

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

Алгоритм Беллмана-Форда решает задачу о кратчайших весах из одной вершины для


случая, когда весам ребер разрешено быть отрицательными. Этот алгоритм возвращает
TRUE, если в графе нет цикла отрицательного веса, достижимого из исходной вершины, и
FALSE, если таковой цикл имеется. В первом случае алгоритм находит кратчайшие пути и
их веса; во втором случае кратчайших путей (по крайней мере, для некоторых вершин) не
существует.

Алгоритм носит имя двух американских учёных: Ричарда Беллмана (Richard


Bellman) и Лестера Форда (Lester Ford). Форд фактически изобрёл этот алгоритм в
1956 г. при изучении другой математической задачи, подзадача которой свелась к
поиску кратчайшего пути в графе, и Форд дал набросок решающего эту задачу
алгоритма. Беллман в 1958 г. опубликовал статью, посвящённую конкретно задаче
нахождения кратчайшего пути, и в этой статье он чётко сформулировал алгоритм
в том виде, в котором он известен нам сейчас

Определения
Ориентированный граф (сокращенно орграф) G = (V, E) состоит из множества вершин V
и множества дуг E. Вершины также называют узлами, а дуги – ориентированными
ребрами. Дуга представима в виде упорядоченной пары вершин (v, w), где вершина v
называется началом, а w – концом дуги.

Описание алгоритма

Мы считаем, что граф не содержит цикла отрицательного веса. Случай наличия


отрицательного цикла будет рассмотрен ниже в отдельном разделе.

Заведём массив расстояний , который после отработки алгоритма будет


содержать ответ на задачу. В начале работы мы заполняем его следующим образом:
, а все остальные элементы равны бесконечности .

Сам алгоритм Форда-Беллмана представляет из себя несколько фаз. На каждой фазе


просматриваются все рёбра графа, и алгоритм пытается произвести релаксацию (relax,
ослабление) вдоль каждого ребра стоимости . Релаксация вдоль ребра — это
попытка улучшить значение значением . Фактически это значит, что мы
пытаемся улучшить ответ для вершины , пользуясь ребром и текущим ответом для
вершины .

Утверждается, что достаточно фазы алгоритма, чтобы корректно посчитать длины


всех кратчайших путей в графе (повторимся, мы считаем, что циклы отрицательного веса
отсутствуют). Для недостижимых вершин расстояние останется равным бесконечности
.

Реализация

Алгоритм.
d[u] – расстояние до вершины u из исходной вершины s

Initialize-Single-Source(G, s)
for (для) всех вершин v из V[G]
do d[v] = ∞
родитель[v] = NIL
d[s] = 0

Relax(u, v, w)
if d[v] > d[u] + w(u, v)
then d[v] = d[u] + w(u, v)
предок[v] = u

Bellman-Ford(G, w, s)
Initialize-Single-Source(G, s)
for i = 1 to |V[G]| - 1
do for (для) каждого ребра (u, v) из E[G]
do Relax(u, v, w)
for (для) каждого ребра (u, v) из E[G]
do if d[v] > d[u] + w(u, v)
then return FALSE
return TRUE

Улучшенная реализация

Этот алгоритм можно несколько ускорить: зачастую ответ находится уже за несколько фаз,
а оставшиеся фазы никакой полезной работы не происходит, лишь впустую
просматриваются все рёбра. Поэтому будем хранить флаг того, изменилось что-то на
текущей фазе или нет, и если на какой-то фазе ничего не произошло, то алгоритм можно
останавливать. (Эта оптимизация не улучшает асимптотику, т.е. на некоторых графах по-
прежнему будут нужны все фаза, но значительно ускоряет поведение алгоритма "в
среднем", т.е. на случайных графах.)

С такой оптимизацией становится вообще ненужным ограничивать вручную число фаз


алгоритма числом — он сам остановится через нужное число фаз.

void solve() {
vector<int> d (n, INF);
d[v] = 0;
for (;;) {
bool any = false;
for (int j=0; j<m; ++j)
if (d[e[j].a] < INF)
if (d[e[j].b] > d[e[j].a] + e[j].cost) {
d[e[j].b] = d[e[j].a] + e[j].cost;
any = true;
}
if (!any) break;
}
// вывод d, например, на экран
}
Алгоритм Форда отыскания кратчайшего пути.
Будем предполагать, что все расстояния в графе положительны. (Если это не
так, то ко всем весам можно всегда добавить такую константу, что все эти веса
станут положительными).
Пусть мы ищем путь от вершины x0 к вершине xn. Будем каждой вершине xi ставить
в соответствие некоторое число li по следующим правилам.
1° Положим l0= 0, li = ¥ (достаточно большое число) для "i > 0.
2° Ищем в графе дугу (xi, xj) удовлетворяющую следующему условию
lj - li > l(xi,xj), (1)
после чего заменяем lj на

.
Пункт 2° повторяется до тех пор, пока невозможно будет найти дугу,
удовлетворяющую условию (1). Обоснуем этот алгоритм и укажем как определяется
кратчайший путь.
Отметим, что ln монотонно уменьшается, то после завершения алгоритма найдется

дуга , такая, что для которой последний раз

уменьшалось ln. (Иначе вообще нет пути между x0 и xn или для верно
(1)).

По этой же самой причине найдется вершина , такая , что

,
этот процесс может продолжаться и дальше, так что получится строго убывающая

последовательность . Отсюда следует, что при некотором k мы

получим .

Покажем, что – минимальный путь с длиной ln, т.е. длина любого


другого пути между x0 и xn не превышает kn.

Возьмем произвольный путь и рассмотрим его длину

.
После завершения алгоритма имеем следующие соотношения

Сложив все эти неравенства, получим

,
что и требовалось доказать.
Рассмотрим пример.

а
б
Рис. 2.1
На рис. 2.1а изображен исходный помеченный граф и начальные значения li. На
рис. 2.1б для того же графа указаны конечные значения li и выделен кратчайший
путь. Пометка вершин графа происходила в следующем порядке (в скобках указана
дуга, вдоль которой выполняется (1)):
l1 = 6 (x0, x1),
l2 = 7 (x0, x2),
l3 = 6 (x0, x3),
l4 = 12 (x1, x3),
l4 = 11 (x2, x4),
l5 = 16 (x3, x4),
l5 = 15 (x4, x5),
l6 = 18 (x4, x6),
l6 = 17 (x5, x6).
Цель работы: Изучение алгоритмов поиска минимального пути в графе.
Разработка программы, реализующей данные алгоритмы поиска минимального
пути в взвешенном графе.

Кратчайший путь

Пусть G - связанный ориентированный граф, в котором каждому


ориентированному ребру сопоставлено положительное вещественное число,
называемое длиной ребра. Такой граф называется взвешенным, т.к. каждое
ребро как бы имеет свой вес или длину. Длина ребра, например, из вершины i в
вершину j обозначается w(i,j). Если в графе отсутствует ребро, например из
вершины i в вершину j, то w(i,j)=MAX. Длина ориентированного пути в графе G -
это сумма длин ребер, составляющих путь.

Ориентированный s-t - путь, имеющий минимальную длину, называется


кратчайшим путем из s в t. Длина кратчайшего ориентированного s-t - пути
называется расстоянием из s в t и обозначается через d(s,t). Ясно, что d(i,i)=0 для
любого i.

Поиск минимального пути по алгоритму Форда

Алгоритм Форда позволяет от какой-либо исходной вершины Xi определить


минимальный путь до произвольной вершины Xj графа G.
Алгоритм Форда состоит из следующих шагов:
1) Каждой вершине Xi графа G ставятся в соответствие максимально
возможные для данной задачи числа Hi;
2) На втором шаге вычисляются разности Hj-Hi для каждой вершины Xi, где Hj -
вес вершины, в которую ведет дуга (Xi,Xj). Здесь возможны три случая: Hj - Hi < Lij,
Hj-Hi=Lij, Hj-Hi>Lij, где Lij - вес дуги, ведущей из вершины Xi в вершину Xj.
Случай, когда Hj-Hi>Lij позволяет нам уменьшить расстояние между
вершинами Xi и Xj благодаря следующим преобразованиям: Hj-Hi>Lij, Hj > Lij + Hi,
отсюда принимаем: Hj=Hi+Lij.
Второй шаг выполняется до тех пор, пока еще существуют пары (i,j), для
которых выполняется неравенство Hj-Hi>Lij.
По окончанию второго этапа метки вершин позволяют нам определить
значение минимального пути из исходной в конечную вершину;
3) На третьем этапе происходит определение минимального пути при помощи
обратного хода от конечной вершины к начальной, причем для вершины Xj
предшествующей будет вершина Xi, если для этой пары выполняется Hj-Hi=Lij.
Если существует несколько таких пар, то выбирается любая из них.

Алгоритм Белмана - Калаба

Использование алгоритма Беллмана-Калаба позволяет нам ускорить поиск


минимального пути между произвольной заданной парой вершин.
На первом этапе заданному графу ставится в соответствие взвешенная
матрица смежности M. Матрица формируется по следующим правилам:
1) M(i,j)=Lij, если существует дуга, ведущая из Xi в Xj, где Lij - вес этой дуги;
2) M(i,j)=max, где max - максимально возможное число для данной задачи,
если не существует дуги, ведущей из вершины Xi в Xj;
3) M(i,j)=0, если i=j.
На втором этапе строится вектор V0 по следующим правилам:
1) V0(i)=Lin, если существует дуга, ведущая из вершины Xi в вершину Xn, где
Xn - конечная заданная вершина, для которой ищется минимальный путь, Lin - вес
этой дуги;
2) V0(i)=max, где max - максимально возможное число для данной задачи, если
не существует дуги, ведущей из вершины Xi в Xn;
3) V0(i)=0, если i=j.
На k-той итерации строится вектор Vk по следующим правилам:
1) Vk(i)=min{Vk-1(j)+Lij}, где i=1..(n-1),j=1..n;
i<>j
2) Vk(n) = 0.
Итерации прекращаются, когда мы получаем вектор, равный предыдущему,
т.е.Vk=Vk-1.
После прекращения процесса элемент вектора Vk, имеющий наименьшее
значение, не равное нулю, дает нам длину минимального пути между заданной
парой вершин.

4 Кратчайшие пути:
1 3 4 3 из 1 в 2 - 1->2 (длина 2);
из 1 в 3 - 1->2->3 (длина 3);
2 2 5 из 1 в 4 - 1->4 (длина 2);
2 19 2 из 1 в 5 - 1->4->5 (длина 5);
из 1 в 6 - 1->2->3->4->5->6 (длина 12).
1 6

Исходный код программы поиска кратчайших путей с использованием каждого из выше


приведенных алгоритмов:

#include <stdio.h>
#include <conio.h>
#include <alloc.h>

#define MAX 30000

struct List{
int v;
int w;
struct List *next;
};
struct Graph{
int h;
int p;
struct List *first;
struct List *last;
}*G;

int N;
int V;

void Ford();
void BelmanKalab();
void PathFord(int );
void Menu();
void ListAdj();
void FreeList();

void main()
{
Menu();
}

void BelmanKalab()
{
int i,j,k;
struct List *c;
int **M=(int **)malloc(N*sizeof(int *));
int *VK=(int *)malloc(N*sizeof(int ));
int *VK_1=(int *)malloc(N*sizeof(int ));
int *t,f=1;
int *P=(int *)malloc(N*sizeof(int ));
for(i=0;i<N;i++)
M[i]=(int *)malloc(N*sizeof(int));
for(i=0;i<N;i++)
for(j=0;j<N;j++)
M[i][j]=(i==j)?0:MAX;
for(i=0;i<N;i++){
c=G[i].first;
while(c!=G[i].last){
M[i][c->v]=c->w;
c=c->next;
}
}
printf("Enter final vertex: ");
scanf("%d",&V);
V--;
for(i=0;i<N;i++){
VK_1[i]=M[i][V];
P[i]=-1;
}
while(f){
for(i=0;i<N;i++)
VK[i]=MAX;
for(i=0;i<N;i++)
for(j=0;j<N;j++)
if(i!=j && VK[i]>VK_1[j]+M[i][j]){
VK[i]=VK_1[j]+M[i][j];
P[i]=j;
}
VK[V]=0;
for(i=0;i<N && VK[i]==VK_1[i];i++)
;
f=(i==N)?0:1;
t=VK_1;
VK_1=VK;
VK=t;
}
for(i=0;i<N;i++){
printf("\nPath from %d to %d is ",i+1,V+1);
if(VK_1[i]==MAX)
printf("not exist.");
else {
for(k=i,j=0;j<N && P[k]!=-1 && k!=V;j++){
printf("->%d",k+1);
k=P[k];
}
printf("->%d",V+1);
printf(". It has length %d.",VK_1[i]);
}
}
for(i=0;i<N;i++)
free(M[i]);
free(P);
free(M);
free(VK);
free(VK_1);
}

void Ford()
{
int i,f=1;
struct List *c;
if(G==NULL)
return;
for(i=0;i<N;i++){
G[i].p=-1;
G[i].h=MAX;
}
printf("Enter start vertex: ");
scanf("%d",&V);
G[--V].h=0;
while(f){
f=0;
for(i=0;i<N;i++){
c=G[i].first;
while(c!=G[i].last){
if(G[c->v].h>G[i].h+c->w){
G[c->v].h=G[i].h+c->w;
G[c->v].p=i;
f=1;
}
c=c->next;
}
}
}
for(i=0;i<N;i++){
printf("\nPath from %d to %d is ",V+1,i+1);
if(G[i].h==MAX)
printf("not exist.");
else{
PathFord(i);
printf(". It has length %d.",G[i].h);
}
}
}

void PathFord(int v)
{
if(v!=V)
PathFord(G[v].p);
printf("->%d",v+1);
}

void Menu()
{
int i;
char ch;
clrscr();
printf("1.To enter the graph\n");
printf("2.Minimum path by Ford's algorithm\n");
printf("3.Minimum path by Belman-Kalab's algorithm\n");
printf("4.Exit\n");
do
ch=getch();
while(ch<'0' || ch>'5');
clrscr();
switch (ch)
{
case '1': ListAdj();break;
case '2': Ford();getch();break;
case '3': BelmanKalab();getch();break;
case '4': FreeList();return;
}
Menu();
}

void ListAdj()
{
int i,v,w;
struct List *c;
if(G)
FreeList();
printf("Enter number of vertexes of graph: ");
scanf("%d",&N);
G=(struct Graph *)malloc(N*sizeof(struct Graph));
printf("\n");
for(i=0;i<N;i++){
printf("%d->",i+1);
G[i].first=(struct List*)malloc(sizeof(struct List));
G[i].last=G[i].first;
G[i].last->next=NULL;
G[i].last->v=-1;
scanf("%d",&v);
while(v){
scanf("%d",&w);
G[i].last->v=v-1;
G[i].last->w=w;
G[i].last->next=(struct List*)malloc(sizeof(struct List));
G[i].last=G[i].last->next;
G[i].last->next=NULL;
G[i].last->v=-1;
scanf("%d",&v);
}
}
}

void FreeList()
{
struct List *c,*t;
while(N--){
c=G[N].first;
while(c!=G[N].last){
t=c->next;
free(c);
c=t;
}
}
free(G);
}

Вывод: В этой работе были рассмотрены алгоритмы поиска кратчайших путей между вершинами в
графе, а именно алгоритм Форда и алгоритм Беллмана-Калаба. Эти алгоритмы часто используются
для решения многих задач.
А. Кофман
Введение в прикладную комбинаторику
http://lib.mexmat.ru/books/4287 -алгоритм Беллмана-Калаб
http://www.studfiles.ru/dir/cat14/subj266/file9089.html
Вуз Уфимский государственный авиационный технический университет Размер файла 12.31 КБ
Добавлен 06 Января 2009

http://e-maxx.ru/algo/ford_bellman Алгоритм Форда-Беллмана

http://referatnatemu.com/21872 БЕЛОРУССКИЙ ГОСУДАРСТВЕННЫЙ УНИВЕРСИТЕТ


ИНФОРМАТИКИ И РАДИОЭЛЕКТРОНИКИ Кафедра информатики РЕФЕРАТ на тему:
«Построение эйлерова цикла. Алгоритм форда и Уоршелла» МИНСК, 2008

http://www.docs.h1.ru/algdocs/ford.html Взвешенные графы. Алгоритмы Форда и Белмана-


Калаба поиска минимального пути.

http://window.edu.ru/window/library/pdf2txt?p_id=23645 В.Н. Бурков, Д.А.


Новиков ЭЛЕМЕНТЫ ТЕОРИИ ГРАФОВ