Sunteți pe pagina 1din 15

Recursivitate. Aplicatii.

Generari combinatoriale

RECURSIVITATE

Definire şi exemple

Să ne imaginăm un copac. O ramura a copacului este formată din mai multe ramuri,
fiecare la rândul ei fiind formată din ramuri mai mici, acestea sunt formate din ramuri
şi mai mici ... şi aşa mai departe pană când ajungem la cele mai mici ramuri, fiecare
fiind formată dintr-o tulpină şi mai multe frunze. Am descris ramura unui copac
folosind-ne de aceeaşi noţiune, ramura, până când am ajuns la cele mai mici elemente
care o descriu, tulpina si frunzele.
Matematica modelează şi exprimă prin relaţii şi formule natura, aşa că regăsim şi
aici astfel de definiţii, numite „relaţii de recurenţa”. De exemplu, pentru a calcula
factorialul unui număr, procedăm astfel:
n! = n * (n-1) * (n-2) * ... 2 * 1
Putem scrie deci că
n!= 1 , dacă n=1 şi
n!=n*(n-1)! , dacă n>1 .
Să observăm din exemplul de mai sus următoarele:
 Ultimul nivel al definiţiei, să-l numim nivel elementar (n=1), trebuie să fie
explicitat.
 In cadrul definiţiei unei noţiuni apare însăşi noţiunea care se defineşte.

Spunem că o noţiune este definită recursiv dacă în definirea ei apare însăşi


noţiunea care se defineşte. In Informatică numim recursivitate directă proprietatea
funcţiilor de a se autoapela.

Structura unei funcţii recursive

tip nume_funcţie(lista de parametri)


{.................
if(condiţie de continuare sau de oprire)
..............…
nume_funcţie(lista de parametri);/// autoapel
..................
}
Observaţii

 Orice funcţie recursivă trebuie să conţină o condiţie de reluare a apelului


recursiv ( respectiv de oprire). Fără această condiţie, funcţia teoretic se reapelează
la infinit, dar nu se întâmplă acest lucru, deoarece se umple segmentul de stivă
alocat funcţiei şi programul se înterupe cu eroare.
 La fiecare reapel al funcţiei se execută aceeaşi secvenţă de instructiuni.
Ţinând seama de observaţiile anterioare, pentru a implementa o funcţie recursivă,
trebuie să:
 Identificăm relaţia de recurenţă (ceea ce se execută la un moment dat şi se reia
la fiecare reapel)
 Identificăm condiţiile de oprire ale reapelului.
În cazul în care funcţia are parametri, aceştia se memorează ca şi variabilele locale pe
stivă, astfel:

CEX - 11/12/2021 Vasilica Rosca 1


Recursivitate. Aplicatii. Generari combinatoriale

 parametrii transmişi prin valoare se memorează pe stivă cu valoarea din acel


moment
 pentru parametrii transmişi prin referinţă se memorează adresa lor
În momentul apelării unui subprogram (funcţie), sistemul pune la dispoziţia
subprogramului un segment de stivă pentru memorarea parametrilor constanţi, a
adreselor parametrilor variabili (transmişi prin referinţă) şi a variabilelor locale. Până
în acest moment am învăţat să apelăm doar funcţii nerecursive, pentru care se crea un
singur nivel pe stivă. Ce se întâmplă însă, când funcţia se autoapelează? Răspunsul
este simplu, se întâmplă acelaşi lucru la fiecare reapel al funcţiei, cu alte cuvinte, la
fiecare reapel se memorează adresa instrucţiunii următoare apelului, se crează un nou
nivel pe stivă, memorându-se parametri şi variabilele locale cu valorile din acel
moment.

Exemplu de funcţie recursivă fară parametri

Se citesc pe rând literele unui cuvânt, urmate de caracterul spatiu, care nu face parte
din cuvânt. Să se afişeze caracterele în ordinea inversă citirii.

#include <iostream>//INVERSARE CUVÅNT


