8
Tehnici de programare prof. Anca Barbulescu Colegiul National Gheorghe TiteicaBACKTRACKING Se da ecuatia: x 2 +y 2 +z 2 =14 in multimea numerelor intregi. A rezolva aceasta ecuatie inseamna a gasi vectorii (x, y, z) din NxNxN care satisfac egalitatea data. Se stie ca patratul unui numar real (deci si natural) este intotdeauna pozitiv, deci termenii sumei sunt, fiecare, cuprinsi intre 0 si 14. 0 x 2 14 si x N 0 x 3 x {0, 1, 2, 3} Analog se rationeaza pentru y, z si ajungem la concluzia ca solutiile, daca exista, sunt elemente ale produsului cartezian A1 x A2 x A3, unde A1 = A2 = A3 = {0, 1, 2, 3}. Pentru a ;e gasi se pot face incercari succesive, lucru posibil datorita numarului finit (in cazul nostru chiar mic) de elemente, posibile solutii. Vom urma tehnica stabilirii valorilor pentru x, y si verificarea posibilitatilor pentru z; daca nu se gaseste o solutie, atunci vom pastra valoarea lui x, dar o vom modifica pe a lui y. Dupa epuizarea tuturor variantelor lui y vom schimba si valoarea lui x si abia dupa terminarea tuturor valorilor posibile pentru x putem preciza cu exactitate cate si care sunt solutiile ecuatiei. 1. Etapa 1: avem fixata valorea x=0 si pentru ea cautam variante 1.1. Fixeaza y=0 si verifica posibilitatile pentru z z=0 x 2 +y 2 +z 2 =0+0+0=0 14 deci nu este solutie z=1 x 2 +y 2 +z 2 =0+0+1=1 14 ~ z=2 x 2 +y 2 +z 2 =0+0+4=4 14 ~ z=3 x 2 +y 2 +z 2 =0+0+9=9 14 ~ 1.2. Am epuizat valorile posibile pentru z, deci trecem la urmatoarea valoare pentru y si anume y=1 z=0 x 2 +y 2 +z 2 =0+1+0=1 14 deci nu este solutie z=1 x 2 +y 2 +z 2 =0+1+1=2 14 ~ z=2 x 2 +y 2 +z 2 =0+1+4=5 14 ~ z=3 x 2 +y 2 +z 2 =0+1+9=10 14 ~ 1.3. Nici la pasul 1.2. nu am gasit solutie; consideram valoarea posibila 2 pentru y z=0 x 2 +y 2 +z 2 =0+4+0=4 14 deci nu este solutie z=1 x 2 +y 2 +z 2 =0+4+1=5 14 ~ z=2 x 2 +y 2 +z 2 =0+4+4=8 14 ~ z=3 x 2 +y 2 +z 2 =0+4+9=13 14 ~ 1.4. Pentru valoarea fixata y=3 avem z=0 x 2 +y 2 +z 2 =0+9+0=9 14 deci nu este solutie z=1 x 2 +y 2 +z 2 =0+9+1=10 14 ~ z=2 x 2 +y 2 +z 2 =0+9+4=13 14 ~ z=3 x 2 +y 2 +z 2 =0+9+9=18 14 ~

Backtracking

Embed Size (px)

Citation preview

Page 1: Backtracking

Tehnici de programare – prof. Anca Barbulescu

Colegiul National “Gheorghe Titeica”

BACKTRACKING

Se da ecuatia: x

2+y

2+z

2=14 in multimea numerelor intregi. A rezolva aceasta

ecuatie inseamna a gasi vectorii (x, y, z) din NxNxN care satisfac egalitatea data.

Se stie ca patratul unui numar real (deci si natural) este intotdeauna pozitiv, deci

termenii sumei sunt, fiecare, cuprinsi intre 0 si 14.

0 x2 14 si x N 0 x 3 x {0, 1, 2, 3}

Analog se rationeaza pentru y, z si ajungem la concluzia ca solutiile, daca exista,

sunt elemente ale produsului cartezian A1 x A2 x A3, unde A1 = A2 = A3 = {0, 1, 2, 3}.

Pentru a ;e gasi se pot face incercari succesive, lucru posibil datorita numarului finit (in

cazul nostru chiar mic) de elemente, posibile solutii.

Vom urma tehnica stabilirii valorilor pentru x, y si verificarea posibilitatilor

pentru z; daca nu se gaseste o solutie, atunci vom pastra valoarea lui x, dar o vom

