Metoda BACKTRACKING prof. Jiduc Gabriel
Un algoritm backtracking este un algoritm de căutare sistematică și exhausivă a tuturor soluțiilor posibile, dintre care se poate alege apoi soluția optimă. Problemele rezolvabile prin metoda backtracking sunt probleme ale căror soluții pot fi exprimate prin vectori de întregi și pot fi enunțate ca probleme: de decizie de enumerare a tuturor soluțiilor de optimizare Totuși, din cauza complexității foarte mari a algoritmului (consum mare de resurse ale calculatorului și timp mare), metoda se recomandă problemelor pentru care nu se cunoaște un algoritm mai eficient sau problemelor de dimensiuni relativ mici (sub 30-50 valori rulate)
Ex: problema are ca date o mulțime de numere naturale M și o valoare naturală val, enunțuri posibile sunt: Să se decidă dacă există o submulțime M a lui M astfel încât suma elementelor ei să fie egală cu val (problemă de decizie) Să se determine toate submulțimile M ale lui M care îndeplinesc condiția ca suma elementelor lor să fie egală cu val (problemă de enumerare) Să se determine o submulțime M a lui M astfel ca suma elementelor din M să aibă valoarea cea mai apropiată de val (dar nu mai mare ca val) (problemă de optimizare)
Căutarea exhausivă a vectorilor soluție este un proces de căutare cu revenire. Acest proces seamănă cu căutarea unui drum către o anumită țintă pe o rețea de drumuri cu ramificații (fără indicatoare) pentru cineva care nu cunoaște zona și traseul. La fiecare ramificație de drumuri persoana alege un drum la întâmplare, dar dacă ulterior acest drum se dovedește greșit, atunci ea revine la ultima bifurcație și încearcă alt traseu Soluțiile acestor probleme au forma unor vectori V ale căror componente satisfac anumite condiții specifice problemei (în general elementele soluției sunt numere naturale dintr-o mulțime finită) Metoda backtracking construiește progresiv un vector soluție, începând cu V[1] și continuând cu V[2], V[3],..,V[n]. Pentru fiecare componentă a vectorului soluție V se încearcă toate variantele posibile 1, 2, 3,..., m.
Ex: determinarea permutărilor de n obiecte (pt n=3, valorile disponibile sunt 1, 2, 3 și nu trebuie să se repete) X[1] X[2] X[3] Observații 1 1 1 nu convine 1 2 1 2 1 nu convine 1 2 2 nu convine 1 2 3 soluție (se reține) 1 2? Nu mai sunt valori disponibile (se întoarce la pasul anterior) 1 3 1 3 1 nu convine 1 3 2 soluție (se reține ) 1 3 3 nu convine 1 3? Nu mai sunt valori disponibile (se întoarce la pasul anterior) 1? Nu mai sunt valori disponibile (se întoarce la pasul anterior) 2 2 1 2 1 1 nu convine 2 1 2 nu convine 2 1 3 soluție (se reține și se revine la pasul anterior) 2 1? Nu mai sunt valori disponibile (se întoarce la pasul anterior) 2 2 nu convine 2 3 2 3 1 soluție (se reține) 2 3 2 nu convine 2 3 3 nu convine 2 3? Nu mai sunt valori disponibile (se întoarce la pasul anterior) 2? Nu mai sunt valori disponibile (se întoarce la pasul anterior) 3 3 1 3 1 1 nu convine 3 1 2 soluție (se reține) 3 1 3 nu convine 3 1? Nu mai sunt valori disponibile (se întoarce la pasul anterior) 3 2 3 2 1 soluție (se reține) 3 2 2 nu convine 3 2 3 nu convine 3 2? Nu mai sunt valori disponibile (se întoarce la pasul anterior) 3 3 nu convine 3? Nu mai sunt valori disponibile (se întoarce la pasul anterior)? Nu mai sunt valori disponibile (se întoarce la pasul anterior) =problema se încheie
Potrivit metodei în pasul k al determinării soluției se încearcă pentru v[k] pe rând toate valorile posibile 1, 2,...m și pentru fiecare se verifică respectarea condițiilor impuse. Pot apărea două situații: Există o valoare pentru vk], care împreună cu valorile anterioare ale vectorului soluție v să respecte condițiile date. În acest caz se continuă cu determinarea componentei următoare a lui v, v[k+1] Nu există nici o valoare pentru v[k] care să respecte condițiile date; în acest caz se revine la componenta anterioară v[k-1] și se încearcă următoarea valoare pentru ea (dacă mai există) Schema generală a metodei Backtracking Pentru o simplificare a rezolvării și o înțelegere mai rapidă se vor utiliza mai multe funcții Funcția void back(int k)-funcția pivot a programului, care implementează strategia de generare a vectorilor candidați și selectare a vectorilor soluție, corespunzătoare pasului k al soluției Funcția void prel_sol()-care prelucrează soluția obținută: O afișează O compară cu soluția optimă parțială, etc.
Funcția int posibil(int k)-determină dacă valoarea atribuită lui V[k] este validă (îndeplinește condițiile impuse de problemă) Funcția void include(int k)-adaugă soluției parțiale elementul V[k] (ex: când trebuie calculată suma elementelor- s=s+v[k];) Funcția void excude(int k)-elimină din soluția parțială elementul V[k], la întoarcerea de la palierul superior (k+1), când trebuie încercată altă valoare pentru V[k]. (ex: pentru problema dată- s=s-v[k];) În unele situații (probleme), operațiile fiind foarte simple, aceste funcții conțin 1-2 instrucțiuni sau pot fi înlocuite cu instrucțiuni scrise direct în funcția bază BACK Metoda backtracking are o implementare atât iterativă cât și una recursivă Datorită simplității, în continuare se prezintă algoritmul general recursiv al metodei (nu este general valabil!)
void back(int k) int i; if(k>n) prel_sol(); else for(i=1;i<=m;i++) v[k]=i; if(posibil(k)) include(k); back(k+1); exclude(k); Observație: în exemplu, s-a considerat că valorile posibile pentru vectorul soluție aparțin mulțimii 1, 2,..., m
Determinarea permutărilor unei mulțimi cu n elemente #include<iostream.h> #include<conio.h> int v[10],n; void prel_sol() int i; for(i=1;i<=n;i++) cout<<v[i]<<"\t"; cout<<endl; int posibil(int k) int i; for(i=1;i<k;i++) if(v[i]==v[k]) return 0; return 1; void back(int k) int i; if(k>n) prel_sol(); else for(i=1;i<=n;i++) v[k]=i; if(posibil(k)) back(k+1); main() cout<<"nr elem multimii="; cin>>n; back(1); getch();