Sunteți pe pagina 1din 27

+

Algoritmi și tehnici de programare

Cursul 7
+

◼ Greedy – metoda optimului local


+ Greedy – metoda optimului local

◼ Metodă rapidă, de complexitate redusă, pentru obținerea


unei soluții acceptabile (nu neapărat cea mai bună)
◼ La fiecare pas se alege cea mai bună cale în contextul
local, ignorând contextul general
◼ Uneori soluția poate fi cea mai puțin dezirabilă

◼ Este folosită atunci cînd găsirea celei mai bune soluții este
prea costisitoare.
+ Greedy – metoda optimului local

◼ Un algoritm Greedy este un algoritm iterativ (nerecursiv)


care determina în fiecare pas k o componentã x[k] a
vectorului solutie si nu mai revine ulterior la aceasta
alegere.
+ Greedy – metoda optimului local

Problema are in general urmatoare structura:

- Se da o multime A={a1 a2, …, an}

◼ Se cere sa determinam o submultime B a multimii A, care


indeplineste anumite conditii pentru a fi acceptata in calitate
de solutie

Solutia este de a alege un element din A succesiv care


maximalizeaza optimalitatea in momentul alegerii lui=>se
construieste B prin respectarea criteriilor obligatorii.
+ Greedy – metoda optimului local

◼ Probleme rezolvate prin metoda Greedy

◼ Problema poate fi imaginată ca o mulțime 𝐴 cu 𝑛 elemente

◼ O soluție posibilă este o submulțime ( 𝐵 ⊆ 𝐴) care


îndeplinește o condiție dată (𝐵 este acceptabilă);

◼ Pot exista mai multe submulțimi diferite acceptabile (soluții


posibile), dintre care una este considerată soluție optimă,
pe baza unui criteriu care trebuie maximizat (minimizat).
+
Spațiul soluțiilor
◼ Fie mulțimile 𝑺𝒊 , 𝒊 = 𝟏, 𝒏, 𝐜𝐚𝐫𝐝(𝑺𝒊 ) = 𝒏𝒊 , ∃ o relație de
ordine pe 𝑺𝒊

◼ 𝑺 = 𝑺𝟏 × 𝑺𝟐 × ⋯ × 𝑺𝒏 = 𝒙 = 𝒙𝟏 , 𝒙𝟐 , … , 𝒙𝒏 | 𝒙𝒊 ∈ 𝑺𝒊
spațiul soluțiilor

◼ 𝝋: 𝑺 → 𝒅𝒂, 𝒏𝒖 funcție de validare

◼ 𝑹 = 𝒙 ∈ 𝑺 | 𝝋 𝒙 = 𝒅𝒂 mulțimea soluțiilor
rezultat
◼ 𝝋 𝒙 = 𝒅𝒂 ⟺ 𝒙 ∈ 𝑹

◼ 𝝋𝒌 : 𝑺𝟏 × 𝑺𝟐 × ⋯ × 𝑺𝒌 → 𝒅𝒂, 𝒏𝒖 funcții de validare


parțială
◼ 𝝋𝒌−𝟏 = 𝒅𝒂 ⇒ se poate adăuga un element 𝒙𝒌 ∈ 𝑺𝒌
cu 𝝋𝒌 = 𝒅𝒂.
+ Greedy – metoda optimului local

◼ Operații (nu sînt întotdeauna evidente)

◼ Alegerea unui element candidat 𝑥 din mulțimea 𝐴 (𝑥 ∈ 𝐴)

◼ Verificarea acceptabilității elementului ales: adăugarea


elementului la soluția parțială construită o menține
acceptabilă sau o face inacceptabilă?

◼ 𝐵 ∪ {𝑥} este acceptabilă?

◼ Adăugarea elementului ales la soluția parțială, dacă ea


rămîne acceptabilă.

◼ 𝐵 = 𝐵 ∪ {𝑥}
+ Greedy – metoda optimului local

◼ Algoritm general

◼ se primește mulțimea 𝐴 cu 𝑛 elemente

◼ se inițializează soluția 𝐵 ca mulțime vidă

◼ repetă de (maxim) 𝑛 ori

◼ alege un element candidat 𝑥 din mulțimea 𝐴

◼ verifică dacă 𝐵 ∪ 𝑥 este soluție acceptabilă

◼ dacă da, adaugă 𝑥 la mulțimea 𝐵: 𝐵 = 𝐵 ∪ 𝑥

◼ se trimite mulțimea 𝐵 ca soluție


+ Greedy – metoda optimului local

◼ Schema generală a unui algoritm bazat pe metoda


Greedy:

While ExistaElemente do

AlegeUnElement(x);

IncludeElementul(x)

