Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Replace int64_t to uint64_t + add namespace + detailed documentation
- Loading branch information
1 parent
2ba05ed
commit e09a057
Showing
1 changed file
with
105 additions
and
87 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,122 +1,140 @@ | ||
/** | ||
* @file | ||
* @brief This program aims at calculating nCr modulo p | ||
* | ||
* @details nCr is defined as n! / (r! * (n-r)!) where n! represents factorial | ||
* of n. In many cases, the value of nCr is too large to fit in a 64 bit integer. | ||
* Hence, in competitive programming, there are many problems or subproblems | ||
* to compute nCr modulo p where p is a given number. | ||
* @author [Kaustubh Damania](https://github.com/KaustubhDamania) | ||
*/ | ||
|
||
#include <cassert> | ||
#include <iostream> | ||
#include <vector> | ||
#include <cassert> /// for assert | ||
#include <iostream> /// for io operations | ||
#include <vector> /// for std::vector | ||
|
||
/** Class which contains all methods required for calculating nCr mod p | ||
* | ||
*/ | ||
class NCRModuloP { | ||
private: | ||
std::vector<int64_t> fac; | ||
int64_t p; | ||
|
||
public: | ||
/** Constructor which precomputes the values of n! % mod from n=0 to size | ||
* and stores them in vector 'fac' | ||
* @params[in] the numbers 'size', 'mod' | ||
/** | ||
* @namespace math | ||
* @brief Mathematical algorithms | ||
*/ | ||
namespace math { | ||
/** | ||
* @brief Class which contains all methods required for calculating nCr mod p | ||
*/ | ||
NCRModuloP(int64_t size, int64_t mod) { | ||
p = mod; | ||
fac = std::vector<int64_t>(size); | ||
fac[0] = 1; | ||
for (int i = 1; i <= size; i++) { | ||
fac[i] = (fac[i - 1] * i) % p; | ||
class NCRModuloP { | ||
private: | ||
std::vector<uint64_t> fac; | ||
uint64_t p; | ||
public: | ||
/** Constructor which precomputes the values of n! % mod from n=0 to size | ||
* and stores them in vector 'fac' | ||
* @params[in] the numbers 'size', 'mod' | ||
*/ | ||
NCRModuloP(uint64_t size, uint64_t mod){ | ||
p = mod; | ||
fac = std::vector<uint64_t>(size); | ||
fac[0] = 1; | ||
for (int i = 1; i <= size; i++) { | ||
fac[i] = (fac[i - 1] * i) % p; | ||
} | ||
} | ||
} | ||
|
||
/** Finds the value of x, y such that a*x + b*y = gcd(a,b) | ||
* | ||
* @params[in] the numbers 'a', 'b' and address of 'x' and 'y' from above | ||
* equation | ||
* @returns the gcd of a and b | ||
*/ | ||
int64_t gcdExtended(int64_t a, int64_t b, int64_t *x, int64_t *y) { | ||
if (a == 0) { | ||
*x = 0, *y = 1; | ||
return b; | ||
} | ||
/** Finds the value of x, y such that a*x + b*y = gcd(a,b) | ||
* | ||
* @params[in] the numbers 'a', 'b' and address of 'x' and 'y' from above | ||
* equation | ||
* @returns the gcd of a and b | ||
*/ | ||
uint64_t gcdExtended(uint64_t a, uint64_t b, int64_t *x, int64_t *y) { | ||
if (a == 0) { | ||
*x = 0, *y = 1; | ||
return b; | ||
} | ||
|
||
int64_t x1 = 0, y1 = 0; | ||
int64_t gcd = gcdExtended(b % a, a, &x1, &y1); | ||
int64_t x1 = 0, y1 = 0; | ||
uint64_t gcd = gcdExtended(b % a, a, &x1, &y1); | ||
|
||
*x = y1 - (b / a) * x1; | ||
*y = x1; | ||
return gcd; | ||
} | ||
|
||
/** Find modular inverse of a with m i.e. a number x such that (a*x)%m = 1 | ||
* | ||
* @params[in] the numbers 'a' and 'm' from above equation | ||
* @returns the modular inverse of a | ||
*/ | ||
int64_t modInverse(int64_t a, int64_t m) { | ||
int64_t x = 0, y = 0; | ||
int64_t g = gcdExtended(a, m, &x, &y); | ||
if (g != 1) { // modular inverse doesn't exist | ||
return -1; | ||
} else { | ||
int64_t res = (x % m + m) % m; | ||
return res; | ||
*x = y1 - (b / a) * x1; | ||
*y = x1; | ||
return gcd; | ||
} | ||
} | ||
|
||
/** Find nCr % p | ||
* | ||
* @params[in] the numbers 'n', 'r' and 'p' | ||
* @returns the value nCr % p | ||
*/ | ||
int64_t ncr(int64_t n, int64_t r, int64_t p) { | ||
// Base cases | ||
if (r > n) { | ||
return 0; | ||
/** Find modular inverse of a with m i.e. a number x such that (a*x)%m = 1 | ||
* | ||
* @params[in] the numbers 'a' and 'm' from above equation | ||
* @returns the modular inverse of a | ||
*/ | ||
int64_t modInverse(uint64_t a, uint64_t m) { | ||
int64_t x = 0, y = 0; | ||
uint64_t g = gcdExtended(a, m, &x, &y); | ||
if (g != 1) { // modular inverse doesn't exist | ||
return -1; | ||
} | ||
else { | ||
int64_t res = ((x + m) % m); | ||
return res; | ||
} | ||
} | ||
if (r == 1) { | ||
return n % p; | ||
} | ||
if (r == 0 || r == n) { | ||
return 1; | ||
} | ||
// fac is a global array with fac[r] = (r! % p) | ||
int64_t denominator = modInverse(fac[r], p); | ||
if (denominator < 0) { // modular inverse doesn't exist | ||
return -1; | ||
} | ||
denominator = (denominator * modInverse(fac[n - r], p)) % p; | ||
if (denominator < 0) { // modular inverse doesn't exist | ||
return -1; | ||
|
||
/** Find nCr % p | ||
* | ||
* @params[in] the numbers 'n', 'r' and 'p' | ||
* @returns the value nCr % p | ||
*/ | ||
int64_t ncr(uint64_t n, uint64_t r, uint64_t p) { | ||
// Base cases | ||
if (r > n) { | ||
return 0; | ||
} | ||
if (r == 1) { | ||
return n % p; | ||
} | ||
if (r == 0 || r == n){ | ||
return 1; | ||
} | ||
// fac is a global array with fac[r] = (r! % p) | ||
int64_t denominator = modInverse(fac[r], p); | ||
if (denominator < 0) { // modular inverse doesn't exist | ||
return -1; | ||
} | ||
denominator = (denominator * modInverse(fac[n - r], p)) % p; | ||
if (denominator < 0) { // modular inverse doesn't exist | ||
return -1; | ||
} | ||
return (fac[n] * denominator) % p; | ||
} | ||
return (fac[n] * denominator) % p; | ||
} | ||
}; | ||
}; | ||
} // namespace math | ||
|
||
void tests(NCRModuloP ncrObj) { | ||
/** | ||
* @brief Test implementations | ||
* @param ncrObj object which contains the precomputed factorial values and | ||
* ncr function | ||
* @returns void | ||
*/ | ||
void tests(math::NCRModuloP ncrObj) { | ||
// (52323 C 26161) % (1e9 + 7) = 224944353 | ||
assert(ncrObj.ncr(52323, 26161, 1000000007) == 224944353); | ||
// 6 C 2 = 30, 30%5 = 0 | ||
assert(ncrObj.ncr(6, 2, 5) == 0); | ||
assert(ncrObj.ncr(6,2,5) == 0); | ||
// 7C3 = 35, 35 % 29 = 8 | ||
assert(ncrObj.ncr(7, 3, 29) == 6); | ||
assert(ncrObj.ncr(7,3,29) == 6); | ||
} | ||
|
||
|
||
/** | ||
* @brief Main function | ||
* @returns 0 on exit | ||
*/ | ||
int main() { | ||
// populate the fac array | ||
const int64_t size = 1e6 + 1; | ||
const int64_t p = 1e9 + 7; | ||
NCRModuloP ncrObj = NCRModuloP(size, p); | ||
const uint64_t size = 1e6 + 1; | ||
const uint64_t p = 1e9 + 7; | ||
math::NCRModuloP ncrObj = math::NCRModuloP(size, p); | ||
// test 6Ci for i=0 to 7 | ||
for (int i = 0; i <= 7; i++) { | ||
std::cout << 6 << "C" << i << " = " << ncrObj.ncr(6, i, p) << "\n"; | ||
} | ||
tests(ncrObj); | ||
tests(ncrObj); // execute the tests | ||
std::cout << "Assertions passed\n"; | ||
} |