Sunteți pe pagina 1din 7

Metoda Backtracking

Aa cum s-a subliniat n capitolele anterioare, complexitatea n timp a


algoritmilor joac un rol esenial. n primul rnd un algoritm este considerat
"acceptabil" numai dac timpul su de executare este polinomial, adic de ordinul
O(nk) pentru un anumit k; n reprezint numrul datelor de intrare.
Pentru a ne convinge de acet lucru, vom considera un calculator capabil s
efectueze un milion de operaii pe secund.
n
2n
3n
3

n=20

n=40

n=60

1 sec
58 min

12,7 zile
3855 secole

0,2 sec
366 secole
1013 secole

Chiar dac n prezent calculatoarele performante sunt capabile de a efectua


zeci de miliarde de operaii pe secund, tabelul de mai sus arat c algoritmii
exponeniali nu sunt acceptabili.

Descrierea metodei
Fie X=X1 ... Xn. Caut xX cu (x), unde :X {0,1} este o
proprietate definit pe X.
Din cele de mai sus rezult c generarea tuturor elementelor produsului
cartezian X nu este acceptabil.
Metoda backtracking ncearc, dar nu reuete totdeauna, micorarea timpului
de calcul. X este numit spaiul soluiilor posibile, iar sintetizeaz condiiile interne.
Vectorul x este construit progresiv, ncepnd cu prima component. Se
avanseaz cu o valoare xk dac este satisfcut condiia de continuare
k(x1,...,xk). Condiiile de continuare rezult de obicei din ; ele sunt strict
necesare, ideal fiind s fie i suficiente.
Distingem urmtoarele cazuri posibile la alegerea lui xk:
1)
Atribuie i avanseaz: mai sunt valori neconsumate din Xk i valoarea xk
aleas satisface k se mrete k.
2)
ncercare euat: mai sunt valori neconsumate din Xk i valoarea xk
aleas dintre acestea nu satisface k se va relua, ncercndu-se alegerea
unei noi valori pentru xk.
3)
"Revenire": nu mai exist valori neconsumate din Xk (Xk epuizat)
ntreaga Xk devine disponibil i kk-1.
4)
"Revenire dup determinarea unei soluii": este reinut soluia.
Reinerea unei soluii const n apelarea unei proceduri retsol care
prelucreaz soluia (o tiprete, o compar cu alte soluii etc.) i fie oprete procesul
(dac se dorete o singur soluie), fie prevede kk-1 (dac dorim s determinm
toate soluiile.

Notm prin CkXk mulimea valorilor consumate din Xk. Algoritmul este
urmtorul:
Ci, i;
k1;
while k>0
if k=n+1
then retsol(x); kk-1;
{ revenire dup obinerea unei soluii }
else if CkXk
then alege vXk\Ck; CkCk{v};
if k(x1,,xk-1,v)
then xkv; kk+1;
{ atribuie i avanseaz }
else
{ ncercare euat }
else Ck; kk-1;
{ revenire }

Pentru cazul particular X1=...=Xn={1,,s}, algoritmul se simplific astfel:


k1; xi0, i=1,...,n
while k>0
if k=n+1
then retsol(x); kk-1;
else if xk<s
then xkxk+1;
if k(x1,,xk)
then kk+1;
else
else xk0; kk-1;

{ revenire dup obinerea unei soluii }

{ atribuie i avanseaz }
{ ncercare euat }
{ revenire }

Exemple
n exemplele care urmeaz, k va fi notat n continuare prin cont(k). Se
aplic algoritmul de mai sus pentru diferite forme ale funciei de continuare.
1) Colorarea hrilor. Se consider o hart. Se cere colorarea ei folosind cel mult
n culori, astfel nct oricare dou ri vecine (cu frontier comun de lungime
strict pozitiv) s fie colorate diferit.
Fie xk culoarea curent cu care este colorat ara k.
function cont(k: integer): boolean;
b true; i 1;
while b and (i<k)
if vecin(i,k) & xi=xk
then b false
else i i+1
cont b
end;

2) Problema celor n dame


Se consider un caroiaj nn. Prin analogie cu o tabl de ah (n=8), se dorete
plasarea a n dame pe ptrelele caroiajului, astfel nct s nu existe dou dame una n
btaia celeilalte (adic s nu existe dou dame pe aceeai linie, coloan sau
diagonal).
Evident, pe fiecare linie vom plasa exact o dam. Fie xk coloana pe care este
plasat dama de pe linia k.
Damele de pe liniile i i k sunt:
- pe aceeai coloan: dac xi=xk ;
- pe aceeai diagonal: dac |xi-xk|=k-i.
function cont(k:integer): boolean;
b true; i 1;
while b and i<k
if |xi-xk|=k-i or xi=xk
then b false
else i i+1;
cont b
end;

3) Problema ciclului hamiltonian


Se consider un graf neorientat. Un ciclu hamiltonian este un ciclu care trece
exact o dat prin fiecare vrf al grafului.
Pentru orice ciclu hamiltonian putem presupune c el pleac din vrful 1. Vom
nota prin xi al i-lea vrf din ciclu.
x=(x1,...,xn) soluie dac:
x1=1 & {x2,...,xn}={2,...,n} & xn,x1 vecine.
Vom considera c graful este dat prin matricea sa de adiacen.
function cont(k:integer): boolean;
if a(xk-1,xk)=0
then cont false
else i 1; b true;
while b & (i<k)
if xk=xi then b false
else i i+1;
if k=n then b b a(xn,x1)=1;
cont b
end;

O descriere succint a metodei backtracking


Metoda backtracking poate fi descris astfel:
Backtracking = parcurgerea limitat *) n adncime a unui arbore
*)

conform condiiilor de continuare

