查找大 n 和 k 模 m 的二项式系数

Finding binomial coefficient for large n and k modulo m

本文关键字:二项式 查找      更新时间:2023-10-16

我想使用以下约束来计算nCk mod m:

n<=10^18

k<=10^5

m=10^9+7

我读过这篇文章:

计算大 n 和 k 的二项式系数 (nCk(

但这里的 m 值是 1009。因此,使用卢卡斯定理,我们只需要计算 aCb 的 1009*1009 个不同值,其中 a,b<=1009

如何在上述约束下做到这一点。我无法在给定约束下制作 O(m*k( 空间复杂度数组。

帮助!

(n, k)的双名义系数由下式计算:

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

要使这适用于大量nk模,m观察到:

  1. 数模m的阶乘可以逐步计算,在每一步采取结果% m。但是,当 n 到 10^18 时,这将太慢了。因此,有一些更快的方法,其中复杂性由模限制,您可以使用其中的一些。

  2. (a / b) mod m的除数等于(a * b^-1) mod m,其中b^-1bm(即(b * b^-1 = 1) mod m(的倒数。

这意味着:

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

使用扩展欧几里得算法可以有效地找到数字的倒数。假设您已经整理了因子计算,则算法的其余部分很简单,只需注意乘法时的整数溢出即可。这是最高可工作的参考代码 n=10^9 .为了处理更大的数字,阶乘计算应该被更有效的算法所取代,并且代码应该稍微调整以避免整数溢出,但主要思想将保持不变:

#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)));
}

只需使用以下事实

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

所以你实际上只有2*k=2*10^5因素。对于数字的倒数,您可以使用 kfx 的建议,因为您的m是素数。

首先,您不需要预先计算和存储所有可能的aCb值! 它们可以按案例计算。

其次,对于 (k <m(> (n 选择 k( mod m

= ((n mod m( 选择 k( mod m

那么由于 (n mod m( <10^9+7,你可以简单地使用 @kfx 提出的代码。

我们要计算 nCk (mod p(。当 0 <= k <= p-2 时,我将处理,因为卢卡斯定理处理其余部分。

威尔逊定理指出,对于素数p,(p-1(!= -1(mod p(,或等价(p-2(!= 1(mod p((按除法(。

按部门: (k!^(-1( = (p-2(!/(k!( = (p-2((p-3(...(k+1((模组 p(

因此,二项式系数为 n!/(k!(n-k(!= n(n-1(...(n-k+1(/(k!( = n(n-1(...(n-k+1((P-2((P-3(...(k+1((模组 p(

瞧。您不必进行任何逆向计算或类似的事情。编码也相当容易。需要考虑的几个优化:(1(您可以替换(p-2((p-3(...与 (-2((-3(...;(2( nCk 是对称的,因为 nCk = nC(n-k( 所以选择需要你做较少计算的那一半。