Sunteți pe pagina 1din 23

UNIVERSITATEA DE STAT DIN TIRASPOL

Cu titlu de manuscris
C.Z.U:

DASCAL ANDRIAN

ABORDĂRI METODICE PRIVIND STUDIEREA


ALGORITMILOR LEE ȘI BELLMAN-FORD ÎN PROCESUL
DE STUDIERE A DISCIPLINEI DIN ÎNVĂȚĂMÂNTUL
PROFESIONAL TEHNIC “UTILIZAREA TEHNICILOR
CLASICE DE PROGRAMARE”

532.02 DIDACTICA ȘCOLARĂ (PE TREPTE ȘI DISCIPLINE DE


ÎNVĂȚĂMÂNT)

Referat științific la tema tezei:

ABORDĂRI METODICE PRIVIND IMPLEMENTAREA NOILOR TEHNOLOGII


INFORMAȚIONALE ÎN PROCESUL DE STUDIERE A DISCIPLINEI DIN
ÎNVĂȚĂMÂNTUL PROFESIONAL TEHNIC “UTILIZAREA TEHNICILOR
CLASICE DE PROGRAMARE”

Aprobat:

Liubomir Chiriac, dr. hab. prof. univ. ______________

CHIȘINĂU, 2019
Cuprins

Introducere ........................................................................................................................................ 3
CAPITOLUL 1 .................................................................................................................................. 4
ABORDĂRI METODICE PRIVIND STUDIEREA ALGORITMULUI LEE ..................................... 4
1. Prezentare generală a algoritmului Lee ............................................................................... 4
2. Domenii de aplicativitate a algoritmului Lee. ...................................................................... 5
3. Analiza algoritmului Lee pentru soluționarea problemei labirintului. ............................... 6
4. Implementarea algoritmului Lee în limbajul C++ pentru problema labirintului. ............. 9
5. Implementarea algoritmului Lee în Matlab. ......................................................................11
CAPITOLUL 2 .................................................................................................................................13
ABORDĂRI METODICE PRIVIND STUDIEREA ALGORITMULUI BELLMAN-FORD .............13
1. Prezentare generală a algoritmului Bellman-Ford .............................................................13
2. Domenii de aplicativitate a algoritmului Bellman-Ford .....................................................14
3. Analiza algoritmului Bellman-Ford ....................................................................................15
4. Implementarea algoritmului Bellman-Ford în limbajul C++ ............................................17
5. Implementarea algoritmului Bellman-Ford în Matlab. .....................................................20
Concluzii ..........................................................................................................................................22
Bibliografie.......................................................................................................................................23

2
Introducere

Un graph este un desen planificat, format din linii, care se referă la numere între ele.
Scopul unui graph este de a prezenta date prea numeroase sau complicate pentru a fi descrise
în mod adecvat în text și în mai puțin spațiu.
Obiectivul acestei lucrări este de a aborda unele aspect metodice privind unitățile de
conținut: Algoritmul Lee și Algoritmul Bellman-Ford. Tot aici voi prezenta importanța și
relevanța teoriei graph-urilor în diverse domenii, cum ar fi matematica pură, informatică,
sociologie, cercetare operațională și alte aplicații științifice. Există o serie de alte domenii de
aplicație interesante în care teoria graficului a jucat un rol vital.
Graph-urile facilitează vizibilitatea informațiilor. Acest lucru este valabil mai ales atunci
când două sau mai multe seturi de numere sunt corelate într-un fel. Când facem calcule în
viața de zi cu zi, avem nevoie de cunoștințele de bază pentru a utiliza graph-uri. Nu este doar
pentru cei care excelează în matematică, ci pentru fiecare elev să folosească în funcție de
nevoile lor. Atunci când facem analize de orice fel, trebuie să folosim structura. Acest lucru se
va face folosind un graph. Chiar și planificarea bugetului dvs. lunar poate fi beneficiată prin
întocmirea unui graph, faceți acest lucru pentru o perioadă de 6 luni și în curând veți putea
vedea unde greșiți și unde prosperați.
Graficele sunt utilizate în viața de zi cu zi, de la ziarul local până la standul de reviste.
Este una dintre acele abilități pe care pur și simplu nu le poți face fără. Oricare ar fi nevoia
sau calculul dvs., dacă este utilizat corect, un graph vă poate ajuta și vă simplifica
viața.Contabilii vor beneficia prin utilizarea graph-urilor pentru a transmite informații
financiare clienților lor. Un graph poate fi foarte util în colectarea și stocarea datelor într-un
singur loc.
Analiza critică a literaturii de specialitate privind natura, specificul, avantajele şi limitele,
precum şi criteriile de alegere ale unei abordări cantitative sau calitative a permis, pe de o
parte, evidenţierea şi susţinerea valorii complementare a utilizării lor combinate într-o
structură metodologică complexă, iar pe de altă parte, trasarea elementelor cadru ale unei
metodologii de cercetare mixte.
Pentru a răspunde la dificila provocare de a analiza metodele calitative și cantitative în
perspectiva integrării lor într-un sistem coerent, s-a avut în vedere faptul că unele metode sunt
utile pentru a atinge obiective specifice pe diferite etape ale cercetării, dar şi că limitele
inerente unei anumite metode pot fi compensate de avantajele alteia.

3
CAPITOLUL 1
ABORDĂRI METODICE PRIVIND STUDIEREA ALGORITMULUI
LEE

1. Prezentare generală a algoritmului Lee

Căutarea (parcurgerea) în lățime (BFS) este un algoritm pentru


