Sunteți pe pagina 1din 3

Prezentare generala

Aceasta metoda generala de programare se aplica problemelor in care solutia se poate


reprezenta sub forma unui vector X = (x1, ..., xn). Pentru fiecare problema concretă sunt date
anumite relatii intre componentele x1, ..., xn ale vectorului X, numite conditii interne.
Multimea finitî S = S1 x S2 x ... x Sn se numeste spatiul solutiilor posibile (este un produs
cartezian). Solutiile posibile care satisfac conditiile interne se numesc solutii rezultat.
Ceea ce ne propunem este de a determina toate solutiile rezultat, cu scopul de a le afişa sau
de a alege dintre ele una care maximizeaza sau minimizeaza o eventuala functie obiectiv data.
O metoda simpla de determinare a solutiilor rezultat consta in generarea intr-un mod
oarecare toate solutiile posibile si de a verifica daca ele satisfac conditiile interne.
Dezavantajul consta in faptul ca timpul cerut de aceasta investigare exhaustiva este foarte mare.
Astfel, chiar pentru |Si| = 2, oricare ar fi i, timpul necesar este de ordinul 2 la puterea n, deci
exponential.
Metoda backtracking urmareste sa evite generarea tuturor solutiilor posibile. In acest scop,
elementele vectorului X primesc pe rand valori in sensul ca lui xk i se atribuie o valoare, numai daca
au fost atribuite deja valori lui x1, ..., xk-1. Mai mult, o valoare pentru xn stabilita, nu se trece direct la
atribuirea de valori lui xk+1, neindeplinirea lor exprimand faptul ca oricum am alege xk+1, ..., xn nu
vom putea ajunge la o solutie rezultat, adica o conditite pentru care conditiile interne sa fie
satisfacute.
Evident că în cazul neindeplinirii conditiilor de continuare, va trebui sa facem o alta alegere pentru
xk, sau daca Sk a fost epuizat, să micşoram pe k cu o unitate, incercand sa facem o noua alegere
pentru xk, etc. Aceasta micsorare a lui k dă numele metodei, ilustrand faptul că atunci cand nu mai
putem avansa, urmărim înapoi secvenţa curentă din solutie. Este evident ca intre conditiile de
continuare si conditiile interne exista o stransa legatura. O buna alegere pentru conditiile de
continuare are ca efect o importanta reducere a numarului de calcule.
Metoda backtracking poate fi reprezentata usor, pe un arbore construit astfel:
- nivelul 1 contine rădăcina;
- din orice vârf de pe nivelul k pleaca sk muchii spre nivelul k+1 etichetati cu cele sk muchii ale lui
Sk.
Nivelul n+1 va contine s1 * s2 * ... *sn varfuri. Pentru fiecare varf de pe nivelul n+1, etichetele
muchiilor continute pe drumul ce leaga radacina de acest varf, reprezinta o solutie posibila.

Exemplu:
Sa consideram problema submultimilor de suma data care consta in urmatoarele:
Fie A = (a1, a2, ..., an) cu ai > 0, oricare ar fi i. Fie M inclus in R. Se cauta toate submultimile B ale
lui A pentru care suma elementelor este M.
Pentru a putea realiza problema prin metoda backtracking vom reprezenta solutia sub forma x = (x1,
..., xn) unde xi = 1 daca ai inclus in B si xi = 0 in caz contrar. Sa ne situam in ipoteza ca n = 4.
Castigul obtinut prin introducerea conditiilor de continuare consta in faptul ca, daca intr-un varf ele
nu mai sunt verificate, se va renunta la parcurgerea subarborelui care are ca radacina acest varf.
Acest exemplu permite prezentarea unei variante a metodei backtracking. Intr-adevar, sa
consideram drept solutie posibila o valoare k <= n impreuna cu cuplul (x1, ...,xk) unde pentru i
inclus in {1, ..., k}, xi reprezinta indicele elementului pe care il introducem in B. Evident xi este
diferit de xj pentru i diferit de j. Pentru a nu se repeta solutii, vom presupune x1 < x2 < ... < xn.
Diferite variante ale metodei backtracking nu schimba esenta ei care consta in faptul ca este
reprezentabila pe un arbore care este parcurs "coborand" in arbore numai daca exista sanse de a
ajunge la o solutie rezultat.
In continuare, problemele care vor fi prezentate vor urma o schema generala si anume:
1
- se va testa daca am obtinut o solutie, situatie in care aceasta se va retine;
- daca nu am obtinut o solutie se incearca plasarea unui nou element in vectorul solutie cu
respectarea conditiilor de continuare.
- daca nu se reuseste plasarea unui nou element si spatiul valorilor posibile de plasat s-a epuizat, se
revine la pozitia anterioara si se incearca sa se plaseze pe ea un alt element.
Faptul ca dupa plasarea unui element in vectorul solutie algoritmul presupune plasarea unui element
pe pozitia imediat urmatoare, adica de fapt reluarea algoritmului, conduce posibilitatea abordarii
recursive a algoritmilor de tip backtracking. Acest lucru permite o scriere mult mai scurta si mai
simpla a algoritmilor si apoi a programelor care ii implementeaza. Astfel, general, un algoritm
backtracking poate fi prezentat astfel:

