Biblioteca
Tutora
BibliotecaInformatică și TIC › clasa a XI-a

Tehnici de programare

Prezentarea principalelor paradigme și tehnici algoritmice avansate.

Bacalaureat

Divide et Impera: împarte, rezolvă, combină

Divide et Impera rezolvă o problemă în trei pași care se repetă recursiv:

Condiția esențială, care se uită la examen: subproblemele trebuie să fie independente — rezolvarea uneia nu depinde de rezultatul alteia. Când subproblemele se suprapun (aceleași calcule apar de mai multe ori), Divide et Impera devine ineficientă și locul ei îl ia programarea dinamică.

Exemple canonice: căutarea binară (împarte intervalul în două, păstrează o singură jumătate), MergeSort (sortează recursiv cele două jumătăți, apoi le interclasează), QuickSort (partiționează în jurul pivotului, apoi sortează recursiv cele două zone), turnurile din Hanoi.

Capcana clasică de grilă: nu orice funcție recursivă înseamnă Divide et Impera. O recursivitate care reduce problema cu 1 (de la n la n-1, ca la suma elementelor) nu împarte problema în subprobleme comparabile — metoda cere împărțirea în bucăți semnificative, tipic înjumătățire, ceea ce duce la complexități cu log n.

Greedy: alegerea local optimă, fără revenire

Metoda Greedy construiește soluția pas cu pas: la fiecare pas alege elementul care pare cel mai bun în acel moment (optimul local), iar alegerea făcută nu se mai revizuiește niciodată. De aici vin și puterea, și slăbiciunea metodei:

Exemple la care Greedy funcționează demonstrabil: problema spectacolelor (selectarea numărului maxim de activități care nu se suprapun — se alege mereu activitatea care se termină cel mai devreme), interclasarea optimă, algoritmii Kruskal și Prim pentru arbore parțial de cost minim, Dijkstra pentru drumuri minime.

Contraexemplul care se cere la examen: problema restului cu monede arbitrare. Pentru monedele 1, 3, 4 și suma 6, Greedy alege 4 + 1 + 1 (trei monede), dar optimul este 3 + 3 (două monede). Concluzia de reținut: Greedy nu explorează alternative și nu revine asupra deciziilor — dacă problema cere garantat optimul și strategia locală nu e demonstrată, se folosește altă metodă (programare dinamică sau backtracking).

Backtracking: explorare sistematică cu revenire

Backtracking generează toate soluțiile posibile construindu-le element cu element, într-un vector soluție x[1..n]. La fiecare pas k se încearcă pe rând valorile candidate pentru x[k]:

Ideea care face metoda utilizabilă: condițiile de continuare taie din start ramurile care nu pot duce la soluție — nu se generează toate combinațiile și abia apoi se verifică, ci se abandonează prefixele invalide cât mai devreme. Fără această tăiere, backtracking degenerează în forță brută pură.

Probleme standard, de recunoscut instantaneu în enunț:

Complexitatea este exponențială (sau factorială la permutări) — de aceea backtracking se aplică doar la dimensiuni mici. Grilă frecventă: la generarea permutărilor lui n, numărul soluțiilor este n!; la submulțimi este 2^n. Cine le inversează pierde punctul fără drept de apel.

Programarea dinamică: subprobleme care se suprapun

Programarea dinamică se aplică problemelor de optimizare care au două proprietăți:

Strategia: fiecare subproblemă se rezolvă o singură dată, iar rezultatul se memorează într-un tablou (de regulă numit d sau dp), de unde e refolosit ori de câte ori e nevoie. Se lucrează fie bottom-up (se completează tabloul de la cazurile mici spre problema mare), fie top-down cu memoizare (recursivitate care își notează rezultatele).

Exemplul didactic standard: șirul lui Fibonacci. Calculat recursiv naiv, F(n) cere timp exponențial pentru că F(n-2) se recalculează de nenumărate ori; cu memorare, timpul devine liniar. Probleme clasice de examen: subșirul crescător de lungime maximă, problema rucsacului (varianta discretă), numărul de drumuri într-o matrice, suma maximă pe un traseu în triunghi de numere.

Delimitarea față de celelalte metode — întrebare de teorie garantată: față de Divide et Impera, programarea dinamică tratează subprobleme care se suprapun (D&I cere subprobleme independente); față de Greedy, programarea dinamică examinează toate alternativele la fiecare pas și garantează optimul, în timp ce Greedy face o singură alegere locală.

