Sunteți pe pagina 1din 8

Elemente de combinatorică

Combinări
Definiţie
Fie nN* şi kN, kn. Se numesc combinări de n luate câte k submulţimile de k elemente ale
mulţimii {1, 2, ..., n}.
De exemplu, pentru n=4 şi k=2:
1 2
1 3
1 4
2 3
2 4
3 4
Observaţie
Ordinea elementelor din submulţime nu contează. Din acest motiv, pentru a nu genera de mai multe ori aceeaşi
submulţime (de exemplu, 1 2 şi 2 1) vom stabili o ordine convenabilă pentru elementele submulţimii – ordinea
crescătoare.
Definiţie
Numărul de combinări de n luate câte k se notează cu 𝐶𝑛𝑘 .
La matematică aţi studiat (sau urmează) mai multe formule pentru calculul combinărilor. Ne vom concentra
pe două dintre acestea şi le vom implementa.
Varianta 1. Relaţie de recurenţă
C nk = C nk−1 + C nk−−11
Evident, 𝐶𝑛0 = 𝐶𝑛𝑛 = 1 şi 𝐶𝑛𝑘 = 0, pentru k>n.
Formula stă la baza algoritmului de calcul al combinărilor cunoscut sub denumirea „triunghiul lui
Pascal”. De exemplu, triunghiul lui Pascal pentru n=5, este:
1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
Observaţi că pe linia i în triunghiul lui Pascal se găsesc combinările de k elemente ale mulţimii {1,
2, ..., i} (k variind de la 0 la i).
Implementarea 1:
Vom implementa triunghiul ca o matrice T cu n linii şi n coloane din care va fi utilizată doar jumătatea de
sub diagonala principală (i<=j). Întrucât combinările cresc foarte repede va fi necesar să lucrăm cu numere
mari.
#include <fstream>
#define LGMAX 100
#define NMAX 102
using namespace std;
ifstream fin("pascal.in");
ofstream fout("pascal.out");

typedef int NrMare[LGMAX];


void afisare(NrMare a, int lga);
void suma(NrMare a, int lga, NrMare b, int lgb, NrMare c, int& lgc);
void zero(NrMare a, int inc, int sf);

Prof. Emanuela Cerchez 1


int n, m;
NrMare T[NMAX][NMAX];
int lg[NMAX][NMAX]; ///lg[i][j]=lungimea numarului mare T[i][j]
void pascal();

int main()
{fin>>n>>m;
pascal();
afisare(T[n][m],lg[n][m]);
return 0;
}

void afisare(NrMare a, int lga)


{ int i;
for (i=lga-1; i>=0; i--) fout<<a[i];
fout<<'\n';
}

void suma(NrMare a, int lga, NrMare b, int lgb, NrMare c, int& lgc)
{int i, t, val;
if (lga<lgb)
{ zero(a,lga,lgb); lgc=lgb; }
else
{ zero(b,lgb,lga); lgc=lga; }
for (i=t=0; i<lgc; i++)
{
val=a[i]+b[i]+t;
c[i]=val%10;
t=val/10;
}
if (t) c[lgc++]=t;
}

void zero(NrMare a, int inc, int sf)


{int i;
for (i=inc; i<sf; i++) a[i]=0;
}

void unu(NrMare a, int & lga)


///a=1;
{ lga=1; a[0]=1; }

void pascal()
{int i, j;
unu(T[0][0], lg[0][0]); unu(T[1][0], lg[1][0]); unu(T[1][1], lg[1][1]);
for (i=2; i<=n; i++)
///calculez combinarile cu elemente din multimea {1,2,...,i}
{
unu(T[i][0],lg[i][0]); unu(T[i][i],lg[i][i]);
for (j=1; j<i; j++)
suma(T[i-1][j-1],lg[i-1][j-1],T[i-1][j],lg[i-1][j],T[i][j],lg[i][j]);
}
}
Implementarea 2:
Tabloul T fiind tridimensional, spaţiul de memorie necesar este O(NMAX*NMAX*LGMAX). Prin urmare, în
funcţie de n, memoria necesară tabloului este posibil să fie prea mare. Observăm că nu este necesar să
memorăm tot triunghiul T, doar două linii succesive sunt suficiente. Funcţia pascal() devine astfel:

Prof. Emanuela Cerchez 2


void pascal()
{int i, j;
unu(T[0][0], lg[0][0]); unu(T[0][1], lg[0][1]);
for (i=2; i<=n; i++)
///calculez combinarile cu elemente din multimea {1,2,...,i}
{unu(T[crt][0],lg[crt][0]); unu(T[crt][i],lg[crt][i]);
for (j=1; j<i; j++)
suma(T[prec][j-1],lg[prec][j-1],T[prec][j],lg[prec][j],T[crt][j],lg[crt][j]);
crt=1-crt; prec=1-prec; }
}
În funcţia main() afişăm T[prec][m].
Varianta 2.
O altă formulă de calcul a combinărilor este:
n!
C nk =
k!(n − k )!

Aparent utilizarea acestei formule este mai dificilă, fiind necesară împărţiri cu numere mari. O idee
care simplifică calculul este de a simplifica n! cu (n-k)! şi apoi de a calcula descompunerea în
factori primi a produsului (n-k+1)...n şi a lui k!. Pentru aceasta vom utiliza un vector p cu NMAX
elemente (p[i]=puterea factorului prim i în descompunerea în factori primi). Împărţirea se reduce
astfel la scăderea puterilor factorilor primi. Rezultatul se va putea calcula prin înmulţirea factorilor
primi (deci va fi necesar doar produsul dintre un număr mare şi un număr mic).
Exerciţiu
Implementaţi calculul combinărilor utilizând această metodă. Comparaţi complexitatea celor două
variante. Ce variantă este preferabilă?

Numărul lui Stirling de speţa a II-a


Numărul lui Stirling de speţa a II-a este notat cu S(n,k) şi reprezintă numărul de partiţii ale unei
mulţimi cu n elemente în k clase. Ordinea claselor, cât şi ordinea elementelor într-o clasă nu
contează.
Relaţie de recurenţă pentru numerele lui Stirling de speţa a II-a
Bazându-ne pe algoritmul de generare a partiţiilor unei mulţimi pe care l-aţi studiat, putem deduce o
relaţie de recurenţă pentru numerele lui Stirling de speţa a II-a. O partiţie a mulţimii {1, 2,..., n} în
k clase se poate obţine:
– din orice partiţie a mulţimii {1, 2, ..., n-1} în k-1 clase la care se adaugă o nouă clasă formată
numai din elementul n;
– din orice partiţie a mulţimii {1, 2, ..., n-1} în k clase, inserând elementul n în oricare dintre cele
k clase din această partiţie.
Obţinem relaţia de recurenţă:
S(n,k)=S(n-1,k-1)+k*S(n-1,k);
S(n,1)=S(n,n)=1.
Exerciţiu
Pe baza relaţiei de recurenţă de mai sus, scrieţi un program care să determine numărul de partiţii ale unei
mulţimi cu n elemente în k clase.

Prof. Emanuela Cerchez 3


O formulă pentru numerele lui Stirling de speţa a II-a
Numerele lui Stirling de speţa a II-a sunt descrise de formula:

1 k
S (n, k ) =   (−1)k −r  Ckr  r n
k ! r =0
Demonstraţia se poate face prin inducţie matematică.

Numărul lui Bell


Numărul de partiţii ale unei mulţimi cu n elemente se notează B(n) şi se numeşte numărul lui
Bell.
Din definiţia numerelor lui Stirling de speţă a II-a, deducem că:
B(n)=S(n,1)+S(n,2)+...+S(n,n); B(0)=1.

