Sunteți pe pagina 1din 16

Backtracking sau tehnica cautarii cu revenire

Principalele si cele mai cunoscute aplicatii:


 Generarea permutarilor
 Problema plasarii reginelor pe tabla de sah
 Colorarea hartilor
 Jocul sudoku
 Gasirea traseelor care unesc doua locatii
 Problema labirintului

Definitii
1. Este o strategie de cautare sistematica in spatiul solutiilor unei probleme.
Se utilizeaza in special pentru rezolvarea problemelor a caror cerinta este
de a determina configuratii care satisfac anumite restrictii (probleme de
satisfacere a restrictiilor).
2. Backtracking este parcurgerea limitata (conform conditiilor de
continuare) in adancime a unui arbore.
Spatiul solutiilor este organizat ca un arbore:
 un varf este viabil daca sunt sanse sa se gaseasca o solutie
explorand subarborele cu radacina in acel varf
 sunt explorati numai subarborii cu radacini viabile
3. Metoda backtracking se poate folosi pentru problemele in care trebuie sa
se genereze toate solutiile, o solutie a problemei putand fi data de un
vector: S = {x1, x2, …, xn} ale carui elemente apartin, fiecare, unor
multimi finite Ai (xi∈Ai), iar asupra elementelor unei solutii exista
anumite restrictii, specifice problemei care trebuie rezolvata, numite
conditii interne. Multimile Ai sunt multimi ale caror elemente sunt in
relatii bine stabilite. Multimile Ai pot sa coincida – sau nu. Pentru a gasi
toate solutiile unei astfel de probleme folosind o metoda clasica de
rezolvare, se executa urmatorul algoritm:
PAS1. Se genereaza toate elementele produsului cartezian A1 x A2 x A3
x … x An.
PAS2. Se verifica fiecare element al produsului cartezian, daca
indeplineste conditiile interne impuse ca sa fie solutie a problemei.
4. Metoda backtracking construieste progresiv vectorul solutiei, pornind de
la primul element si adaugand la vector urmatoarele elemente – cu
revenire la elementul anterior din vector – in caz de insucces. Elementul
care trebuie adaugat se cauta in multime, printre elementele care respecta
conditiile interne.

Idei de baza:
Principiul tehnicii poate fi vizualizat folosind un arbore de parcurgere care
ilustreaza modul in care se viziteaza spatial solutiilor:
Radacina arborelui corespunde starii initiale (inainte de a incepe completarea
componentelor)
 Un nod intern corespunde unei solutii partiale valide
 Un nod extern (frunza) corespunde:
o fie unei solutii partiale invalide
o fie unei solutii finale
Solutiile sunt construite in maniera incrementala prin gasirea succesiva a
valorilor potrivite pentru componente (la fiecare etapa se completeaza o
componenta; o solutie in care doar o parte dintre componente sunt completate
este denumita solutie partiala).
Fiecare solutie partiala este evaluate cu scopul de a stabili daca este valida
(promitatoare, viabila). O solutie partiala valida poate conduce la o solutie
finala pe cand una invalida incalca restrictiile partiale, insemnand ca nu va
conduce niciodata la o solutie care sa satisfaca toate restrictiile problemei.
•Daca nici una dintre valorile corespunzatoare unei componente nu conduce la o
solutie partiala valida, atunci se revine la componenta anterioara si se incearca
alta valoare pentru aceasta.

Mecanismul metodei backtracking


Solutiile problemei sunt generate intr-un tablou X de tip stiva:
 se alege din mulþimea M1, elementul valid x1;
 se presupune ca in tabloul X s-au depus elementele x1∈ M1, x2∈ M2, …,
xk-1∈ Mk-1 si se cauta prima valoare candidat la construirea solutiei xk∈
Mk;
 elementul xk pastreaza valoarea candidat doar daca aceasta verifica
conditiile de validare (conditiile interne);
 daca xk a fost depus in tabloul X, se verifica daca s-a obtinut o solutie
rezultat, in caz afirmativ solutia este pastrata si se trece la construirea
unei alte solutii, altfel se incrementeaza k (stiva creste) si se cauta o
valoare candidat pentru xk+1 din multimea Mk+1;
 daca in multimea Mk nu se mai gasesc valori valide (s-au testat toate
valorile) se decrementeaza k (stiva scade) si se cauta o alta valoare pentru
xk-1 din multimea Mk-1;
 algoritmul se opreste atunci cand in multimea M1 nu mai exista nicio
