【问题标题】:Finding binomial coefficient for large n and k modulo m找到大 n 和 k 模 m 的二项式系数
【发布时间】:2016-05-15 14:03:11
【问题描述】:

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

n

k

m=10^9+7

我读过这篇文章:

Calculating Binomial Coefficient (nCk) for large n & k

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

如何在上述约束下做到这一点。 我不能在给定的约束条件下创建一个 O(m*k) 空间复杂度的数组。

救命!

【问题讨论】:

    标签: c++ algorithm modulus modular-arithmetic binomial-coefficients


    【解决方案1】:

    (n, k)的二项式系数由以下公式计算:

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

    要使此功能适用于大量 nkm 请注意:

    1. 模数 m 的阶乘可以逐步计算,在 每一步都会得到结果% m。但是,如果 n 达到 10^18,这将太慢。因此,faster methods 的复杂性受模数限制,您可以使用其中的一些。

    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
    

    使用Extended Euclidean algorithm 可以有效地找到数字的倒数。假设您已经解决了阶乘计算,那么算法的其余部分很简单,只需注意乘法时的整数溢出。这是适用于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)));
    }
    

    【讨论】:

    • modfact 将“永不”停止 n = 10^18
    • @iggy 代码是一个简单的草图,如前所述,最多可用于 10^9,而不是 OP 所需的完整实现。
    【解决方案2】:

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

    其次,对于 (k

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

    那么由于 (n mod m)

    【讨论】:

      【解决方案3】:

      只要使用事实

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

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

      【讨论】:

      • 对,对于 OP 的小 k,这将起作用,但不会推广到更大的值。
      • @kfx 你能解释一下,为什么这不适用于更大的值?
      【解决方案4】:

      我们要计算 nCk (mod p)。我会在 0

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

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

      因此,二项式系数为 n!/(k!(nk)!) = n(n-1)...(n-k+1)/(k!) = n(n-1)。 ..(n-k+1)(p-2)(p-3)...(k+1) (mod p)

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-06-15
        • 2013-02-21
        • 2016-02-01
        • 2012-02-20
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多