计算素数模的阶乘最有效的方法是什么?


20

您知道有什么算法可以有效地计算模后阶乘吗?

例如,我要编程:

for(i=0; i<5; i++)
  sum += factorial(p-i) % p;

但是,p直接应用阶乘是一个很大的数字(素数)。(p108)

在Python中,此任务确实很容易,但是我真的很想知道如何优化。


6
似乎该问题希望您使用威尔逊定理。对于素数,。因此,无需使用任何编程语言:答案是。也许您想概括一下您的问题?p 1 = - 1p100章(p1)!=1modp100
Aryabhata 2012年

5
您能否更清楚地陈述问题?您要计算(X!) (mod (X+1))还是更通用(X!) (mod Y)?而且我认为这factorial(100!)并不意味着您要两次应用阶乘函数。
基思·汤普森

1
即使您没有威尔逊定理,您也确实具有,这至少将有助于避免溢出问题。(mn)modp=(mmodp)(nmodp
戴夫·克拉克

8
注意,威尔逊定理仅在为素数时适用。您的问题没有说明是质数,因此您写的内容不正确。ppp
戴夫·克拉克2012年

Answers:


11

(此答案最初是由提问者jonaprieto在问题内发布的。)

我记得威尔逊定理,并且我注意到了一些小事情:

在上面的程序中,最好编写:

(p1)!1(modp)(p2)!(p1)!(p1)11(modp)(p3)!(p2)!(p2)1(p2)1(modp)(p4)!(p3)!(p3)1(p2)1(p3)1(modp) (p5)!(p4)!(p4)1(p2)1(p3)1(p4)1(modp)

并且您可以找到因为,所以使用扩展的Euclidian算法,您可以找到,即反模数。 gcd p p i = 1(pi)1gcd(p,pi)=1p-一世-1个

您也可以查看相同的全等内容,例如: 因此,总和等于: 并且如果在开始时将阶乘分解,则得到 而且,瞧,逆模数比阶乘更有效。

(p5)!(p24)1(modp)(p4)!(p+6)1(modp)(p3)!(p2)1(modp)(p2)!1(modp)(p1)!1(modp)
(24)1+(6)1+(2)1
8(24)1(modp)

所以基本上。整齐!(pk)!(p+(k1)!(1)k)1(modp)
托马斯·阿勒

抱歉,但是当我分解,我得到:-24-1个+6-1个+-2-1个
9-24-1个=-38

1

您发布的示例与Euler问题#381密切相关。因此,我将发布无法解决欧拉问题的答案。我将发布如何计算素数模的阶乘。

因此:如何计算n!p取模

快速观察:如果n≥p,则n!有一个因子p,所以结果为0。非常快。如果我们忽略p应该是质数的要求,那么让q是p的最小质数,而n是n!如果n≥q,则模p为0。也没有太多理由要求p是质数来回答您的问题。

现在在您的示例中(n-i)!当1≤i≤5时出现。您不必计算五个阶乘:您计算(n-5)!,乘以(n-4)即可得到(n-4)!,乘以(n-3)即可得到(n-3)!等等,这几乎减少了工作量。5。不要从字面上解决问题。

问题是如何计算n!模数 一种明显的方法是计算n !,即一个大约具有n个对数n个十进制数字的数字,并计算p的余数。辛苦了 问题:我们如何更快地获得此结果?通过不做明显的事情。

我们知道((a * b * c)模p =(((a * b)模p)* c)模p。

为了计算n !,我们通常从x = 1开始,然后将x乘以1,2,3,... n。使用取模公式,我们计算n!通过从x = 1开始计算p!而不计算n!的模p,然后对于i = 1,2,3,..,n,我们用(x * i)模p代替x。

我们总是有x <p和i <n,所以我们只需要足够的精度来计算x * p,而不是要高得多的精度来计算n!。所以要计算n!对于p≥2取p为模,我们采取以下步骤:

Step 1: Find the smallest prime factor q of p. If n ≥ q then the result is 0.
Step 2: Let x = 1, then for 1 ≤ i ≤ n replace x with (x * i) modulo p, and x is the result. 

(一些答案提到了威尔逊定理,该定理仅在给出的示例的非常特殊的情况下回答了该问题,对解决欧拉问题381非常有用,但通常对解决所提出的问题没有帮助)。


-1

这是我对威尔逊定理的实现:

当MOD-n相对于n很小时,factMOD函数是用来计算(n!)%MOD的函数。

如果不是这种情况,有人知道其他有效的方法吗(例如:n = 1e6和MOD = 1e9 + 7)?

ll powmod(ll a, ll b){//a^b % MOD
  ll x=1,y=a;
  while(b){
    if(b&1){
      x*=y; if(x>=MOD)x%=MOD;
    }
    y*=y; if(y>=MOD)y%=MOD;
    b>>=1;
  }
  return x;
} 
ll InverseEuler(ll n){//modular inverse of n
  return powmod(n,MOD-2);
}
ll factMOD(ll n){ //n! % MOD efficient when MOD-n<n
   ll res=1,i;
   for(i=1; i<MOD-n; i++){
     res*=i;
     if(res>=MOD)res%=MOD;
   }
   res=InverseEuler(res);   
    if(!(n&1))
      res= -res +MOD;
  }
  return res%MOD;
}

1
代码并不是真正的主题,在这里。对算法的描述非常有用,因为它不需要人们理解您决定用哪种语言编写代码,并且因为实际实现常常以使其难以理解的方式进行了优化。并请以单独的问题而不是在回答中提问。Stack Exchange是一个问答网站,而不是讨论板,如果将答案隐藏在其中,则很难找到问题。谢谢!
David Richerby 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.