我需要找到n!%1000000009。 对于范围为1到20的k,n的类型为2 ^ k。 我正在使用的功能是:
#define llu unsigned long long
#define MOD 1000000009
llu mulmod(llu a,llu b) // This function calculates (a*b)%MOD caring about overflows
{
llu x=0,y=a%MOD;
while(b > 0)
{
if(b%2 == 1)
{
x = (x+y)%MOD;
}
y = (y*2)%MOD;
b /= 2;
}
return (x%MOD);
}
llu fun(int n) // This function returns answer to my query ie. n!%MOD
{
llu ans=1;
for(int j=1; j<=n; j++)
{
ans=mulmod(ans,j);
}
return ans;
}
我的要求是,我需要调用函数'fun',n / 2次。对于15左右的k值,我的代码运行速度太慢。有没有办法更快?
编辑: 实际上我正计算2 * [(i-1)C(2 ^(k-1)-1)] * [((2 ^(k-1))!)^ 2]对于范围2中的所有i ^(k-1)到2 ^ k。我的程序要求(nCr)%MOD关心溢出。
编辑:我需要一种有效的方法来为大n找到nCr%MOD。
答案 0 :(得分:2)
由于您正在为nCr
的多个连续值寻找n
,因此您可以使用以下内容:
(n+1)Cr = (n+1)! / ((r!)*(n+1-r)!)
(n+1)Cr = n!*(n+1) / ((r!)*(n-r)!*(n+1-r))
(n+1)Cr = n! / ((r!)*(n-r)!) * (n+1)/(n+1-r)
(n+1)Cr = nCr * (n+1)/(n+1-r)
这样可以避免显式调用每个i
的阶乘函数。
此外,要保存对nCr的第一次调用,您可以使用:
nC(n-1) = n //where n in your case is 2^(k-1).
修改强>:
正如Aki Suihkonen指出的那样,(a/b) % m != a%m / b%m
。所以上面的方法所以上面的方法不会开箱即用。有两种不同的解决方案:
1000000009
是素数,这意味着a/b % m == a*c % m
其中c
是b
modulo m
的倒数。您可以找到有关如何计算here的说明,并点击Extended Euclidean Algorithm
的链接,了解有关如何计算它的更多信息。
另一个可能更容易的选择是识别出,因为 编辑根据评论,我不相信这种方法会工作nCr * (n+1)/(n+1-r)
必须给出一个整数,所以必须可以将n+1-r == a*b
写在a | nCr
和{ {1}}(此处b | n+1
表示除法,如果您愿意,可以将其重写为|
。在不失一般性的情况下,让nCr % a == 0
然后让a = gcd(n+1-r,nCr)
。这给出了b = (n+1-r) / a
。现在你的分区保证是准确的,所以你只需计算它们然后像以前那样继续乘法。
我可能尝试的另一件事是(n+1)Cr == (nCr / a) * ((n+1) / b) % MOD
llu mulmod(llu a,llu b)
这也可以节省一些宝贵的时间。
答案 1 :(得分:2)
mulmod
例程可以通过较大的因子K来加速。
1)&#39;%&#39;因为(a + b)都小于N,所以是过度的
- 它足以评估c = a+b; if (c>=N) c-=N;
2)可以一次处理多个比特;请参阅&#34; Russian peasant's algorithm&#34;
的优化
3)a * b
实际上足够小,可以适应64位无符号长long而不会溢出
由于实际问题是关于 nCr mod M ,因此高级优化需要使用重复
(n+1)Cr mod M = (n+1)nCr / (n+1-r) mod M.
因为公式的左侧(( nCr )mod M )*( n +1)不能被(< em> n + 1- r ),除法需要实现为与模逆的乘法:(n + r-1)^( - 1)。模幂逆 b ^( - 1)是 b ^(M-1), M 是素数。 (否则它 b ^( phi ( M )),其中 phi 是Euler&#39 ; s Totient功能。)
模幂运算最常用重复平方来实现,在这种情况下,每个除数需要~45次模乘。
如果可以使用重复
nC(r+1) mod M = nCr * (n-r) / (r+1) mod M
只需要计算(r + 1)^(M-1)mod M一次。