modifica pe a lui y. Dupa epuizarea tuturor variantelor lui y vom schimba si valoarea lui

x si abia dupa terminarea tuturor valorilor posibile pentru x putem preciza cu exactitate

cate si care sunt solutiile ecuatiei.

1. Etapa 1: avem fixata valorea x=0 si pentru ea cautam variante

1.1. Fixeaza y=0 si verifica posibilitatile pentru z

z=0 x2+y

2+z

2=0+0+0=0 14 deci nu este solutie

z=1 x2+y

2+z

2=0+0+1=1 14 ~

z=2 x2+y

2+z

2=0+0+4=4 14 ~

z=3 x2+y

2+z

2=0+0+9=9 14 ~

1.2. Am epuizat valorile posibile pentru z, deci trecem la urmatoarea valoare pentru y

si anume y=1

z=0 x2+y

2+z

2=0+1+0=1 14 deci nu este solutie

z=1 x2+y

2+z

2=0+1+1=2 14 ~

z=2 x2+y

2+z

2=0+1+4=5 14 ~

z=3 x2+y

2+z

2=0+1+9=10 14 ~

1.3. Nici la pasul 1.2. nu am gasit solutie; consideram valoarea posibila 2 pentru y

z=0 x2+y

2+z

2=0+4+0=4 14 deci nu este solutie

z=1 x2+y

2+z

2=0+4+1=5 14 ~

z=2 x2+y

2+z

2=0+4+4=8 14 ~

z=3 x2+y

2+z

2=0+4+9=13 14 ~

1.4. Pentru valoarea fixata y=3 avem

z=0 x2+y

2+z

2=0+9+0=9 14 deci nu este solutie

z=1 x2+y

2+z

2=0+9+1=10 14 ~

z=2 x2+y

2+z

2=0+9+4=13 14 ~

z=3 x2+y

2+z

2=0+9+9=18 14 ~

Page 2: Backtracking

Tehnici de programare – prof. Anca Barbulescu

Colegiul National “Gheorghe Titeica”

Am epuizat toate variantele posibile pentru valoarea fixata x=0; trecem la etapa a

urmatoare ce poate fi considerata luand urmatoarea valoare posibila pentru x.

2. Avem fixata valoarea x=1

2.1. Pentru y=0 avem variantele

z=0 x2+y

2+z

2=1+0+0=1 14 deci nu este solutie

z=1 x2+y

2+z

2=1+0+1=2 14 ~

z=2 x2+y

2+z

2=1+0+4=5 14 ~

z=3 x2+y

2+z

2=1+0+9=10 14 ~

2.2. Fixam y=1 si variem z

z=0 x2+y

2+z

2=1+1+0=2 14 deci nu este solutie

z=1 x2+y

2+z

2=1+1+1=3 14 ~

z=2 x2+y

2+z

2=1+1+4=6 14 ~

z=3 x2+y

2+z

2=1+1+9=11 14 ~

2.3. Schimbam din nou valoarea lui y si luam y=2

z=0 x2+y

2+z

2=1+4+0=5 14 deci nu este solutie

z=1 x2+y

2+z

2=1+4+1=6 14 ~

z=2 x2+y

2+z

2=1+4+4=9 14 ~

z=3 x2+y

2+z

2=1+4+9=14 = 14 si am obtinut solutia (1, 2, 3)

Pentru aflarea urmatoarele solutii se continua procedeul de cautare de mai sus: fixam

valori posibile pentru primele si variem ultimele componente ale vectorului; cand nu mai

sunt variante pentru acestea din urma ne intoarcem catre inceputul vectorului cu cate o

pozitie, luand in consideratie urmatoarea valoare posibila.

Deci, dupa cum se observa, solutia se construieste element cu element, prin punerea

unor valori din cele posibile si verificarea conditiei de indeplinirea a egalitatii.

Ca sa intelegem si sa automatizam acest mecanism, vom aseza intr-o stiva valorile

pe care le incercam: pentru ca mai intai se fixeaza o valoare pentru x, apoi pentru y, iar z

este cel care variaza “cel mai repede”, x va reprezenta primul nivel al stivei (primul

element), y al doilea, iar z varful stivei (cand epuizam toate variantele pentru el il

scoatem din stiva si modificam valoarea lui y).

Deci, pentru etapa 1 de incercari valorile depuse in stiva se reprezinta astfel:

1.1.

