diff --git a/math/ncr_modulo_p.cpp b/math/ncr_modulo_p.cpp index 8297032c5..f417935fc 100644 --- a/math/ncr_modulo_p.cpp +++ b/math/ncr_modulo_p.cpp @@ -1,109 +1,117 @@ /** * @file - * @brief This program aims at calculating nCr modulo p + * @brief This program aims at calculating nCr modulo p. To know more about it, + * visit https://cp-algorithms.com/combinatorics/binomial-coefficients.html * @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. + * 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 /// for assert -#include /// for io operations -#include /// for std::vector +#include /// for assert +#include /// for io operations +#include /// for std::vector + /** - * @namespace math - * @brief Mathematical algorithms - */ +* @namespace math +* @brief Mathematical algorithms +*/ namespace math { -/** - * @brief Class which contains all methods required for calculating nCr mod p - */ -class NCRModuloP { - private: - std::vector fac; - uint64_t p; + /** + * @namespace ncr_modulo_p + * @brief Functions for nCr modulo p implementation. + */ + namespace ncr_modulo_p { + /** + * @brief Class which contains all methods required for calculating nCr mod p + */ + class NCRModuloP { + private: + std::vector fac{}; + uint64_t p = 0; + 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(const uint64_t& size, const uint64_t& mod){ + p = mod; + fac = std::vector(size); + fac[0] = 1; + for (int i = 1; i <= size; i++) { + fac[i] = (fac[i - 1] * i) % 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(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 + */ + uint64_t gcdExtended(const uint64_t& a, const uint64_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; + uint64_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; + } - *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(const uint64_t& a, const 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; + } + } - /** 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; - } - } - - /** 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; - } -}; -} // namespace math + /** Find nCr % p + * + * @params[in] the numbers 'n', 'r' and 'p' + * @returns the value nCr % p + */ + int64_t ncr(const uint64_t& n, const uint64_t& r, const 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; + } + }; + } // namespace ncr_modulo_p +} // namespace math /** * @brief Test implementations @@ -111,15 +119,16 @@ class NCRModuloP { * ncr function * @returns void */ -void tests(math::NCRModuloP ncrObj) { +static void tests(math::ncr_modulo_p::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 @@ -128,11 +137,12 @@ int main() { // populate the fac array const uint64_t size = 1e6 + 1; const uint64_t p = 1e9 + 7; - math::NCRModuloP ncrObj = math::NCRModuloP(size, p); + math::ncr_modulo_p::NCRModuloP ncrObj = math::ncr_modulo_p::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); // execute the tests + tests(ncrObj); // execute the tests std::cout << "Assertions passed\n"; + return 0; }