parcurgerea sau căutarea într-o structură de date de tip arbore sau
graf. Aceasta începe cu rădăcina arborelui (sau cu un nod arbitrar
dintr-un graf, uneori denumit „cheie de căutare”) și explorează
nodurile mai întâi nodurile vecine acestuia, înainte de a trece la
vecinii de pe nivelul următor (vecinii vecinilor).
Algoritmul Lee este o soluție posibilă pentru problemele de
rutare labirintică. Acest algoritm reprezintă stratul de rutare ca o
grilă, unde fiecare punct de grilă poate conține conexiuni la punctele din grilă adiacente.
Acest algoritm este de fapt o parcurgere în lățime (BFS) a unui graf, doar că e aplicat pentru o
grilă. BFS este un algoritm destul de eficient, având complexitatea de O(M*N), fiind foarte
utilizat în problemele în care apare un labirint.
Algoritmul lui Lee este un algoritm ce determină numărul minim de pași, pentru a ajunge
din punctul x în punctul y în anumite condiții (de exemplu: evitând obstacole). Cu acest
algoritm se face introducerea unei noi structuri în informatica: coada (sau queue). Înainte de a
ne apuca sa implementam acest algoritm, vreau să vă spun că trebuie să aveți câteva
cunoștiințe de bază în limbajul C++. Algoritmul lui Lee reprezintă una dintre cele mai
cunoscute aplicații ale cozii (ca structură de date) și este folosit de obicei pentru determinarea
drumului minim dintre două celule ale unei matrice. Algoritmul lui Lee oferă întotdeauna o
soluție optimă, dacă există, dar este lent și necesită o memorie considerabilă.
Prin implementarea acestui algoritm putem rezolva o problemă frecvent întâlnită în
practică: determinarea unui drum cu număr minim de paşi, în anumite condiţii, spaţiul de
căutare fiind un tablou (cel mai frecvent, bidimensional). Reprezintă de asemenea una dintre
cele mai remarcabile aplicaţii ale unei structuri de date abstracte, fundamentală în informatică,
coada.
Din punct de vedere didactic, algoritmul lui Lee este un subiect dificil, care solicită
extrem de multă energie: trebuie creată imaginea vizuală, reprezentarea în memorie, explicat
modul de funcţionare şi în plus toate acestea urmărite în paralel cu implementarea. Aceasta
este provocarea la care soft-ul nostru încearcă să propună o soluţie.
4
Punctul forte al aplicaţiei constă în abordarea metodică, în construirea pas cu pas a
cunoaşterii, în spirală, plecând de la o problemă binecunoscută, problema Labirintului. Până
la momentul final prezintă structurile de date, implementarea şi reprezentarea vizuală pentru
problema studiată.
Abordarea reprezintă de asemenea un model de bună practică pentru rezolvarea de
probleme la informatică. În continuare voi prezenta algoritmul lui Lee, cum se implementează
la problema labirintului, precum și câteva aplicații ale acestuia. Este eficient, având o
complexitate de O(M*N) şi frecvent utilizat. Acesta determină drumul minim de ieşire dintr-
un labirint sau în probleme asemănătoare.

2. Domenii de aplicativitate a algoritmului Lee.

BFS și aplicarea acestuia în găsirea de componente conexe⁠


ale grafurilor au fost inventate în 1945 de către Konrad Zuse, în
teza sa de doctorat (respinsă) despre limbajul de programare
Plankalkül⁠, dar aceasta a fost publicată abia în 1972. A fost
reinventat în 1959 de către E. F. Moore⁠, care l-a folosit pentru a
găsi cea mai scurtă cale de ieșire dintr-un labirint, descoperită
independent⁠ de C. Y. Lee ca algoritm de rutare a cablajelor⁠
(publicat în 1961). imagine 1 - cip VLSI
Rutarea este sarcina de a găsi un set de conexiuni care vor
conecta terminalele diferitelor module pe o placă de circuit
imprimat sau un cip VLSI (vezi imagine 1). Fiecare conexiune
conectează un terminal sursă la un terminal de destinație (vezi
imagine 2).
Algoritmul de rutare din labirint este un algoritm pentru a
găsi conexiuni între terminale, iar algoritmul Lee este o soluție
imagine 2 – placă de circuit
posibilă pentru problemele de rutare din labirint. imprimată
Algoritmul Lee caută cea mai scurtă cale între două terminale și garantează găsirea unei
rute între două puncte dacă există conexiunea. Acest algoritm garantează, de asemenea, cea
mai scurtă cale între terminalele sursă și destinație. Algoritmii de rutare pot fi aplicați la
cablaje automate, rutare globală, rutare detaliată, CAD și alte probleme, cum ar fi planificarea
traseului unui robot.

5
Problema labirintului.

Se dă un labirint sub forma unei matrice cu n linii


și n coloane, unde dacă o celulă este 0, aceasta se consideră
accesibilă de către șoarece, iar dacă este -1 inaccesibilă. De
asemenea, se dau coordonatele pozițiilor în care se află inițial
șoarecele și bucata de brânză.
La fiecare pas, șoarecele se poate deplasa într-una dintre
pozițiile imediat vecine la nord, sud, est sau vest, cu condiția ca
aceasta să fie accesibilă și, desigur, să se afle în interiorul
imagine 3 – labirint sub forma
matricei. Să se determine lungimea minimă a unui drum de la
șoarece la brânză, precum și un astfel drum. Dacă nu se poate
ajunge la brânză (este înconjurată de obstacole), se va afișa 0.
Reprezentarea matricii corespunzătoare problemei: am
reprezentat cu violet pozițiile accesibile, cu negru pozițiile
inaccesibile, cu roșu poziția șoarecelui, iar cu albastru poziția
brânzei (vezi imagine 3). Soluția problemei la care trebuie să
unei matrice
ajungem am desenat-o printr-o linie orange (vezi imagine 4).
imagine 4 – soluția problemei
labirintului