using namespace std;
void inv()
{char lit;
cin.get(lit);
if (lit!=' ') //condiţia de reluare
inv(); //autoapelarea funcţiei inv()
cout<<lit;
}
int main()
{cout<<"introduceti cuvantul:";
inv(); //apelul funcţei inv()
}
Să urmărim codul programului şi ceea ce se întâmplă pe stivă.
Presupunem că citim cuvântul “rac“ urmat de un spaţiu.
La primul apel al funcţiei inv(), în cadrul funcţiei main(), se crează nivelul 1 pe stivă.
Controlul programului în acest moment se dă funcţiei inv(). În cadrul acestei funcţii
am declarat variabila locală lit în care citim prima literă ‘r’. Această variabilă
împreună cu valoarea ei se memorează pe stivă. Urmează testul de continuare,
verificăm dacă nu s-a terminat de citit cuvântul, adică dacă am citit un caracter diferit
de spaţiu. Intrucât caracterul citit a fost ‘r’, reapelăm funcţia inv().
Se crează nivelul 2 pe stivă. Se citeşte următoarea literă, ‘a’. Pe nivelul 2 se
memorează în variabila lit, caracterul ‘a’. În urma testului dacă litera citită nu este
spaţiu se decide reapelarea funcţiei inv().
Se crează nivelul 3 pe stivă, se citeşte următoarea literă şi se memorează lit cu
valoarea ‘c’ pe acest nivel. Întrucât nu am citit caracterul ‘ ‘(spatiu), se reapelează
funcţia. De data aceasta cuvântul s-a terminat, se citeşte caracterul spaţiu. Se crează
nivelul 4 pe stivă. Se memorează variabila lit cu valoarea ‘ ‘(spaţiu). În urma testului
se constată că lit conţine caracterul spaţiu, prin urmare cuvântul s-a terminat şi funcţia
nu se mai reapelează. În acest moment se revine la instrucţiunea aflată imediat după
reapelul funcţiei, se afişează conţinutul variabilei lit, adică ‘ ‘. Se închide nivelul 4 de
stivă, se revine la instrucţiunea următoare şi se afişează ‘c’. Se închide nivelul 3 de
stivă, se afişează ‘a’. Se închide nivelul 2 şi se afişează ‘r’, se închide şi nivelul 1 şi se

CEX - 11/12/2021 Vasilica Rosca 2


Recursivitate. Aplicatii. Generari combinatoriale

revine în funcţia main(), care în acest moment se încheie şi astfel se termină


programul.

În concluzie, la fiecare apelare al funcţiei se efectuează următoarele:


1. Se salvează adresa următoarei instrucţiuni de după apelul funcţiei.
2. Controlul programului este transferat primei instrucţiuni din cadrul funcţiei
apelate.
3. Pe segmentul de stivă al funcţiei se crează un nou nivel .
4. Se memorează pe acest nivel de stivă parametrii formali ai funcţiei (în cazul în
care există) şi variabilele locale.
5. Instrucţiunile funcţiei se execută în secventă .Funcţia conţine un test, pe una
dintre ramurile testului se reapelează funcţia şi se reia procedeul de la punctul 1.
Pe cealaltă ramură a testului se vor executa instrucţiunile de după reapelarea funcţiei
pentru fiecare nivel al stivei, cu valorile parametrilor şi ale variabilelor de pe acel
nivel de stivă. Acest lucru se întâmplă deoarece la fiecare reapel s-a salvat adresa de
revenire din funcţia apelantă.
6. Se termină execuţia funcţiei şi se eliberează segmentul de stivă alocat.

Exemplu de funcţie recursivă cu parametri

Să se calculeze n!, pentru un număr natural n citit de la tastatură.

#include <iostream>//FACTORIAL
using namespace std;
int fact(int x)
{if (x<=1) //condiţia de oprire
return 1;
else
return (x*fact(x-1)); //autoapelul functiei
}

int main()
{ int n;
cin>>n;
cout<<"n!="<<fact(n); //apelul functiei
}