Branch and Bound și alegerea metodei potrivite

Branch and Bound explorează, la fel ca backtracking, un arbore de soluții candidate, dar cu două diferențe:

În plus, ordinea de explorare nu e neapărat în adâncime: se poate alege mereu nodul cel mai promițător (best-first). Metoda se folosește la probleme de optimizare grele (problema comis-voiajorului, rucsacul), unde backtracking simplu ar explora inutil ramuri evident proaste.

Cum alegi metoda la examen — schema de decizie care rezolvă majoritatea grilelor de teorie:

Confuzia frecventă: Branch and Bound nu e „backtracking mai rapid” în general — tăierea depinde de calitatea estimării; cu o margine proastă, explorează la fel de mult.

De reținut

Divide et Impera
tehnică ce împarte problema în subprobleme independente de același tip, le rezolvă recursiv și combină soluțiile; exemple: căutarea binară, MergeSort, QuickSort
metoda Greedy
tehnică ce construiește soluția pas cu pas, alegând la fiecare pas optimul local, fără a reveni asupra alegerilor făcute; garantează optimul global doar dacă acest lucru este demonstrat pentru problema dată
backtracking
tehnică ce generează soluțiile element cu element într-un vector soluție, abandonând prin revenire prefixele care nu satisfac condițiile de continuare
condiții de continuare
condițiile verificate la fiecare pas al backtracking-ului, care decid dacă valoarea curentă poate conduce la o soluție; ele taie ramurile invalide înainte de a fi explorate complet
programare dinamică
tehnică de optimizare pentru probleme cu substructură optimă și subprobleme suprapuse; fiecare subproblemă se rezolvă o singură dată, iar rezultatul se memorează și se refolosește
principiul optimalității
proprietatea conform căreia soluția optimă a unei probleme conține soluții optime ale subproblemelor sale; este condiția de aplicare a programării dinamice
memoizare
varianta top-down a programării dinamice: recursivitate care memorează rezultatul fiecărui apel, astfel încât fiecare subproblemă se calculează o singură dată
Branch and Bound
tehnică de explorare a arborelui de soluții în care fiecare ramură primește o estimare optimistă (margine); ramurile cu margine mai slabă decât cea mai bună soluție cunoscută se elimină fără explorare
spațiul soluțiilor la backtracking
numărul soluțiilor generate: n! la permutări, 2^n la submulțimi, aranjamente și combinări după formulele combinatoricii; explică natura exponențială a metodei

Greșeli frecvente

Greșit: Orice algoritm recursiv ar fi Divide et Impera
Corect: Divide et Impera cere împărțirea în subprobleme independente de dimensiuni comparabile (tipic înjumătățire); o recursie care doar scade n cu 1 este recursivitate simplă, nu Divide et Impera
Greșit: Greedy ar garanta întotdeauna soluția optimă pentru că alege mereu cel mai bun element
Corect: Greedy alege optimul local și nu revine; optimul global e garantat doar la problemele unde strategia e demonstrată — la restul cu monedele 1, 3, 4 pentru suma 6, Greedy dă 3 monede în loc de 2
Greșit: Programarea dinamică s-ar aplica atunci când subproblemele sunt independente
Corect: Exact pe dos: subproblemele independente țin de Divide et Impera; programarea dinamică e valoroasă tocmai când subproblemele se suprapun și rezultatele memorate se refolosesc
Greșit: La generarea permutărilor prin backtracking s-ar obține 2^n soluții
Corect: Permutările a n elemente sunt n!; 2^n este numărul submulțimilor unei mulțimi cu n elemente — cele două formule se confundă frecvent în grile
Greșit: Backtracking-ul ar genera toate combinațiile posibile și abia la final le-ar verifica validitatea
Corect: Backtracking verifică condițiile de continuare la fiecare pas și abandonează imediat prefixele invalide; generarea completă urmată de verificare este forța brută, tocmai ce evită metoda

Test — 6 întrebări ca la examen

1. Care dintre următorii algoritmi este o aplicare directă a metodei Divide et Impera?
  1. căutarea secvențială într-un vector
  2. sortarea prin interclasare (MergeSort)
  3. determinarea maximului printr-o singură parcurgere
  4. calculul sumei elementelor unui vector