3. Analiza algoritmului Lee pentru soluționarea problemei labirintului.

Vom utiliza o matrice mat, cea pe care o citim, și pe care o vom folosi de asemenea
pentru a calcula niște rezultate parțiale despre care voi vorbi imediat. Avem nevoie și de o
coadă q, cu elemente de tip Pos. Tipul Pos este un struct pentru reținerea coordonatelor
celulelor din matrice. Acesta conține câmpurile lin și col, pentru linia și coloana poziției
stocate.

OBSERVAȚII:
Algoritmul lui Lee presupune doi paşi importanţi:
1. Primul şi poate cel mai important pas este folosirea unei cozi, sub forma unui vector de
structuri (de preferabil), care va menţine toţi paşii pe care o să-i facem de acum în colo.
În această coadă se pun, pentru fiecare pas, locurile care s-au marcat la punctul anterior.
2. Se marchează cu numere consecutive toate locurile posibile prin care putem trece,
parcurgând în ordine elementele cozii, până când nu mai putem marca, sau am ajuns la
final.
6
Descrierea succintă a celor 4 etape de implementare a algoritmului Lee:

1. Initializare
 Selectați punctul de pornire, marcați cu 0
 i: = 0

2. Extinderea valurilor
 REPETĂ
o Marcați toți vecinii nemarcati ai punctelor marcate cu i cu i +1
o i: = i + 1
 ÎN FINAL ((obiectiv atins) sau (nu se pot marca puncte))

3. Backtrace (întoarcerea)
 mergeți la punctul țintă
 REPETĂ
o mergeți la următorul nod care are o notă mai mică decât nodul curent
o adăugați acest nod la cale
 ÎN FINAL (punctul de pornire atins)

4. Clearance-ul (degajarea de obstacole )


- Blocați calea pentru obstacole viitoare
- Ștergeți toate notele

OBSERVAȚII:
1. În coada q se adaugă poziția șoarecelui.
2. Se completează această poziție din mat cu valoarea 1.
3. Cât timp coada nu este vidă și nici nu am găsit lungimea minimă:
 Extragem primul element din coadă. Să-i zicem pos.
 Îi parcurgem vecinii din matrice, dacă vecinul curent este accesibil și nevizitat, atunci
îl marcăm drept vizitat, completând celula sa corespunzătoare din mat cu
valoarea mat[pos.lin][pos.col] + 1, apoi îl introducem în coadă.
4. Afișăm valoarea din mat de pe poziția unde se află bucata de brânză.

IMPORTANT:
1. Complexitatea algoritmului este O(m⋅n)O(m⋅n), deoarece fiecare celulă a matricei este
vizitată maxim o singură dată. Această complexitate este extrem de bună, având în
vedere dimensiunile input-ului.
2. Problema se poate rezolva şi cu ajutorul metodei backtracking, dar nu este metoda cea
mai eficientă, complexitatea fiind O(4(M*N)), sau O(3(M*N)) după caz, ceea ce este foarte
mult. În primul pas vom pune în coadă coordonatele locului de plecare, urmând apoi să
parcurgem pe rând coada, până când nu mai există cale de ieşire sau am găsit una.

7
Explicația algoritmului

În timpul execuției algoritmului, în matricea mat, o poziție are valoarea -1 dacă este
inaccesibilă, 0 dacă încă nu a fost vizitată, sau distanța minimă de la șoarece la ea altfel.
Algoritmul lui Lee se bazează pe următoarea idee: Dacă știm lungimea drumului optim de la
șoarece până la poziția accesibilă de coordonate i și j (mat[i][j]), putem actualiza lungimile
drumurilor minime pentru vecinii accesibili (și nevizitați încă) ai ei; aceste valori vor fi egale
cu mat[i][j] + 1, pentru că de la poziția (i, j) până la un vecin de-ai ei se mai face un singur
pas. Asta e practic o relație de recurență, așa că Algoritmul lui Lee poate fi considerat un
algoritm de programare dinamică.
Totuși, recurența asta nu e suficientă. Mai trebuie să ținem cont de ordinea în care
completăm matricea mat. După ce am completat toate celulele din mat cu valoarea x (cele
până la care se poate ajunge în minim x pași), trebuie să completăm lungimile minime
pentru vecinii lor (cu x + 1), apoi pentru vecinii vecinilor lor (cu x + 2), și tot așa. Doar în
acest fel putem fi siguri că toate rezultatele din mat sunt corecte. Completarea matricei poate
fi asemănată cu modul în care o picătură de cerneală se extinde pe o bucată de hârtie.
Pentru a menține această ordine a completării matricei, se folosește o coadă, căci această
structură de date funcționează pe principiul primul venit, primul servit. La fiecare pas, din
coadă extragem primul element pentru a-l prelucra (a completa lungimile corespunzătoare
vecinilor accesibili și a-i pune la sfârșitul cozii). Întotdeauna, o parte dintre pozițiile din coadă
(primele) vor avea o anumită valoare, iar restul (vecinii lor) vor avea valoarea cu o unitate mai
mare.
Tabelul 1 – Date de intrare / Date de ieșire

DATE DE INTRARE DATE DE IEȘIRE