Numărare şiruri
Să se determine numărul şirurilor formate din k litere A şi m litere B, care au proprietatea: pentru orice
1≤i≤m+k, numărul de litere B nu depăşeşte numărul de litere A în prefixul şirului de lungime i.
Soluţie
Numărul acestor şiruri este nenul dacă şi numai dacă este îndeplinită condiţia m≤k. Numărul
şirurilor care conţin litera A de k ori, iar litera B de m ori este
Nr(k,m)=(m+k)!/(m!k!)=Comb(m+k,m)
unde cu Comb(n,m) notăm numărul de submulţimi de m elemente ale unei mulţimi cu n
elemente.
Dintre aceste şiruri, vom determina numărul şirurilor care nu verifică proprietatea cerută.
Să considerăm S un şir care conţine litera A de k ori, litera B de m ori şi care nu verifică proprietatea
cerută. Există în acest şir o poziţie 2p+1 (p≥0) astfel încât S[2p+1]=B, iar prefixul şirului S de
lungime 2p conţine p litere A şi p litere B. Vom alege p minim cu această proprietate şi transformăm
şirul S după cum urmează. Adăugăm pe prima poziţie o literă A. Se obţine astfel un şir format din m
litere B şi k+1 litere A, iar prefixul de lungime 2p+2 al acestui şir conţine un număr egal de litere A
şi B. În prefixul de lungime 2p+2 al şirului obţinut schimbăm litera A în litera B, iar litera B în litera
A. Prin această transformare numărul total de litere A şi B nu se modifică, iar prima literă din şir este
acum B.
Prin aceste transformări asociem în mod biunivoc unui şir care conţine litera A de k ori, litera B de m
ori şi care nu verifică proprietatea P un şir care conţine litera A de k+1 ori, litera B de m ori şi care
începe cu litera B. Adică numărul şirurilor care conţin litera A de k ori, litera B de m ori şi care nu
verifică proprietatea P este egal cu numărul şirurilor care conţin litera A de k+1 ori, litera B de m ori
şi care încep cu litera B (m<k+1).
Suprimând prima literă din aceste şiruri, obţinem toate şirurile formate din m-1 litere B şi k+1 litere
A. Numărul lor este Nr(k+1,m-1)=Comb(m+k,m-1).
Deci numărul şirurilor care conţin litera A de k ori, litera B de m ori şi satisfac proprietatea cerută
este:
Comb(m+k,m)-Comb(m+k,m-1)=Comb(m+k,m)*(k-m+1)/(k+1)

Prof. Emanuela Cerchez 4


Numărul lui Catalan
Dacă în problema precedentă vom avea k=m=n, atunci obţinem problema parantezelor (şirurile
formate din n perechi de paranteze rotunde care se închid corect). Numărul de şiruri formate din n
perechi de paranteze rotunde care se închid corect se numeşte numărul lui Catalan de ordin n şi este:
Catalan(n)=Comb(2n,n)/(n+1); Catalan(0)=Catalan(1)=1.
Observaţie
Datorită modului de obţinere a numerelor Catalan, prezentat în soluţia problemei precedente,
deducem un mod de calcul al numerelor Catalan, bazat pe triunghiul combinărilor al lui Pascal.
Catalan(n) se obţine din diferenţa dintre termenul n de pe linia 2n a triunghiului lui Pascal şi
termenul din stânga sa :
Catalan(n)= Comb(2n,n)-Comb(2n,n-1).
Din formula care descrie numerele Catalan, putem deduce şi o relaţie de recurenţă:
Catalan(0)=1
(n+2)Catalan(n+1)=(4n+2)Catalan(n), pentru n>0.
Conform formulei de mai sus, numerele lui Catalan constituie un şir de numere naturale, primii
termeni fiind:
1, 1, 2, 5, 14, 42, 132, 429, 1430, 4862, 16796, 58786, 208012, 742900, 2674440,
9694845, ...
Numerele lui Catalan intervin în numeroase probleme de combinatorică. Amintim unele dintre cele
mai cunoscute interpretări combinatorice ale numerelor lui Catalan.
1. Fie * o operaţie binară neasociativă. În câte moduri distincte poate fi parantezată expresia
a1*a2*...*an?
Soluţia acestei probleme a fost dată de matematicianul belgian Eugene Charles Catalan în 1838 şi
este Catalan(n-1).
2. Numărul de posibilităţi de a trianguliza (partiţiona în triunghiuri) un poligon cu n vârfuri cu
ajutorul a n-3 diagonale care nu se intersectează (exceptând extremităţile) este Catalan(n-2).
3. Numărul de moduri în care 2n persoane aşezate la o masă rotundă îşi pot strânge mâinile fără ca
braţele lor să se intersecteze este Catalan(n).
4. Numărul de profiluri montane distincte care se pot desena cu ajutorul a n caractere / şi n caractere
\, fără a coborî sub „nivelul mării” (linia orizontală care trece prin punctul de plecare) este
Catalan(n).
5. Să considerăm o tablă de şah de dimensiune (n+1)*(n+1) şi o piesă plasată iniţial într-un colţ
al tablei. O mutare constă în plasarea piesei în una dintre poziţiile alăturate (pe orizontală sau
verticală). Numărul de drumuri distincte de lungime 2n pe care le poate străbate piesa din colţul
iniţial în colţul opus, fără a intersecta diagonala principală este Catalan(n).
6. Numărul de permutări ce se pot obţine cu ajutorul unei stive în care se introduc succesiv numerele
1, 2, ..., n este Catalan(n).
O altă abordare recursivă
Să revenim la problema iniţială, problema parantezelor (să determinăm numărul de şiruri formate din
n perechi de paranteze care se închid corect).
Analizând problema pentru n=1, obţinem o soluţie (), pentru n=2 obţinem două soluţii ()() şi
(()).