Vezi răspunsul
sortarea prin interclasare (MergeSort). MergeSort împarte vectorul în două jumătăți, le sortează recursiv și combină rezultatele prin interclasare — exact cei trei pași ai metodei. Căutarea secvențială e tentantă doar prin contrast cu cea binară, dar parcurge liniar, fără nicio împărțire în subprobleme.
2. Trăsătura definitorie a metodei Greedy este:
  1. memorarea rezultatelor subproblemelor pentru refolosire
  2. revenirea la pasul anterior când soluția nu poate fi continuată
  3. alegerea optimului local la fiecare pas, fără revizuirea alegerilor
  4. împărțirea problemei în subprobleme independente
Vezi răspunsul
alegerea optimului local la fiecare pas, fără revizuirea alegerilor. Greedy face la fiecare pas alegerea care pare cea mai bună local și nu se mai întoarce asupra ei. Revenirea la pasul anterior este mecanismul backtracking-ului — distractorul clasic, pentru că ambele metode construiesc soluția pas cu pas, dar numai backtracking revine.
3. Pentru monedele cu valorile 1, 3 și 4, strategia Greedy (alege mereu cea mai mare monedă care încape) aplicată sumei 6 produce:
  1. soluția optimă, cu două monede
  2. o soluție cu trei monede, deși optimul este de două monede
  3. nicio soluție, pentru că 6 nu se poate forma
  4. o soluție cu patru monede
Vezi răspunsul
o soluție cu trei monede, deși optimul este de două monede. Greedy alege 4, apoi nu mai încape decât 1 și 1 — total trei monede; optimul este 3 + 3, două monede. Exemplul arată exact limita metodei: optimul local (moneda 4) strică optimul global. Suma 6 se poate forma evident, deci varianta a treia pică imediat.
4. Prin metoda backtracking se generează toate permutările a 5 elemente. Câte soluții se obțin?
  1. 32
  2. 25
  3. 120
  4. 3125
Vezi răspunsul
120. Numărul permutărilor a n elemente este n!, deci 5! = 120. Distractorul 32 = 2^5 corespunde numărului de submulțimi, confuzia standard între cele două probleme de generare; 3125 = 5^5 ar fi produsul cartezian.
5. Calculul recursiv naiv al lui F(n) din șirul Fibonacci este exponențial, dar devine liniar prin programare dinamică, deoarece:
  1. recursivitatea este înlocuită cu o structură repetitivă, ceea ce e întotdeauna mai rapid
  2. fiecare subproblemă se rezolvă o singură dată, iar rezultatul memorat se refolosește
  3. problema se împarte în subprobleme independente rezolvate separat
  4. se renunță la calculul exact în favoarea unei aproximări
Vezi răspunsul
fiecare subproblemă se rezolvă o singură dată, iar rezultatul memorat se refolosește. Câștigul vine din eliminarea recalculărilor: F(n-2), F(n-3) etc. apar de multe ori în arborele recursiv, dar memorate se calculează o singură dată. Prima variantă e capcana: simpla trecere la iterativ fără memorare nu schimbă numărul de calcule — memorarea e cheia, nu forma buclei.
6. O problemă cere numărul maxim de spectacole care pot fi vizionate integral, cunoscând intervalele lor de desfășurare. Metoda adecvată și criteriul corect sunt:
  1. Greedy, alegând mereu spectacolul care începe cel mai devreme
  2. Greedy, alegând mereu spectacolul care se termină cel mai devreme
  3. backtracking, pentru că se cer toate combinațiile de spectacole
  4. Divide et Impera, împărțind ziua în două jumătăți
Vezi răspunsul
Greedy, alegând mereu spectacolul care se termină cel mai devreme. Alegerea spectacolului care se termină cel mai devreme lasă loc maxim pentru restul — criteriu demonstrat optim. Prima variantă e distractorul puternic: un spectacol care începe devreme dar durează enorm blochează ziua. Backtracking ar da și el răspunsul, dar exponențial — enunțul cere doar maximul, nu toate combinațiile.
Deschide varianta interactivă — cu AI care îți explică
← Structuri de dateAlgoritmi de sortare și căutare →
BiologieChimieEconomieFilosofieFizicăGeografieInformatică și TICIstorieLogică și argumentareMatematicăPsihologieLimba și literatura română