8
NOTĂ:
1. Datele corespunzătoare problemei le-am scris într-un fișier, astfel am reprezentat cu 0 -
pozițiile accesibile, iar cu -1 am marcat pozițiile inaccesibile.
2. Rândul 12 din fișier ne reprezintă poziția șoarecelui, iar rândul 13 din fișier ne reprezintă
poziția brânzei (vezi Tabelul 1).

4. Implementarea algoritmului Lee în limbajul C++ pentru problema labirintului.

Vom elabora o librărie în care vom include toate subprogramele de care avem nevoie
pentru a obține soluția problemei labirintului. Ca rezultat final am elaborat librăria cu numele
Lee.h

#ifndef LEE_H_INCLUDED
#define LEE_H_INCLUDED
#include <fstream>
#define DMAX 618
using namespace std;

ifstream fin("lee.txt");
ofstream fout("lee.out");

const int dL[] = {-1, 0, 1, 0};


const int dC[] = { 0, 1, 0, -1};
//************************************************
struct Pos {
int lin;
int col;
};
Pos a, b;
int m, n;
int mat[DMAX][DMAX];
int in, sf;
Pos q[DMAX * DMAX];
int vf;
Pos st[DMAX * DMAX];
//************************************************
void lee() {
int k;
Pos pos, ngh;
q[0] = a;
in = sf = 0;
mat[a.lin][a.col] = 1;
while (in <= sf && !mat[b.lin][b.col]) {
pos = q[in++];
for (k = 0; k < 4; k++) {
ngh.lin = pos.lin + dL[k];
ngh.col = pos.col + dC[k];
if (!mat[ngh.lin][ngh.col]) {
mat[ngh.lin][ngh.col] = mat[pos.lin][pos.col] + 1;
q[++sf] = ngh;
}
}
}
}
//************************************************
9
void surround() { //functia obstacol
int i, j;
for (i = 0; i <= m + 1; i++)
mat[i][0] = mat[i][n + 1] = -1;
for (j = 1; j <= n; j++)
mat[0][j] = mat[m + 1][j] = -1;
}
//************************************************
void scan() {
int i, j;
fin >> m >> n;
for (i = 1; i <= m; i++)
for (j = 1; j <= n; j++)
fin >> mat[i][j];
fin >> a.lin >> a.col;
fin >> b.lin >> b.col;
}
//************************************************
void print() {
fout << mat[b.lin][b.col] << '\n';
int k;
Pos pos, ngh;
st[++vf] = pos = b;
while (mat[pos.lin][pos.col] > 1)
for (k = 0; k < 4; k++) {
ngh.lin = pos.lin + dL[k];
ngh.col = pos.col + dC[k];
if (mat[ngh.lin][ngh.col] == mat[pos.lin][pos.col] - 1) {
st[++vf] = pos = ngh;
break;
}
}
while (vf) {
fout << st[vf].lin << ' ' << st[vf].col << '\n';
vf--;
}
}
#endif // LEE_H_INCLUDED

Vom elabora un program ce va aplica această librărie pentru a obține soluția problemei
labirintului. Ca rezultat final am elaborat programul Lee.cpp

#include <iostream>
#include "Lee.h"
int main() {
scan();
surround(); //functia obstacol
lee();
print();
fout.close();
return 0;
}

NOTĂ:
După execuția codului de program C++ în conformitate cu datele de intrare (vezi Tabelul 1)
obținem soluția problemei: drumul minim (vezi imagine 4).

10
5. Implementarea algoritmului Lee în Matlab.

Exemplul 1 - Găsirea tuturor căilor cele mai scurte într-un graph direcționat.
Rezolvare:
1. Creați și vizualizați un grafic direcționat cu 6 noduri și 11 muchii.
2. Găsiți toate cele mai scurte căi între fiecare pereche de noduri în graph-ul direcționat.
Funcția în Matlab:
function [ output_args ] = Untitled( input_args )
W = [.41 .99 .51 .32 .15 .45 .38 .32 .36 .29 .21];
DG = sparse([6 1 2 2 3 4 4 5 5 6 1],[2 6 3 5 4 1 6 3 4 3 5],W)
view(biograph(DG,[],'ShowWeights','on'))
graphallshortestpaths(DG)
end

Soluția

Observație:
Matricea rezultată arată cea mai scurtă cale de la nodul
1 (primul rând) la nodul 6 (a șasea coloană) este 0,95.
Puteți vedea acest lucru în grafic urmărind calea de la
nodul 1 la nodul 5 la nodul 4 până la nodul 6 (0,21 +
0,36 + 0,38 = 0,95).

