n 可以任意大
好吧,n
不能任意大 - 如果n >= m
,则n! ≡ 0 (mod m)
(因为m
是因子之一,根据阶乘的定义)。
假设n << m
并且您需要一个确切的值,据我所知,您的算法不会变得更快。但是,如果n > m/2
,您可以使用以下身份(威尔逊定理- 谢谢@Daniel Fischer!)
将乘法次数限制在大约m-n
(米-1)!≡ -1 (mod m)
1 * 2 * 3 * ... * (n-1) * n * (n+1) * ... * (m-2) * (m-1) ≡ -1 (mod m)
嗯!* (n+1) * ... * (m-2) * (m-1) ≡ -1 (mod m)
嗯!≡ -[(n+1) * ... * (m-2) * (m-1)] -1 (mod m)
这为我们提供了一种简单n! (mod m)
的m-n-1
乘法计算方法,以及模逆:
def factorialMod(n, 模数):
答案=1
如果 n <= 模数//2:
#正常计算阶乘(range() 的右参数是独占的)
对于范围内的 i (1,n+1):
ans = (ans * i) % 模数
别的:
#大n的Fancypants方法
对于范围内的 i(n+1,模数):
ans = (ans * i) % 模数
ans = modinv(ans,模数)
ans = -1*ans + 模数
返回 ans % 模数
我们可以用另一种方式改写上述等式,这可能会或可能不会执行得稍微快一些。使用以下身份:
我们可以将方程改写为
嗯!≡ -[(n+1) * ... * (m-2) * (m-1)] -1 (mod m)
嗯!≡ -[(n+1-m) * ... * (m-2-m) * (m-1-m)] -1 (mod m)
(术语的倒序)
嗯!≡ -[(-1) * (-2) * ... * -(mn-2) * -(mn-1)] -1 (mod m)
嗯!≡ -[(1) * (2) * ... * (mn-2) * (mn-1) * (-1) (mn-1) ] -1 (mod m)
嗯!≡ [(mn-1)!] -1 * (-1) (mn) (mod m)
这可以用 Python 编写如下:
def factorialMod(n, 模数):
答案=1
如果 n <= 模数//2:
#正常计算阶乘(range() 的右参数是独占的)
对于范围内的 i (1,n+1):
ans = (ans * i) % 模数
别的:
#大n的Fancypants方法
对于范围内的 i(1,模数-n):
ans = (ans * i) % 模数
ans = modinv(ans,模数)
#因为m是奇素数,(-1)^(mn) = -1如果n是偶数,+1如果n是奇数
如果 n % 2 == 0:
ans = -1*ans + 模数
返回 ans % 模数
如果您不需要精确的值,生活会轻松一些 - 您可以使用斯特林的近似值O(log n)
及时计算近似值(使用平方取幂)。
最后,我应该提一下,如果这是时间紧迫的,并且您使用的是 Python,请尝试切换到 C++。根据个人经验,您应该期望速度会提高一个数量级或更多,仅仅是因为这正是本机编译代码擅长的那种受 CPU 限制的紧密循环(此外,无论出于何种原因,GMP 似乎比 Python 的 Bignum 更精细)。