Documente Academic
Documente Profesional
Documente Cultură
Majoritatea problemelor simple se prezintă ca o sumă de mai multe probleme dintre cele prezentate anterior.
16. Determinarea tuturor factorilor primi ai unu număr natural n
d=2;
while(n>1){
if(n%d==0){p=0;
while(n%d==0){p++; n=n/d;}
// în acest moment d este un divisor prim, iar puterea la care acesta apare în descompunerea lui n
}
d++;
}
Metoda backtracking
Prezentare
Acesta metodă se utilizează pentru rezolvarea unor probleme ale căror soluţii se prezintă sub forma unui
vector x=x1,x2,....,xn,cu x1M1, x2M2, xnMn. Mulţimile M1, M2 ,.......,Mn sunt finite şi elementele lor se
află într-o relaţie de ordine bine stabilită.
Având în vedere că ne interesează toate soluţiile problemei, o modalitate ar fi să generăm produsul
cartezian al mulţimilor M1 *M2 *.........*Mn ,iar de aici să ne alegem soluţiile prin verificare directă. Dacă
acest lucru poate fi realizat în cazul în care problema are puţine soluţii candidat ,pentru probleme a căror
mulţime de soluţii candidat este foarte mare acest lucru este practic imposibil datorită timpului foarte mare
de execuţie al algoritmului.
O altă modalitate constă în utilizarea unei stive care să conţină pe fiecare nivel al său câte un element al
unei posibile soluţii, fiecare element fiind ales din câte o mulţime în aşa fel încât să se obţină la un moment
dat o soluţie parţială a problemei. În momentul în care se obţine utilizând această modalitate o soluţie a
întregii probleme aceasta va fi afişată şi se va trece la căutarea următoarei soluţii. Deşi şi în acest caz timpul
necesar obţinerii soluţiilor este foarte mare, totuşi el este mai mic decât în cazul produsului cartezian.
Concret se alege un element x1M1 care este valid şi se plasează pe primul nivel al stivei… Considerând
generată o soluţie parţială a problemei x1, x2,…, xk vom căuta să adăugăm pe stivă conform metodei descrise
un element xk+1Mk+1. Există două posibilităţi:
a) Există un element xk+1Mk+1 astfel încât x1, x2,…, xk,xk+1 să fie o soluţie parţială a problemei. În acest
caz se verifică dacă x1, x2,…, xk,xk+1 reprezintă o soluţie a întregii probleme, caz în care se va afişa
soluţia. Dacă nu se consideră generată soluţia până la nivelul k+1 şi se trece conform algoritmului la
alegerea unui element xk+2Mk+2.
b) Nu există nici un element xk+1Mk+1 astfel încât x1, x2,…, xk,xk+1 să fie o soluţie parţială a problemei. În
acest caz se consideră generată soluţia x1, x2,…, xk-1 şi se coboară la nivelul k, căutându-se un alt
element care să poată fi o soluţie parţială, începând cu succesorul lui xkMk ales înainte.
Algoritmul se încheie odată ce au fost epuizate toate elementele lui M1.
Recursivitate
Spunem despre o entitate că este definită recursiv dacă pentru definirea ei se apelează la o altă entitate de
acelaşi tip. Concret despre un algoritm spunem că este recursiv dacă pe parcursul execuţiei sale se
autoapelează. Un proces recursiv este finit atunci când încheierea sa se face după un număr finit de operaţii
executate.
Orice algoritm recursiv se poate considera că are două componente una în care se efectuează anumite
prelucrări urmate de apelul recursiv şi o condiţie de terminare a recursivităţii, adică un caz pentru care se
încheie procesul recursiv. Pentru a se ajunge la condiţia de încheiere a procesului recursiv este necesar ca
parametrii ce apar în cadrul apelului recursiv să tindă spre valorile care sunt tratate în condiţia de încheiere.
Există două tipuri de recursivitate:
1. Recursivitate directă
În acest caz există un singur subprogram care se apelează în mod recursiv.
2. Recursivitate indirectă( mutual recursivitate )
În acest caz există cel puţin două subprograme care se apelează unul pe celălalt în mod recursiv. Adică
dacă avem de exemplu două subprograme A şi B, A îl apelează pe B, iar B îl apelează pe A în mod repetat.
Ambele subprograme au o structură recursivă.
Trebuie ştiut că în momentul apelului recursiv toate variabilele globale şi locale utilizate în subprogramul
recursiv se salvează pe stiva pusă la dispoziţie de compilator. Excepţie fac parametrii formali ai
subprogramului care sunt transmişi prin referinţă, valorile acestora nefiind salvate. Pe lângă aceste valori se
mai salvează pe stivă şi o adresă de revenire în subprogram, adică adresa instrucţiunii ce se va executa
imediat după revenirea din apelul recursiv.
Un algoritm recursiv ni-l putem imagina ca acţionând în două etape: în prima efectuează toate operaţiile
până în momentul apelului recursiv, salvând valorile obţinute pe stivă în momentul apelului recursiv.
Această etapă se poate considera că ţine până în momentul în care s-a ajuns la condiţia de încheiere a
algoritmului, rezolvându-se şi acest caz. În a doua etapă se revine cu valorile obţinute în momentul în care s-
a ajuns la condiţia de încheiere şi coboară nivel cu nivel până se goleşte stiva, efectuându-se la fiecare pas
operaţiile care au mai rămas de efectuat din subprogramul respectiv. Doar după terminarea celor două etape
se obţine rezultatul final pentru o astfel de problemă.
Un algoritm recursiv este mare consumator de resurse, deoarece salvează pe parcursul execuţiei sale
foarte multe valori pe stivă. De aceea se recomandă utilizarea unui algoritm recursiv doar în cazurile în
cazurile în care nu se cunoaşte o altă modalitate de rezolvare a problemei sau în cazurile în care modalitatea
alternativă mu este elegantă sau este foarte complicată.
Un tip de probleme recursive întâlnite în ultimii anii la examene constau în evaluarea rezultatului
apelului unui subprogram recursiv utilizând anumite valori ca parametrii de pornire. În acest caz se scriu pas
cu pas relaţiile dintre termenii consecutivi ai apelului recursiv până se ajunge la valoarea care se obţine
pentru cazul de terminare a recursivităţii. Cu valoarea obţinută se revine pas cu pas efectuându-se calcule
până se ajunge la valoarea corespunzătoare nivelului cerut.
De exemplu, pentru problema:
int f(int n)
{if(n= =1) return 2;
else return 3*f(n-1);}
Ce valoare se obţine pentru n=4?
Rezolvare:
Se scriu relaţiile:
f(4)=3*f(3);
f(3)=3*f(2);
f(2)=3*f(1)=3*2=6;
Apoi se revine nivel cu nivel până se ajunge la valoarea lui n=4.
Adică f(3)=3*f(2)=3*6=18;
f(4)=3*f(3)=54;
Deci rezultatul care se obţine este 54.