Endwhile.
+ Greedy – metoda optimului local

◼ Se consideră o mulţime A cu n numere reale. Se cere o


submulţime S a sa, astfel încât suma elementelor sale să fie
maximă.

◼ Exemplu. Pentru numerele 1 -5 6 2 -2 4 răspunsul este 1 6 2 4


(suma 13)
+ Greedy – metoda optimului local

◼ Rezolvare

◼ 1. Initial submultimea solutie căutată este vidă S=Ø

◼ 2. Se alege un prim element x al mulţimii de numere reale.


Dacă este posibil (adică dacă x>0), acesta va fi adăugat
soluţiei S=S U {x}

◼ 3. Se alege un al doilea număr, cu care se procedează în mod


asemănător.

◼ 4. Algoritmul se încheie când au fost alese şi eventual


adăugate toate elementele mulţimii.
+ Greedy – metoda optimului local

◼ Pentru a rezolva o problemă cu Greedy, soluţia se construieşte,


după regula:
+ Greedy – metoda optimului local
◼ void Greedy(int n, float a[], float *b[], int *m)
{ int i,nr;
nr = 0;
for (i = 0; i < n; i++)
if (a[i] > 0)
{
(nr)++;
}
*b = (float*)malloc((nr) * sizeof(float));
(*m) = 0;
for (i = 0; i < n; i++)
if (a[i] > 0)
{
(*b)[*m] = a[i];
(*m)++;
}
}
+ Greedy – metoda optimului local
◼ Dacă in prealabil ordonăm descrescător secvenţa considerată obţinem:
◼ 6 4 2 1 -2 -5

◼ void Greedy_modificat(int n, float a[], float *b[], int *m)


{ int i,nr;
nr = 0;
for (i = 0; i < n; i++)
if (a[i] > 0)
{
(nr)++;
}
else i=n;
*b = (float*)malloc((nr) * sizeof(float));
(*m) = 0;
for (i = 0; i < n; i++)
if (a[i] > 0)
{
(*b)[*m] = a[i];
(*m)++;
}
else i=n;

}
+ Greedy – metoda optimului local

◼ Fie A un vector ale cărui componente A(0) , A(1),…, A(n-1) sunt numere
naturale nenule. Să se determine suma componentelor care sunt
numere pare. Atunci când un număr par se repetă, el va fi luat în
consideraţie o singură dată.

Să luăm un exemplu concret , pe baza căruia vom ilustra cum se ‘’construieşte’’ soluţia.

Fie secvenţa : 5, 8, 7, 8, 4, 9, 7, 5, 5, 4, 8, 7, 5, 9.

Deoarece A (0) este 5, nefiind număr par se porneşte cu soluţia iniţială S:=0 (dacă A
(0) ar fi fost număr par, am fi pornit cu soluţia s:=A(0)).

Apoi valorile lui S vor fi:

S:=0

S:=0+8=8

S:=8+4=12
+ Greedy – metoda optimului local

◼ De remarcat caracterul constructiv al soluţiei.

◼ Un termen curent (fie acesta A(i)) contribuie la valoarea lui S dacă îndeplineşte
două condiţii :
◼ - este un număr par;
◼ - n-a mai fost utilizat.

◼ Dacă in prealabil ordonăm crescător secvenţa considerată obţinem:

◼ 4, 4, 5, 5, 5, 5, 7, 7, 7, 8, 8, 8, 9, 9

◼ şi astfel se observa mult mai uşor repetabilitatea unui numar par (faţă de situaţia
iniţială când pentru a constata dacă un număr par a fost sau nu utilizat trebuie să
parcurgem întreaga secvenţă de fiecare dată ).
+ Greedy – metoda optimului local
Pe baza afirmațiilor anterioare rezultă un model de rezolvare pentru
problema propusă, model care dă structura generală a metodei Greedy :
int Greedy1(int n, int a[], int b[], int *m)
{ int i,s;
*m = 0;
s = 0;
if (a[0] % 2 == 0)
{
s = a[0];
b[*m] = a[0];
(*m)++;
}
for (i = 1; i < n; i++)
if ((a[i] != a[i-1])&&(a[i]%2==0))
{
b[*m] = a[i];
s = s + b[*m];
(*m)++;
}
return s;
}
+ Greedy – metoda optimului local
Problema rucsacului
◼ Se consideră un set de n obiecte si un rucsac de capacitate dată
q. Pentru fiecare obiect k se cunoaste capacitatea c(k) si venitul
v(k) care poate fi obtinut prin transportarea acestuia cu rucsacul
până la o destinatie fixată.
◼ Să se determine o încărcare optimală a rucsacului, în ipoteza în
care orice obiect poate fi încărcat întreg în rucsac sau partial.
+ Greedy – metoda optimului local
Problema rucsacului
// I: nr. obiecte (n), capacitate ocupata (c), venit (v)

