【问题标题】:Fast algorithm to calculate large n! mod 2³²计算大 n 的快速算法!模 2³²
【发布时间】:2013-03-05 17:38:06
【问题描述】:

我想计算 N 的准确值!模组 232。 N 最大为 231

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

【问题讨论】:

  • 这个问题有实际应用吗?数字(2^31)!有点大得可笑。请查看stackoverflow.com/questions/1384160/… 以了解更多关于此的讨论。
  • 一台(标准台式机)计算机甚至不可能在一秒钟内写下这么大的数字。
  • @Andrew:大约 40 亿,真的没有那么巨大..
  • @BlueRaja-DannyPflughoeft 我说的是 (2^32)!而不是 2^32。前者包含超过十亿位数字。
  • @Andrew:在正常情况下计算n! mod p(即当p为素数时),每次乘法后你总是mod;那么你处理的数字永远不会大于p。有关更多信息,请参阅我的评论 here

标签: algorithm biginteger modulo factorial


【解决方案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,和/或您可以在阶乘中预先考虑 2 的幂,这很容易计算(见上文)。

【讨论】:

  • 只需要模 2**32 部分,因为 Python 支持 bigint 数学。在大多数普通语言中,您只需要对 32 位类型进行直接乘法,结果已经自动模 2³²
【解决方案2】:

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

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

【讨论】:

    【解决方案3】:

    一般而言,您可以使用大多数编程语言中可用的整数类型(int、long)来实现算法以 2 的小幂为模,而无需使用 bignum 或模约简。对于模 232,您将使用 32 位 int。 "Integer overflow" takes care of the modular arithmetic.

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

    【讨论】:

      【解决方案4】:

      当两个任意长度的数字相乘时,低位总是精确的,因为它不依赖于高位。基本上 a×b mod m = [(a mod m)×(b mod m)] mod em> m 这样做N! mod m 做就行了

      1×2×...×N mod m = (...(((1×2 mod m)×3 mod m)×4 mod m)...)×N mod m
      

      Modulo 2n 是一种特殊情况,因为使用AND 操作很容易获得模数。模 232 更加特殊,因为 C 和大多数类 C 语言中的所有无符号运算都被简化为 32 位无符号类型的模 232

      因此,您可以将数字乘以两倍宽的类型,然后将 AND 与 232 - 1 相乘以获得模数

      uint64_t p = 1;
      for (uint32_t i = 1; i <= n; i++)
          p = p*i & 0xFFFFFFFFU;
      return p;
      

      【讨论】:

        【解决方案5】:

        计算模数是一种非常快速的运算,尤其是 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 并在每次之间取模要困难得多。

        【讨论】:

        • 这实际上会慢很多。首先,在进行因式分解时需要做很多额外的工作,然后是更多的乘法。我不知道你为什么认为乘以 5、3 和 2,每次取模,比乘以 30 快。
        • 再一次,要分解的数字非常小,所以分解很容易。但是要乘的数字很大,很难。很明显,如果你可以减少中间结果的大小,你会更快。尝试计算 67 * 4 mod 100。最好的方法是:67 * 2 = 134, 34 * 2 = 68。这样做我避免了将 134 乘以 2。
        • 乘法不是“运算”,复杂性理论试图教它。是的,这个算法比第一个算法做了更多的乘法运算,但是每个乘法运算的复杂度要小得多。
        • @double_squeeze:实际上,正如我在回答中提到的那样,您可以完全忽略 2 的因数。然而,对于n &lt; 34,这几乎不值得:我可以在我并不特别花哨的笔记本电脑上在 32 毫秒内计算整个范围(包括 python 启动)。
        • @double_squeeze:如果您手动操作,您会想要使用en.wikipedia.org/wiki/De_Polignac%27s_formula,而不是尝试一次将所有这些数字分解,无论多么小,这都会严重低效他们是。
        猜你喜欢
        • 2011-05-23
        • 1970-01-01
        • 2011-11-27
        • 1970-01-01
        • 1970-01-01
        • 2010-12-17
        • 1970-01-01
        • 1970-01-01
        • 2023-02-10
        相关资源
        最近更新 更多