Rolul condiiilor de continuare este ilustrat n figura ce urmeaz. Dac pentru


xk este aleas o valoare ce nu satisface condiiile de continuare, atunci la parcurgerea
n adncime este evitat parcurgerea unui ntreg subarbore.
x1

x2

xk

Variante
Variantele cele mai uzuale ntlnite n aplicarea metodei backtracking sunt
urmtoarele:
- soluia poate avea un numr variabil de componente
i/sau
- dintre ele alegem una care optimizeaz o funcie dat.
Exemplu. a=(a1,...,an)Zn. Caut un subir cresctor de lungime maxim.
Deci caut 1x1<...<xkn cu

k maxim
ax1 < ax2 <...< axk

Pentru n=8 i a=(1,4,2,3,7,5,8,6) va rezulta k=5.


Aici, soluia posibil: care nu poate fi continuat. Exemplu: (4,7,8).
Notm prin xf i kf soluia optim i lungimea sa.
Completez cu - i + : a0 -; nn+1; an +;
Funcia cont are urmtoarea form:
function cont(k)
cont axk-1<ak
end;

Procedura retsol are forma:

procedure retsol(k)
if k>kf then xfx; kfk;
end;

Algoritmul backtracking se modific astfel:

k1; x00; x10; kf0;


while k>0
if xk<n
then xkxk+1;
if cont(k)
then if xk=n
{ an=+ }
then retsol(k); kk-1
else kk+1; xkxk-1
else
else kk-1;

Observaie. Se face tot o parcurgere limitat n adncime a unui arbore.

Varianta recursiv
O descriem pentru nceput pentru X1=...=Xn={1,...,s}.
Apelul iniial este: back(1).
procedure back(k)
if k=n+1
then retsol
else for i=1,s
xki;
if cont(k)
then back(k+1); revenire din recursivitate
else
end.

Exemplu. Dorim s producem toate irurile de n paranteze ce se nchid corect.


Exist soluii n par.
Fie dif=nr( - nr). Condiiile sunt :
dif0 pentru k<n;
dif=0 pentru k=n.
Pornirea algoritmului backtracking se face prin:

a1( ; dif1; back(2);

Procedura back are urmtoarea form:


procedure back(k)
if k=n+1
then retsol
{scrie soluia}

else ak( ; dif++;


if dif n-k then back(k+1)
dif--;
ak); dif--;
if dif0 then back(k+1)
dif++;
end.

Observaie. n exemplul tratat backtracking-ul este optimal! : se avanseaz


dac i numai dac exist anse de obinere a unei soluii. Cu alte cuvinte, condiiile
de continuare nu sunt numai necesare, dar i suficiente.

Metoda backtracking n plan


Se consider un caroiaj (matrice) A cu m linii i n coloane. Poziile pot fi:
libere: aij=0;
ocupate: aij=1.
Se mai d o poziie (i0,j0). Se caut toate drumurile care ies n afara
matricii, trecnd numai prin poziii libere.
-

Variante:
cum pot ajunge ntr-o poziie (i1,j1) dat?
se cere determinarea componentelor conexe.

Micrile posibile sunt date printr-o matrice depl cu dou linii i ndepl
coloane. De exemplu, dac deplasrile permise sunt cele ctre poziiile
vecine situate la Est, Nord, Vest i Sud, matricea are forma:
1

0
1

1 0

0 1

Bordez matricea cu 2 pentru a nu studia separat ieirea din matrice.


Pentru refacerea drumurilor, pentru fiecare poziie atins memorm minte
legtura la precedenta.
Dac poziia e liber i pot continua, pun aij=-1 (a fost atins), continum
i apoi repunem aij0 (ntoarcere din recursivitate).
Programul n Java are urmtoarea form:

class elem {
int i,j; elem prec;
static int m,n,i0,j0,ndepl;
static int[][] mat;
static int[][] depl = { {1,0,-1,0}, {0,-1,0,1} };
static { ndepl = depl[0].length; }
elem() {
int i,j;
IO.write("m,n = "); m = (int) IO.read();
n = (int) IO.read();
// de fapt m+2,n+2
mat = new int[m][n];

for(i=1; i<m-1; i++)


for(j=1; j<n-1; j++) mat[i][j]
for (i=0; i<n; i++) {mat[0][i] =
for (j=0; j<m; j++) {mat[j][0] =
IO.write("i0,j0 = "); i0 = (int)
j0 = (int) IO.read();

= (int) IO.read();
2; mat[m-1][i] = 2; }
2; mat[j][n-1] = 2; }
IO.read();

}
elem(int ii, int jj, elem x) { i = ii; j = jj; prec = x; }
String print(elem x) {
if (x == null) return "(" + i + "," + j + ")";
else return x.print(x.prec)+" "+"("+i+","+j+")";
}

void p() {
elem x; int ii,jj;
for (int k=0; k<ndepl; k++) {
ii = i+depl[0][k]; jj = j+depl[1][k];
if (mat[ii][jj] == 1);
else if (mat[ii][jj] == 2) IO.writeln(print(prec));
else if (mat[ii][jj] == 0) {
mat[i][j] = -1; x = new elem(ii,jj,this);
x.p(); mat[i][j] = 0;
}
}
}

class DrumPlan {
public static void main(String[] args) {
new elem(); elem start = new elem(elem.i0,elem.j0,null);
start.p();
}
}

m,n =
1 1 0
0 1 0
1 0 0
i0,j0

Exemplu de test. Se introduc:

5 6
1
1
1
= 2 2

Contraexemplu. n plan, se cere s se determine toate punctele n care se


poate ajunge din (i0,j0), precum i numrul minim de deplasri pn la ele.
0
0

0
0

(i0,j0)

Soluia corect const n a folosi o coad!

3
0

2
1

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