Documente Academic
Documente Profesional
Documente Cultură
Lab3 Apa
Lab3 Apa
R APO R T
Analiza si Proiectarea Algoritmilor
Lucrarea nr.3
Tema: Tehnica greedy de proiectare a algoritmilor
A elaborat
A verificat:
Bagrin Veronica
Chiinu 2015
Scopul lucrrii:
1. Studierea tehnicii greedy.
2. Analiza i implementarea algoritmilor greedy
Consideratii teoretice:
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.
A
for i 2 to n do vecin[i] 1
mincost[i] C[i, 1]
{bucla greedy}
repeat n1 times
min +
for j 2 to n do
if 0 < mincost[ j] < min then min mincost[ j]
kj
A A {{k, vecin[k]}}
mincost[k] 1 {adaug vrful k la U}
for j 2 to n do
if C[k, j] < mincost[ j] then mincost[ j] C[k, j]
vecin[ j] k
return A
Bucla principal se execut de n1 ori i, la fiecare iteraie, buclele for din interior necesit un timp n
O(n). Deci, algoritmul Prim necesit un timp n O(n2). Am vzut c timpul pentru algoritmul lui
Kruskal este n O(m log n), unde m = #M. Pentru un graf dens se deduce c m se apropie de n(n1)/2.
n acest caz, algoritmul Kruskal necesit un timp n O(n2 log n) i algoritmul Prim este probabil mai
bun. Pentru un graf rar m se apropie de n i algoritmul Kruskal necesit un timp n O(n log n), fiind
probabil mai eficient dect algoritmul Prim.
Listingul programului
I.Algoritmul Kruskal:
#include<stdio.h>
#include<conio.h>
#include<process.h>
#include<iostream>
#include <time.h>
#include <dos.h>
using namespace std;
struct Graf
{
int x;
int y;
int cost;
}*arc,*virf;
int nv,na,*set,*nod,*h,**mad;
void introd()
{
int i,j;
cout<<"\n|V| = ";
cin>>nv;nod=new int[nv];set=new int[nv];h=new int[nv];
for(i=0;i<nv;i++)
{
nod[i]=i;
set[i]=i;
h[i]=0;
}
cout<<"\n|A| = ";
cin>>na;
arc=new Graf[na];
4
}
}
}
void Kruskal()
{
int count=0;
SortCost(count);
virf=new Graf[nv-1];
Graf tmp;
int j=0,ucomp,vcomp;
for(int i=0;i<nv-1;i++)
{
count++;
do
{
count++;
tmp=arc[j];
ucomp=gaseste(arc[j].x);
vcomp=gaseste(arc[j].y);
if(ucomp!=vcomp)
{
merge(ucomp,vcomp);
virf[i]=tmp;
break;
}
j++;
}
while (1);
}
cout<<"\n\nNr de iteratii este : "<<count<<"\n\n";
}
void AfisVirf()
{
cout<<"\n\nVirfurile prelucrate sunt : {";
for(int i=0;i<nv;i++)
cout<<nod[i]+1<<",";
printf("%c}",8);
}
void AfisMuchii()
{
cout<<"\nMuchiile ce participa in algoritmul Kruskal sunt :\n";
for(int i=0;i<nv-1;i++)
cout<<"\n("<<virf[i].x+1<<","<<virf[i].y+1<<"); cu costul = "<<virf[i].cost;
}
int main()
{
cout<<"\n\t\tALGORITMUL KRUSKAL\n";
int L=0;
introd();
clock_t start,end;
start=clock();
6
Kruskal();
end=clock();
printf("Timpul de executie este : %.8f\n",((double)(clock()-start))/CLOCKS_PER_SEC);
AfisMuchii();AfisVirf();
for(int i=0;i<nv-1;i++)
L=L+virf[i].cost;
cout<<"\n\nLmin = "<<L;
getch();
}
II.Algoritmul Prim:
#include<stdio.h>
#include<conio.h>
#include<process.h>
#include<iostream>
#include<stdlib.h>
#include<dos.h>
#include<time.h>
using namespace std;
struct Graf
{
int x,y,cost;
Graf(int x,int y,int c)
{
this->x=x;
this->y=y;
this->cost=c;
}
Graf(Graf&a)
{
this->x=a.x;
this->y=a.y;
this->cost=a.cost;
}
}**arc,**virf;
int nv,na,*nod,*u,*vecin,*costmin,**matr;
void introd()
{
int i,j,x,y,c;
cout<<"\n|V| = ";
cin>>nv;
nod=new int[nv];
for(i=0;i<nv;i++)
nod[i]=i;
cout<<"\n|A| = ";
cin>>na;
arc=new Graf*[na];
cout<<"\nDati x, y, costul :\n";
for(i=0;i<na;i++)
{
cin>>x;x-=1;
cin>>y;y-=1;
cin>>c;
arc[i]=new Graf(x,y,c);
7
}
}
void Big()
{
int i,j;
matr=new int*[nv];
for(i=0;i<nv;i++)
{
matr[i]=new int[nv];
for(j=0;j<nv;j++)
matr[i][j]=999;
}
for(i=0;i<na;i++)
{
matr[arc[i]->x][arc[i]->y]=arc[i]->cost;
matr[arc[i]->y][arc[i]->x]=arc[i]->cost;
}
}
Graf *find(int k,int j)
{
for(int i=0;i<na;i++)
if((arc[i]->x==k&&arc[i]->y==j)||(arc[i]->y==k&&arc[i]->x==j))
return arc[i];
return arc[0];
}
void Prim(int &count)
{
int i,j,k,min;
vecin=new int[nv];
costmin=new int[nv];
u=new int[nv];
for(i=0;i<nv;i++)
u[i]=-1;
virf=new Graf*[nv-1];
Graf *tmp;
u[0]=0;//rand()%nv+1;
for(i=0;i<nv;i++)
{
count++;
if(i==u[0]) continue;
vecin[i]=u[0];
costmin[i]=matr[i][u[0]];
}
for(i=0;i<nv-1;i++)
{
count++;
min=999;
for(j=1;j<nv;j++)
{
count++;
if(0<costmin[j]&&costmin[j]<min)
{
min=costmin[j];
8
k=j;
}
}
tmp=find(k,vecin[k]);
virf[i]=tmp;
costmin[k]=-1;
for(j=1;j<nv;j++)
{
count++;
if(matr[k][j]<costmin[j])
{
costmin[j]=matr[k][j];
vecin[j]=k;
}
}
}
}
void AfisVirf()
{
cout<<"\n\nVirfurile prelucrate sunt : {";
for(int i=0;i<nv;i++)
cout<<nod[i]+1<<",";
printf("%c}",8);
}
void AfisMuchii()
{
cout<<"\n\nMuchiile ce participa in algoritmul Prim sunt :\n";
for(int i=0;i<nv-1;i++)
cout<<"\n("<<virf[i]->x+1<<","<<virf[i]->y+1<<"); cu costul = "<<virf[i]->cost;
}
int main()
{
cout<<"\n\t\tALGORITMUL PRIM\n";
int L=0,count=0;
introd();Big();
clock_t start,end;
start=clock();
Prim(count);
end=clock();
cout<<"\nNr. de iteratii este : "<<count;
printf("\n\nTimpul de executie este : %.8f",((double)(clock()-start))/CLOCKS_PER_SEC);
AfisMuchii();AfisVirf();
for(int i=0;i<nv-1;i++)
L=L+virf[i]->cost;
cout<<"\n\nLmin = "<<L;
getch();
}
10
11
II.Prim
12
13
14
Nr.iteratii
Timpul
Kruskal
10
70
0.00100000s
Prim
10
61
0.00100000s
Exemplu
Algoritmu
l
Nr.iteratii
Timpul
Kruskal
23
299
0.00100000s
Prim
23
145
0.00200000s
15
Prim
Prim/timp
Prim/iteratii
160
145
140
120
100
80
61
60
40
20
0
Kruskal
350
299
300
250
200
Kruskal/Iteratii
Kruskal/timp
150
100
70
50
0
16
Concluzia final:
Efectuind lucrarea data ne-am facut cunoscuti cu diferiti algoritmi greedy.Totodata am
analizat eficienta lor pentru diferite cazuri ale problemei. Implementind si analizind
algoritmul Kruskal si Prim am concluzionat ca acesti algoritmi sunt rapizi si deci sunt
algoritmi eficienti si foarte buni pentru rezolvarea problemei de construire a arborelui
de cost minim.Observ ca dindui numarul de virufuri 6 in cazul algoritmului Kruskal
numarul de iteratii este mai mare si timpul este constan.Dindui numarul de virfuri 9 la
Kruskal numarul de iteratii creste mult mai mult decit la Prim in schim din punct de
vedre a timpului Kruskal ramine constant iar Prim incepe a creste.Astfel reiese ca daca
vom da un numar mare de virfuri la Kruskal va creste in iterati dar timpul va fi favorabil
in schimb algoritmul Prim ve avea un numar mic de iteratii da timpul de executie va
creste semnificativ.
17