Documente Academic
Documente Profesional
Documente Cultură
RAPORT
Lucrare de laborator Nr.3
Analiza i proiectarea algoritmilor
A efectuat:
A verificat:
lect. Sup.
Bagrin Veronica
Chiinu 2015
Scopul lucrrii:
1. Studierea tehnicii greedy.
2. Analiza i implementarea algoritmilor greedy
Sarcina lucrrii
Indicaii teoretice
Tehnica greedy
Algoritmii greedy (greedy = lacom) sunt n general simpli i sunt folosii la probleme de
optimizare, cum ar fi: s se gseasc cea mai bun ordine de executare a unor lucrri pe calculator, s se
gseasc cel mai scurt drum ntr-un graf etc. n cele mai multe situaii de acest fel avem:
o mulime de candidai (lucrri de executat, vrfuri ale grafului etc);
o funcie care verific dac o anumit mulime de candidai constituie o soluie posibil, nu
neaprat optim, a problemei;
o funcie care verific dac o mulime de candidai este fezabil, adic dac este posibil s
completm aceast mulime astfel nct s obinem o soluie posibil, nu neaprat optim, a problemei;
o funcie de selecie care indic la orice moment care este cel mai promitor dintre candidaii
nc nefolosii;
o funcie obiectiv care d valoarea unei soluii (timpul necesar executrii tuturor lucrrilor ntr-o
anumit ordine, lungimea drumului pe care l-am gsit etc); aceasta este funcia pe care urmrim s o
optimizm (minimizm/maximizm).
Pentru a rezolva problema de optimizare, se caut o soluie posibil care s optimizeze valoarea
funciei obiectiv. Un algoritm greedy construiete soluia pas cu pas. Iniial, mulimea candidailor
selectai este vid. La fiecare pas, se adaug acestei mulimi cel mai promitor candidat, conform
funciei de selecie. Dac, dup o astfel de adugare, mulimea de candidai selectai nu mai este fezabil,
se elimin ultimul candidat adugat; acesta nu va mai fi niciodat considerat. Dac, dup adugare,
mulimea de candidai selectai este fezabil, ultimul candidat adugat va rmne de acum ncolo n ea. De
fiecare dat cnd se lrgete mulimea candidailor selectai, se verific dac aceast mulime nu
constituie o soluie posibil a problemei. Dac algoritmul greedy funcioneaz corect, prima soluie gsit
va fi totodat o soluie optim a problemei. Soluia optim nu este n mod necesar unic: se poate c
funcia obiectiv s aib aceeai valoare optim pentru mai multe soluii posibile. Funcia de selecie este
de obicei derivat din funcia obiectiv; uneori aceste dou funcii sunt chiar identice.
Algoritmul lui Kruskal
Arborele parial de cost minim poate fi construit muchie, cu muchie, dup urmtoarea metoda a lui
Kruskal (1956): se alege nti muchia de cost minim, iar apoi se adaug repetat muchia de cost minim
nealeas anterior i care nu formeaz cu precedentele un ciclu. Alegem astfel #V1 muchii. Este uor de
dedus c obinem n final un arbore. Este nsa acesta chiar arborele parial de cost minim cutat?
Proprietatea 1. n algoritmul lui Kruskal, la fiecare pas, graful parial <V, A> formeaz o pdure de
componente conexe, n care fiecare component conex este la rndul ei un arbore parial de cost minim
pentru vrfurile pe care le conecteaz. n final, se obine arborele parial de cost minim al grafului G.
Pentru a implementa algoritmul, trebuie s putem manipula submulimile formate din vrfurile
componentelor conexe. Folosim pentru aceasta o structura de mulimi disjuncte i procedurile de tip find i
merge (Seciunea 3.5). n acest caz, este preferabil s reprezentm graful c o lista de muchii cu costul
asociat lor, astfel nct s putem ordona aceast list n funcie de cost. Iat algoritmul:
adaugate.push_back(f=neadaugate.back());
neadaugate.pop_back();
parinte[f]=p;
muchie.push_back(Muchie(p,f,rand()%101-50));
}
ult.resize(n+1);
for(uint i=1;i<=n;i++)
ult[i]=i+1;
for(uint i=1;i<=m-(n-1);i++) {
p=rand()%adaugate.size();
while(ult[adaugate[p]]<=n && (ult[adaugate[p]]==parinte[adaugate[p]] ||
parinte[ult[adaugate[p]]]==adaugate[p]))
++ult[adaugate[p]];
if(ult[adaugate[p]]>n) {
swap(adaugate[p],adaugate[adaugate.size()-1]);
adaugate.pop_back();
--i;
}
else {
muchie.push_back(Muchie(adaugate[p],ult[adaugate[p]],rand()%101-50));
++ult[adaugate[p]];
}
}
ofstream fo(numeFisier);
if(fo.is_open()) {
fo<<n<<' '<<m<<endl;
for(uint i=0;i<muchie.size();i++)
fo<<muchie[i].u<<' '<<muchie[i].v<<' '<<muchie[i].cost<<endl;
fo.close();
creareListe();
return true;
}
cout<<"Eroare la deschiderea fisierului \""<<numeFisier<<"\" !!!"<<endl;
return false;
}
bool citeste(char *numeFisier="apm.in") {
ifstream fi(numeFisier);
Muchie M;
if(fi.is_open()) {
fi>>n>>m;
for(uint i=0;i<m;++i) {
fi>>M;
muchie.push_back(M);
}
fi.close();
creareListe();
return true;
}
cout<<"Eroare la deschiderea fisierului \""<<numeFisier<<"\" !!!"<<endl;
return false;
}
void scrie(char *numeFisier) {
ofstream fo(numeFisier);
if(fo.is_open()) {
if(A.size()==n-1) {
fo<<"Costul arborelui partial de cost minim este: "<<costMin<<endl;
fo<<"Muchiile arborelui sunt:"<<endl;
for(uint i=0;i<A.size();i++)
fo<<A[i]<<endl;
}
else
fo<<"Graful nu este conex!"<<endl;
fo.close();
}
else
cout<<"Eroare la deschiderea fisierului \""<<numeFisier<<"\" !!!"<<endl;
}
vector<uint> celMaiApropiatVecin,nodNeadaugat,pozitieInHeap;
vector<int> costVecin;
void upHeap(int k) {
int k2,p,f;
while(k>0) {
k2=(k+1)/2-1;
f=nodNeadaugat[k];
p=nodNeadaugat[k2];
if(costVecin[f]<costVecin[p]) {
swap(nodNeadaugat[k],nodNeadaugat[k2]);
swap(pozitieInHeap[f],pozitieInHeap[p]);
k=k2;
}
else break;
}
}
void downHeap(int k) {
uint k1,k2,kmin,p,f;
while((k1=2*k+1)<nodNeadaugat.size()) {
k2=k1+1;
kmin=k;
if(costVecin[nodNeadaugat[k1]]<costVecin[nodNeadaugat[kmin]]) kmin=k1;
if(k2<nodNeadaugat.size())
if(costVecin[nodNeadaugat[k2]]<costVecin[nodNeadaugat[kmin]]) kmin=k2;
if(kmin!=k) {
p=nodNeadaugat[k];
f=nodNeadaugat[kmin];
swap(nodNeadaugat[k],nodNeadaugat[kmin]);
swap(pozitieInHeap[p],pozitieInHeap[f]);
k=kmin;
}
else break;
}
}
int celMaiApropiatNod() {
uint ultimulDinHeap,rez=nodNeadaugat.front();
pozitieInHeap[rez]=INFINIT;
ultimulDinHeap=nodNeadaugat.back();
nodNeadaugat.pop_back();
if(nodNeadaugat.size()>0) {
nodNeadaugat[0]=ultimulDinHeap;
pozitieInHeap[ultimulDinHeap]=0;
downHeap(0);
}
return rez;
}
void actualizareCeiMaiApropiatiVecini(int nod) {
uint poz,nodVecin;
int costVec;
for(uint i=0;i<listaVecini[nod].size();i++) {
nodVecin=listaVecini[nod][i].first;
poz=pozitieInHeap[nodVecin];
costVec=listaVecini[nod][i].second;
if(poz!=INFINIT)
if(costVec<costVecin[nodVecin]) {
costVecin[nodVecin]=costVec;
celMaiApropiatVecin[nodVecin]=nod;
upHeap(poz);
}
}
}
void Prim() {
uint nodAles;
celMaiApropiatVecin.resize(n+1);
costVecin.resize(n+1);
pozitieInHeap.resize(n+1);
startTime=GetTickCount();
nodNeadaugat.clear();
for(uint i=2;i<=n;i++) {
nodNeadaugat.push_back(i);
pozitieInHeap[i]=i-2;
celMaiApropiatVecin[i]=0;
costVecin[i]=INFINIT;
}
pozitieInHeap[1]=INFINIT;
A.clear();
costMin=0;
actualizareCeiMaiApropiatiVecini(1);
while(nodNeadaugat.size()>0) {
actualizareCeiMaiApropiatiVecini(nodAles=celMaiApropiatNod());
if(costVecin[nodAles]==INFINIT) break;
A.push_back(Muchie(celMaiApropiatVecin[nodAles],nodAles,costVecin[nodAles]));
costMin+=costVecin[nodAles];
}
scrie("prim.out");
}
vector<int> nrComp;
int nrComponenta(uint x) {
if(nrComp[x]==x) return x;
return nrComp[x]=nrComponenta(nrComp[x]);
}
void reuneste(uint u,uint v) {
nrComp[nrComponenta(u)]=nrComponenta(v);
}
void Kruskal() {
Muchie M;
A.clear();
costMin=0;
nrComp.resize(n+1);
for(uint i=1;i<=n;++i)
nrComp[i]=i;
sort(muchie.begin(),muchie.end());
for(uint i=0;i<muchie.size();i++) {
M=muchie[i];
if(nrComponenta(M.u)!=nrComponenta(M.v)) {
A.push_back(M);
costMin+=M.cost;
reuneste(M.u,M.v);
}
}
scrie("kruskal.out");
}
int main() {
cout<<"Introduceti numarul de noduri: ";
cin>>n;
cout<<"Introduceti numarul de muchii: ";
cin>>m;
cout<<"Se genereaza graful... ";
startTime=GetTickCount();
if(genereazaGraf(n,m)) {
endTime=GetTickCount();
cout<<endTime-startTime<<" ms"<<endl;
startTime=GetTickCount();
Prim();
endTime=GetTickCount();
cout<<"Timpul executiei algoritmului Prim: "<<endTime-startTime<<" ms"<<endl;
startTime=GetTickCount();
Kruskal();
endTime=GetTickCount();
cout<<"Timpul executiei algoritmului Kruskal: "<<endTime-startTime<<" ms"<<endl;
}
_getch();
return 0;
}
Rezultatul:
Kruskal:
Introduceti numarul de virfuri : 7
introduceti numarul de arce : 6
introduceti arcul si costul acestuia
012
156
2 5 14
238
349
463
Nr de iteratii este:37
The time was: 0.000000
Nodurile cercetate sunt: 0 1 2 3 4 5 6
-------Arborele minimizat-----{0-> 1 cost 2}
{4-> 6 cost 3}
{1-> 5 cost 6}
{2-> 3 cost 8}
{3-> 4 cost 9}
{2-> 5 cost 14}
Lungimea minima este L=42
Prim
Introduceti numarul de virfuri : 7
Introduceti numarul de arce : 6
Introduceti arcul si costul acestuia
012
156
2 5 14
238
349
463
The time was: 0.000000
Nodurile cercetate sant: 0 1 2 3 4 5 6
---------Arborele minimizat-------{0->1 cost 2}
{1->5 cost 6}
d[w]=d[v]+L[v][w];
T[w]=v;
upheap(i);
}
}
}
scrie("dijkstra.out");
}
int main() {
cout<<"Introduceti numarul de noduri: ";
cin>>n;
cout<<"Introduceti nodul sursa: ";
cin>>sursa;
--sursa;
cout<<"Se genereaza graful... ";
startTime=GetTickCount();
generareGraf(n);
endTime=GetTickCount();
cout<<endTime-startTime<<" ms"<<endl;
cout<<"Se executa Dijkstra pentru nodul sursa "<<sursa+1<<"... ";
startTime=GetTickCount();
Dijkstra(sursa);
endTime=GetTickCount();
cout<<endTime-startTime<<" ms"<<endl;
_getch();
}
Rezultatul:
Dijkstra:
Introduceti numarul de virfuri : 5
Introduceti numarul de arce : 8
Introduceti nodurile si costul arcului dintre ele:
0 1 50
0 2 30
0 3 100
0 4 10
215
2 3 50
3 1 20
4 3 10
D=[ 35 30 20 10 ]The time was: 0.000000
nr.de iteratii este:32
Concluziile:
In urma efectuarii lucrarii de laborator 3 am facut cunostinta cu algoritmii greedy.Mai concret cu
algoritmii Kruskal,Prim si Dijkstra.Algoritmul Kruskal si Prim ne ofera posibilitatea sa construim arborele
de cost minim.Algoritmul Dijkstra ne ofera posibilitatea sa determinam drumurile minimale pina la fiecare
virf in parte.
Implementind si analizind algoritmul Kruskal si Prim, am ajuns la concluzia ca acesti algoritmi
sunt foarte rapizi si deci sunt algoritmi eficienti si foarte buni,p-ru rezolvarea problemei de construire a
arborelui de cost minim.Algoritmul Dijkstra de asemenea este un algoritm foarte eficient.Timpul de
executie necesar p-ru rezolvarea sarcinei este minimal.Initial am analizat un graf de complexitatea nu prea
inalta.Timpul de executie a fost foarte mic,si s-au utilizat 32 de iteratii.Apoi,am analizat un graf de o
complexitate mai sporita si am observat ca timpul de executie de asemenea este foarte mic.Astfel
algoritmul Dijkstra este foarte rapid si deci este un algoritm foarte bun,ce ne ofera posibilitatea sa
determinam drumurile minimale pina la fiecare virf in parte.