STIVA
x=1 nivelul 5
x=2 nivelul 4
x=3 nivelul 3
x=4 nivelul 2
x=5 nivelul 1
Priviţi codul programului FACTORIAL, de mai sus. Presupunem că dorim să
calculăm valoarea lui 5!, prin urmare n=5. Să observăm că funcţia are parametrul
transmis prin valoare, x. Prin urmare x se va memora pe fiecare nivel al stivei,
întocmai ca variabila locală lit din programul precedent. Conform programului de mai
sus, valoarea funcţiei se calculează astfel:
pe nivelul 1: x=5 se reapelează fact(4)
pe nivelul 2: x=4 se reapelează fact(3)
pe nivelul 3: x=3 se reapelează fact(2)

CEX - 11/12/2021 Vasilica Rosca 3


Recursivitate. Aplicatii. Generari combinatoriale

pe nivelul 4: x=2 se reapelează fact(1),


pe nivelul 5: x=1 şi în acest moment funcţia se poate calcula şi avem fact(1)=1. În
momentul în care valoarea funcţiei a fost calculată, se închide nivelul 5 şi se revine la
nivelul 4. Se calculează 2*1=2, se închide nivelul 4 şi se revine la nivelul 3. Se
calculează 3*2=6, se închide nivelul 3 şi se revine la nivelul 2. Se calculează
4*6=24, se închide nivelul 2 şi se revine la nivelul 1. Se calculează 5*24=120, se
închide nivelul 1 şi se revine în funcţia main(). Pentru a verifica dacă formula de
recurenţa determinată este corectă, putem proceda ca în exemplul de mai jos, pentru
5!: fact(5)=5*fact(4)=5*4*fact(3)=5*4*3*fact(2)=5*4*3*2*fact(1)=5*4*3*2*1=120.

Cum gândim o funcţie recursivă în general?

Stabilim ce se execută la o anumită etapă, apoi reluăm procedeul pentru toate celelalte
elemente rămase, prin reapelarea funcţiei, având grijă să impunem condiţii de oprire
ale reapelării funcţiei.

In Informatică numim recursivitate indirectă, proprietatea funcţiilor de a se


autoapela prin intermediul altor funcţii.
Structura unei funcţii recursive indirect apelate prin intermediul unei alte funcţii:

tip b(lista de parametri)


{ .................
a(lista de parametri); //funcţia b apelează funcţia a
...............
}
tip a(lista de parameri) //funcţia a este recursivă şi
apelează funcţia b
{.................
if(condiţie de continuare)
.................
b(lista de parametri);
...............
}
Exemplu: Sirul mediilor aritmetico-geometrice al lui Gauss

Se consideră şirurile definite recurent astfel:

a0=a, b0=b (a,b>0)


an=(an-1+bn-1)/2 , bn= an  1 * b n  1 .

Vom construi două funcţii recursive cu formulele de recurenţă date de expresiile de


mai sus, funcţia an(n) pentru construirea elementelor şirului an şi funcţia bn(n) pentru
construirea elementelor şirului bn. Condiţiile de oprire ale recursivităţii vor fi n=0
pentru care ultimii termeni ai şirului sunt definiţi explicit a0=a, respectiv b0=b.

#include<iostream> //ŞIRURI
using namespace std;
double an(int n);
double bn(int n);
double a,b;
int main()

CEX - 11/12/2021 Vasilica Rosca 4


Recursivitate. Aplicatii. Generari combinatoriale

{int n;
cin>>a;
cin>>b;
cin>>n;
cout<<"an("<<n<<")="<<an(n)<<" bn("<<n<<")="<<bn(n);
//apelul funcţiilor an() şi bn();
}
double an(int n)
{if(n>0)
return (an(n-1)+bn(n-1))/2; //funcţia an() se autoapelează
//direct, dar se autoapelează şi
//indirect prin intermediul
//funcţiei bn()
else
return a;
}
double bn(int n)
{if(n>0)
return sqrt(an(n-1)*bn(n-1)); //funcţia bn() se autoapelează
//direct, dar se autoapelează şi
//indirect prin intermediul funcţiei
//an();

else
return a;
}

Compararea unei funcţii recursive cu o funcţie iterativă, din punct de vedere


al eficienţei

