Clasa a XII-a · Matematică pentru programareRead in English
Combinări modulo un număr prim în C++
În engleză: Combinations modulo a prime
Ideea
Calculăm C(n, k) mod P, cu P = 1 000 000 007 (prim) și n ≤ 10^6. Avem C(n, k) = n! / (k! * (n-k)!), dar împărțirea nu se poate face direct pe resturi. Înlocuim împărțirea cu înmulțirea cu inversul modular: după mica teoremă a lui Fermat, x^(P-1) ≡ 1 (mod P) pentru x nedivizibil cu P, deci inversul lui x este x^(P-2) mod P, calculat cu exponențiere rapidă. Precalculăm factorialele modulo P, apoi C(n, k) = n! * inv(k!) * inv((n-k)!) mod P.
Descrierea în engleză
We compute C(n, k) mod P, with P = 1,000,000,007 (prime) and n ≤ 10^6. We have C(n, k) = n! / (k! * (n-k)!), but division cannot be done directly on remainders. We replace division with multiplication by the modular inverse: by Fermat's little theorem, x^(P-1) ≡ 1 (mod P) for x not divisible by P, so the inverse of x is x^(P-2) mod P, computed with fast exponentiation. We precompute the factorials modulo P, then C(n, k) = n! * inv(k!) * inv((n-k)!) mod P.
- Complexitate
- O(n + log P)
- Clasa
- Clasa a XII-a (Grade 12)
- Capitol
- Matematică pentru programare
Exemple
Intrare
5 2
Ieșire așteptată
10
Intrare
100 50
Ieșire așteptată
538992043
Intrare
1000000 500000
Ieșire așteptată
996692777
Intrare
3 7
Ieșire așteptată
0
Codul C++
Codul complet, cu comentarii în română. Îl poți modifica liber și îl poți reseta oricând.
/*
* Titlu: Combinări modulo un număr prim
* Clasa: 12
* Capitol: Matematică pentru programare
* Descriere: Calculăm C(n, k) mod P, cu P = 1 000 000 007 (prim) și n ≤ 10^6.
* Avem C(n, k) = n! / (k! * (n-k)!), dar împărțirea nu se poate face
* direct pe resturi. Înlocuim împărțirea cu înmulțirea cu inversul
* modular: după mica teoremă a lui Fermat, x^(P-1) ≡ 1 (mod P) pentru
* x nedivizibil cu P, deci inversul lui x este x^(P-2) mod P, calculat
* cu exponențiere rapidă. Precalculăm factorialele modulo P, apoi
* C(n, k) = n! * inv(k!) * inv((n-k)!) mod P.
* Complexitate: O(n + log P)
*
* Exemplu
* Intrare: 5 2
* Ieșire: 10
*
* Exemplu
* Intrare: 100 50
* Ieșire: 538992043
*
* Exemplu
* Intrare: 1000000 500000
* Ieșire: 996692777
*
* Exemplu
* Intrare: 3 7
* Ieșire: 0
*/
#include <iostream>
#include <vector>
using namespace std;
const long long P = 1000000007;
long long putere(long long baza, long long exponent)
{
long long rezultat = 1;
baza %= P;
while (exponent > 0)
{
if (exponent % 2 == 1)
rezultat = rezultat * baza % P;
baza = baza * baza % P;
exponent /= 2;
}
return rezultat;
}
long long invers(long long x)
{
return putere(x, P - 2); // mica teoremă a lui Fermat
}
int main()
{
int n, k;
cin >> n >> k;
if (k < 0 || k > n)
{
cout << 0 << '\n';
return 0;
}
vector<long long> fact(n + 1);
fact[0] = 1;
for (int i = 1; i <= n; i++)
fact[i] = fact[i - 1] * i % P;
long long rez = fact[n] * invers(fact[k]) % P * invers(fact[n - k]) % P;
cout << rez << '\n';
return 0;
}
Rulează pe un g++ adevărat (C++17, -O2) prin Compiler Explorer. Programele trebuie să se termine repede: există o limită de timp.