快速算法计算大n! mod 2 ^ 32

问题描述 投票:3回答:5

我想计算N的确切值! mod 2 ^ 32。 N最高可达231

任何语言都可以,但我希望详细解释算法。时间限制<1秒

algorithm biginteger modulo factorial
5个回答
21
投票

在python中:

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

它是每个较大因子的一个因子,因此所有较大的因子都是0 mod 232

对于小的N值,如果你没有可用的bignum算法,你可以进行单独的乘法mod 232,和/或你可以在factorial中预先计算2的幂,这很容易计算(见上文)。


6
投票

正常计算阶乘(乘以数字1,2,3,...),在每次乘法后执行模数。这将为您提供N的小值的结果。

对于较大的N值,请执行相同的操作。很快,你的中间结果将是0,然后你可以立即停止循环并返回0。您停止的点将相对较快:对于N == 64,结果将已经是0,因为1..64的乘积包含32个偶数,因此可被2^32整除。你得到0的N的实际最小值将小于64。


0
投票

通常,您可以使用大多数编程语言中可用的整数类型(int,long)来实现模数为2的小功率而无需bignums或模块缩减的算法。对于模232,您将使用32位int。 "Integer overflow" takes care of the modular arithmetic.

在这种情况下,由于只有34个不同的结果,因此查找表可能比计算阶乘更快,假设经常使用阶乘以使表加载到CPU缓存中。执行时间将以微秒为单位。


0
投票

当乘以2个任意长度的数字时,低位总是精确的,因为它不依赖于高位。 Modulo 2n也是一个特例,因为使用AND操作获得模数非常容易。 Modulo 232更加特殊,因为对于32位无符号类型,C中的所有无符号运算都以模232减少,因此您不需要更宽的类型。

所以你可以将数字相乘并忽略溢出,在AND之后用232 - 1得到模数,这实际上是在C中进行正常的非加宽乘法(即结果与操作数相同,而不是宽度的两倍) )

unsigned int p = 1;
for (unsigned int i = 1; i <= n; i++)
    p *= i;
return p;

如果你的语言不像C那样减少模数学,那么你需要在for循环中使用它

p = (p * i) & 0xffffffff;

-1
投票

计算模数是一种非常快速的运算,尤其是2的幂的模数。相比之下,乘法是非常昂贵的。最快的算法会将素数中的因子因子分解(由于数字小于33,因此非常快)。通过将所有这些乘以一起得到结果,在每次乘法之间取模数,然后从大数字开始。

例:计算10! mod 2 ** 32:使用de Polignac的公式,得到10的素因子!这给你:

10! = 7 * 5 * 5 * 3 * 3 * 3 * 3 * 2 ...

这将比基本算法更快,因为计算(29!mod 2 ** 32)X 30比乘以5,3和2要困难得多,并且在每次之间取模数。

© www.soinside.com 2019 - 2024. All rights reserved.