valoare candidat pentru x1 (cand stiva este vida).

Memorarea elementelor
Pentru a memora elementele xk ale solutiei se foloseste o structura de
date de tip vector.
Pentru indicele elementului care se adauga la solutie se foloseste variabila
k. Cand s-a gasit elementul xk al solutiei, se trece la pasul urmator, prin
incrementarea indicelui cu 1 (k++), pentru a cauta elementul xk+1 al solutiei,
iar pentru a reveni la elementul xk-1 al solutiei se decrementeaza indicele cu 1
(k--). Initial, vectorul va avea dimensiunea 1 (kmin=1), corespunzatoare pozitiei
de pornire, si va contine valoarea primului element al solutiei. Pentru elementul
xk al solutiei se va atribui o valoare din multimea Ak care poate fi un element al
solutiei: s[k]=a[i]. Dupa parcurgerea completa a multimilor Ak, vectorul va
avea dimensiunea n (kmax=n), corespunzatoare numarului de elemente ale
solutiei.
Indicele vectorului va fi initial 1, la gasirea unei solutii va avea valoarea
n, iar la terminarea algoritmului indicele va avea valoarea 0. Ne putem inchipui
vectorul solutie ca o constructie pe verticala, in care fiecare element k al solutiei
reprezinta un nivel k care se adauga la aceasta constructie. Solutia se
construieste de la nivelul 1 pana la nivelul n.

Se mai folosesc urmatoarele variabile de memorie:


 as – pentru a sti daca pentru elementul xk al solutiei mai exista un
succesor, adica daca mai exista un element in multimea Ak care ar putea
fi elementul xk al solutiei (este o variabila logica ce are valoarea 1 – true,
daca exista succesor; altfel, are valoarea 0 – false);
 ev – pentru a sti daca succesorul gasit respecta conditia de continuare si
poate fi elementul xk al solutiei (este o variabila logica ce are valoarea 1
– true, daca succesorul este element al solutiei; altfel, are valoarea 0 –
false)
 n – pentru dimensiunea solutiei (numarul de elemente ale solutiei, in
cazul problemelor in care toate solutiile au acelasi numar de elemente).

Pentru simplificarea implementarii, toate aceste date si structuri de date sunt


declarate globale deoarece valoarea indicelui elementului care se adauga la
solutie se va transmite mai usor – intre subprograme – ca variabila globala.

Subprograme
Algoritmul va fi implementat prin: un subprogram, care va fi acelasi pentru
toti algoritmii de rezolvare prin metoda backtracking (parte fixa) – si care
descrie strategia generala backtracking – si subprograme care au aceeasi
semnificatie pentru toti algoritmii, dar al caror continut difera de la o problema
la alta, depinzand de conditiile interne ale solutiei.

Subprogramul init (functie procedurala). Se initializeaza elementul care


urmeaza sa se adauge la solutie (elementul de pe nivelul k). Acest element se
initializeaza cu o valoare care nu face parte din multimea Ak considerata,
urmand ca in urmatorul pas al algoritmului sa se atribuie acestui element prima
valoare din multimea Ak. Valoarea returnata de functie se va atribui variabilei
as. Initial, valoarea variabilei de memorie as este 1 (true) – se presupune ca mai
exista un succesor.

Subprogramul succesor (functie operand). Verifica daca mai exista in


multimea Ak un element pentru nivelul k al solutiei (un succesor). Daca mai
exista un succesor, se trece la urmatorul element din multimea Ak, iar functia va
returna valoarea 1 (true). Daca nu mai exista un succesor, functia va returna
valoarea 0 (false).Valoarea returnata de functie se va atribui variabilei ev.
Initial, valoarea variabilei de memorie ev este 0 (false) – se presupune ca
succesorul gasit nu este elementul k al solutiei.

Subprogramul valid (functie operand). Verifica daca valoarea atribuita


