Sunteți pe pagina 1din 12

Ministerul Educaţiei și Cercetării al Republicii Moldova

Universitatea Tehnică a Moldovei


Facultatea Calculatoare, Informatică și Microelectronică

RAPORT
Lucrarea de laborator nr.3
la Analiza și Proiectarea Algoritmilor

A efectuat:
st. gr. TI-224 Dombrovschi Iulia

A verificat:
asist. univ. Astafi Valentina

Chişinău - 2023
Lucrare de laborator nr 3

Tema: Algoritmii greedy

Scopul lucrării:
1. Studierea tehnicii greedy.
2. Analiza şi implementarea algoritmilor greedy

Sarcina:
1. De studiat tehnica greedy de proiectare a algoritmilor.
2. De implementat într-un limbaj de programare algoritmii Kruskal, Prim.
3. De făcut analiza empirică a algoritmilor Kruskal şi Prim.
4. De alcătuit un raport.
Analiza empirică a algoritmilor

Sursa cod:

#include <iostream>
#include <vector>
#include <algorithm>
#include <ctime>
#include <cstdlib>
#include <climits>

using namespace std;

class Edge
{
public:
int src, dest, weight;
};

class Graph
{
public:
int V, E;
vector<Edge> edges;
};

Graph createGraph(int V, int E)


{
Graph graph;
graph.V = V;
graph.E = E;
graph.edges.resize(E);
return graph;
}

int find(vector<int> &parent, int i, int *pt)


{
(*pt)++;
if (parent[i] == -1)
{
(*pt)++;
return i;
}
return find(parent, parent[i], pt);
}

void Union(vector<int> &parent, int x, int y, int *pt)


{
(*pt)++;
int xset = find(parent, x, pt);
int yset = find(parent, y, pt);
if (xset != yset)
{
(*pt)++;
parent[xset] = yset;
}
}

bool compare(const Edge &a, const Edge &b)


{
return a.weight < b.weight;
}

int partition(vector<Edge> &edges, int low, int high, int *pt)


{
(*pt)++;
Edge pivot = edges[high];
int i = (low - 1);

for (int j = low; j <= high - 1; j++)


{
(*pt)++;
if (edges[j].weight < pivot.weight)
{
(*pt)++;
i++;
swap(edges[i], edges[j]);
(*pt)++;
}
}

swap(edges[i + 1], edges[high]);


(*pt)++;
return (i + 1);
}

void quickSort(vector<Edge> &edges, int low, int high, int *pt)


{
(*pt)++;
if (low < high)
{
(*pt)++;
int pivot = partition(edges, low, high, pt);
quickSort(edges, low, pivot - 1, pt);
(*pt)++;
quickSort(edges, pivot + 1, high, pt);
(*pt)++;
}
}

int KruskalMST(Graph graph)


{
int V = graph.V;
int e = 0;
int i = 0;
int numIterations = 0;
vector<Edge> result;
vector<int> parent(V, -1);
numIterations++;

quickSort(graph.edges, 0, graph.edges.size() - 1, &numIterations);

while (e < V - 1 && i < graph.E)


{
numIterations++;
Edge next_edge = graph.edges[i];
int x = find(parent, next_edge.src, &numIterations);
numIterations++;
int y = find(parent, next_edge.dest, &numIterations);
numIterations++;

if (x != y)
{
numIterations++;
result.push_back(next_edge);
numIterations++;
Union(parent, x, y, &numIterations);
numIterations++;
e++;
numIterations++;
}

i++;
}

return numIterations;
}

int PrimMST(Graph graph)


{
int V = graph.V;
vector<int> parent(V, -1);
vector<int> key(V, INT_MAX);
vector<bool> inMST(V, false);
key[0] = 0;
int numIterations = 0;

for (int count = 0; count < V - 1; count++)


{
numIterations++;
int u = -1;
for (int v = 0; v < V; v++)
{
numIterations++;
if (!inMST[v] && (u == -1 || key[v] < key[u]))
{
numIterations++;
u = v;
}
}

inMST[u] = true;

for (int i = 0; i < graph.E; i++)


{
numIterations++;
if (!inMST[graph.edges[i].src] && !inMST[graph.edges[i].dest])
{
numIterations++;
if ((graph.edges[i].src == u || graph.edges[i].dest == u) &&
graph.edges[i].weight < key[graph.edges[i].src])
{
numIterations++;
int v = (graph.edges[i].src == u) ? graph.edges[i].dest :
graph.edges[i].src;
parent[v] = u;
key[v] = graph.edges[i].weight;
}
}
}
}

return numIterations;
}

