【问题标题】:What complexity are operations on BigInteger?BigInteger 上的操作有多复杂?
【发布时间】:2010-01-28 11:33:47
【问题描述】:

目前BigInteger 中的multiplydividepow 方法有多复杂?文档中没有提到计算复杂性(其他任何地方也没有)。

【问题讨论】:

  • 你可以查看源代码。
  • 其实我已经试过了。但是如果没有很多知识,似乎很难分析。我希望阅读 StackOverflow 的人恰好具备这些知识。也许这太乐观了?
  • 您提到 Java 7 有什么特别的原因吗? BigInteger 自 Java 1.1 起就存在。
  • 我提到 Java 7 是因为我想考虑目前正在进行的更改。
  • (我已经编辑了这个问题以推广到所有版本的 Java ......因为这些天几乎没有人关心 Java-7 和更早的版本。)

标签: java complexity-theory biginteger


【解决方案1】:

如果您查看 BigInteger 的代码(随 JDK 提供),在我看来 multiply(..)O(n^2)(其实方法是multiplyToLen(..))。其他方法的代码稍微复杂一些,大家自己看吧。

注意:这是针对 Java 6 的。我认为它在 Java 7 中不会有所不同。

【讨论】:

  • 乘法有几种复杂性:en.wikipedia.org/wiki/… ... 你能区分 O(n^2) 和 O(n^1.585) 还是 O(n^1.465)?
  • 我相信 Java 7 已经发生了变化。我不记得我找到的详细信息,但它们很少。
  • Rössel:存在其他乘法算法,但 Java 6 不使用它们。当乘以大数时,您肯定会注意到教科书算法和 Karatsuba 乘法之间的区别。除非你用数字填满主内存,否则其他的就不太容易了。
  • OpenJDK 8 做得更好,具有自适应策略。对于较大的 n 值,其复杂度低于 O(n^2)。另见stackoverflow.com/a/20037505/2307853
【解决方案2】:

有一个新的“更好”的 BigInteger 类没有被 sun jdk 用于保守主义和缺乏有用的回归测试(巨大的数据集)。算法更好的人可能在 cmets 中讨论过旧的 BigInteger。

给你http://futureboy.us/temp/BigInteger.java

【讨论】:

    【解决方案3】:

    正如@Bozho's answer 上的 cmets 中所述,Java 8 及更高版本使用更有效的算法来实现乘法和除法,而不是 Java 7 及更早版本中幼稚的O(N^2) 算法。

    Java 8 乘法自适应地使用简单的 O(N^2) 长乘法算法、Karatsuba algorithm 或 3 路 Toom-Cook algorithm,具体取决于所乘数字的大小。后者(分别)是O(N^1.58)O(N^1.46)

    Java 8 除法自适应地使用 Knuth 的 O(N^2) 长除法算法或 Burnikel-Ziegler algorithm。 (根据研究论文,后者是2K(N) + O(NlogN),用于将2N位数字除以N位数字,其中K(N)是两个N位数字的Karatsuba乘法时间。)

    同样对其他一些操作进行了优化。


    文档中没有提到计算复杂性(其他任何地方也没有)。

    Java 8 源代码中提到了复杂性的一些细节。 javadocs 没有提到复杂性的原因是它是特定于实现的,无论是在理论上还是在实践中。 (正如 Java 7 和 8 之间某些操作的复杂性明显不同的事实所示。)

    【讨论】:

      【解决方案4】:

      测量它。使用线性增加的操作数进行操作并在图表上绘制时间。 不要忘记预热 JVM(多次运行)以获得有效的基准测试结果。

      如果运算是线性 O(n),二次 O(n^2),多项式或指数应该是显而易见的。

      编辑:虽然您可以为算法提供理论界限,但它们在实践中可能没有那么有用。首先,复杂性并没有给出因素。一些线性或次二次算法根本没有用,因为它们消耗了太多时间和资源,不足以解决手头的问题(例如 Coppersmith-Winograd 矩阵乘法)。 那么你的计算可能包含你只能通过实验检测到的所有问题。有一些准备算法对解决问题没有任何作用,但可以加速真正的求解器(矩阵条件)。有次优的实现。长度越长,您的速度可能会急剧下降(缓存丢失、内存移动等)。因此,出于实际目的,我建议进行实验。

      最好的办法是每次将输入的长度加倍并比较时间。 是的,您确实找出算法是否具有 n^1.5 或 n^1.8 复杂度。简单的四倍 输入长度,您只需要 1.5 而不是 2 的一半时间。如果将长度乘以 256 倍,您将再次获得 1.8 的近一半时间。

      【讨论】:

      • 这可能行得通。我需要测试较大的 n 值。如果我测量了乘以两个 n 位 BigInteger (t_0) 和两个 2n 位 BigInteger (t_1) 的时间。那么我可能期望复杂度为 O(n^(log2(t_1/t_0)))。总的来说,我对经验方法有点怀疑(可能不公平)。
      • 不过,这是一种很难采用的方法。 先验,没有理由认为使用单个算法而不是算法的组合。因此,从 10 位到 1000 位的缩放可能与从 1000 位到 3000 位的缩放不同。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多