elementului xk al solutiei indeplineste conditia de continuare, adica poate fi
considerata ca face parte din solutia problemei (daca succesorul gasit este
element al solutiei). Daca este indeplinita conditia (se evalueaza expresia prin
care este descrisa conditia), functia va returna valoarea 1 (true); altfel, va
returna valoarea 0 (false). Valoarea returnata de functie se va atribui variabilei
ev. Initial, valoarea variabilei de memorie ev este 0 (false) – se presupune ca
succesorul gasit nu este elementul k al solutiei

Subprogramul solutie (functie operand). Verifica daca s-au obtinut toate


elementele solutiei. Subprogramul solutie va verifica daca au fost gasite toate
cele n elemente ale solutiei. Daca s-a gasit solutia, subprogramul intoarce
valoarea 1 (true); altfel, intoarce valoarea 0 (false).

Subprogramul tipar (functie procedurala). Afiseaza elementele solutiei. De


obicei, afisarea solutiei consta in afisarea valorilor din vectorul solutie

Algoritmul de rezolvare a problemelor cu backtracking poate fi recursiv


sau iterative:
Recursiv:
Iterativ
EXEMPLE DE PROBLEME

1. Permutari - Se citeste un număr natural n. Să se genereze toate


permutările mulţimii {1, 2, …, n}. Pentru n=3
 Solutie: x = (x1, x2,…, xn)
 Condiţii interne: pentru orice i≠j, xi≠xj
 Condiţii de continuare: pentru orice i<k, xi≠xk
#include <iostream>
using namespace std;
int n , x[100];
void afisare(int k){
for(int i = 1 ; i <= k ; i ++)
cout << x[i] << " ";
cout << endl;
}
bool OK(int k){
for(int i = 1 ; i < k ; i ++)
if(x[i] == x[k])
return 0;
return 1;
}
void back()
{
int k = 1;
x[1] = 0;
while(k > 0)
{
bool gasit = false;
do{
x[k] ++;
if(x[k] <= n && OK(k))
gasit = true;
}
while(! gasit && x[k ] <= n);
if(! gasit)
k --;
else
if(k < n)
{
k ++;
x[k] = 0;
}
else
afisare(k);
}
}

int main(){
cin >> n;
back();
return 0;
}
2. Problema reginelor - Fiind data o tabla de sah de dimensiune n x n, se
afiseze toate solutiile de aranjare a n regine astfel incat sa nu atace (sa nu
se afle doua regine pe aceeasi linie, coloana sau diagonala). Pt n=4.
 Solutie: x = (x1, x2,…, xn), xk = coloana pe care este plasatǎ regina de pe
linia k
 Condiţii interne: pentru orice i≠j, xi≠xj și |xi-xj|≠|j-i|.
 Condiţii de continuare: pentru orice i<k, xi≠xk și |xi-xk|≠k-i
#include<iostream.h>
#include <math.h>
int n,v[100],sol;
void afisare()
{int i,j,x;
sol++; cout<<"\n Solutia: "<<sol<<'\n';
for (i=1;i<=n;i++)
{for (j=1;j<=n;j++)
if (v[i]==j) cout<<"D ";
else cout<<"_ ";
cout<<'\n';
}
}
int valid(int k)
{int i;
for (i=1;i<=k-1;i++)
if ((v[i]==v[k])||(abs(v[k]-v[i])==(k-i)))
return 0;
return 1;}
int solutie(int k)
{if (k==n)
return 1;
return 0;}
void BK(int k)
{int i;
for (i=1;i<=n;i++)
{v[k]=i;
if (valid(k)==1)
{if (solutie(k)==1)
afisare();
else
BK(k+1);
}
}
}
void main()
{cout<<"n= ";cin>>n;
BK(1);
}
3. Colorarea hartilor - Se considera o harta. Se cere colorarea ei folosind cel
mult 4 culori, astfel incat oricare doua tari vecine sa fie colorate diferit.
 Soluţie: x = (x1, x2,…, xn), unde xk = culoarea curentă cu care este
coloratǎ ţara k, xk ∈ {1,2,3,4} (pk = 1, uk = 4).
 Condițiile interne: xi ≠ xj pentru orice două țări vecine i și j.
 Condiții de continuare: xi ≠ xk pentru orice țarăi∈{1,2,…,k-1} vecină cu