int main()
{
// Afisham nr de noduri
srand(time(nullptr));
cout << "\n\n\t\t\t\t--KRUSKAL--\n"
<< endl;
cout << "Nr de noduri : ";
for (int n : {100, 200, 300, 400, 500, 600, 700, 800, 900, 1000})
{
cout << n << " || ";
}
cout << "\b\b\b\b"
<< " " << endl;

// Afisam operatiile pentru cazul favorabil Kruskal


cout << "Caz favorabil : ";
for (int n : {100, 200, 300, 400, 500, 600, 700, 800, 900, 1000})
{
int m_favorabil = n - 1;
Graph graph_favorabil = createGraph(n, m_favorabil);

for (int i = 0; i < m_favorabil; i++)


{
if (i + 1 < n)
{
graph_favorabil.edges[i] = {i, i + 1, rand() % 10 + 1};
}
else
{
graph_favorabil.edges[i] = {i, 0, rand() % 10 + 1};
}
}

cout << KruskalMST(graph_favorabil) << ", ";


}
cout << "\b\b"
<< " " << endl;

// Afisam operatiile pentru cazul defavorabil Kruskal


cout << "Caz defavorabil : ";
for (int n : {100, 200, 300, 400, 500, 600, 700, 800, 900, 1000})
{
int m_defavorabil = ((n - 1) * n) / 2;
Graph graph_defavorabil = createGraph(n, m_defavorabil);

int edge_index = 0;
for (int i = 0; i < n; i++)
{
for (int j = i + 1; j < n; j++)
{

graph_defavorabil.edges[edge_index] = {i, j, rand() % 100 + 1};


edge_index++;
}
}
cout << KruskalMST(graph_defavorabil) << ", ";
}
cout << "\b\b"
<< " " << endl;

// Afisam operatiile pentru cazul mediu 80% Kruskal


cout << "Caz mediu de 80%: ";
for (int n : {100, 200, 300, 400, 500, 600, 700, 800, 900, 1000})
{
int m_defavorabil = ((n - 1) * n) / 2;
int m_mediu_80 = (int)((m_defavorabil)*0.8);
Graph graph_mediu_80 = createGraph(n, m_mediu_80);

for (int i = 0; i < m_mediu_80; i++)


{

graph_mediu_80.edges[i] = {rand() % n, rand() % n, rand() % 100 + 1};


}
cout << KruskalMST(graph_mediu_80) << ", ";
}
cout << "\b\b"
<< " " << endl;

// Afisam operatiile pentru cazul mediu 20% Kruskal


cout << "Caz mediu de 20%: ";
for (int n : {100, 200, 300, 400, 500, 600, 700, 800, 900, 1000})
{
int m_defavorabil = ((n - 1) * n) / 2;
int m_mediu_20 = (int)((m_defavorabil)*0.2);
Graph graph_mediu_20 = createGraph(n, m_mediu_20);

for (int i = 0; i < m_mediu_20; i++)


{

graph_mediu_20.edges[i] = {rand() % n, rand() % n, rand() % 100 + 1};


}
cout << KruskalMST(graph_mediu_20) << ", ";
}
cout << "\b\b"
<< " " << endl;

//////////////////////////////////////////////////////////////////////////////////////
///////////

// Afisam numarul de noduri


cout << "\n\n\t\t\t\t--PRIM--\n"
<< endl;
cout << "Nr de noduri : ";
for (int n : {100, 200, 300, 400, 500, 600, 700, 800, 900, 1000})
{
cout << n << " || ";
}
cout << "\b\b\b\b"
<< " " << endl;
// Afisam operatiile pentru cazul favorabil Prim
cout << "Caz favorabil : ";
for (int n : {100, 200, 300, 400, 500, 600, 700, 800, 900, 1000})
{
int m_favorabil = n - 1;
Graph graph_favorabil = createGraph(n, m_favorabil);
for (int i = 0; i < m_favorabil; i++)
{
if (i + 1 < n)
{
graph_favorabil.edges[i] = {i, i + 1, rand() % 10 + 1};
}
else
{
graph_favorabil.edges[i] = {i, 0, rand() % 10 + 1};
}
}

cout << PrimMST(graph_favorabil) << ", ";


}
cout << "\b\b"
<< " " << endl;

// Afisam operatiile pentru cazul defavorabil Prim


cout << "Caz defavorabil : ";
for (int n : {100, 200, 300, 400, 500, 600, 700, 800, 900, 1000})
{
int m_defavorabil = ((n - 1) * n) / 2;
Graph graph_defavorabil = createGraph(n, m_defavorabil);

int edge_index = 0;
for (int i = 0; i < n; i++)
{
for (int j = i + 1; j < n; j++)
{

graph_defavorabil.edges[edge_index] = {i, j, rand() % 100 + 1};


edge_index++;
}
}
cout << PrimMST(graph_defavorabil) << ", ";
}
cout << "\b\b"
<< " " << endl;

// Afisam operatiile pentru cazul mediu 80% Prim


cout << "Caz mediu de 80%: ";
for (int n : {100, 200, 300, 400, 500, 600, 700, 800, 900, 1000})
{
int m_defavorabil = ((n - 1) * n) / 2;
int m_mediu_80 = (int)((m_defavorabil)*0.8);
Graph graph_mediu_80 = createGraph(n, m_mediu_80);

for (int i = 0; i < m_mediu_80; i++)


{

graph_mediu_80.edges[i] = {rand() % n, rand() % n, rand() % 100 + 1};


}
cout << PrimMST(graph_mediu_80) << ", ";
}
cout << "\b\b"
<< " " << endl;

// Afisam operatiile pentru cazul mediu 20% Prim


cout << "Caz mediu de 20%: S";
for (int n : {100, 200, 300, 400, 500, 600, 700, 800, 900, 1000})
{
int m_defavorabil = ((n - 1) * n) / 2;
int m_mediu_20 = (int)((m_defavorabil)*0.2);
Graph graph_mediu_20 = createGraph(n, m_mediu_20);

for (int i = 0; i < m_mediu_20; i++)


{

graph_mediu_20.edges[i] = {rand() % n, rand() % n, rand() % 100 + 1};


}
cout << PrimMST(graph_mediu_20) << ", ";
}
cout << "\b\b"
<< " " << endl;
return 0;
}