0 1 2 3

0 0 0 0

0 0 0 0

1.3.

0 1 2 3

2 2 2 2

0 0 0 0

1.2.

0 1 2 3

1 1 1 1

0 0 0 0

1.4.

0 1 2 3

3 3 3 3

0 0 0 0

A fost etapa in care pe nivelul 1 al stivei s-a mentinut 0 si, pe fiecare subetapa, s-a fixat

cate o valoare pentru nivelul al doilea. Varful stivei si-a modificat valorile in permanenta,

Page 3: Backtracking

Tehnici de programare – prof. Anca Barbulescu

Colegiul National “Gheorghe Titeica”

iar trecerea de la o subetapa la urmatoarea consta in extragerea varfului, modificarea

valorii pentru noul varf (nivelul 2) si adaugarea unui nou element posibil.

Acelasi mecanism se urmeaza si in continuare. Etapele pana la gasirea primei

solutii le reprezentam astfel:

2.1.

0 1 2 3

0 0 0 0

1 1 1 1

2.2.

0 1 2 3

1 1 1 1

1 1 1 1

2.3.

0 1 2 3

2 2 2 2

1 1 1 1

Sa caracterizam acum metoda backtracking.

Este o metoda de programare conceputa pentru a rezolva probleme a caror solutie

se poate reprezenta sub forma de vector. Mai exact, tipul de probleme pentru care se

potriveste solicita determinarea unor valori (x1, x2, …, xn) A1 x A2 x … x An care sa

indeplineasca anumite conditii, unde A1, A2, … , An sunt multimi cu cardinal finit.

Metoda backtracking este sigura din punct de vedere al gasirii solutiilor, atunci

cand acestea exista, dar nu este eficienta din punct de vedere al spatiului de memorie si al

timpului de calcul. Se bazeaza pe incercarea tuturor variantelor posibile si depistarea

celor corecte, fapt pentru care se indica folosirea ei numai in situatiile in care nu se

dispune de o alta metoda de rezolvare.

Principiul de cautarea a unei solutii este urmatorul: se dau valori, pas cu pas,

pentru componentele vectorului solutie; daca pentru o valoare aleasa nu se poate ajunge

la solutie, atunci se renunta la acea valoare si se reia cautarea de la pasul anterior (cu o

pozitie spre stanga).

Algoritmul general al acestei metode, pentru aflarea unei solutii (x1, x2, …, xn),

poate fi descris astfel:

1. alegem o valoare oarecare pentru x1

2. cat timp sunt fixate valori pentru x1, … , xk

cautam o valoare posibila pentru xk+1 din Ak+1 si verificam daca se supune

conditiilor problemei

daca exista o asemenea valoare

daca avem k+1 ultima componenta afisam solutia

altfel aplicam metoda de completare pentru urmatoarea pozitie,

considerand x1, … , xk+1 fixate

altfel reluam completarea lui xk, intorcandu-ne cu o pozitie si considerand

completate x1, … , xk-1

3. sfarsit

Observam ca algoritmul testeaza toate variantele, deoarece se incheie atunci cand

nu mai poate fi fixata nici o valoare pentru x1, adica putem spune ca am coborat in stiva

pana cand aceasta s-a golit.

La aplicarea acestei metode pentru o anumita problema, programatorul va trebui

sa stabileasca urmatoarele:

1) cum se face alegerea primei valori pentru un element xk (care este criteriul de alegere

a primei valori de pe nivelul k al stivei);

2) functia de testare a conditiilor problemei;

Page 4: Backtracking

Tehnici de programare – prof. Anca Barbulescu

Colegiul National “Gheorghe Titeica”

3) modul de afisare a solutiilor gasite;

4) functia pentru verificarea completitudinii unei solutii.

Daca se opteaza pentru o implementare structurata, atunci se va folosi urmatorul

algoritm:

1. k=1, dam o valoare lui x[k]

2. cat timp k>=0 (varful este cel putin pe nivelul 1)

2.1. repetam

alegerea unei valori x[k] din Ak

verificarea validitatii acesteia

cat timp gasim valori in Ak, dar nu sunt valide

2.2. daca am gasit o valoare valida

daca solutia este completa o afisam

altfel trecem la completarea urmatorului nivel

k=k+1 si x[k] ia o valoare initiala

altfel ne intoarcem si refacem nivelul ultim din stiva k=k-1

3. sfarsit

La rezolvarea ecuatiei x2+y

