Sunteți pe pagina 1din 5

9.

METODA BACKTRACKING ( ITERATIVĂ SAU RECURSIVĂ )


9.1. Prezentare generală
Metoda backtracking se aplică problemelor pentru care soluţia se poate reprezenta sub
forma unui vector X = ( x 1 , x 2 , ... , x n ) ∈ S. S este mulţimea soluţiilor posibile ale problemei.
S = S 1 × S 2 ... × S n, unde S i sunt mulţimi finite, având s i elemente şi x i ∈ S i , pentru i = 1 ... n.
Pentru fiecare problemă se dau anumite relaţii între componentele vectorului X, numite
condiţii interne. Soluţiile posibile care satisfac condiţiile interne se numesc soluţii rezultat.
Metoda de generare a tuturor soluţiilor posibile şi apoi de determinare a soluţiilor rezultat
prin verificarea îndeplinirii condiţiilor interne este mare consumatoare de timp. Metoda
backtracking evită această generare şi este deci mai eficientă.
Elementele vectorului X primesc pe rând valori, în ordinea crescătoare a indicilor lor ( lui x
k i se atribuie o valoare numai dacă au fost atribuite valori elementelor x 1 , x 2 , ... , x k–1 ). La
atribuirea valorii lui x k se verifică îndeplinirea unor condiţii de continuare, referitoare la x 1 , x 2 , ...
, x k–1. Neîndeplinirea acestor condiţii la pasul k exprimă faptul că oricum s-ar alege valorile x k + 1
, ... , x n nu se va putea ajunge la o soluţie rezultat.
Metoda backtracking construieşte un vector soluţie în mod progresiv, începând cu prima
componentă a vectorului şi mergând spre ultima, cu eventuale reveniri asupra valorilor atribuite
anterior.
Metoda se aplică astfel :
– se alege prima valoare din S1 şi i se atribuie lui x 1
– se presupun generate elementele x 1 , x 2 , ... , x k–1 , cu valori din S 1 , S 2 , ... , S k – 1 ; pentru
generarea elementului x k se alege primul element disponibil din S k ; pentru valoarea aleasă se
testează îndeplinirea condiţiilor de continuare; pot apare următoarele situaţii :
– x k îndeplineşte condiţiile de continuare; se verifică dacă s-a ajuns la soluţia finală ( k = n )
şi în caz afirmativ se tipăreşte soluţia obţinută; dacă nu s-a ajuns la soluţia finală se trece la
generarea elementului următor ( x k+1 )
– x k nu îndeplineşte condiţiile de continuare; se încearcă următoarea valoare disponibilă din
S k ; dacă nu există nici o valoare în S k care să îndeplinească condiţiile de continuare se revine la
elementul x k–1 şi se reia algoritmul pentru o nouă valoare a acestuia.
– algoritmul se încheie când au fost luate în consideraţie toate elementele mulţimii S 1.
Problemele rezolvate prin backtracking necesită timp mare de execuţie. Din acest motiv este
bine să se utilizeze metoda numai dacă nu există alt algoritm la dispoziţie.
Dacă mulţimile S 1 , S 2 , ... , S n au toate acelaşi număr k de elemente, timpul necesar
execuţiei algoritmului backtracking este k n. Dacă mulţimile S 1 , S 2 , ... , S n nu au toate acelaşi
număr de elemente, atunci se consideră m = min { | S 1 | , | S 2 | , ... , | S n | } şi M = max { | S 1 | , |
S 2 | , ... , | S n | }. Timpul necesar execuţiei este cuprins între m n şi M n. Metoda backtracking are
deci complexitate exponenţială, fiind în cele mai multe cazuri ineficientă. Ea însă nu poate fi
înlocuită cu alte variante de rezolvare mai rapide în situaţia în care se cere determinarea tuturor
soluţiilor unei probleme.
Probleme care se rezolvă prin backtracking sunt: problema damelor, problema colorării unei
hărţi, generarea elementelor combinatoriale etc.
Procedura generală backtracking este:
k=1
x[k]=0 // configuraţia iniţială
while(k>0) // nu s-a ajuns la configuraţia finală
{ while(Sk≠∅ ) // nu s-au epuizat toate valorile din Sk
{ xk←cel mai mic element din Sk
Sk=Sk-{xk}
if cc(k) // testează dacă xk îndeplineşte
// condiţiile de continuare
if(k==n) afisare() // afişează soluţia generată
// complet
else { k=k+1; // avans la poziţia următoare
xk=0; } // iniţializare element următor
}
k=k-1; // revenire la poziţia anterioară }
}
Funcţia cc verifică dacă valorile primelor k componente ale vectorului X îndeplinesc
condiţiile de continuare impuse de problemă.

