0

I have came across this problem many time but I am unable to solve it. There would occur some cases or the other which will wrong answer or otherwise the program I write will be too slow. Formally I am talking about calculating

nCk mod p where p is a prime n is a large number, and 1<=k<=n.

What have I tried:

I know the recursive formulation of factorial and then modelling it as a dynamic programming problem, but I feel that it is slow. The recursive formulation is (nCk) + (nCk-1) = (n+1Ck). I took care of the modulus while storing values in array to avoid overflows but I am not sure that just doing a mod p on the result will avoid all overflows as it may happen that one needs to remove.

Aman Deep Gautam
  • 8,091
  • 21
  • 74
  • 130

3 Answers3

1

To compute nCr, there's a simple algorithm based on the rule nCr = (n - 1)C(r - 1) * n / r:

def nCr(n,r):
  if r == 0:
    return 1
  return n * nCr(n - 1, r - 1) // r

Now in modulo arithmetic we don't quite have division, but we have modulo inverses which (when modding by a prime) are just as good

def nCrModP(n, r, p):
  if r == 0:
    return 1
  return n * nCrModP(n - 1, r - 1) * modinv(r, p) % p

Here's one implementation of modinv on rosettacode

dspyz
  • 5,280
  • 2
  • 25
  • 63
0

Not sure what you mean by "storing values in array", but I assume they array serves as a lookup table while running to avoid redundant calculations to speed things up. This should take care of the speed problem. Regarding the overflows - you can perform the modulo operation at any stage of computation and repeat it as much as you want - the result will be correct.

SomeWittyUsername
  • 18,025
  • 3
  • 42
  • 85
0

First, let's work with the case where p is relatively small. Take the base-p expansions of n and k: write n = n_0 + n_1 p + n_2 p^2 + ... + n_m p^m and k = k_0 + k_1 p + ... + k_m p^m where each n_i and each k_i is at least 0 but less than p. A theorem (which I think is due to Edouard Lucas) states that C(n,k) = C(n_0, k_0) * C(n_1, k_1) * ... * C(n_m, k_m). This reduces to taking a mod-p product of numbers in the "n is relatively small" case below.

Second, if n is relatively small, you can just compute binomial coefficients using dynamic programming on the formula C(n,k) = C(n-1,k-1) + C(n-1,k), reducing mod p at each step. Or do something more clever.

Third, if k is relatively small (and less than p), you should be able to compute n!/(k!(n-k)!) mod p by computing n!/(n-k)! as n * (n-1) * ... * (n-k+1), reducing modulo p after each product, then multiplying by the modular inverses of each number between 1 and k.

tmyklebu
  • 13,915
  • 3
  • 28
  • 57