Documente Academic
Documente Profesional
Documente Cultură
Produsul cartezian
Fie n∈N și mulțimile A1, A2,…, An. Produsul cartezian A1×A2×…× An este mulțimea
n-uplurilor (x1,x2,…,xn) cu proprietatea că x1∈A1, x2∈A2, …, xn∈An.
Regula produsului: Dacă mulțimile A1, A2., …, An au respective k1,k2., …,kn atunci
numărul de elemente al produsului cartezian A1×A2×…×An este k1⋅k2⋅…⋅kn.
1. ∅
2. 1 ; 2 ; 3
3. 1,2 ;1,3 ; 2,3
4. 1,2,3
Permutări
Se numește permutare o corespondență biunivocă (bijecție) între o mulțime finită și ea
însăși.
Altfel spus, se numește permutare a unei mulțimi finite o aranjare a elementelor sale într-
o anumită ordine.
(123123),(123132),(123213),(123231),(123312),(123321)
Dacă σ=(123312), atunci σ(1)=3,σ(2)=1 și σ(3)=2.
Numărul de permutări al unei mulțimi cu n elemente este Pn=n!=1⋅2⋅⋅…⋅n.
Acest articol conține mai multe despre factorialul unui număr.
Numărul permutărilor unei mulțimi crește foarte repede. Pentru valori mici ale lui n,
numărul permutărilor depășește domeniul de valori al datelor întregi, fiind necesară
implementarea operațiilor pe numere mari.
Problema
Fie un număr natural n. Să se afișeze, în ordine lexicografică, permutările
mulțimii {1,2,,⋯,n}{1,2,,⋯,n}.
Lectia nr. 3. – Elemente de combinatorica si jocuri combinatoriale
Exemplu
Pentru n=3, se va afișa:
1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1
Rezolvare
Bineînțeles, vom rezolva problema prin metoda backtracking. Vectorul soluție x[] va
reprezenta o permutare candidat. Să ne gândim care sunt proprietățile unei permutări, pe
care le va respecta și vectorul x[]:
Sursă C++
Următorul program afișează pe ecran permutările, folosind un algoritm recursiv:
#include <iostream>
using namespace std;
int x[10] ,n;
void Afis()
{
for( int j=1;j<=n;j++)
cout<<x[j]<<" ";
cout<<endl;
Lectia nr. 3. – Elemente de combinatorica si jocuri combinatoriale
}
bool OK(int k){
for(int i=1;i<k;++i)
if(x[k]==x[i])
return false;
return true;
}
bool Solutie(int k)
{
return k == n;
}
void back(int k){
for(int i=1 ; i<=n ; ++i)
{
x[k]=i;
if( OK(k) )
if(Solutie(k))
Afis();
else
back(k+1);
}
}
int main(){
cin>>n;
back(1);
return 0;
}
Varianta iterativă
În general, algoritmii nerecursivi sunt mai buni decât cei recursivi, deși uneori sunt mai dificil
de urmărit. Următorul program iterativ afișează și el permutările pe ecran:
#include <iostream>
using namespace std;
int n , x[100];
void afisare(int k){
Lectia nr. 3. – Elemente de combinatorica si jocuri combinatoriale
Permutări cu repetiție
Considerăm un set de n obiecte de k.tipuri. Avem n1 obiecte de tipul 1, n2 obiecte de tipul
2, …, nk obiecte de tipul k și n1+n2+…+nk=n. Numărul de permutări distincte ale celor n
obiecte este:
PR(n)=n! / (n1!⋅n2!⋅…⋅nk!)
Exemplu: Numărul de anagrame distincte ale cuvântului ABABA este:
Aranjamente
Dacă A este o mulțime cu n elemente, submulțimile ordonate cu k elemente ale lui A, 0
≤ k ≤ n se numesc aranjamente a n elemente luate câte k.
Combinări
Pentru o mulțime dată o combinare reprezintă o modalitate de alegere a unor elemente,
fără a ține cont de ordinea lor. Se numesc combinări de k elemente submulțimile cu k
elemente ale mulțimii, presupuse cu n elemente. Numărul de asemenea submulțimi se
numește combinări de n luate câte k și se notează Cnk.
Formule:
• Cnk=Ank / Pk
• Cnk=n⋅(n−1)⋅…⋅(n−k+1). / (1⋅2⋅…⋅k)
• Cnk=n! / (k!⋅(n−k)!)
• Cnk={ 1 dacă k=0,
(n–k+1)/k⋅Cnk altfel.
Probleme infoarena
Lectia nr. 3. – Elemente de combinatorica si jocuri combinatoriale
https://www.infoarena.ro/problema/superbec
Proprietăți
Putem rearanja elementele tabloului astfel:
Problemă
Se citește un număr natural n. Afișați linia n din triunghiul lui Pascal.
Soluție cu combinări
O primă soluție constă în calcularea valorii Cnk pentru fiecare k∈{0,1,2,⋯, n}.
#include <iostream>
using namespace std;
int comb(int n , int k)
{
int p = 1;
for(int i = 1 ; i <= k ; i ++)
p = p * (n-i+1) / i;
return p;
}
int main()
{
int n;
cin >> n;
for(int i = 0 ; i <= n ; i ++)
cout << comb(n,i) << " ";
Lectia nr. 3. – Elemente de combinatorica si jocuri combinatoriale
return 0;
}
Soluție cu matrice
În soluția următoare aplicăm formula Ai,j=Ai−1,j−1+Ai−1,j, folosind un tablou
unidimensional:
#include <iostream>
using namespace std;
int main()
{
int n, A[31][31];
cin >> n;
for(int i = 0 ; i <= n ; i ++)
{
A[i][0] = A[i][i] = 1;
for(int j = 1 ; j < i ; j ++)
A[i][j] = A[i-1][j-1] + A[i-1][j];
}
for(int j = 0 ; j <= n ; j ++)
cout << A[n][j] << " ";
Lectia nr. 3. – Elemente de combinatorica si jocuri combinatoriale
return 0;
}
int main()
{
int n, v[31],u[31];
cin >> n;
v[0] = 0;
for(int i = 1 ; i <= n ; i ++)
{
u[0] = u[i] = 1;
for(int j = 1 ; j < i ; j ++)
u[j] = v[j-1] + v[j];
for(int j = 0 ; j <= i ; j ++)
v[j] = u[j];
}
for(int j = 0 ; j <= n ; j ++)
cout << v[j] << " ";
return 0;
}
int main()
{
Lectia nr. 3. – Elemente de combinatorica si jocuri combinatoriale
Probleme infoarena:
https://www.infoarena.ro/problema/magic4
https://www.infoarena.ro/problema/xor3
𝑛+𝑘
Cn=C2nn–C2nn+1=1/(n+1)⋅C2nn=∏𝑛𝑘=2 ,pentru n≥0
𝑘
O formulă recursivă:
𝑛
𝐶𝑛+1 = ∑ 𝐶𝑖 ∙ 𝐶𝑛−𝑖
𝑖=0
/\
/\ /\ /\/\ / \
/\/\/\ / \/\ /\/ \ / \ / \
Alte probleme care au care rezultat numărul lui Catalan sunt pe Wikipedia sau în
documentul Exercises on Catalan and Related Numbers, Cambridge University Press,
Richard P_ Stanley, June 1998.
Probleme inforena :
https://www.infoarena.ro/problema/permutare4
https://www.infoarena.ro/problema/colors
#include<iostream>
Lectia nr. 3. – Elemente de combinatorica si jocuri combinatoriale
#include <iostream>
using namespace std;
unsigned long int catalanDP(unsigned int n)
{
unsigned long int catalan[n + 1];
catalan[0] = catalan[1] = 1;
for (int i = 2; i <= n; i++) {
catalan[i] = 0;
Lectia nr. 3. – Elemente de combinatorica si jocuri combinatoriale
1. Ai⊆A,∀i=1,k
2. A1∪A2∪…∪Ak=A
3. Ai∩Aj=∅, ∀i≠j
• 1,2,3
• 1,2,3, 1,3,2, 1,2,3
• 1,2,3
#include <bits/stdc++.h>
using namespace std;
int main() {
int n, k; cin >> n >> k;
vector<vector<int>> stir(n + 1, vector<int>(k + 1));
stir[0][0] = 1;
for (int i = 1; i <= n; i++)
Lectia nr. 3. – Elemente de combinatorica si jocuri combinatoriale
Cazurile particulare sunt: s(0,0)=1, s(n,0)=0 (n>0) și s(0,k)=0 (k>0). Motivele sunt
similare cu cele de la numerele de speța a II-a.
Recurența este: s(n,k)=s(n−1,k−1)+(n−1)⋅s(n−1,k)
#include<fstream>
#define MOD 98999
using namespace std;
ifstream fin("stirling.in");
ofstream fout("stirling.out");
int s[202][202],S[202][202];
int main(){
int t,p,n,m,i,j;
s[0][0]=1; S[0][0]=1;
for(i=1;i<=200;i++)
Lectia nr. 3. – Elemente de combinatorica si jocuri combinatoriale
for(j=1;j<=i;j++){
s[i][j]=s[i-1][j]*(1-i)+s[i-1][j-1];
s[i][j]%=MOD;
S[i][j]=S[i-1][j]*j+S[i-1][j-1];
S[i][j]%=MOD;
}
fin>>t;
for(i=1;i<=t;i++) {
fin>>p>>n>>m;
if(p==1) fout<<s[n][m];
else fout<<S[n][m];
fout<<'\n';
}
return 0;
}
Numărul total de partiții ale unei mulțimi cu n elemente este numărul lui Bell:
𝑛
𝐵𝑛 = ∑ 𝑆(𝑛, 𝑘)
𝑘=0
Începând cu B0=B1=1, primele numere Bell sunt: 1,1,2,5,15,52,203,877,4140,….
Numerele Bell pot fi construite cu ajutorul așa-numitului triunghi Bell. Vom construi
(parțial) un tablou A[][]:
• A[0][1] = 1
• primul element de pe liniile următoare este egal cu ultimul de pe linia
precedentă: A[i][1] = A[i-1][i]
• celelalte elemente ale fiecărei linii sunt egale cu suma celor după elemente din
stânga (linia curentă și linia precedentă): A[i][j] = A[i][j-1] + A[i-1][j-
1], pentru j=2,..., i+1
• primul element de pe fiecare linie este numărul Bell corespunzător: Bn=A[n][1].
Lectia nr. 3. – Elemente de combinatorica si jocuri combinatoriale
Soluție:
#include<iostream>
using namespace std;
int bellNumber(int n) {
int bell[n+1][n+1];
bell[0][0] = 1;
for (int i=1; i<=n; i++) {
bell[i][0] = bell[i-1][i-1];
for (int j=1; j<=i; j++)
bell[i][j] = bell[i-1][j-1] + bell[i][j-1];
}
return bell[n][0];
}
int main() {
for (int n=0; n<=5; n++)
cout<<"Bell Number "<<n<<" este "<< bellNumber(n)<<endl;
return 0;
}
Numerele Narayana
Vezi paper20.pdf
Soluție
#include<bits/stdc++.h>
using namespace std;
int Nara(int n, int k)
{
int C[n + 1][k + 1];
for (int i = 0; i <= n; i++)
for (int j = 0; j <= min(i, k); j++)
if (j == 0 || j == i) C[i][j] = 1;
else C[i][j] = C[i - 1][j - 1] + C[i - 1][j];
return C[n][k] * C[n][k - 1];
}
int Solve(int n, int k){return (Nara(n, k)) / n;}
int main(){
Lectia nr. 3. – Elemente de combinatorica si jocuri combinatoriale
int n = 8, k = 5;
cout << Solve(n, k) << endl;
return 0;
}