9.2. Probleme de generare. Oportunitatea utilizării metodei backtracking


Problema celor opt regine
Fiind dată o tablă de şah de dimensiune nxn, se cer toate posibilităţile de aranjare a n
dame, astfel încât două dame oarecare să nu se atace reciproc.
Soluţia problemei se reprezintă sub forma unui vector X cu n elemente. x k conţine numărul
de ordine al coloanei pe care este plasată dama de pe linia k. Neatacul pe linii este sigurat de
unicitatea indicilor din X. Neatacul pe coloane pentru regina din poziţia ( k , x [ k ] ) se exprimă
prin x [ k ] ≠ x [ j ] ( ∀ j ≠ k ). Neatacul pe diagonală al reginei din poziţia ( k , x [ k ] ) se
exprimă prin :
| k – j | ≠ | x [ k ] – x [ j ] ( ∀ j ≠ k ).
Varianta iterativã :
#include<iostream.h>
#include<conio.h>
#include<math.h>
int x[20],nr,n;

int cc(int k)
{ for(int i=1;i<=k-1;i++)
if(x[i]==x[k] || abs(x[i]-x[k])==k-i) return 0;
return 1; }

void afisare()
{ nr++;
cout<<"Solutia "<<nr<<": "<<endl;
for(int i=1;i<=n;i++)
{ for(int j=1;j<=n;j++) if(x[i]==j) cout<<"R ";
else cout<<"* ";
cout<<endl; }
cout<<endl; }

void regine()
{ int k=1; x[k]=0;
while(k>0) { while(x[k]<n) { x[k]++;
if(cc(k)) if(k==n) afisare();
else x[++k]=0; }
k--; } }

void main()
{ clrscr();
cout<<"n="; cin>>n;
regine();
getch();
}

Varianta recursivã :
#include<iostream.h>
#include<conio.h>
#include<math.h>
int x[20],nr,n;

int cc(int k)
{ for(int i=1;i<=k-1;i++)
if(x[i]==x[k] || abs(x[i]-x[k])==k-i) return 0;
return 1; }

void afisare()
{ nr++;
cout<<"Solutia "<<nr<<": "<<endl;
for(int i=1;i<=n;i++)
{ for(int j=1;j<=n;j++) if(x[i]==j) cout<<"R ";
else cout<<"* ";
cout<<endl; }
cout<<endl; }

void regine(int k)
{ if(k>n) afisare();
else { x[k]=0;
while(x[k]<n) { x[k]++;
if(cc(k)) regine(k+1); } } }

void main()
{ clrscr(); cout<<"n="; cin>>n;
regine(1); getch(); }

Generarea partiţiilor unui număr natural


Se dă un număr natural n. Să se determine toate partiţiile numărului n.
Printr-o partiţie a unui număr natural n se înţelege descompunerea sa în sumă de numere
naturale strict pozitive. Problema se reduce la determinarea valorilor n 1 , n 2 , ... , n k , cu
proprietatea că n = n 1 + n 2 + ... + n k , cu n i ∈ N* şi k >1. Pentru a obţine partiţii distincte ( care să
difere prin cel puţin o valoare una de cealaltă ), şirul ( n i ) va fi generat ordonat crescător. Fiecare
element n k va avea valori cuprinse între n k – 1 şi n – suma primilor k – 1 termeni din şir.

