我想计算 N! mod 232 的确切值。N 可以高达 231。
任何语言都可以,但我希望得到详细的算法说明。 时间限制为 <1 秒。
我想计算 N! mod 232 的确切值。N 可以高达 231。
任何语言都可以,但我希望得到详细的算法说明。 时间限制为 <1 秒。
if n > 33:
return 0
else
return reduce(lambda x, y: x*y, range(1, n+1)) % 2**32
理由:
我们知道34!可以被232整除,因为在下列序列中:
1 * 2 * 3 * 4 * ... * 34
有:
17 multiples of 2
8 multiples of 4
4 multiples of 8
2 multiples of 16
1 multiple of 32
--
32 multiplications by 2
这是每个更大的阶乘的因子,所以所有更大的数都模232为0。
对于小的N值,如果没有大数计算,您可以对每个乘法进行模232运算,或者您可以预先计算阶乘中的2的幂次方,这很容易计算(见上文)。
1×2×...×N mod m = (...(((1×2 mod m)×3 mod m)×4 mod m)...)×N mod m
uint32_t p = 1;
for (uint64_t i = 1; i <= n && p /* early exit because product = 0 */; i++)
p = uint32_t(p*i); // or p = p*i & 0xFFFFFFFFU;
return p;
计算模数是一项非常快速的操作,特别是2的幂次方的模数。相比之下,乘法代价很高。
最快的算法将阶乘的因子分解为质数(由于数字小于33,因此非常快)。通过在每次乘法之间取模,并从大数开始将它们全部相乘来获得结果。
例如:要计算10!mod 232:使用de Polignac的公式获取10!的质因数,这会给你:
10!= 7 * 5 * 5 * 3 * 3 * 3 * 3 * 2 ...
这比基本算法更快,因为计算(29!mod 232) X 30比乘以5、3和2,并在每次之间取模要困难得多。
n < 34
,这几乎不值得:我可以在我的普通笔记本电脑上计算整个范围,包括Python启动,仅用32毫秒。 - rici
p
为质数时),计算n! mod p
时,每次乘法后都要进行模运算;这样处理的数字永远不会大于p
。有关更多信息,请参阅我在此处的评论:(https://dev59.com/MWkw5IYBdhLWcg3wrsdS#9728079)。 - BlueRaja - Danny Pflughoeft