2+z

2=14, vom implementa urmatoarele functii:

Initializarea, prin atribuirea valorii –1 pentru x[k]

Alegerea unei valori pentru x[k] presupune inlocuirea valorii existente a lui x[k] cu

urmatorul numar natural, dar care sa fie mai mic decat 3

Validarea valorii actuale a lui x[k] consta in verificarea inegalitatii x2+y

2+z

2<=14

Afisarea solutiei se realizeaza prin afisarea vectorului (x, y, z)

Functia pentru testarea completitudinii solutiei contine verificarea identitatilor

k=3 si x2+y

2+z

2=14

In functia principala se implementeaza algoritmul structurat pe care l-am dat

anterior.

Programul C este urmatorul:

#include <iostream.h>

#include <conio.h>

int x[10],i,k;

int gaseste(int k)

{if(x[k]<3) {x[k]++;return 1;} else return 0;}

int valid(int k)

{ int s;

for(i=1,s=0;i<=k;i++) s+=x[i]*x[i];

if(s<=14) return 1; else return 0;}

int solutie(int k)

{int s=0,sk=0;

if(k==3) {s=1; for(i=1;i<=k;i++)sk+=x[i]*x[i];if(sk!=14) s=0;}

return s;}

void afis(int k)

{cout << "\n";

for(i=1;i<=k;i++) cout << x[i] << " ";}

Page 5: Backtracking

Tehnici de programare – prof. Anca Barbulescu

Colegiul National “Gheorghe Titeica”

void main()

{int g,v;

clrscr();

cout << "\nDati termenul drept al ecuatiei: "; cin >> n;

cout << "\nSolutiile ecuatiei x^2+y^2+z^2=14 sunt:\n";

k=1;x[k]=0;

while(k)

{do{g=gaseste(k);if(g) v=valid(k);}while(g && !v);

if(g) if(solutie(k))afis(k); else {k++;x[k]=0;}

else k--;

}

getch();

}

O generalizare a acestei probleme se enunta astfel:

Sa se rezolve, in multimea numerelor naturale, ecuatia:

x12+ … +xn

2=r

Lasam pentru studiul individual scrierea algoritmului si implementarea

programului. Singura observatie pe care o facem este referitoare la valorile posibile ce

vor fi incercate de algoritm: acestea trebuie sa fie cuprinse intre 0 si [k], iar solutia

completa trebuie sa aiba n componente ce verifica ecuatia.

PROBLEME REZOLVATE:

1. O problema mai simpla, dar asemanatoare cu aceasta este partitonarea unui numar

natural n ca suma de k numere nenule.

Prezentam direct algoritmul structurat, bazat pe cel general:

1. citim n, k

2. i=1, x[i]=0

3. cat timp i>0

3.1. repetam

alegerea unei valori posibile x[i]

verificarea validarii lui x[i]

cat timp gasim x[i], dar nu e valid

3.2. daca avem x[i] valid

daca solutia e completa o afisam

altfel completam urmatoarea componenta i++ si x[i]=0

altfel refacem nivelul anterior i--

4. sfarsit

Functiile necesare sunt:

Alegerea unei valori are ca argument pozitia i

daca x[i]<n urmatoarea valoare este x[i]++ si se returneaza 1

altfel se returneaza 0

Validarea unei valori de pe pozitia i

s=0

pentru j=1 la i actualizam s=s+x[j]

daca s<=n se returneaza 1

altfel se returneaza 0

Page 6: Backtracking

Tehnici de programare – prof. Anca Barbulescu

Colegiul National “Gheorghe Titeica”

Completitudinea solutiei

daca i==k atribuim c=1

altfel c=0

daca c==1 calculam

s=0

pentru j=1 la i calculam s=s+x[j]

daca s!=n actualizam c=0

se returneaza valoarea lui c

Afisarea solutiei

pentru i=1 la k afisam x[i]

Programul C:

#include <iostream.h>

#include <conio.h>

int x[10],i,n,k;

int gaseste(int i)

{if(x[i]<n) {x[i]++;return 1;} else return 0;}

int valid(int i)

{ int s;

for(j=1,s=0;j<=i;j++) s+=x[j];

if(s<=n) return 1; else return 0;}

int solutie(int i)

{int s=0,sk=0;

if(i==n) {s=1; for(j=1;j<=i;j++)sk+=x[j];if(sk!=n) s=0;}

return s;}

void afis(int i)

