Documente Academic
Documente Profesional
Documente Cultură
RAPORT
Lucrare de laborator nr.4
Disciplina: Analiza și proiectarea algoritmică
Tema: Analiza algoritmilor Floyd și Dijkstra.
Chişinău 2019
1. Tema: Metoda programării dinamice
2. Scopul lucrarii:
- Studierea metodei programării dinamice.
- Analiza şi implementarea algoritmilor de programare dinamică.
3. Sarcina lucrarii:
- De studiat metoda programării dinamice de proiectare a algoritmilor.
- De implementat într-un limbaj de programare algoritmii:
Floyd;
Dijkstra.
- De făcut analiza acestor algoritmi.
4. Teorie:
Programarea dinamică.
O problemă rezolvabilă prin metoda programării dinamice trebuie adusă mai întâi la o formă
discretă în timp. Deciziile care se iau pentru a obţine un rezultat trebuie să se poată lua pas cu pas. De
asemenea, foarte importantă este ordinea în care acestea se iau. Programarea dinamică este (şi nu
luaţi aceste rânduri ca pe o definiţie) în esenţă un proces decizional în mai multe etape: în starea
iniţială a problemei luăm prima decizie, care determină o nouă stare a problemei în care luăm o
decizie. Termenul dinamic se referă chiar la acest lucru: problema este rezolvată în etape dependente
de timp. Variabilele, sau funcţiile care descriu fiecare etapă trebuie să fie în aşa fel definite încât să
descrie complet un proces, deci pentru acest lucru va trebui să răspundem la două întrebări:
1) care este etapa iniţială (caz în care avem de a face cu un proces decizional descendent) sau care
este etapa finală (caz în care avem de a face cu un proces decizional ascendent)?
2) care este regula după care trecem dintr-o etapă în alta ? De obicei această regulă este exprimată
printr-o recurenţă.
Deoarece, avem de a face cu o problemă care se rezolvă în mai multe etape, nu ne mai rămâne
decât să vedem cum luăm deciziile dintr-o etapă în alta. Nu mă refer aici la o relaţie de recurenţă de
care am vorbit mai sus, ci la faptul că foarte probabil apare posibilitatea ca la un anumit moment să
putem alege din mai multe decizii. De exemplu, problema calculului numerelor lui Fibonaci se
încadrează în categoria programării dinamice deoarece:
este un proces în etape;
fiecărei etape k îi corespunde calculul celui de al k-lea număr Fibonacci;
există o singură decizie pentru a trece la o etapă superioară;
Determinarea unui drum ce leagă două oraşe A şi B şi care trece printr-un număr minim de alte oraşe
este tot o problemă de programare dinamică deoarece:
este un proces în etape,
fiecărei etape k îi corespunde determinarea unui drum de lungime k ce pleacă din oraşul A,
dar există mai multe decizii pentru trecerea la drumul de lungimea k + 1.
În cele ce urmează prin strategie înţelegem un şir de decizii. Conform principiului lui Bellman,
numit principiul optimalităţii avem:
O strategie are proprietatea că oricare ar fi starea iniţială şi decizia iniţială, deciziile rămase
trebuie să constituie o strategie optimă privitoare la starea care rezultă din decizia anterioară.
Demonstrarea corectitudinii unui algoritm de programare dinamică se face, aşa cum rezultă şi din
principiul optimalităţii, prin inducţie matematică.
for(i=0;i<n;i++)
{
for(j=0;j<n;j++)
{
if(i!=j)
{
cout<<"Costul [ "<<i+1<<", "<<j+1<<" ] = ";
cin>>L[i][j];
}
}
}
cout<<endl<<" Pasul v C
D"<<endl;
for(i=0;i<n-1;i++)
C[i]=i+2;
k=n-1;
for(i=1;i<n;i++)
{
D[i]=L[0][i];
if(!L[0][i]) D[i]=9999;
}
for(i=1;i<17;i++)
cout<<" ";
cout<<"{";
for(i=0;i<k;i++)
{
cout<<C[i];
if(i<k-1) cout<<", ";
}
cout<<"}";
for(i=1;i<12;i++)
cout<<" ";
cout<<"[";
for(i=1;i<n;i++)
{
cout<<D[i];
if(i<n-1) cout<<", ";
}
cout<<"]"<<endl;
t1=clock();
for(int gredy=1;gredy<n;gredy++)
{
for(i=0,min=9999;i<k;i++)
if(D[C[i]-1]<min && D[C[i]-1])
{
min=D[C[i]-1];contor++;
v=C[i];
}
for(i=0;i<k;i++)
if(C[i]>=v) C[i]=C[i+1];
k--;
for(i=0;i<k;i++)
if( (D[v-1]+L[v-1][C[i]-1]) < D[C[i]-1] && L[v-
1][C[i]-1])
{
D[C[i]-1]=D[v-1]+L[v-1][C[i]-1];
contor++;
}
t2=clock();
cout<<" "<<gredy;
for(i=1;i<8;i++)
cout<<" ";
cout<<v;
for(i=1;i<6;i++)
cout<<" ";
cout<<"{";
for(i=0;i<k;i++)
{
cout<<C[i];
if(i<k-1) cout<<", ";
}
cout<<"}";
for(i=1;i<12+3*gredy;i++)
cout<<" ";
cout<<"[";
for(i=1;i<n;i++)
{
cout<<D[i];
if(i<n-1) cout<<", ";
}
cout<<"]"<<endl;
}
cout<<"\n\nNumaru de iteratii este : "<<contor;
cout<<endl<<"\tTimpul de lucru al algoritmul : "<<fixed<<(t2-
t1)/1000<<"sec";
getch();
}
Afisarea la ecran
Algoritmul Floyd
Codul sursa in limbajul C++ pentru algoritmul Floyd
#include<iostream>
#include<time.h>
t1=clock();
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
if(L[i][j]==0 && i!=j) L[i][j]=9999;
for(floyd=1;floyd<=n;floyd++)
{
j=1;
for(i=1;i<=n;i++)
{
if(i==floyd) continue;
C[j]=i;
D[i]=L[floyd][i];
j++;
}
while(C[2]!=0)
{
pas++;
min=999;
for(i=1;i<=n;i++)
{//determinam minimum din D if(i==floyd) continue;
count++;
if(min>D[i] && D[i]!=0 && marcat[i]==0)
{
min=D[i];
k=i;
}
}
marcat[k]=1;
u=-1;
for(i=1;C[i]!=0;i++)
{
count++;
if(C[i]==k)
{
for(j=i;C[j]!=0;j++)
{
C[j]=C[j+1];
u=j;
}
C[u+1]=0;
}
}
for(i=1;C[i]!=0;i++)
if(D[k]+L[k][C[i]] < D[C[i]])
{
D[C[i]]=D[k]+L[k][C[i]];
sol[floyd][C[i]]=k;
}
for(i=0;i<=n;i++)
{
C[i]=0; D[i]=0;
marcat[i]=0;
}
pas=1;
for(j=1;j<=n;j++)
{
sol[0][j]=j;
sol[j][0]=j;
}
for(j=1;j<=n;j++)
if(sol[1][j]==0) sol[1][j]=floyd;
}
cout<<"\n\n\nVectorul solutiilor:\n"<<endl;
for(i=0;i<=n;i++)
{
for(j=0;j<=n;j++)
{
if(j==0) cout<<" ";
cout<<" "<<sol[i][j];
}
if(i==0) cout<<endl;
cout<<endl;
}
t2=clock();
cout<<"\n\nNumaru de iteratii este : "<<count;
cout<<endl<<"\tTimpul de lucru al algoritmul : "<<fixed<<(t2-t1)/1000<<" sec";
return 0;
}
Afisarea la ecran
6. Compararea rezultatelor
algoritmilor n=8;
m = 11 m = 16
Numarul de Timpul de Numarul de Timpul de
Iteratii Executie Iteratii Executie
Dijkstra 14 0.07 18 0.07
Floyd 328 0.08 345 0.08
lungimea drumurilor din orice vîrf către celelalte vîrfuri, se obtine o complexitate n*O(n 2) ceea ce
Floyd:
Este foarte evident ca algoritmul Floyd avind 3 cicluri de for de la 1 la n imbricate unul in
altul, functia de complexitatea a acestui algoritm este exact O(n3).
Se observa o mica performanta a algoritmului Dijkstra fata de Floyd, insa in practica se
dovedeste a nu fi astfel. Deoarece majoritatea grafurilor orientate sunt dense, algoritmul Floyd ne
da un rezultat exact n3, pe cind algoritmul Dijkstra intrece aceasta valoare.
7. Concluzie:
În urma elaborării lucrării de laborator nr. 4 am facut cunostinta cu o noua metoda de
programare, si anume – metoda programarii dinamice, care reprezinta o determinare a solutiei
prin etape. Am realizat analiza empirica a algoritmilor de determinare a celor mai scurte
drumuri intr-un graf si anume algoritmii Dijkstra si Floyd. In urma analizei s-a dovedit ca
algoritmul Dijkstra ar fi cu foarte putin mai eficient ca Floyd, insa practica ne arata ca
algoritmul Floyd este cu mult mai eficient decit algoritmul Dijkstra in cazul grafurilor orientate
dense. Deasemenea algoritmul Floyd este mai simplu de implementat intr-un limbaj de
programare, constituind simplu 3 cicluri imbricate.