10
10
*/
11
11
12
12
#include < cassert> // / for assert
13
- #include < iostream> // / for io operations
13
+ #include < iostream> // / for std::cout
14
14
#include < vector> // / for std::vector
15
15
16
16
/* *
@@ -25,71 +25,95 @@ namespace math {
25
25
* implementation.
26
26
*/
27
27
namespace ncr_modulo_p {
28
+
28
29
/* *
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
30
33
*/
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;
48
50
}
49
51
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);
62
54
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
+ }
65
59
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);
69
73
}
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
70
84
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
75
91
*/
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;
84
98
}
99
+ return res;
85
100
}
86
101
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
91
106
*/
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 {
93
117
// Base cases
94
118
if (r > n) {
95
119
return 0 ;
@@ -101,50 +125,71 @@ class NCRModuloP {
101
125
return 1 ;
102
126
}
103
127
// 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
110
131
return -1 ;
111
132
}
112
- return (fac[n] * denominator ) % p;
133
+ return (fac[n] * denominator_inv ) % p;
113
134
}
114
135
};
115
136
} // namespace ncr_modulo_p
116
137
} // namespace math
117
138
118
139
/* *
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
123
141
*/
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\n All tests have successfully passed!\n " ;
131
170
}
132
171
133
172
/* *
134
- * @brief Main function
135
- * @returns 0 on exit
173
+ * @brief example showing the usage of the math::ncr_modulo_p::NCRModuloP class
136
174
*/
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
144
185
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 " ;
146
188
}
147
- tests (ncrObj); // execute the tests
148
- std::cout << " Assertions passed\n " ;
189
+ }
190
+
191
+ int main () {
192
+ tests ();
193
+ example ();
149
194
return 0 ;
150
195
}
0 commit comments