{cout << "\n";

for(j=1;j<=i;j++) cout << x[j] << " ";}

void main()

{int g,v;

clrscr();

cout << "\nDati numarul ce trebuie partitionat: "; cin >> n;

cout << "\nDati numarul de partitii: "; cin >> k;

cout << "\nDescompunerile lui " << n << “ ca suma de “ << k << “termeni sunt:\n";

i=1;x[i]=0;

while(i)

{do{g=gaseste(i);if(g) v=valid(i);}while(g && !v);

if(g) if(solutie(i))afis(i); else {i++;x[i]=0;}

else i--;

}

getch();

}

Observatii:

1) Se poate impune ca termenii sumei sa fie numere distincte; atunci se va modifica

validarea, adaugand conditia ca x[i] sa fie diferit de orice x[j] anterior.

Page 7: Backtracking

Tehnici de programare – prof. Anca Barbulescu

Colegiul National “Gheorghe Titeica”

2) Daca nu ne intereseaza ordinea aparitiei termenilor in suma, atunci se pot genera

numai solutiile ale caror componente sunt asezate in ordine crescatoare.

2. Permutarile unei multimi

Se stie de la matematica notiunea de multime si faptul ca elementele sale sunt

unice (un element nu apare de doua ori in aceeasi multime). O metoda de reprezentare a

unei multimi finite este enumerarea elementelor. In programare, o multime se reprezinta

ca un vector, in care sunt enumerate elementele multimii respective. Datorita indicilor

unui vector, apare o relatie de ordine, prin care fiecare componenta ocupa o anumita

pozitie.

Prin permutarile unei multimi se intelege formarea tuturor vectorilor cu

elementele unei multimi, prin schimbarea ordinii acestora. Mai exact, generarea tuturor

posibilitatilor de aranjare a n elemente ale unei multimi intr-un vector.

Pentru ca intre orice multime A cu n elemente si multimea {1, 2, …, n} putem

construi bijectia care pune in corespondenta al I-lea element al multimii cu numarul I,

convenim ca orice problema cu multimi sa o reducem la interpretarea ei in multimea

{1,2,…,n} , intelegand prin i al i-lea element al unei multimi date.

Deci, generarea permutarilor unei multimi cu n elemente o vom realiza permutand

in toate modurile numerele 1,2,…,n, cu ajutorul metodei backtracking.

O solutie consta intr-un vector de dimensiune n, ale carui elemente sunt numerele

naturale de la 1 la n. Conditia ca o valoare sa fie valida este ca sa nu mai fi aparut pe

pozitiile anterioare si, bineinteles, sa fie cuprinsa intre 1 si n. Alegerea unei valori o

facem luand valoarea urmatoare celei deja alese. Afisarea este afisarea unui vector cu n

elemente.

Dam direct programul implementat in C: #include <iostream.h>

#include <conio.h>

int k,x,v,n,s[10],h=0;

char r;

/*Functia de alegere a unei noi valori pentru x[k]*/

int aleg(int k)

{if(s[k]<n){s[k]++;return 1;}

else return 0;

}

/*Functia de validare a valorii lui x[k]*/

int validez(int k)

{int i,v=1;

for(i=1;i<k;i++) if(s[k]==s[i])v=0;

return v;

}

/*Functia de afisare a vectorului solutie*/

void afis()

{int i;

for(i=1;i<=n;i++) cout << s[i] << " ";

cout << "\t";

}

Page 8: Backtracking

Tehnici de programare – prof. Anca Barbulescu

Colegiul National “Gheorghe Titeica”

/*Functia principala*/

void main()

{cout << "\nDati n: "; cin >> n;

k=1;s[k]=0;

while(k>0)

{do{x=aleg(k); if(x) v=validez(k);}while(x && !v);

if(x)

if(k==n){afis();h++;getch();}

else {k++;s[k]=0;}

else k--;

}

cout << "\nExista " << h << " posibilitati de permutare!";

r=getch();

}

3. Aranjamente de n luate cate k

Problema aranjamentelor cere generarea tuturor vectorilor de dimensiune k, cu cele n

elemente ale unei multimi.

Ca si la permutari, vom genera multimile cu k elemente numere naturale cuprinse

intre 1 si n, folosind aceeasi metoda backtracking.

De data aceasta, solutia este completa daca are exact k elemente distincte, cuprinse

intre 1 si n

4. Combinari de n luate cate k

5. Produsul cartezian al mai multor multimi