我想计算N的确切值! mod 2 ^ 32。 N最高可达231
任何语言都可以,但我希望详细解释算法。时间限制<1秒
在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的幂,这很容易计算(见上文)。
正常计算阶乘(乘以数字1,2,3,...),在每次乘法后执行模数。这将为您提供N
的小值的结果。
对于较大的N
值,请执行相同的操作。很快,你的中间结果将是0
,然后你可以立即停止循环并返回0
。您停止的点将相对较快:对于N == 64
,结果将已经是0
,因为1..64
的乘积包含32个偶数,因此可被2^32
整除。你得到0的N
的实际最小值将小于64。
通常,您可以使用大多数编程语言中可用的整数类型(int,long)来实现模数为2的小功率而无需bignums或模块缩减的算法。对于模232,您将使用32位int。 "Integer overflow" takes care of the modular arithmetic.
在这种情况下,由于只有34个不同的结果,因此查找表可能比计算阶乘更快,假设经常使用阶乘以使表加载到CPU缓存中。执行时间将以微秒为单位。
当乘以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;
计算模数是一种非常快速的运算,尤其是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要困难得多,并且在每次之间取模数。