如何找到大输入的选择(m,n)%P?

时间:2015-11-01 00:49:59

标签: c++ combinatorics binomial-coefficients

P = (10^9 + 7)
Choose(m, n) = m! / (n! * (m - n)!)

我想为大Choose(m, n) mod Pm计算n的值。 我怎么能用C ++做到这一点?

2 个答案:

答案 0 :(得分:1)

这是我使用的,因为它具有相当好的范围而没有太多的中间溢出。然而,C(n,k)变得很快,毕竟它是O(n ^ k)。

size_t N_choose_K(size_t n, size_t k)
{
    size_t numer = 1;
    size_t denom = 1;
    if (k > n - k) {
        k = n - k;
    }
    while (k > 0) {
        numer *= n;
        denom *= k;
        --n; --k;
    }
    return numer / denom;
}

编辑:假设您需要整体结果。如果需要,您可以移动到浮点结果并获得更多范围,并且可能会失去准确性。

答案 1 :(得分:0)

你可以使用在 Z p 下关闭乘法的事实,这意味着: ab mod p =(a mod p)(b mod p)mod p 的。使用这个定理,可以有效地计算 a b mod p (例如,这个Python implementation

接下来我们可以使用 Euler定理说: a -1 mod p = a (p-2)< / sup> mod p

现在我们知道了这些事实,我们可以提出一个有效的解决方案:首先我们乘以分子中的所有元素,因此这是从 k + 1 (包括)到<的范围em> n ,因为这是一个乘法,我们总是可以执行一个模数:

long long numerator(int n, int k, int p) {
    long long l = 1;
    for(int j = k+1; j <= n; j++) {
        l = (l*j)%p;
    }
    return l;
}

现在我们仍然需要将它除以(n-k)!。我们可以通过首先计算(n-k)来做到这一点! mod p 就像我们在前面的代码片段中已经做过的那样:

long long denominator(int n, int k, int p) {
    long l = 1;
    for(int j = 2; j <= n-k; j++) {
        l = (l*j)%p;
    }
    return l;
}

现在为了划分它,我们可以在denominator的结果上使用欧拉定理。因此,我们首先使用modulo:

实现pow函数
long long pow(long long a, int k, int p) {
    if(k == 0) {
        return 1;
    }
    long long r = pow((a*a)%p,k>>0x01,p);
    if((k&0x01) == 0x01) {//odd number
        r = (r*a)%p;
    }
    return r;
}

现在我们可以将它们合并在一起,如:

long long N_choose_K(int n, int k, int p) {
    long long num = numerator(n,k,p);
    long long den = denominator(n,k,p);
    return (num*pow(den,p-2,p))%p;
}

所以你基本上做的是确定 Z p 中的分子num中分母den的值Z p ,然后使用欧拉定理在 Z p 中找到分母的倒数,这样您可以乘以并执行最后一个模运算。然后你可以退货。