Varianta iterativã :
#include<fstream.h>
#include<conio.h>
ofstream f("part_it.out");
int x[20],n;

int suma(int k)
{ int s=0;
for(int i=1;i<=k;i++) s+=x[i];
return s; }

void afisare(int k)
{ f<<n<<" = "<<x[1];
for(int i=2;i<=k;i++) f<<" + "<<x[i];
f<<endl; }
void partitii()
{ int k=1; x[k]=0;
while(k>0) { while(x[k]<n-suma(k-1))
{ x[k]++;
if(n==suma(k)) { if(k>1) afisare(k); }
else x[++k]=x[k-1]-1; }
k--; } }

void main()
{ clrscr();
cout<<"n="; cin>>n;
partitii();
f.close(); }

Varianta recursivã :
#include<fstream.h>
#include<conio.h>
ofstream f("part_it.out");
int x[20],n,val;

void init(int k)
{ if(k==1) x[k]=0;
else x[k]=x[k-1]-1; }

void afisare(int k)
{ f<<n<<" = "<<x[1];
for(int i=2;i<=k;i++) f<<" + "<<x[i];
f<<endl; }

void partitii(int k)
{ if(val==n) afisare(k-1);
else { init(k);
while(x[k]<n-val) { x[k]++;
val=val+x[k];
partitii(k+1);
val=val-x[k]; } } }

void main()
{ clrscr();
cout<<"n="; cin>>n;
partitii(1);
f.close(); }

Plata unei sume cu bancnote de valori date


Fiind dată o sumă de bani s, se cer toate modalităţile în care ea poate fi achitată cu
monezile disponibile.
Sunt dsiponibile n tipuri de monezi. Pentru fiecare tip k, valoarea unei monezi este a [ k ],
iar numărul de monezi disponibile de acest tip este nr [ k ].

Varianta iterativã :
#include<iostream.h>
int a[10],x[10],nr[10],i,n,s;

void afisare(int k)
{ int i;
for(i=1;i<=k;i++)
if(x[i]!=0) cout<<x[i]<<" * "<<a[i]<<" lei; ";
cout<<endl; }
int suma(int k)
{ int i,s=0;
for(i=1;i<=k;i++) s=s+x[i]*a[i];
return s; }

void main()
{ cout<<"Suma de platit "; cin>>s;
cout<<"Tipuri de monezi "; cin>>n;
for(i=1;i<=n;i++)
{ cout<<"Valoarea si cantitatea disponibila: ";
cin>>a[i]>>nr[i]; }
i=1; x[i]=-1;
while(i>0)
{ while(x[i]<nr[i]) { x[i]=x[i]+1;
if(suma(i)==s) afisare(i);
else if(i<n) { i=i+1;
x[i]=-1; } }
i=i-1; }
}

Varianta recursivã :
#include<iostream.h>
int a[10],x[10],nr[10],i,n,s,suma;

void afisare()
{ int i;
for(i=1;i<=n;i++)
if(x[i]!=0) cout<<x[i]<<" * "<<a[i]<<" lei; ";
cout<<endl; }

void citire()
{ cout<<"Suma de platit "; cin>>s;
cout<<"Tipuri de monezi "; cin>>n;
for(i=1;i<=n;i++)
{ cout<<"Valoarea si cantitatea disponibila: ";
cin>>a[i]>>nr[i]; } }

void plata(int i)
{ if(i>n) { if(suma==s) afisare(); }
else { x[i]=-1;
while(x[i]<nr[i]) { x[i]=x[i]+1;
if(suma+x[i]*a[i]<=s)
{ suma=suma+x[i]*a[i];
plata(i+1);
suma=suma-x[i]*a[i]; } } } }

void main()
{ citire(); plata(1); }

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