Finding binomial coefficient for large n and k modulo m

后端 未结 4 1361
伪装坚强ぢ
伪装坚强ぢ 2021-01-03 04:06

I want to compute nCk mod m with following constraints:

n<=10^18

k<=10^5

m=10^9+7

I have read this article:

Calculating Binomia

相关标签:
4条回答
  • 2021-01-03 04:11

    First, you don't need to pre-compute and store all the possible aCb values! they can be computed per case.

    Second, for the special case when (k < m) and (n < m^2), the Lucas theorem easily reduces to the following result:

    (n choose k) mod m = ((n mod m) choose k) mod m

    then since (n mod m) < 10^9+7 you can simply use the code proposed by @kfx.

    0 讨论(0)
  • 2021-01-03 04:13

    Just use the fact that

    (n, k) = n! / k! / (n - k)! = n*(n-1)*...*(n-k+1)/[k*(k-1)*...*1]
    

    so you actually have just 2*k=2*10^5 factors. For the inverse of a number you can use suggestion of kfx since your m is prime.

    0 讨论(0)
  • 2021-01-03 04:25

    We want to compute nCk (mod p). I'll handle when 0 <= k <= p-2, because Lucas's theorem handles the rest.

    Wilson's theorem states that for prime p, (p-1)! = -1 (mod p), or equivalently (p-2)! = 1 (mod p) (by division).

    By division: (k!)^(-1) = (p-2)!/(k!) = (p-2)(p-3)...(k+1) (mod p)

    Thus, the binomial coefficient is n!/(k!(n-k)!) = n(n-1)...(n-k+1)/(k!) = n(n-1)...(n-k+1)(p-2)(p-3)...(k+1) (mod p)

    Voila. You don't have to do any inverse computations or anything like that. It's also fairly easy to code. A couple optimizations to consider: (1) you can replace (p-2)(p-3)... with (-2)(-3)...; (2) nCk is symmetric in the sense that nCk = nC(n-k) so choose the half that requires you to do less computations.

    0 讨论(0)
  • 2021-01-03 04:27

    The binominal coefficient of (n, k) is calculated by the formula:

    (n, k) = n! / k! / (n - k)!
    

    To make this work for large numbers n and k modulo m observe that:

    1. Factorial of a number modulo m can be calculated step-by-step, in each step taking the result % m. However, this will be far too slow with n up to 10^18. So there are faster methods where the complexity is bounded by the modulo, and you can use some of those.

    2. The division (a / b) mod m is equal to (a * b^-1) mod m, where b^-1 is the inverse of b modulo m (that is, (b * b^-1 = 1) mod m).

    This means that:

    (n, k) mod m = (n! * (k!)^-1 * ((n - k)!)^-1) mod m
    

    The inverse of a number can be efficiently found using the Extended Euclidean algorithm. Assuming you have the factorial calculation sorted out, the rest of the algorithm is straightforward, just watch out for integer overflows on multiplication. Here's reference code that works up to n=10^9. To handle for larger numbers the factorial computation should be replaced with a more efficient algorithm and the code should be slightly adapted to avoid integer overflows, but the main idea will remain the same:

    #define MOD 1000000007
    
    // Extended Euclidean algorithm
    int xGCD(int a, int b, int &x, int &y) {
        if (b == 0) {
            x = 1;
            y = 0;
            return a;
        }
    
        int x1, y1, gcd = xGCD(b, a % b, x1, y1);
        x = y1;
        y = x1 - (long long)(a / b) * y1;
        return gcd;
    }
    
    // factorial of n modulo MOD
    int modfact(int n) {
        int result = 1;
        while (n > 1) {
            result = (long long)result * n % MOD;
            n -= 1;
        }
        return result;
    }
    
    // multiply a and b modulo MOD
    int modmult(int a, int b) {
        return (long long)a * b % MOD;
    }
    
    // inverse of a modulo MOD
    int inverse(int a) {
        int x, y;
        xGCD(a, MOD, x, y);
        return x;
    }
    
    // binomial coefficient nCk modulo MOD
    int bc(int n, int k)
    {
        return modmult(modmult(modfact(n), inverse(modfact(k))), inverse(modfact(n - k)));
    }
    
    0 讨论(0)
提交回复
热议问题