Exemplul 2 - Găsirea tuturor căilor cele mai scurte într-un graph nedirecționat.
Rezolvare:
1. Creați și vizualizați un grafic nedirecționat cu 6 noduri și 11 muchii.
2. Găsiți toate cele mai scurte căi între fiecare pereche de noduri în graph-ul nedirecționat.
Funcția în Matlab:
function [ output_args ] = Untitled( input_args )
W = [.41 .99 .51 .32 .15 .45 .38 .32 .36 .29 .21];
DG = sparse([6 1 2 2 3 4 4 5 5 6 1],[2 6 3 5 4 1 6 3 4 3 5],W)
UG = tril(DG + DG')
view(biograph(UG,[],'ShowArrows','off','ShowWeights','on'))
graphallshortestpaths(UG,'directed',false)
end

11
Soluția

Observație:
Matricea rezultată este simetrică deoarece
reprezintă un grafic nedirecționat. Acesta arată că
cea mai scurtă cale de la nodul 1 (primul rând) la
nodul 6 (a șasea coloană) este 0,83. Puteți vedea
acest lucru în grafic urmărind calea de la nodul 1 la
nodul 4 până la nodul 6 (0,45 + 0,38 = 0,83).
Deoarece UG este un grafic nedirecționat, putem
folosi marginea dintre nodul 1 și nodul 4, lucru pe
care nu l-am putut face în graficul direcționat DG.

12
CAPITOLUL 2
ABORDĂRI METODICE PRIVIND STUDIEREA ALGORITMULUI
BELLMAN-FORD

1. Prezentare generală a algoritmului Bellman-Ford

Algoritmul Bellman-Ford este un algoritm de căutare în graph care calculează cele mai
scurte căi de la un vertex (vârf) unic sursă la toate celelalte vârfuri dintr-un graph ponderat.
Acest algoritm poate fi utilizat atât pe grafice ponderate, cât și pe cele neponderate.
Algoritmul a fost propus pentru prima dată de Alfonso Shimbel (1955), dar în schimb este
numit după Richard Bellman și Lester Ford, Jr., care l-au publicat în 1958 și respectiv în
1956. Edward F. Moore a publicat același algoritm în 1957, iar din acest motiv este numit
uneori și algoritmul Bellman – Ford – Moore.
Greutățile negative ale marginilor se găsesc în diferite aplicații ale graph-urilor, de unde și
utilitatea acestui algoritm. Dacă un graph conține un „ciclu negativ” (adică un ciclu ale cărui
muchii se însumează la o valoare negativă) accesibilă de la sursă, atunci nu există o cale mai
ieftină: orice cale care are un punct pe ciclul negativ poate fi făcută mai ieftină cu încă o
plimbare în jurul ciclului negativ. Într-un astfel de caz, algoritmul Bellman-Ford poate detecta
și raporta ciclul negativ.
Ca și algoritmul cu cea mai scurtă cale a lui Dijkstra, algoritmul Bellman-Ford este
garantat să găsească cea mai scurtă cale dintr-un grafic. Deși este mai lent decât algoritmul lui
Dijkstra, Bellman-Ford este capabil să manipuleze grafice care conțin greutăți negative ale
marginilor, deci este mai versatil.
Este demn de remarcat faptul că dacă există un ciclu negativ în grafic, atunci nu există cea
mai scurtă cale. Parcurgând ciclul negativ, un număr infinit de ori va continua să scadă costul
căii (chiar dacă lungimea căii este în creștere).
Greutățile negative ale marginilor se găsesc în diferite aplicații ale graficelor, de aici
utilitatea acestui algoritm. Cu toate acestea, dacă un grafic conține un "ciclu negativ", adică
un ciclu ale cărui muchii se însumează la o valoare negativă, atunci nu există o cale mai
ieftină, deoarece orice cale poate fi făcută mai ieftină cu încă o plimbare prin ciclul negativ.
Într-un astfel de caz, algoritmul Bellman-Ford poate detecta cicluri negative și raporta
existența acestora, dar nu poate produce un răspuns „pe cea mai scurtă cale” dacă un ciclu
negativ este accesibil de la sursă.

13
2. Domenii de aplicativitate a algoritmului Bellman-Ford

De ce ai avea vreodată margini cu greutăți negative în viața reală? Marginile negative de


greutate pot părea fără valoare la început, totuși ele pot explica multe fenomene precum fluxul
de numerar, căldura degajată / absorbită într-o reacție chimică etc. Dacă există diferite
modalități de a ajunge de la o substanță chimică A la alta chimică B, fiecare metodă va avea
sub-reacții care implică atât disiparea căldurii și absorbția.
O versiune de Bellman-Ford este utilizată în protocolul de rutare a vectorului distanței.
Acest protocol decide modul de rutare a pachetelor de date într-o rețea. Ecuația distanței
(pentru a decide greutățile din rețea) este numărul de routere pe care trebuie să parcurgă o
anumită cale pentru a ajunge la destinația sa.
Pentru internet în special, există multe protocoale care folosesc Bellman-Ford. Un
exemplu este protocolul Informații de rutare. Acesta este unul dintre cele mai vechi
protocoale de internet și previne buclele prin limitarea numărului de hamei pe care un pachet
le poate face în drum spre destinație. Un al doilea exemplu este protocolul de rutare a
gateway-ului interior. Acest protocol proprietar este utilizat pentru a ajuta mașinile să facă
schimb de date de rutare într-un sistem.

Problema taxei

Dacă ne-am imagina un scenariu în care trebuie să jucăm baseball la mine acasă. Pe
parcurs, pe fiecare drum, se poate întâmpla unul din două lucruri:
 În primul rând, uneori drumul pe care îl utilizați este un drum cu taxă și trebuie să plătiți o
anumită sumă de bani.
 În al doilea rând, uneori cineva cunoscut trăiește pe acea stradă (ca un membru al familiei
sau un prieten).

imagine 5– Problema taxei


14
Acești oameni vă pot oferi bani pentru a vă ajuta să vă restabiliți portofelul. Trebuie să
trecem prin oraș și dorim să ajungem în oraș cu cât mai mulți bani posibil, astfel încât să
putem cumpăra un hot dog. Dat fiind faptul că știm care drumuri sunt drumuri cu taxă și care
drumuri au oameni care vă pot oferi bani, puteți utiliza Bellman-Ford pentru a vă ajuta să
planificați ruta optima (vezi imagine 5).
În locul casei mele, al unui joc de baseball și al străzilor care fie iau bani de la tine, fie îți
dau bani, Bellman-Ford se uită la un graph ponderat. Graph-ul este o colecție de muchii care
leagă diferite vârfuri în graph, la fel ca drumurile. Marginile au un cost pentru ele. Fie este un
cost pozitiv (ca taxă), fie un cost negativ (ca un prieten care vă va da bani).
Deci, în graph-ul de mai sus, o săgeată roșie înseamnă că trebuie să plătiți bani pentru a
utiliza acel drum, iar o săgeată verde înseamnă că veți primi bani plătiți pentru a utiliza acel
drum. În grafic, vertexul sursă este casa dvs., iar vertexul țintă este stadionul de baseball. În
drumul dvs. acolo, doriți să maximizați numărul și valoarea absolută a marginilor negative
ponderate. În schimb, doriți să minimizați numărul și valoarea muchiilor ponderate pozitiv pe
care le luați. Bellman-Ford face doar asta.
Algoritmul minimizează drumul de la nodul de start la oricare vârf x până la obținerea
costului minim. Costurile se rețin într-un vector d , iar pentru fiecare arc (j,k) se verifică dacă
minimizează distanța de la nodul de start la nodul x (d[x] > d[j] + cost[j][x] ), iar această
operație se repetă de n ori, îmbunătăţirea costului nodurilor vecine se face introducându-le
într-o coadă în cazul scăderii costului, dacă nu apar deja.

3. Analiza algoritmului Bellman-Ford

Complexitatea algoritmului
Bellman-Ford face | E | relaxări pentru fiecare iterație și există | V | -1 iterații. Prin
urmare, cel mai rău caz este că Bellman-Ford rulează în timp O (| V | ⋅| E |). Cu toate acestea,
în unele șcenarii, numărul de iterații poate fi mult mai mic.
Pentru anumite graph-uri este nevoie de o singură iterație, prin urmare în cel mai bun caz
de scenariu, este nevoie doar de timp O (| E |). Un exemplu de graph care ar avea nevoie de o
singură rundă de relaxare este un graph în care fiecare vertex se conectează numai la
următorul în mod linear.
Algoritmul Bellman Ford funcționează supraestimând lungimea căii de la vertexul de
pornire la toate celelalte vârfuri. Apoi, relaxează iterativ acele estimări găsind noi căi mai
scurte decât căile supraestimate anterior.

15
Bellman Ford Pseudocode

Trebuie să menținem distanța de cale a fiecărui vertex. Putem stoca asta într-un tablou de
dimensiuni v, unde v este numărul de vârfuri. De asemenea, dorim să putem obține cea mai
scurtă cale, nu numai să cunoaștem lungimea celei mai scurte căi. Pentru aceasta, mapăm
fiecare vertex la vertexul care și-a actualizat ultima lungime a căii.
Odată ce algoritmul s-a încheiat, putem face backtrack de la vârful de destinație la
vertexul sursă pentru a găsi calea.

function bellmanFord(G, S)
for each vertex V in G
distance[V] <- infinite
previous[V] <- NULL
distance[S] <- 0
for each vertex V in G
for each edge (U,V) in G
tempDistance <- distance[U] + edge_weight(U, V)
if tempDistance < distance[V]
distance[V] <- tempDistance
previous[V] <- U
for each edge (U,V) in G
If distance[U] + edge_weight(U, V) < distance[V}
Error: Negative Cycle Exists
return distance[], previous[]

Analiza comparativă a subprogramelor Bellman-Ford vs Dijkstra

Algoritmul lui Bellman Ford și algoritmul lui Dijkstra sunt foarte similare în structură. În
timp ce Dijkstra privește doar vecinii imediați ai unui vertex, Bellman trece prin fiecare
margine în fiecare iterație (vezi imagine 6).

imagine 6– subprogramul Bellman-Ford vs Dijkstra

16
4. Implementarea algoritmului Bellman-Ford în limbajul C++

Problemă
Se dă un graf orientat conex cu N noduri şi M muchii cu costuri. Definim un lanţ ca fiind
un şir de noduri cu proprietatea că între oricare două consecutive există o muchie. Costul unui
lanţ este dat de suma costurilor muchiilor care unesc nodurile ce îl formează. Definim un ciclu
ca fiind un lanţ cu proprietatea că primul element al său este egal cu ultimul. Să se determine
dacă în graful dat există un ciclu de cost negativ. Dacă nu există, să se determine costul minim
al unui lanţ de la nodul 1 la fiecare dintre nodurile 2, 3, ... , N-1, N.

Date de intrare Date de ieşire Restricţii


 Fişierul de intrare  În fişierul de ieşire  1 ≤ N ≤ 50 000.
bellmanford.in conţine pe bellmanford.out se va afişa  1 ≤ M ≤ 250 000.
prima linie numerele N şi pe prima linie mesajul  Costurile muchiilor
M cu semnificaţia din "Ciclu negativ!" dacă în graf sunt numere întregi cel
enunţ. Pe următoarele M există un astfel de ciclu sau, mult egale în modul cu
linii se vor afla câte 3 în caz contrar, N-1 numere 1 000.
numere x, y şi c cu separate printr-un spaţiu. Al
semnificaţia că există o i-lea număr va reprezenta
muchie de cost c de la costul minim al unui lanţ de
nodul x la nodul y. la nodul 1 la nodul i+1.

Rezolvarea problemei în limbajul C++


#include <iostream>
#include <fstream>
#include <vector>
#include <queue>
#define INF 0x3f3f3f3f
using namespace std;
ifstream in("bellmanFord.txt");
ofstream out("bellmanford.out");
int n,m;
vector< pair<int, int> >graf[50005];
vector <int> v;
queue <int> coada;
int d[50005]; int viz[50005]; int esteincoada[50005];
bool bellmanford(int s)
{
for(int i=1;i<=n;i++)
{
viz[i]=0; esteincoada[i]=0; d[i]=INF;
}
d[s] = 0; coada.push(s); esteincoada[s] = 1;
while(!coada.empty())
{
int nodcurent = coada.front(); viz[nodcurent]++;
if(viz[nodcurent] >= n) return false;

17
coada.pop();
esteincoada[nodcurent] = 0;
for(size_t i=0;i<graf[nodcurent].size();i++)
{
int vecin = graf[nodcurent][i].first;
int cost = graf[nodcurent][i].second;
if(d[nodcurent]+cost < d[vecin])
{
d[vecin] = d[nodcurent]+cost;
if(!esteincoada[vecin]) coada.push(vecin);
}
}
}
return true;
}
int main(){
in>>n>>m;
for(int i=1;i<=m;i++)
{
int x,y,c; in>>x>>y>>c;
graf[x].push_back(make_pair(y,c));
}
if(bellmanford(1))
{
for(int i=2;i<=n;i++) out<<d[i]<<" ";
}
else out<<"Ciclu negativ!";
return 0;
}

Exemplu:
bellmanford.in bellmanford.out
5 8 -5 -3 4 7
1 3 -3
1 5 7
3 2 -2
3 4 7
5 1 4
5 2 3
5 3 4
4 5 3

Altă metodă de implementare a algoritmului Bellman-Ford

Vom elabora o librărie în care vom include toate subprogramele de care avem nevoie
pentru a obține soluția problemei labirintului. Ca rezultat final am elaborat librăria cu numele
Bellman_Ford.h
#ifndef BELLMAN_FORD_H_INCLUDED
#define BELLMAN_FORD_H_INCLUDED
using namespace std;
void BellmanFord(int graph[][3], int V, int E, int src){
// Inițializează distanța tuturor vârfurilor ca 0.
int dis[V];
for (int i = 0; i < V; i++)
dis[i] = INT_MAX;
// inițializează distanța sursei ca 0
dis[src] = 0;

18
for (int i = 0; i < V - 1; i++) {
for (int j = 0; j < E; j++) {
if (dis[graph[j][0]] + graph[j][2] <
dis[graph[j][1]])
dis[graph[j][1]] =
dis[graph[j][0]] + graph[j][2];
}
}
for (int i = 0; i < E; i++) {
int x = graph[i][0];
int y = graph[i][1];
int weight = graph[i][2];
if (dis[x] != INT_MAX &&
dis[x] + weight < dis[y])
cout << "Graph contains negative"
" weight cycle"
<< endl;
}
cout << " Distanța vertexului de la sursă: " << endl;
for (int i = 0; i < V; i++)
cout << i << "\t" << dis[i] << endl;
}
#endif // BELLMAN_FORD_H_INCLUDED

Vom elabora un program ce va aplica această librărie pentru a obține soluția problemei
labirintului. Ca rezultat final am elaborat programul Bellman_Ford.cpp
#include <iostream>
#include "BELLMAN_FORD.h"
using namespace std;
int main(){
int V = 5; // Numărul de vârfuri în graph
int E = 8; // Numărul de muchii in graph
/* Fiecare muchie are trei valori (u, v, w) când marginea este de la
vertexul u la v. Și greutatea marginii este w. */
int graph[][3] = { { 0, 1, -1 }, { 0, 2, 4 },
{ 1, 2, 3 }, { 1, 3, 2 },
{ 1, 4, 2 }, { 3, 2, 5 },
{ 3, 1, 1 }, { 4, 3, -3 } };
BellmanFord(graph, V, E, 0);
return 0;
}

Rezultate obținute în urma execuției:

Distanta vertexului de la sursa:


0 0
1 -1
2 2
3 -2
4 1

19
5. Implementarea algoritmului Bellman-Ford în Matlab.

Exemplul 1 - Găsirea tuturor căilor cele mai scurte într-un graph direcționat.
Rezolvare:
1. Creați și vizualizați un grafic direcționat cu 6 noduri și 11 muchii.
2. Găsiți toate cele mai scurte căi între fiecare pereche de noduri în graph-ul direcționat.
Funcția în Matlab:
function [ output_args ] = Untitled6( input_args )
W = [.41 .99 .51 .32 .15 .45 .38 .32 .36 .29 .21];
DG = sparse([6 1 2 2 3 4 4 5 5 6 1],[2 6 3 5 4 1 6 3 4 3 5],W)
h = view(biograph(DG,[],'ShowWeights','on'))
[dist,path,pred] = graphshortestpath(DG,1,6)
set(h.Nodes(path),'Color',[1 0.4 0.4])
edges = getedgesbynodeid(h,get(h.Nodes(path),'ID'));
set(edges,'LineColor',[1 0 0])
set(edges,'LineWidth',1.5)
end

Soluția

Biograph object with 6 nodes and 11 edges.


dist =
0.9500
path =
1 5 4 6
pred =
0 6 5 5 1 4

Exemplul 2 - Găsirea tuturor căilor cele mai scurte într-un graph nedirecționat.
Rezolvare:
1. Creați și vizualizați un grafic nedirecționat cu 6 noduri și 11 muchii.
2. Găsiți toate cele mai scurte căi între fiecare pereche de noduri în graph-ul nedirecționat.
Funcția în Matlab:
function [ output_args ] = Untitled6( input_args )
%UNTITLED6 Summary of this function goes here
% Detailed explanation goes here
W = [.41 .99 .51 .32 .15 .45 .38 .32 .36 .29 .21];
DG = sparse([6 1 2 2 3 4 4 5 5 6 1],[2 6 3 5 4 1 6 3 4 3 5],W)
UG = tril(DG + DG')
h = view(biograph(UG,[],'ShowArrows','off','ShowWeights','on'))
[dist,path,pred] = graphshortestpath(UG,1,6,'directed',false)
set(h.Nodes(path),'Color',[1 0.4 0.4])
fowEdges = getedgesbynodeid(h,get(h.Nodes(path),'ID'));
revEdges = getedgesbynodeid(h,get(h.Nodes(fliplr(path)),'ID'));
edges = [fowEdges;revEdges];
set(edges,'LineColor',[1 0 0])
set(edges,'LineWidth',1.5)
end

20
Soluția

Biograph object with 6 nodes and 11 edges.


dist =
0.8200
path =
1 5 3 6
pred =
0 5 5 1 1 3

Marcarea nodurilor și a marginile pentru celei mai scurte trasee prin colorarea roșie și
îngroșarea liniei.

Exemplul 1 Exemplul 2
Graph direcționat Graph nedirecționat

21
Concluzii

Un graph este reprezentarea simbolică a unei rețele și a conectivității. Este preocupat că


modul în care rețelele pot fi codificate. Un graph este o pereche ordonată de seturi de vârfuri
V și set de marginile E. Vârfurile pot fi prezentate cu punctele dintr-un plan și aceste tipuri de
date sunt numite noduri. O linie care leagă aceste noduri se numește muchie. Dacă un graph
este direcționat, atunci marginile sunt perechi ordonate și dacă un graph este nedirecționat
atunci marginile sale sunt perechi neordonate. Ordinea graph-ului este egal cu numărul de
vârfuri din el. LEONHARD EULAR a devenit tatăl teoria graph-ului când a soluționat o
faimoasă problemă nesoluționată numită Problemă Podul Konigsberg.
Graph-urile au multe caracteristici, cum ar fi diagrama fluxului de date, capacitatea de
luare a deciziilor, afișarea relațiilor între obiecte, modificări ușoare și modificări în sistemul
existent etc.
Aplicativitatea graph-urilor pentru unele din cele mai importante domenii:
1. IMPORTANȚĂ ÎN MATEMATICA PURĂ
 Teorema lui Cantor Schroder-Bernstein
 Teorema lui Fermat
2. IMPORTANȚĂ ÎN ȘTIINȚA COMPUTATORULUI
 Sistemul de rețea și securitatea acestuia
 Minerirea datelor și structurile de date
3. IMPORTANȚĂ ÎN CERCETAREA OPERAȚIUNII
 Problema de gestionare a tabelului de timp
 Hărți rutiere
4. IMPORTANȚĂ ÎN CERCETAREA OPERAȚIUNII
 Problema de gestionare a tabelului de timp
 Hărți rutiere
5. IMPORTANȚĂ ÎN SOCIOLOGIE
 Măsurarea performanței și raportul de progres
 Grafic de cunoștințe
6. IMPORTANȚĂ ÎN ȘTIINȚĂ
 Grafic de suprapunere de nișă în Ecologie
 Bioinformatică - ansamblu de fragmente de ADN

22
Bibliografie

Capitolul 1

1. Kleinberg, Jon; Tardos, Éva (2006). Algorithm Design. New York: Pearson Education,
Inc.
2. Johnson, D.B. (1977). Efficient algorithms for shortest paths in sparse networks. Journal
of the ACM 24(1), 1-13.
3. Siek, J.G., Lee, L-Q, and Lumsdaine, A. (2002). The Boost Graph Library User Guide
and Reference Manual, (Upper Saddle River, NJ:Pearson Education).
4. KOLÁŘ, Josef. Teoretická informatika. 2. ed. Praha : Česká informatická společnost,
2004. 205 p. ISBN 80-900853-8-5.
5. Moore, Edward F. (1959). The shortest path through a maze. Proc. Internat. Sympos.
Switching Theory 1957, Part II. Cambridge, Massachusetts: Harvard Univ. Press.
pp. 285–292. MR 0114710.

Capitolul 2

6. Shimbel, A. (1955). Structure in communication nets. Proceedings of the Symposium on


Information Networks. New York, New York: Polytechnic Press of the Polytechnic
Institute of Brooklyn. pp. 199–203.
7. Bellman, Richard (1958). "On a routing problem". Quarterly of Applied
Mathematics. 16: 87–90. MR 0102435.
8. Bannister, M. J.; Eppstein, D. (2012). Randomized speedup of the Bellman–Ford
algorithm (PDF). Analytic Algorithmics and Combinatorics (ANALCO12), Kyoto,
Japan. pp. 41–47. arXiv:1111.5414. Bibcode:2011arXiv1111.5414B.
9. Bang-Jensen, Jørgen; Gutin, Gregory (2000). "Section 2.3.4: The Bellman-Ford-Moore
algorithm". Digraphs: Theory, Algorithms and Applications (First ed.). ISBN 978-1-
84800-997-4.
10. Sedgewick, Robert (2002). "Section 21.7: Negative Edge Weights". Algorithms in
Java (3rd ed.). ISBN 0-201-36121-3. Archived from the original on 2008-05-31.
Retrieved 2007-05-28.

23

S-ar putea să vă placă și