Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add ncr mod p code #1325

Merged
merged 18 commits into from Nov 22, 2020
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
96 changes: 96 additions & 0 deletions math/ncr_modulo_p.cpp
@@ -0,0 +1,96 @@
/**
* @file
* @brief This program aims at calculating nCr modulo p
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a detailed description using @details.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Provide a Wikipedia link in Markdown format (if available/possible).
If there's no Wikipedia link, add another web source for algorithm explanation. 🙂

*
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
*
*
* @author [Kaustubh Damania](https://github.com/KaustubhDamania)

*/

#include <cassert>
#include <iostream>
#include <vector>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
#include <cassert>
#include <iostream>
#include <vector>
#include <cassert> /// for assert
#include <iostream> /// for io operations
#include <vector> /// for std::vector


/** Finds the value of x, y such that a*x + b*y = gcd(a,b)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/** Finds the value of x, y such that a*x + b*y = gcd(a,b)
/**
* @namespace math
* @brief Mathematical algorithms
*/
namespace math {
/** Finds the value of x, y such that a*x + b*y = gcd(a,b)

*
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
*

* @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;
}

int64_t x1 = 0, y1 = 0;
int64_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;
}
}

std::vector<int64_t> fac;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Avoid using global variables, they may cause overflows and other issues.
Pass them as local function parameters, or as class private members.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what if I keep that variable inside namespace?
And later access them as math::fac?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Easiest thing you can do is to pass them as local function parameters.
Or even better, add them as (public) class members, and call the variable directly to the function.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I refactored the code so that the factorial vector and the remaining functions are inside a class


/** 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;
}
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;
}

int main() {
KaustubhDamania marked this conversation as resolved.
Show resolved Hide resolved
// populate the fac array
const int64_t size = 1e6 + 1;
fac = std::vector<int64_t>(size);
fac[0] = 1;
const int64_t p = 1e9 + 7;
for (int i = 1; i <= size; i++) {
fac[i] = (fac[i - 1] * i) % p;
}

// test 6Ci for i=0 to 7
for (int i = 0; i <= 7; i++) {
std::cout << 6 << "C" << i << " = " << ncr(6, i, p) << "\n";
}

// (52323 C 26161) % (1e9 + 7) = 224944353
assert(ncr(52323, 26161, p) == 224944353);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add the assert checks in a separate test function. 🙂

std::cout << "Assertion passed, (52323 C 26161) % (1e9 + 7) = 224944353\n";
}