În principiu, orice algoritm poate fi elaborat atât recursiv cât şi iterativ. O funcţie
recursivă se reapelează de mai multe ori. Aceasta înseamnă un salt în program, de
fiecare dată când se face un reapel, prin urmare consum de timp. La fiecare reapel al
funţiei pe segmentul de stivă alocat funcţiei, se crează un nou nivel, unde se
memorează parametri şi variabilele locale. Acest lucru înseamnă consum de memorie,
dar segmentul de stivă este oricum rezervat. Observăm deci că o funcţie recursivă
consumă mai mult timp, prin reapelări succesive şi mai multă memorie decât o funcţie
iterativă. Acest lucru afectează programul doar în momentul în care se efectuează un
număr foarte mare de reapelări, ceea ce ar putea duce şi la ocuparea totală a
segmentului de stivă, caz în care programul se întrerupe cu eroare( un exemplu in
acest sens este calculul recursiv al unui termen de rang n din sirul lui Fibonacci)
Pe de altă parte o funcţie recursivă se scrie cu mai multă uşurinţă, codul sursă
poate fi mult restrâns, este mai naturală şi mai uşor de urmărit.

CEX - 11/12/2021 Vasilica Rosca 5


Recursivitate. Aplicatii. Generari combinatoriale

APLICATII ALE RECURSIVITATII

Anagrame

Se citesc două şiruri de caractere x şi y. Să se afişeze dacă şirul x este anagrama


şirului y.

Soluţie

Pentru a verifica dacă cele două şiruri sunt anagrame vom proceda asfel:
a. ) Dacă cele două şiruri au lungimi diferite, nu pot fi anagrame.
b. ) Dacă au aceeaşi lungime, vom căuta primul caracter din şirul x în al şirul y.
Dacă îl găsim, vom şterge acest caracter atât din şirul x cât şi din şirul y. Reluăm
procedeul până când cele două şiruri devin vide.
c. ) În cazul în care caracterul din x nu se găseşte în y, înseamnă că şirurile nu
sunt anagrame.

#include <iostream>///ANAGRAME
#include <cstring>
using namespace std;
int anagrama(char x[50],char y[50]);
int main()
{
char x[50],y[50];
cin>>x;
cin>>y;
if (strlen(x)!=strlen(y))
cout<<"sirurile nu au ac lungime";
else if (anagrama(x,y)) ///apelul funcţiei
cout<<"sirurile sunt anagrame";
else cout<<"sirurile nu sunt anagrame";
}
int anagrama(char x[50],char y[50])
{
char *p;
if ((strcmp(x,"")==0)&&(strcmp(y,"")==0))
return 1;
p=strchr(y,x[0]); ///se cauta prima litera din x in y
if (p==0) return 0;
strcpy(x,x+1); ///daca se gaseste, se sterge din x si
///din y
strcpy(p,p+1);
return anagrama(x,y); ///autoapelarea funcţiei
}

CEX - 11/12/2021 Vasilica Rosca 6


Recursivitate. Aplicatii. Generari combinatoriale

MORSE

Fie n un numar natural. Să se genereze toate succesiunile de n (n<20) caractere '.'


si '_'.
Vom genera într-un vector a succesiunea de caractere conform cu cerinţele
problemei. De exemplu pentru n=3 vom genera succesiunile de “.” şi “_” în
ordinea de mai jos:
...
.._
._.
.__
_..
_._
__.
___

Soluţie

Pentru a obţine această succesiune va trebui să reapelăm funcţia de două ori, o


dată pentru a completa “.” ,a doua oară pentru a completa “_”. Reapelurile se termină
după ce am completat toate cele n caractere. Urmăriţi programul de mai jos şi
comentariile lui pentru a înţelege acest mecanism.

#include <iostream> //GENERARE “.” ŞI “_”


using namespace std;
char a[20]; ///în vectorul a vom afişa succesiunea de
/// puncte şi liniuţe “.” şi “_”
int n;
void afisare();
void generare(int i);
int main()
{
cin>>n; ///n-lungimea şirului de generat
generare(1);
}
void afisare() ///afişează conţinutul vectorului a
{
for(int i=1; i<=n; i++)
cout<<a[i]<<" ";
cout<<'\n';
}
void generare(int i) ///i-indică poziţia pe care o vom
///completa cu un '.' sau o '_'