Tabelul_1: Caz favorabil (Se creează un graf rar - cu n noduri și n-1 muchii pentru a forma un arbore)

caz
favorabi 100 200 300 400 500 600 700 800 900 1000
l
Kruskal 3679 8332 14504 21576 28505 38138 46629 58539 68088 82272
Prim 24750 99500 223250 399000 623750 898500 1223250 1598000 2022750 2497500

 Pentru un graf rar m se apropie de n şi algoritmul Kruskal necesită un timp în O(n log n), fiind probabil mai
eficient decât algoritmul Prim

Caz favorabil
3000000

2500000

2000000

1500000

1000000

500000

0
100 200 300 400 500 600 700 800 900 1000

Kruskal Prim
Tabelul_2: Caz defavorabil (Se creează un graf complet cu n noduri, adică fiecare nod este conectat la
fiecare alt nod și cu m=((n-1)*n)/2 - muchii)

caz
defavor 100 200 300 400 500 600 700 800 900 1000  P
abil e
2451 2562 11126 33938 81031 166936 305546 519776 829484 1251545 n
Kruskal t
53 514 152 114 747 099 035 621 310 990
r
6618 5313 17955 42587 83209 143821 228422 341014 485596 6661684
Prim u
48 698 548 398 248 098 948 798 648 98
un graf dens se deduce că m se apropie de n(n–1)/2. În acest caz, algoritmul Kruskal necesită un timp în O(m
log n) şi algoritmul Prim este probabil mai bun.

Caz defavorabil
1400000000

1200000000

1000000000

800000000

600000000

400000000

200000000

0
100 200 300 400 500 600 700 800 900 1000

Kruskal Prim

Tabelul_3: Caz mediu (Se creează un graf dens cu n noduri și o densitate de muchii între și 30% dintr-un
graf complet(caz defavorabil))