void sortare_desc(float* c, float* v, int n)


{
int i, j;
float a;
for (i = 0; i < n - 1; i++)
for (j = i + 1; j < n; j++)
if (v[i] / c[i] < v[j] / c[j]) //castig unitar
{
a = v[i]; v[i] = v[j]; v[j] = a;
a = c[i]; c[i] = c[j]; c[j] = a;
}
}
+ Greedy – metoda optimului local
Problema rucsacului

// I: capacitate totala (q), nr. obiecte (n), capacitate ocupata (c),


// E: solutia x

◼ void Rucsac_intreg(float q, int n, float* c, float* x)


{
float qr;
int i, j;
qr = q;
for (i = 0; i < n && qr>0; i++)
if (qr >= c[i])
{
x[i] = 1;
qr = qr - c[i];
}
else
x[i] = 0;
}
+ Greedy – metoda optimului local
Problema rucsacului
// I: capacitate totala (q), nr. obiecte (n), capacitate ocupata (c),
// E: solutia x
void Rucsac_cont(float q, int n, float* c, float* x)
{
float qr;
int i, j;
qr = q;
for (i = 0; i < n && qr>0; i++)
if (qr >= c[i])
{
x[i] = 1;
qr = qr - c[i]; //x[i]=1; qr=qr - c[i]*x[i];
}
else
{
x[i] = qr / c[i];
qr = 0; //qr=qr - c[i]*x[i];
for (j = i + 1; j < n; j++)
x[j] = 0;
}
}

Dacă pentru orice obiect i putem să luăm doar o parte xi ∈[0,1] din el, atunci spunem că
avem problema continuă a rucsacului
+ Greedy – metoda optimului local
◼ Se doreste să se plătească o sumă s cu un număr minim
de bancnote cu valori date. Se consideră că din fiecare tip
de bancnotă se poate folosi un număr nelimitat de
bancnote, iar pentru ca problema să aibă soluţie, vom
considera că există şi bancnote cu valoarea 1.
+ Greedy – metoda optimului local
◼ Se doreste să se plătească o sumă s cu un număr minim de bancnote cu valori
date. Se consideră că din fiecare tip de bancnotă se poate folosi un număr
nelimitat de bancnote, iar pentru ca problema să aibă soluţie, vom considera că
există şi bancnote cu valoarea 1.

◼ void sortare(int* x, int n)


{
int i, j;
int a;
for (i = 0; i < n - 1; i++)
for (j = i + 1; j < n; j++)
if (x[i] < x[j])
{
a = x[i]; x[i] = x[j]; x[j] = a;
}
}
void greedy(int s, int *x, int n, int *a)
{
int i;
for (i = 0; i < n; i++)
{
if (s / x[i] > 0) a[i] = s / x[i];
s = s % x[i];
}
}
+ Greedy – metoda optimului local

◼ Exemple de probleme rezolvate prin algoritmi de tip Greedy:


◼ Problema rucsacului
◼ Întreagă: obiectele pot fi transferate doar in intregime;
◼ Continuă: pot fi selectate obiecte in intregime sau fractiuni ale
obiectelor.

◼ Plata unei sume (cu bancnotă unitate)


◼ Problema suma maximă
◼ Problema spectacolelor
◼ Algoritmul Dijkstra
◼ Determinarea arborelui parțial de cost minim (soluția este
întotdeauna optimă)
◼ Algoritmul lui Kruskal, algoritmul lui Prim
+ Greedy – metoda optimului local

◼ Se dă o mulţime S cu n componente naturale nenule. Să se


scrie un subprogram care determină produsul componentelor
impare. Atunci când un număr impar se repetă, el va fi luat în
considerație o singură dată. Scrieți un program care
demonstrează utilizarea acestui subprogram. Transmiterea
datelor se va realiza exclusiv prin parametri. Elementele
din masiv sunt indexate de la 0.
+
Bibliografie

◼ I. Gh. Roşca, B. Ghilic-Micu, C. Cocianu, M. Stoica, C.


Uscatu, Programarea calculatoarelor. Ştiinţa învăţării
unui limbaj de programare, Teorie şi aplicaţii, Ed. ASE,
2003

◼ I. Gh. Roşca, B. Ghilic-Micu, C. Cocianu, M. Stoica, C.


Uscatu, M. Mircea, Programarea calculatoarelor.
Algoritmi în programare, Ed. ASE Bucureşti, 2007

◼ C. Uscatu, M. Popa, L. Bătăgan, C. Silvestru, Programarea


Calculatoarelor. Aplicații, Ed. ASE, 2012

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