引入

本文讨论了某一模数下阶乘计算的相关结论,并提供一种时间复杂度线性相关于模数大小的计算方法,因而该方法主要适用于模数不太大()的情形。除了本文介绍的方法外,根据场景不同,还可以应用 多项式技术 进行快速计算。

根据 中国剩余定理,阶乘取模问题可以转化为模数为素数幂 的情形。在处理这类问题时,常常需要对于素数 和正整数 ,将阶乘 中的所有因子 都提取出来,进而得到分解:

其中, 表示阶乘 的素因数分解中 的幂次, 表示在阶乘 的结果中去除所有 的幂次得到的整数。本文将讨论 在素数(幂)模下的余数以及幂次 的具体计算方法。

这种分解在解决阶乘同时出现在所求表达式的分子和分母的问题时尤为有用,比如 计算某一模数下的二项式系数。对于这类问题,分子和分母中 的幂次可以直接相减,而与 互素的部分 则可以利用 乘法逆元 计算。

本文还介绍了与上述问题相关的 Wilson 定理及其推广、Legendre 公式和 Kummer 定理等内容。

Wilson 定理

Wilson 定理给出了判断某个自然数是素数的一个充分必要条件。

Wilson 定理

对于自然数 ,当且仅当 是素数时,

利用本文的记号,Wilson 定理可以写作

推广

Wilson 定理可以推广到一般模数的情形。

定理(Gauss)

对于自然数 ,有

而且,余数中的 取值为 当且仅当模 原根存在,即 时,其中 是奇素数且 是正整数。

在计算中,尤为重要的是模数为素数幂的情形:

推论

对于素数 和正整数 ,有

注意,左侧并非 ,因为后者还需要统计 的倍数的贡献。

阶乘余数的计算

本节讨论余数 的计算。

素数模的情形

算式 有明显的递归结构。为注意到这一点,首先考察一个具体的例子:

例子

要计算 ,可以做如下递归计算:

可以看出,利用模 余数的周期性,可以将这一乘积划分为若干个长度为 的块,每一块的唯一差异就是最后一个元素的余数。因为 除以 得到的商是 且余数是 ,所以,该乘积可以划分为 个完整的块和最后一段长度为 的不完整的块。因此,可以将前 个块除了最后一个元素之外的部分提取出来(这一部分恰好是 Wilson 定理能够解决的),再乘上最后一个不完整的块的乘积,最后乘上前 个块的最后一个元素的连乘积。每个块的最后一个元素都是 的倍数,去掉 的幂次后,它们的连乘积恰好是 。这就将原来的问题转化为了规模更小的问题。

将该例子中的递归的结构一般化,就得到如下递推公式:

递推公式

对于素数 和正整数 ,有

利用该递推式做计算,递归深度为 。如果每次都重新计算中间那一项,那么每层计算的复杂度都是 的,总的时间复杂度是 ;如果对所有 都预先处理了 ,那么预处理的复杂度是 的,每层计算的复杂度都是 的,总的时间复杂度是 的。

在实现时,因为是尾递归,可以用迭代实现。下面的实现对前 个阶乘做了预计算,如果需要多次调用,可以将预计算放到函数外进行。

如果空间有限,无法存储所有阶乘,也可以将函数调用中实际用到的阶乘 中的 都计算出来,然后对它们进行排序,从而可以在最后一次性计算出来这些阶乘的值,汇总到最终结果中,而避免存储所有阶乘的值。

素数幂模的情形

对于素数幂模的情形,可以仿照素数模的情形解决,只需要将 Wilson 定理替换成它的推广形式。本节两个结论中的 ,均特指这样的定义:当模数 时取 ,其余情形取

递推公式

对于素数 和正整数 ,有

其中, 的取值如同 Wilson 定理的推广 中规定的那样。

与素数模的情形不同之处,除了 可能需要替换为 之外,还需要注意预处理的数据的不同。对于素数幂模的情形,需要对所有不超过 的正整数 预处理自 但并非 的倍数的所有整数的乘积,即

在素数模的情形,它退化为 ,但是该表达式在一般的素数幂的情形不再适用。

下面提供了在素数幂模的情形下计算阶乘余数的例子,以便理解上述方法:

例子

要计算 ,可以做如下递归计算:

的算式分解的结果同样可以分为三部分:

  • 完整的块:由 之间所有不被 整除的整数的乘积,共 块;
  • 尾部不完整的块:所有不被 整除的整数从 一直乘到
  • 所有被 整除的整数的乘积,对比倒数第二个等号的结果可知,这就是它的前 项,亦即

最后一个括号里的递归求解即可,这样就将原问题转化为了更小的问题。

由此,就可以得到如下递推结果:

递推结果

对于素数 和正整数 ,有

其中, 的取值与上文所述相同。

素数幂模的情形的实现和素数模的情形类似,只有一些细节上的区别。与上文类似,同样可以将预处理放到函数外进行。

预处理的时间复杂度为 ,单次询问的时间复杂度为

幂次的计算

本节讨论阶乘 的幂次 的计算,它可以用于计算二项式系数的余数。因为二项式系数中,分子和分母都会出现阶乘,而分子和分母中素数 能否互相抵消,就成为了决定最后的余数的重要因素。

Legendre 公式

阶乘 中素数 的幂次可以通过 Legendre 公式计算,而且与 进制下的表示有关。

Legendre 公式

对于正整数 ,阶乘 中含有的素数 的幂次

其中, 进制下 的各个数位的和。特别地,阶乘中 的幂次是

求阶乘中素数幂次的参考实现如下:

它的时间复杂度为

Kummer 定理

组合数对一个数取模的结果,往往构成分形结构,例如谢尔宾斯基三角形就可以通过组合数模 得到。

如果仔细分析, 是否整除组合数其实和上下标在 进制下减法是否需要借位有关。这就有了 Kummer 定理

Kummer 定理

素数 在组合数 中的幂次,恰好是 进制下 减掉 需要借位的次数,亦即

特别地,组合数中 的幂次是 .

例题

给定 , 计算

参考资料

本页面主要译自博文 Вычисление факториала по модулю 与其英文翻译版 Factorial modulo p。其中俄文版版权协议为 Public Domain + Leave a Link;英文版版权协议为 CC-BY-SA 4.0。内容有改动。