Documente Academic
Documente Profesional
Documente Cultură
Combinări
Definiţie
Fie nN* şi kN, kn. 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");
int main()
{fin>>n>>m;
pascal();
afisare(T[n][m],lg[n][m]);
return 0;
}
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 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:
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ă?
1 k
S (n, k ) = (−1)k −r Ckr r n
k ! r =0
Demonstraţia se poate face prin inducţie matematică.
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)
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 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);
}