ţara k.
 Folosim o matrice a pentru a memora relaţia de vecinătate dintre ţări:
 A[i][j] este:
a. 1, dacă i şi j sunt ţări vecine
b. 0, dacă i şi j nu sunt ţări vecine
#include<iostream.h>
#include<math.h>
typedef int sir[100];
sir x;
int m,i,k,n,j;
int as,ev;
int a[100][100];
void succ(sir x, int k, int &as)
{
if(x[k]<n)
 {
  as=1;
  x[k]=x[k]+1;
 }
else as=0;
}
void valid(sir x, int k, int &ev)
{
ev =1;
for(i=1;i<=k-1;i++)
  if((a[k][i]==1)&&(x[k]==x[i]))
  ev=0;
}
void afis(sir x,int k)
{
int i;
for(i=1;i<=k;i++)
  cout<<x[i]<<" ";
cout<<endl;
}
int main(void)
{
cout<<"Dati numarul de tari:";
cin>>n;
for(i=1;i<=n;i++)
  for(j=1;j<=n;j++)
 {
  cin>>a[i][j];
  a[j][i]=a[i][j];
 }
k=1;
x[k]=0;
while(k>0)
 {
    do
      {
        succ(x,k,as);
        if(as) valid(x,k,ev);
      }
    while(as&&!ev);
    if(as)
        if(k==n) afis(x,k);
        else
 {
    k=k+1; x[k]=0;
 }
    else k=k-1;
    }
}

4. Problema labirintului
Se considerăun labirint definit pe o grilănxn. Săse găseascătoate traseele
care pornesc din (1,1) și se terminăîn (nxn)
 Solutie: S=(s1,…,sn) cu skreprezentand indicii celulei vizitate la etapa k
 Multimile A1,…,Ansunt submultimi ale multimii {1,2,…,n}x{1,2,…,n}.
Pentru fiecare celula(i,j) existaun set de 4 vecini: (i,j-1), (i,j+1), (i-1,j),
(i+1,j)
 Conditii de continuare: o solutie partiala(s1,s2,…,sk) trebuie sasatisfaca:
o sk!= sq pentru orice q in {1,2,…,k-1}
o M(sk)=0 sk-1si sksunt celule vecine
 Conditia ca o solutie partiala(s1,…,sk) safie solutie finala: sk= (n,n)

#include<iostream>
#include<fstream>
using namespace std;
int st[20][20],i,m,n,v[20][20],a[20][20];
void tipar(int k)
{for(i=1;i<=k;i++)
cout<<st[i][1]<<” „<<st[i][2]<<” „;
cout<<endl;}
int valid(int k)
{if(v[st[k][1]][st[k][2]]==0)
return 0;
for(i=1;i<k;i++)
if((st[k][1]==st[i][1])&&(st[k][2]==st[i][2]))
return 0;
return 1;
}
int solutie(int k)
{return((st[k][1]==1)||(st[k][1]==m)||(st[k][2]==1)||(st[k][2]==n));}
void bk(int k)
{int val;
for(val=1;val<=4;val++)
{st[k][1]=st[k-1][1]+a[val][1];
st[k][2]=st[k-1][2]+a[val][2];
if(valid(k))
if(solutie(k))
tipar(k);
else
bk(k+1);}}
int main()
{int j,k,r;
fstream f(„labirint.in”);
f>>m>>n;
for(i=1;i<=m;i++)
for(j=1;j<=n;j++)
f>>v[i][j];
for(i=1;i<=4;i++)
for(j=1;j<=2;j++)
f>>a[i][j];
cin>>k;cin>>r;
st[1][1]=k;
st[1][2]=r;
bk(2);}
Bibliografie:
 Informatica: manual pentru clasa a XI-a / Mioara Gheorghe (coord.),
Monica Tataram, Corina Achinca, Constanta Nastase. – Ed. a 3-a. –
Bucuresti: Corint, 2008
 Lectii de pregatire admitere 2017, prof Florentin Ipate
 Manual de informatica intensive, clasa a 11 a, Vlad Hutan, Tudor Sorin,
Bucuresti, Editura L&S Soft, 2006
 Aplicatii elementare cu arbori, Computer Science and Electrical
Engineering Department Adrian FLOREA
 Tehnici de programare, Editura didactica si pedagocica

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