Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit af72fab

Browse files
vil02Panquesito7realstealthninja
authored
fix: make interface of NCRModuloP fail-safe (TheAlgorithms#2469)
* fix: set proper size of fac * style: use std::size_t as a type of loop counter * style: use uint64_t as a type of loop counter * fix: remove p from the argument list of NCRModuloP::ncr * refactor: add utils namespace * refactor: use references in gcdExtended * refactor: add NCRModuloP::computeFactorialsMod * style: make NCRModuloP::ncr const * test: reorganize tests * test: add missing test cases * refactor: simplify logic * style: make example object const * style: use auto * style: use int64_t to avoid narrowing conversions * docs: update explanation why to import iostream * docs: remove `p` from docstr of `NCRModuloP::ncr` * docs: udpate doc-strs and add example() * Apply suggestions from code review Co-authored-by: David Leal <halfpacho@gmail.com> * dosc: add missing docs * feat: display message when all tests pass Co-authored-by: David Leal <halfpacho@gmail.com> * style: initialize `NCRModuloP::p` with `0` Co-authored-by: David Leal <halfpacho@gmail.com> --------- Co-authored-by: David Leal <halfpacho@gmail.com> Co-authored-by: realstealthninja <68815218+realstealthninja@users.noreply.github.com>
1 parent 8bde3ea commit af72fab

File tree

1 file changed

+128
-83
lines changed

1 file changed

+128
-83
lines changed

‎math/ncr_modulo_p.cpp

Lines changed: 128 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
*/
1111

1212
#include <cassert> /// for assert
13-
#include <iostream> /// for io operations
13+
#include <iostream> /// for std::cout
1414
#include <vector> /// for std::vector
1515

1616
/**
@@ -25,71 +25,95 @@ namespace math {
2525
* implementation.
2626
*/
2727
namespace ncr_modulo_p {
28+
2829
/**
29-
* @brief Class which contains all methods required for calculating nCr mod p
30+
* @namespace utils
31+
* @brief this namespace contains the definitions of the functions called from
32+
* the class math::ncr_modulo_p::NCRModuloP
3033
*/
31-
class NCRModuloP {
32-
private:
33-
std::vector<uint64_t> fac{}; /// stores precomputed factorial(i) % p value
34-
uint64_t p = 0; /// the p from (nCr % p)
35-
36-
public:
37-
/** Constructor which precomputes the values of n! % mod from n=0 to size
38-
* and stores them in vector 'fac'
39-
* @params[in] the numbers 'size', 'mod'
40-
*/
41-
NCRModuloP(const uint64_t& size, const uint64_t& mod) {
42-
p = mod;
43-
fac = std::vector<uint64_t>(size);
44-
fac[0] = 1;
45-
for (int i = 1; i <= size; i++) {
46-
fac[i] = (fac[i - 1] * i) % p;
47-
}
34+
namespace utils {
35+
/**
36+
* @brief finds the values x and y such that a*x + b*y = gcd(a,b)
37+
*
38+
* @param[in] a the first input of the gcd
39+
* @param[in] a the second input of the gcd
40+
* @param[out] x the Bézout coefficient of a
41+
* @param[out] y the Bézout coefficient of b
42+
* @return the gcd of a and b
43+
*/
44+
int64_t gcdExtended(const int64_t& a, const int64_t& b, int64_t& x,
45+
int64_t& y) {
46+
if (a == 0) {
47+
x = 0;
48+
y = 1;
49+
return b;
4850
}
4951

50-
/** Finds the value of x, y such that a*x + b*y = gcd(a,b)
51-
*
52-
* @params[in] the numbers 'a', 'b' and address of 'x' and 'y' from above
53-
* equation
54-
* @returns the gcd of a and b
55-
*/
56-
uint64_t gcdExtended(const uint64_t& a, const uint64_t& b, int64_t* x,
57-
int64_t* y) {
58-
if (a == 0) {
59-
*x = 0, *y = 1;
60-
return b;
61-
}
52+
int64_t x1 = 0, y1 = 0;
53+
const int64_t gcd = gcdExtended(b % a, a, x1, y1);
6254

63-
int64_t x1 = 0, y1 = 0;
64-
uint64_t gcd = gcdExtended(b % a, a, &x1, &y1);
55+
x = y1 - (b / a) * x1;
56+
y = x1;
57+
return gcd;
58+
}
6559

66-
*x = y1 - (b / a) * x1;
67-
*y = x1;
68-
return gcd;
60+
/** Find modular inverse of a modulo m i.e. a number x such that (a*x)%m = 1
61+
*
62+
* @param[in] a the number for which the modular inverse is queried
63+
* @param[in] m the modulus
64+
* @return the inverce of a modulo m, if it exists, -1 otherwise
65+
*/
66+
int64_t modInverse(const int64_t& a, const int64_t& m) {
67+
int64_t x = 0, y = 0;
68+
const int64_t g = gcdExtended(a, m, x, y);
69+
if (g != 1) { // modular inverse doesn't exist
70+
return -1;
71+
} else {
72+
return ((x + m) % m);
6973
}
74+
}
75+
} // namespace utils
76+
/**
77+
* @brief Class which contains all methods required for calculating nCr mod p
78+
*/
79+
class NCRModuloP {
80+
private:
81+
const int64_t p = 0; /// the p from (nCr % p)
82+
const std::vector<int64_t>
83+
fac; /// stores precomputed factorial(i) % p value
7084

71-
/** Find modular inverse of a with m i.e. a number x such that (a*x)%m = 1
72-
*
73-
* @params[in] the numbers 'a' and 'm' from above equation
74-
* @returns the modular inverse of a
85+
/**
86+
* @brief computes the array of values of factorials reduced modulo mod
87+
* @param max_arg_val argument of the last factorial stored in the result
88+
* @param mod value of the divisor used to reduce factorials
89+
* @return vector storing factorials of the numbers 0, ..., max_arg_val
90+
* reduced modulo mod
7591
*/
76-
int64_t modInverse(const uint64_t& a, const uint64_t& m) {
77-
int64_t x = 0, y = 0;
78-
uint64_t g = gcdExtended(a, m, &x, &y);
79-
if (g != 1) { // modular inverse doesn't exist
80-
return -1;
81-
} else {
82-
int64_t res = ((x + m) % m);
83-
return res;
92+
static std::vector<int64_t> computeFactorialsMod(const int64_t& max_arg_val,
93+
const int64_t& mod) {
94+
auto res = std::vector<int64_t>(max_arg_val + 1);
95+
res[0] = 1;
96+
for (int64_t i = 1; i <= max_arg_val; i++) {
97+
res[i] = (res[i - 1] * i) % mod;
8498
}
99+
return res;
85100
}
86101

87-
/** Find nCr % p
88-
*
89-
* @params[in] the numbers 'n', 'r' and 'p'
90-
* @returns the value nCr % p
102+
public:
103+
/**
104+
* @brief constructs an NCRModuloP object allowing to compute (nCr)%p for
105+
* inputs from 0 to size
91106
*/
92-
int64_t ncr(const uint64_t& n, const uint64_t& r, const uint64_t& p) {
107+
NCRModuloP(const int64_t& size, const int64_t& p)
108+
: p(p), fac(computeFactorialsMod(size, p)) {}
109+
110+
/**
111+
* @brief computes nCr % p
112+
* @param[in] n the number of objects to be chosen
113+
* @param[in] r the number of objects to choose from
114+
* @return the value nCr % p
115+
*/
116+
int64_t ncr(const int64_t& n, const int64_t& r) const {
93117
// Base cases
94118
if (r > n) {
95119
return 0;
@@ -101,50 +125,71 @@ class NCRModuloP {
101125
return 1;
102126
}
103127
// fac is a global array with fac[r] = (r! % p)
104-
int64_t denominator = modInverse(fac[r], p);
105-
if (denominator < 0) { // modular inverse doesn't exist
106-
return -1;
107-
}
108-
denominator = (denominator * modInverse(fac[n - r], p)) % p;
109-
if (denominator < 0) { // modular inverse doesn't exist
128+
const auto denominator = (fac[r] * fac[n - r]) % p;
129+
const auto denominator_inv = utils::modInverse(denominator, p);
130+
if (denominator_inv < 0) { // modular inverse doesn't exist
110131
return -1;
111132
}
112-
return (fac[n] * denominator) % p;
133+
return (fac[n] * denominator_inv) % p;
113134
}
114135
};
115136
} // namespace ncr_modulo_p
116137
} // namespace math
117138

118139
/**
119-
* @brief Test implementations
120-
* @param ncrObj object which contains the precomputed factorial values and
121-
* ncr function
122-
* @returns void
140+
* @brief tests math::ncr_modulo_p::NCRModuloP
123141
*/
124-
static void tests(math::ncr_modulo_p::NCRModuloP ncrObj) {
125-
// (52323 C 26161) % (1e9 + 7) = 224944353
126-
assert(ncrObj.ncr(52323, 26161, 1000000007) == 224944353);
127-
// 6 C 2 = 30, 30%5 = 0
128-
assert(ncrObj.ncr(6, 2, 5) == 0);
129-
// 7C3 = 35, 35 % 29 = 8
130-
assert(ncrObj.ncr(7, 3, 29) == 6);
142+
static void tests() {
143+
struct TestCase {
144+
const int64_t size;
145+
const int64_t p;
146+
const int64_t n;
147+
const int64_t r;
148+
const int64_t expected;
149+
150+
TestCase(const int64_t size, const int64_t p, const int64_t n,
151+
const int64_t r, const int64_t expected)
152+
: size(size), p(p), n(n), r(r), expected(expected) {}
153+
};
154+
const std::vector<TestCase> test_cases = {
155+
TestCase(60000, 1000000007, 52323, 26161, 224944353),
156+
TestCase(20, 5, 6, 2, 30 % 5),
157+
TestCase(100, 29, 7, 3, 35 % 29),
158+
TestCase(1000, 13, 10, 3, 120 % 13),
159+
TestCase(20, 17, 1, 10, 0),
160+
TestCase(45, 19, 23, 1, 23 % 19),
161+
TestCase(45, 19, 23, 0, 1),
162+
TestCase(45, 19, 23, 23, 1),
163+
TestCase(20, 9, 10, 2, -1)};
164+
for (const auto& tc : test_cases) {
165+
assert(math::ncr_modulo_p::NCRModuloP(tc.size, tc.p).ncr(tc.n, tc.r) ==
166+
tc.expected);
167+
}
168+
169+
std::cout << "\n\nAll tests have successfully passed!\n";
131170
}
132171

133172
/**
134-
* @brief Main function
135-
* @returns 0 on exit
173+
* @brief example showing the usage of the math::ncr_modulo_p::NCRModuloP class
136174
*/
137-
int main() {
138-
// populate the fac array
139-
const uint64_t size = 1e6 + 1;
140-
const uint64_t p = 1e9 + 7;
141-
math::ncr_modulo_p::NCRModuloP ncrObj =
142-
math::ncr_modulo_p::NCRModuloP(size, p);
143-
// test 6Ci for i=0 to 7
175+
void example() {
176+
const int64_t size = 1e6 + 1;
177+
const int64_t p = 1e9 + 7;
178+
179+
// the ncrObj contains the precomputed values of factorials modulo p for
180+
// values from 0 to size
181+
const auto ncrObj = math::ncr_modulo_p::NCRModuloP(size, p);
182+
183+
// having the ncrObj we can efficiently query the values of (n C r)%p
184+
// note that time of the computation does not depend on size
144185
for (int i = 0; i <= 7; i++) {
145-
std::cout << 6 << "C" << i << " = " << ncrObj.ncr(6, i, p) << "\n";
186+
std::cout << 6 << "C" << i << " mod " << p << " = " << ncrObj.ncr(6, i)
187+
<< "\n";
146188
}
147-
tests(ncrObj); // execute the tests
148-
std::cout << "Assertions passed\n";
189+
}
190+
191+
int main() {
192+
tests();
193+
example();
149194
return 0;
150195
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /