Upload
alexandru-rusu
View
16
Download
1
Embed Size (px)
Citation preview
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 ~
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,
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;
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] << " ";}
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
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.
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";
}
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