2. Prezentare generală
Această metodă generală de programare se aplică problemelor în care soluţia se poate
reprezenta sub forma unui vector X = (x1, ..., xn)ÎS unde S = S1 x ... x Sn , unde mulţimile
S1, ...,Sn sunt mulţimi finite având |Si| = si elemente. Pentru fiecare problemă concretă sunt
date anumite relaţii între componentele x1 , ... xn ale vectorului X, numite condiţii interne.
Mulţimea finită S = S1 x S2 x... x Sn se numeşte spaţiul soluţiilor posibile (este un produs
cartezian). Soluţiile posibile care satisfac condiţiile interne se numesc soluţii rezultat.
Ceea ce ne propunem este de a determina toate soluţiile rezultat, cu scopul de a le afişa sau de a
alege dintre ele una care maximizează sau minimizează o eventuală funcţie obiectiv dată.
O metoda simplă de determinare a soluţiilor rezultat constă în a genera într-un mod oarecare
toate soluţiile posibile şi de a verifica dacă ele satisfac condiţiile interne. Dezavantajul constă în
faptul că timpul cerut de această investigare exhaustivă este foarte mare. Astfel, chiar pentru |Si|
= 2, " i, timpul necesar este de ordinul 2n, deci exponenţial.
Metoda backtracking urmăreşte să evite generarea tuturor soluţiilor posibile. În acest
scop, elementele vectorului X primesc pe rând valori în sensul că lui xk i se atribuie o valoare
numai dacă au fost atribuite deja valori lui x1 ,... xk-1 . Mai mult, odată o valoare pentru xn
stabilită, nu se trece direct la atribuirea de valori lui xk+1 , neîndeplinirea lor exprimând faptul
că oricum am alege xk+1,...,xn nu vom putea ajunge la o soluţie rezultat, adică o condiţie pentru
care condiţiile interne să fie satisfăcute. Evident că în cazul neîndeplinirii condiţiilor de
continuare va trebui să facem o altă alegere pentru xk sau dacă Sk a fost epuizat să micşorăm pe
k cu o unitate încercând să facem o nouă alegere pentru xk etc.; această micşorare a lui k dă
numele metodei, ilustrând faptul că atunci când nu mai putem avansa, urmărim înapoi secvenţa
curentă din soluţie. Este evident că între condiţiile de continuare şi condiţiile interne există o
strânsă legătură. O bună alegere pentru condiţiile de continuare are ca efect o importantă
reducere a numărului de calcule.
3. Reprezentarea
Metoda backtracking poate fi reprezentată uşor, pe un arbore
construit astfel:
- nivelul 1 conţine rădăcina;
- din orice vârf de pe nivelul k pleacă sk muchii spre nivelul k+1
etichetaţi cu cele sk muchii ale lui Sk.
Nivelul n+1 va conţine s1 × s2 × ... × sn vârfuri. Pentru fiecare
vârf de pe nivelul n+1, etichetele muchiilor conţinute pe drumul
ce leagă rădăcina de acest vârf reprezintă o soluţie posibilă.
Exemplu - Să considerăm problema submulţimilor de sumă
dată care constă în următoarele: Fie A = (a1, a2, ..., an) cu ai >
0, " i. Fie MÎR+. Se caută toate submulţimile B ale lui A pentru
care suma elementelor este M.
Pentru a putea realiza problema prin metoda backtracking vom
reprezenta soluţia sub forma x = (x1, ..., xn) unde xi = 1 dacă
aiÎB şi xi = 0 în caz contrar. Sa ne situăm în ipoteza ca n=4.
Arborele ataşat metodei backtracking este următorul:
4.
5. Probleme
Probleme de generare. Oportunitatea utilizării metodei backtracking
Problemele care se rezolvă prin metoda backtracking pot fi împărţite în mai multe
grupuri de probleme cu rezolvări asemănătoare, in funcţie de modificările pe care le
vom face în algoritm. Principalele grupuri de probleme sunt:
a) probleme în care vectorul soluţie are lungime fixă şi fiecare element apare o singură
dată în soluţie;
b) probleme în care vectorul soluţie are lungime variabilă şi fiecare element poate să
apară de mai multe ori în soluţie;
c) probleme în plan, atunci când spaţiul în care ne deplasăm este un tablou
bidimensional.
Vom prezenta în cele ce urmează câteva probleme care pac parte din primul grup.
Cele mai cunoscute sunt:
generarea permutărilor unei mulţimi
generarea aranjamentelor unei mulţimi
generarea submulţimilor unei mulţimi
generarea submulţimilor cu m elemente ale unei mulţimi (combinări)
generarea produsului cartezian a n mulţimi
generarea tuturor secvenţelor de n (par) paranteze care se închid corect.
colorarea ţărilor de pe o hartă astfel încât oricare două ţări vecine să aibă culori diferite
aranjarea a n regine pe o tablă de şah de dimensiune n fără ca ele să se atace.
Toate problemele din acest grup au particularitatea că soluţia se obţine atunci când
vectorul soluţie ajunge să conţină un anumit număr de elemente.
Vom exemplifica modul de lucru al metodei backtracking pentru problema damelor.
6. Exemplu
Aranjarea reginelor. Dându-se o tablă de şah de dimensiune nxn (n>3) să se aranjeze pe ea n
regine fără ca ele să se atace. Reamintim că o regină atacă linia, coloana şi cele 2 diagonale pe
care se află. În figura de mai jos celulele colorare mai închis sunt atacate de regina poziţionată
unde indică litera “R”.
În algoritmul de mai sus avem de particularizat următoarele:
Instrucţiunea pentru fiecare valoare i din mulţimea Sk execută va fi înlocuită cu o
instrucţiune pentru care parcurge toate valorile de la 1 până la n.
Condiţia de a putea plasa o regină pe poziţia k este un pic mai complicată şi presupune
verificarea ca să nu se atace cu nici una dintre celelalte k-1 regine deja plasate pe tabla. Dacă pe
poziţia k din vectorul X punem o valoare ea va reprezenta coloana pe care se plasează pe tablă
regina k. Condiţiile devin astfel:
x[i]¹x[k] şi |k-i|¹|x[k]-x[i]| cu i de la 1 la k-1 şi |x| reprezentând modului lui x.
Condiţia de soluţie este simplă şi presupune plasarea corectă a tuturor celor n regine.
Programele Pascal si C++ rezultate prin implementarea algoritmului descris mai sus
următoarele:
7. var x:array[1..100] of byte;
n:byte;
nrsol:word;
procedure scriesol;
var i,j:byte;
begin
inc(nrsol);
writeln('Solutia a',nrsol,'este');
for i:=1 to n do begin
writeln;
for j:=1 to n do if x[j]=i then write('X',' ') else write('O',' ');
end;
end;
function potcont(k:byte):boolean;
var i:byte;
atac:boolean;
begin
atac:=false;
for i:=1 to k-1 do
if(x[i]=x[k]) or (k-i=abs(x[k]-x[i])) then atac:=true;
potcont:=not atac;
end;
procedure back(k:byte);
var i:byte;
begin
for i:=1 to n do
begin
x[k]:=i;
if potcont(k) then
if k=n then scriesol
else back(k+1);
end;
end;
begin
read(n);
nrsol:=0;
back(1);
writeln(nrsol,'solutii');
end.