caz
mediu 100 200 300 400 500 600 700 800 900 1000
30%
4834 33853 12828 357967 809515 159012 292342 4908340 7704928 1165486
Kruskal
2 2 51 2 9 75 21 8 6 65
2053 16289 54480 128898 251132 433663 688377 1026941 1459309 2002394
Prim
49 08 79 52 53 04 66 23 53 50

 Pentru un graf dens se deduce că m se apropie de n(n–1)/2. În acest caz, algoritmul Kruskal necesită un timp
în O(n 2 log n) şi algoritmul Prim este probabil mai bun.
Caz mediu - 30 %
250000000

200000000

150000000

100000000

50000000

0
100 200 300 400 500 600 700 800 900 1000

Kruskal Prim

Tabelul_4: Caz mediu (Se creează un graf dens cu n noduri și o densitate de muchii între și 80% dintr-un
graf complet(caz defavorabil))

caz
medi 100 200 300 400 500 600 700 800 900 1000
u 80%
Krusk 18120 16843 740769 219437 526445 1069840 1963981 3329749 5320321 8091038
al 9 81 0 62 69 08 95 57 37 46
53431 42553 143567 340794 665882 1150555 1826395 2728345 3877730 5324837
Prim
7 46 96 84 90 86 94 88 16 38

Cazul mediu - 80%


900000000
800000000
700000000
600000000
500000000
400000000
300000000
200000000
100000000
0
100 200 300 400 500 600 700 800 900 1000

Kruskal Prim
Concluzie :
Algoritmii Kruskal și Prim sunt două tehnici eficiente utilizate pentru rezolvarea problemei arborelui de
acoperire minim în cadrul teoriei grafurilor. Acești algoritmi sunt esențiali în optimizarea rețelelor și a
sistemelor de transport, având aplicații practice în domenii precum infrastructura de comunicații, logistică și
proiectarea rețelelor.

Algoritmul Kruskal:
Avantaje și Caracteristici:
Kruskal are un avantaj distinct în gestionarea grafului sub forma unei liste de muchii, ceea ce facilitează
implementarea și înțelegerea.
Algoritmul funcționează independent de noduri, comparând și alegând muchiile în funcție de greutatea lor.
Abordarea descentralizată și orientarea către muchii permit o implementare ușoară a paralelismului,
facilitând optimizarea pentru sisteme distribuite.
Comportament în Diverse Cazuri:
În cazul favorabil, când numărul de muchii este n−1 pentru un graf rar, Kruskal prezintă o complexitate
constantă.
În cazurile mediu și defavorabil, complexitatea Kruskal este mai instabilă, variind în funcție de structura și
densitatea specifică a grafului.
Sensibilitate la Structură:
Kruskal este sensibil la structura grafului, iar instabilitatea sa în cazurile mediu și defavorabil este legată de
modul în care explorează și compară muchiile în contexte cu un număr mare de conexiuni posibile fără a
forma cicluri.

Algoritmul Prim:
Avantaje și Caracteristici:
Prim selectează și adaugă noduri în arborele de acoperire minim, facilitând optimizarea pentru structuri
orientate mai mult spre noduri.
Algoritmul are complexitate constantă în toate cazurile, ceea ce îl face predictibil și eficient în contexte
diverse.
Strategia de Alegere a Muchiilor:
Prim alege întotdeauna muchia cu cea mai mică greutate la fiecare pas, asigurând construirea unui arbore de
acoperire minim cu cost minim.
Această abordare constantă în complexitate face ca Prim să fie robust și ușor de utilizat în aplicații diverse.
Rezistența la Variații:
Prim este rezistent la variații în densitatea muchiilor sau în structura grafului, păstrându-și constanta în
complexitate în cazurile favorabil, mediu și defavorabil.

În general ambii algoritmi au locul lor în rezolvarea problemei arborelui de acoperire minim, iar alegerea
între Kruskal și Prim depinde de cerințele specifice ale problemei și de caracteristicile grafului în cauză.
Kruskal oferă flexibilitate în contextul paralelismului și este potrivit pentru grafuri rare, în timp ce Prim
rămâne robust și eficient, adaptându-se la diverse structuri de grafuri. Alegerea între acești algoritmi ar
trebui să țină cont de condițiile specifice ale problemei și de particularitățile grafului asociat.

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