Prof. Emanuela Cerchez 5


Observăm că orice şir format din n perechi de paranteze care se închid corect începe cu paranteză
deschisă şi există o paranteză închisă care corespunde acestei paranteze deschise.
Deci şirul poate fi partiţionat în două secvenţe sub forma: (X)Y, unde X este un şir de k perechi de
paranteze care se închid corect, iar Y este un şir de n-k-1 perechi de paranteze care se închid corect
(0≤k<n). Deducem astfel următoarea formulă de recurenţă:
Catalan(n)=Catalan(0)*Catalan(n-1)+ Catalan(1)*Catalan(n-2) +
Catalan(2)*Catalan(n-3) + ... + Catalan(n-1)*Catalan(0).

Prof. Emanuela Cerchez 6


Aplicaţie
Catalan
https://www.pbinfo.ro/probleme/2917/catalan
Soluţie
Ne vom baza pe ideea de a descompune numitorul şi numărătorul în factori primi şi apoi de a
calcula rezultatul
#include <iostream>
#define LGMAX 604
#define NMAX 2002
using namespace std;

typedef int NrMare[LGMAX];

void afisare(NrMare a, int lga);


void zero(NrMare a, int inc, int sf);
void calcul();
void descompunere(int x, int semn);
void produs (NrMare a, int lga, int b, NrMare c, int& lgc);

NrMare rez;
int lgrez;
int n;
int p[NMAX];

int main()
{int k;
cin>>n;
for (k=2; k<=n; k++)
{descompunere(n+k,1);
descompunere(k,-1);}
calcul();
afisare(rez, lgrez);
return 0;
}

void afisare(NrMare a, int lga)


{ int i;
for (i=lga-1; i>=0; i--)
cout<<a[i];
cout<<'\n';
}

void zero(NrMare a, int inc, int sf)


{ int i;
for (i=inc; i<sf; i++)
a[i]=0;
}

Prof. Emanuela Cerchez 7


void unu(NrMare a, int & lga)
///a=1;
{ lga=1; a[0]=1; }

void calcul()
{int i, j;
unu(rez,lgrez);
for (i=2; i<NMAX; i++)
for (j=0; j<p[i]; j++)
produs(rez,lgrez,i,rez,lgrez);
}

void descompunere(int x, int semn)


{int d=2;
while (d*d<=x)
{
while (x%d==0)
{p[d]+=semn;
x/=d;}
d++;
}
if (x>1) p[x]+=semn;
}

void produs (NrMare a, int lga, int b, NrMare c, int& lgc)


{int i, t, val;
if (b==0 || a[0]==0 && lga==1) {c[0]=0; lgc=1; return;}
for (i=t=0; i<lga; i++)
{
val=a[i]*b+t;
c[i]=val%10;
t=val/10;
}
lgc=lga;
while (t) {c[lgc++]=t%10; t/=10;}
}
Temă
De pe arhiva educaţională .campion rezolvaţi problemele: pm, depou, race şi expoziţie
Resurse
Prof. Marinel Şerban – Numere Catalan, numere Narayana
http://campion.edu.ro/arhiva/index.php?page=paper&action=view&id=20
Prof. Alin Burţa – Introducere în combinatorică
http://campion.edu.ro/arhiva/index.php?page=paper&action=view&id=32

Prof. Emanuela Cerchez 8

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