{
if(i>n) ///dacă i a ajuns la n, înseamnă ca am completat
///toate cele n semne
afisare();
else
{
a[i]='.'; ///punem un '.' pe poziţia i a vectorului
generare(i+1); ///prin apeluri recursive,

CEX - 11/12/2021 Vasilica Rosca 7


Recursivitate. Aplicatii. Generari combinatoriale

///funcţia va completa toate cele n elemente cu '.'


a[i]='_'; ///pe măsură ce se coboară în stivă se
///înlocuieşte câte un '.' cu '_'
generare(i+1);
///ATENŢIE! după ce se înlocuieşte un
/// caracter, funcţia se va reapela pentru a completa '.' ,
///apoi iarăşi pentru '_'.
}
}

GENERARI COMBINATORIALE

Permutări

Fie n  N*. Scrieţi un program care generează recursiv permutările de ordin n.

Soluţie

Reprezentăm o soluţie ca pe un tablou p cu n elemente. Definite matematic,


permutările de ordin n sunt funcţii bijective de forma p:{1,2,…,n}  {1,2,..,n}, ceea
ce face ca un element p[i] al soluţiei să reprezinte valoarea asociată prin funcţie
elementului i.
Datorită faptului ca p este o funcţie injectivă, valorile din tabloul soluţie trebuie să fie
distincte între ele. Vom utiliza un vector caracteristic în care marcăm fiecare element
care este deja imaginea unui alt element, pentru a nu-l refolosi.

#include <iostream>
using namespace std;
int p[20],n, use[20];
void afisare()
{
int i;
for(i=1;i<=n;i++)
cout<<p[i]<<' ';
cout<<'\n';
}
void perm(int k)
{
int i;
if(k<=n)
{
for(i=1;i<=n;i++)
if(!use[i])
{
p[k]=i;
use[i]=1;
perm(k+1);
use[i]=0;
}
}
else
afisare();
}
int main()
{
cin>>n;

CEX - 11/12/2021 Vasilica Rosca 8


Recursivitate. Aplicatii. Generari combinatoriale

perm(1);
return 0;}

Aranjamente

Fie n  N* si m  N,m<=n. Scrieţi un program care să genereze recursiv


aranjamentele de n luate câte m.
De exemplu, pentru n=3 si m=2 , programul va genera
12
13
21
23
31
32

Soluţie

Aranjamentele de n luate câte m reprezintă toate submulţimile ordonate de m


elemente ale unei mulţimi de n elemente. Definite matematic, ele reprezintă funcţii
injective de forma f:{1,2,…,m}  {1,2,..,n}. Vom reprezenta o soluţie ca pe un tablou
cu m elemente x[i], i=1,m în care x[i]=elementul asociat prin funcţie elementului i.
Elementele soluţiei sunt diferite două câte două şi pentru a nu plasa o valoare care
este deja imaginea unui element utilizăm un vector caracteristic.