pentru ficare valoare i din multimea Sk executa


xk <-i
daca X respecta conditiile interne atunci
daca X este solutie atunci
afiseaza X
altfel
apeleaza back(k+1)
sfdaca
sfdaca
sfpentru

In functie de problema concreta, in algoritmul descris mai sus se vor modifica doar instructiunea
pentru, conditiile interne si cele de solutie, structura algoritmului pastrandu-se.
Problemele care se rezolva prin metoda backtracking pot fi impartite in mai multe grupuri de
probleme cu rezolvari asemanatoare, in functie de modificarile pe care le vom face in algoritm. a)
Probleme in care vectorul solutie are lungime fixa si fiecare element apare o singura data in solutie;
b) Probleme in care vectorul solutie are lungime variabila si fiecare element poate sa apara de mai
multe ori in solutie;
c) Probleme in plan, atunci cand spatiul in care ne deplasam este un tablou bidimensional.

Vom prezenta in cele ce urmeaza cateva probleme care fac parte din primul grup. Cele mai
cunoscute sunt:
- generarea permutarilor unei multimi;
- generarea aranjamentelor unei multimi;
- generarea submultimilor unei multimi;
- generarea submultimilor cu m elemente ale unei multimi (combinari);
- generarea produsului cartezian a n multimi;
- generarea tuturor secventelor de n (par) paranteze care se inchid corect;
- colorarea tarilor de pe o harta astfel incat oricare din doua tari vecine sa aiba culori diferite;
- aranjarea a n regine pe o tabla de sah de dimensiune n fara ca ele sa se atace.

Toate problemele din acest grup au particularitatea ca solutia se obtine atunci cand vectorul solutie
ajunge sa contina un anumit numar de elemente.

Partitiile unui numar natural. Fie n > 0, natural. Sa se scrie un program care sa afiseze toate
partitiile unui numar natural n.
Numim partitie a unui numar natural nenul n o multime de numere naturale nenule {p1, p2, ..., pk}
care indeplinesc conditia p1 + p2 + ... + pk = n.

2
Ex: pt. n = 4 programul va afisa:
4 = 1 + 1 + 1 +1
4=1+1+2
4=2+2
4=4

Observatii:
- lungimea vectorului solutie este cel mult n;
- exista posibilitatea ca solutiile sa se repete;
- conditia de final este indeplinita atunci cand suma elementelor vectorului solutie este n.

Am mentionat mai sus ca vom folosi doi parametri, unul pentru pozitia in vectorul solutie si un al
doilea, in care avem sumele partiale la fiecare moment. Avem determinata o solutie atunci cand
valoarea celui de-al doilea parametru este egala cu n.
In aceasta situatie la fiecare plasare a unei valori in vectorul sol valoarea celui de al doilea parametru
se mareste cu elementul ce se plaseaza in vectorul solutie. Apelul procedurii back din programul
principal va fi back(1, 0).
Exista si posibilitatea de a apela procedura back din programul principal back(1, n) si valoarea celui
de al doilea parametru se decrementeaza cu elementul ce se plaseaza in vectorul sol, iar o solutie
avem cand acest parametru este zero. Indiferent care modalitate este aleasa acest al doilea parametru
ne permite sa optimizam putin programul in sensul ca putem considera niste conditii de continuare
mai stranse.

#include <stdio.h>
#include <conio.h>
int n, ns, sol[20];
void afis(int l)
{int i;
ns++;
printf("\nSolutia nr. %d:\n", ns);
for(i=1; i <= l; i++) printf("%d ", sol[i]);
printf("\n");
}
void back(int i, int sp)
{int j;
if(sp==n)
afis(i-1);
else
for(j=1; j <= n-sp; j++)
if(j >= sol[i-1])
{sol[i]=j;
back(i+1, sp+j);
}
}
int main( void )
{printf("n = "); scanf("%d", &n);
back(1, 0);
printf("\n\n%d solutii!\n\n", ns);
printf("Apasa o tasta pentru a inchide..."); getch();
return 0;
}

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