Rezumat n urm cu aproximativ 16 de ani Tudor Sorin propunea lumii informatice din Romnia un ir de materiale prin care standardiza metoda backtracking. Acest lucru a dus la crearea unui ablou prin care o metod putea fi folosit deopotriv de ctre elevi, studeni, profesori. n continuare voi prezenta aceast metod de programare ca o continuare fireasc a capitolului Recursivitate din clasa a X-a, profil matematic- informatic, intensiv informatic.
Introducere
Muli profesori ncheie capitolul recursivitate cu probleme de tipul:
Se d un numr natural n<20 i un alfabet cu dou litere (A={a,b}). Determinai toate cuvintele de n litere folosind alfabetul dat. Exemplu cuvinte.in cuvinte.out 2 aa ab ba bb
Rezolvarea problemei fiind urmtoarea:
Un cuvnd poate fi memorat ntr-un vector x cu n componente de tip char (fiecare component putnd s aib una din valorile a,b). Recursiv, cuvintele se pot determina folosind programul urmtor:
var x:array[0..21]of char; n:integer; f:text;
procedure afisare; var i:integer; begin for i:=1 to n do write(f,x[i]); writeln(f);; end; #include <fstream.h>
char x[21]; int n; ofstream fout("cuvinte.in");
void afisare(){ int i; for(i=1;i<=n;i++) fout<<x[i]; fout<<'\n'; }
procedure generare(k:integer); begin if k=n+1 then afisare else begin x[k]:='a'; generare(k+1); x[k]:='b'; generare(k+1); end end;
begin assign(f,'cuvinte.in'); reset(f); read(f,n); close(f); assign(f,'cuvinte.out'); rewrite(f); generare(1); close(f) end.
Pentru exemplul dat n enun irul de apeluri este urmtorul:
generare(1) generare(2) generare(3) x[1]=a x[2]=a se stopeaz generarea prin condiia k=n+1 i se afieaz aa
x[2]=b generare(3) se stopeaz generarea prin condiia k=n+1 i se afieaz ab x[1]=b generare(2) generare(3) x[2]=a se stopeaz generarea prin condiia k=n+1 i se afieaz ba
x[2]=b generare(3) se stopeaz generarea prin condiia k=n+1 i se afieaz bb stop
Se obine astfel urmtorul arbore, cu evoluia vectorului x:
Generalizri
Ne punem n continuare problema, cum putem determina cuvintele care folosesc un alfabet mai mare (de exemplu cu primele m litere din alfabetul englez). Mai mult, cum determinm cuvintele care ndeplinesc o anumit proprietate (de exemplu: s nu aib dou litere la fel pe poziii consecutive) .a.m.d.
Dac ne concentrm pe problema determinrii cuvintelor, folosind primele m litere din alfabetul englez cu restricia: s nu existe litere egale pe poziii consecutive, o variant de subprogram ce genereaz vectorul x ar putea fi:
procedure generare(k:integer); var i:integer; begin if k=n+1 then afisare else for i:=1 to m do begin x[k]:=chr(ord(a)+i-1); generare(k+1) end end; void generare(int k){ int i; if(k==n+1) afisare(); else for(i=1;i<=m;i++) { x[k]='a'+i-1; generare(k+1); } }
Menionm faptul c, n funcia de afiare se va verifica condiia ca s nu existe dou litere egale pe poziii consecutive, afiarea fcndu-se numai n acest caz.
n aceste condiii pentru n=2 i m=2 se genereaz acelai arbore ca mai sus. Unele dintre ramuri fiind generate degeaba, pentru c odat construite dou componente cu indici consecutivi i aceai liter, sigur nu se va ajunge la un cuvnt ca s fie afiat. Astfel pentru a eficientiza algoritmul de generare se impune adugarea unei condiii la fiecare pas (legat de alegerea valorii pentru componenta x[k], adic x[k]x[k-1]).
Obinem astfel rafinarea:
x=() x=(a) x=(b) x=(a,a) x=(a,b) x=(b,a) x=(b,b)
procedure generare(k:integer); var i:integer; begin if k=n+1 then afisare else for i:=1 to m do begin x[k]:=chr(ord(a)+i-1); if conditie(k) then generare(k+1) end end; void generare(int k){ int i; if(k==n+1) afisare(); else for(i=1;i<=m;i++) { x[k]='a'+i-1; if(conditie(k)) generare(k+1); } }
Funcia condiie returneaz true/1 sau false/0 n funcie de situaie (dac x[k]x[k-1], respectiv x[k]=x[k-1]). Pentru a nu avea un caz particular k=1, nainte de apelul generare(1) se face iniializarea lui x[0] cu un caracter care nu este n alfabet (de exemplu x[0]=*).
Trecerea la metoda backtracking
Folosind consideraiile anterioare putem s discutm despre rezolvarea unei probleme generale.
Metoda backtracking permite s se rezolve probleme de tipul:
Se dau n mulimi A 1 , A 2 , ..., A n i o proprietate P, care depinde de x 1 , x 2 , ..., x n (x 1 din A 1 , x 2 din A 2 , ..., x n din A n ). Se cere s se determine toate irurile x 1 , x 2 , ..., x n , care verific proprietatea P.
Particularizare Pentru problema cu generarea cuvintelor n care literele de pe poziii consecutive sunt distincte, avem:
A 1 , A 2 , ..., A n sunt toate formate din primele m litere mici ale alfabetului englez. P este condiia x i x i-1 , i din mulimea {2,3, , n}.
Descrierea metodei de rezolvare (numit bactracking datorit mecanismului de generare a soluiilor)
- Construirea componentelor vectorului x se va face n ordinea cresctoare a indicilor, x 1 , x 2 , ..., x k , ..., x n . x k fiind din multimea A k .
- Din condiia P se deduc condiii parilale, pentru componentele x 1 , x 2 , ..., x k . Acestea vor fi notate cu P(k).
- Ideea metodei const n faptul c dac, la un mont dat trebuie s se construiasc x k , se consider pe rnd elementele lui A k i dac sunt verificate condiiile P(k), atunci se trece la urmtoarea component, altfel se merge napoi la componenta anterioar i se caut alt valoare. n permanen exist un dute-vino (realizat de mecanismul recursivitii) pn se ajunge la cte o soluie.
- Forma general a unui subprogram recursiv de generare a vectorului x este prezentat n continuare.
procedure back(k:integer); var i:Tip; begin if k=n+1 then afisare else for i din A k do begin x[k]:=i; if P(k) then back(k+1) end end; void back(int k){ Tip i; if(k==n+1) afisare(); else for(i din A k ) { x[k]=i; if(P(k)) back(k+1); } }
- Subprogramul de generare trebuie apelat prin back(1), pentru a construi vectorul x ncepnd cu prima component.
Observaii
- Metoda backtracking determin toate soluiile problemei. - Cu ct condiiile pariale P(k) sunt mai restrictive cu att timpul de execuie este mai mic. - Uneori este de preferat s folosim n componentele lui x, indicii elementelor din mulimile A 1 , A 2 , ..., A n , pentru c acetia sunt numere consecutive i pot fi parcuri mai uor. - Timpul de execuie al algoritmilor ce folosesc metoda backtracking este exponeial (relativ la n). - Dac exist alte metode de rezolvare cu timp de execuie polinomial, atunci aceast metod poate fi folosit doar pentru a confirma corectitudinea celeilate, prin compararea rezultatelor. - Pentru a nsui aceast metod de programare este nevoie de rezolvarea unui numr mare de probleme, ncepnd cu cele clasice (problema damelor, colorarea unei hri, generarea elementelor combinatoriale, plata unei sume de bani, etc.), apoi continund cu cele care necesit modificri ale subprogramului de generare. - Metoda backtracking poate fi utilizat i pentru generri de lanuri n tablouri bidimensonale (aa numitul backtracking n plan) ns pentru aceast variant ar trebui s scris un alt material.
Probleme propuse
1. Se d o mulime A cu n (n<20) elemente numere naturale <32000. Se cere s se determine toate modalitile de mprire a lui A n trei mulimi cu aceeai sum a elementelor. Exemplu multime.in multime.out 9 2 8 7 11 6 4 9 3 10 {7 3 6 4} {2 8 10} {11 9} ...
2. Se d un ir cu n (n<50) elemente numere naturale <100. Se cere s se determine toate numere din ir, care se pot scrie ca sum de numere prime distincte. Exemplu sir.in sir.out 3 6 7 10 7 10
3. Se d n numr natural (n<20). Determinai toate numerele n baza 2 cu n cifre, care au numrul de cifre de 1 egal cu cel al celor de 0. Exemplu baza2.in baza2.out 4 1100 1010 1001
4. Se d un ir cu n (n<20) elemente, numere naturale <200 distincte. Se cere s se determine cel mai mic numr natural, care nu poate fi scris ca o sum de termeni din irul dat. Exemplu mic.in mic.out 4 1 2 4 10 8
5. Se d un ir cu n (n<20) elemente, numere naturale <200 distincte. Se cere s se determine toate subirurile cresctoare de lungime maxim ale irului dat. Exemplu subsir.in subsir.out 7 8 3 5 9 2 3 4 3 5 9 2 3 4
6. Se d un ir cu n (n<20) elemente, numere ntregi cu valoarea absolut <200. Se cere s se determine toate valorile epresiilor aritmetice ce se pot obine punnd ntre orice dou numere vecine operatorul + sau -. Exemplu exp.in exp.out Explicatie 3 2 8 -2 8 12 -8 -4 2+8+(-2) 2+8-(-2) 2-8+(-2) 2-8-(-2)