#include <iostream>
using namespace std;
int x[20],n,m, use[20];
void afisare()
{
int i;
for(i=1;i<=m;i++)
cout<<x[i]<<' ';
cout<<'\n';
}
void aranj(int k)
{
int i;
if(k<=m)
{
for(i=1;i<=n;i++)
if(!use[i])
{
x[k]=i;
use[i]=1;
aranj(k+1);
use[i]=0;
}
}
else
afisare();
}
int main()
{
cin>>n>>m;
aranj(1);
return 0;

CEX - 11/12/2021 Vasilica Rosca 9


Recursivitate. Aplicatii. Generari combinatoriale

Combinări

Fie n  N* si m  N,m<=n. Scrieţi un program care să genereze recursiv


combinările de n luate câte m.
De exemplu, pentru n=4 si m=2, programul va genera
12
13
14
23
24
34

Soluţie

Combinările de n elemente luate câte m reprezintă toate submulţimile de m


elemente ale unei mulţimi de n elemente. Spre deosebire de aranjamente, ordinea
elementelor din mulţime nu contează. De aceea, pentru a nu genera de mai multe ori
aceeasi submulţime, vom fixa o ordine în care elementele soluţiei vor fi
plasate( care este unică)- de ex., crescătoare. Aceasta observaţie ne va permite ca
valoarea unui element de pe pozitia k din vectorul c sa fie aleasă dintr-un domeniu de
valori limitat de valoarea elementului anterior si valoarea maximă care poate ocupa
nivelul k al soluţiei. Deci c[k]>c[k-1] si c[k]<=n-m+k.

#include <iostream>
using namespace std;
int c[20],n,m;
void afisare()
{
int i;
for(i=1;i<=m;i++)
cout<<c[i]<<' ';
cout<<'\n';
}
void comb(int k)
{
int i;
if(k<=m)

for(i=c[k-1]+1;i<=n-m+k;i++)

{c[k]=i;

comb(k+1);
}

else
afisare(m);
}
int main()
{
cin>>n>>m;
comb(1);

CEX - 11/12/2021 Vasilica Rosca 10


Recursivitate. Aplicatii. Generari combinatoriale

return 0;
}

Submulţimi

Se consideră mulţimea {1,2,…,n} Să se genereze recursiv toate submulţimile


sale( mulţimea părtilor).

Soluţie

Vom reprezenta o submulţime ca pe un vector cu dimensiune variabilă( de la 1 la n),


în care elementele vor fi plasate crescător ( s[k]>s[k-1]), pentru a evita generarea
redundantă. Orice soluţie de dimensiune k<=n va fi afişată.

#include <iostream>
using namespace std;
int s[20],n;
void afisare(int k)
{
int i;
for(i=1; i<=k; i++)
cout<<s[i]<<' ';
cout<<'\n';
}
void subm(int k)
{
int i;
for(i=s[k-1]+1; i<=n; i++)

{
s[k]=i;
if(k<=n)
afisare(k);
subm(k+1);
}

}
int main()
{
cin>>n;
subm(1);
return 0;
}

Produs cartezian

Se consideră n  N si A1,A2,…,An n mulţimi cu respectiv c1,c2,…,cn elemente


fiecare( ci= card Ai). Să se genereze elementele produsului cartezian A1xA2x…xAn.

Soluţie

Prin definiţie, produsul cartezian A1xA2x…xAn={(a1,a2,…an)/ ai  Ai,


 i  {1,2,…,n}}

CEX - 11/12/2021 Vasilica Rosca 11


Recursivitate. Aplicatii. Generari combinatoriale

Vom reprezenta o soluţie ca pe un tablou x cu n elemente, în care


x[i]  {1,2,..,ci},  i  {1,2,…,n}

#include <iostream>
using namespace std;
int x[20],n,c[20];
void afisare()
{
int i;
for(i=1; i<=n; i++)
cout<<x[i]<<' ';
cout<<'\n';
}
void cart(int k)
{
int i;
if(k<=n)

for(int i=1;i<=c[k];i++)
{x[k]=i;
cart(k+1);
}
else
afisare();

}
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
cin>>c[i];
cart(1);
return 0;
}

Partiţiile unei mulţimi

Fie n  N*. Să se scrie un program care să genereze recursiv partiţiile mulţimii


{1,2,…,n}.
Fie M o mulţime nevidă. Definim o partiţie a lui M o mulţime de clase { S1,S2,…,Sk}
pentru care:
A) Si  Ø,  i  {1,2,…,n} (clasele partiţiei sunt nevide)
B) Si  Sj =Ø,  i  j ( i,j  {1,2,…,n}) ( clase disjuncte)
C) S1  S2  …  Sk=M ( reuniunea claselor este mulţimea iniţială)

De exemplu, pentru n=3, programul va afişa:

P1: {1 2 3}
P2:{1 2 } {3}
P3:{1 3} {2}
P4:{1} {2 3}
P5:{1} {2} {3}

CEX - 11/12/2021 Vasilica Rosca 12


