P = (10^9 + 7)
Choose(m, n) = m! / (n! * (m - n)!)
我想为大Choose(m, n) mod P
和m
计算n
的值。
我怎么能用C ++做到这一点?
答案 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 中找到分母的倒数,这样您可以乘以并执行最后一个模运算。然后你可以退货。