Recursivitate. Aplicatii. Generari combinatoriale

Soluţie

Vom considera o soluţie ca fiind un vector p cu n elemente a caror semnificaţie este


p[i]= indicele clasei din care face parte elementul i.

#include <iostream>
using namespace std;
int p[20],n,ns;// ns= numarul curent de clase care formeaza partitia
void afisare(int k)
{
int i,j;
for(i=1; i<=ns; i++)///fixez cate o clasa a partitiei
{
cout<<'{'; /// afisez toate valorile din clasa fixata
for(j=1; j<=n; j++)
if(p[j]==i)
cout<<j<<' ';
cout<<"} ";
}
cout<<'\n';
}
void partitii(int k)/// pana la nivelul k, exista deja ns clase
{
int i;
if(k<=n)
{
for(i=1; i<=ns; i++)/// plasez pe rand in solutie fiecare
/// clasa deja existenta
{
p[k]=i;
partitii(k+1);
}
p[k]=++ns; /// generez alte solutii plasand intr-o clasa
/// noua elementul k
partitii(k+1);
ns--; /// la revenirea din apel restaurez numarul
/// de clase
}
else
afisare(n);

}
int main()
{
cin>>n;
partitii(1);
return 0;
}

CEX - 11/12/2021 Vasilica Rosca 13


Recursivitate. Aplicatii. Generari combinatoriale

Partiţiile unui numar natural n

Fie n  N*. Să se scrie un program care să genereze recursiv partiţiile numărului n.


Numim partiţie a lui n o descompunere a sa în sume de forma p1+p2+…+pk=n.
Valorile pot fi numere distincte sau nu, numere prime, pare/impare, etc.
Daca n=5 şi dorim o descompunere în valori nu neapărat distincte, obţinem

n=1+1+1+1+1
n=1+1+1+2
n=1+1+3
n=1+2+2
n=1+4
n=2+3
n=5

Soluţie

Vom reprezenta o soluţie ca pe un vector în care pe fiecare nivel i vom plasa o valoare
mai mare sau egală cu elementul anterior p[k]>=p[k-1]( pentru aceasta, plasăm pe
nivelul 0 al soluţiei valoarea 1, ca valoare de referinţă) şi mai mică sau egală cu n-
suma valorilor deja plasate în solutie(sp). Vom considera aceasta sumă parţială ca pe
o variabilă globală la care adaugăm fiecare element al soluţiei înainte de urcarea în
recursie şi pe care o vom restaura la valoarea anterioară la coborârea din recursie.
Avem o partiţionare a lui n când suma parţială este egală cu n.

#include <iostream>
using namespace std;
int p[20],n,sp;
void afisare(int k)
{
int i,j;
cout<<"n=";
for(i=1; i<=k; i++)
cout<<p[i]<<' ';
cout<<'\n';
}

void part_num(int k)
{
int i;
if(sp<n)
{
for(i=p[k-1]; i<=n-sp; i++)
{
p[k]=i;
sp=sp+p[k];
part_num(k+1);
sp=sp-p[k];
}

}
else

CEX - 11/12/2021 Vasilica Rosca 14


Recursivitate. Aplicatii. Generari combinatoriale

afisare(k-1);
}

int main()
{
cin>>n;
p[0]=1;
part_num(1);
return 0;
}

TEMA de lucru in clasa-

#1809, #3095,#830

Pbinfo.ro

#1842-https://www.pbinfo.ro/probleme/1842/crearenumarrec
#826-https://www.pbinfo.ro/probleme/826/cifminparrec
#831-https://www.pbinfo.ro/probleme/831/generare3
#839-https://www.pbinfo.ro/probleme/839/vraja2
#3605-https://www.pbinfo.ro/probleme/3605/descprime
#841-https://www.pbinfo.ro/probleme/841/bomber

Tema suplimentara

Windows-http://campion.edu.ro/arhiva/index.php?page=pro
blem&action=view&id=520
Imagine-http://campion.edu.ro/arhiva/index.php?page=pro
blem&action=view&id=771

CEX - 11/12/2021 Vasilica Rosca 15

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