【问题标题】:new BigInteger(String) performance / complexity新的 BigInteger(String) 性能/复杂性
【发布时间】:2013-02-07 17:20:11
【问题描述】:

我想知道使用 new BigInteger(String) 构造函数构造 BigInteger 对象的性能/复杂性

考虑以下方法:

  public static void testBigIntegerConstruction()
  {
    for (int exp = 1; exp < 10; exp++)
    {
      StringBuffer bigNumber = new StringBuffer((int) Math.pow(10.0, exp));
      for (int i = 0; i < Math.pow(10.0, exp - 1); i++)
      {
        bigNumber.append("1234567890");
      }

      String val = bigNumber.toString();
      long time = System.currentTimeMillis();
      BigInteger bigOne = new BigInteger(val);
      System.out.println("time for constructing a 10^" + exp
          + " digits BigInteger : " + ((System.currentTimeMillis() - time))
          + " ms");
    }
  }

此方法创建BigInteger 字符串的对象10^x 数字,其中x=1 在开头,并且随着每次迭代而增加。它测量并输出构造相应BigInteger对象所需的时间。

在我的机器上(Intel Core i5 660,JDK 6 Update 25 32 位)输出是:

time for constructing a 10^1 digits BigInteger : 0 ms
time for constructing a 10^2 digits BigInteger : 0 ms
time for constructing a 10^3 digits BigInteger : 0 ms
time for constructing a 10^4 digits BigInteger : 16 ms
time for constructing a 10^5 digits BigInteger : 656 ms
time for constructing a 10^6 digits BigInteger : 59936 ms
time for constructing a 10^7 digits BigInteger : 6227975 ms

忽略不超过 10^5 的行(由于(处理器)缓存效果、JIT 编译等可能引入的失真),我们可以清楚地看到 O(n^2) 的复杂性 这里。 请记住,由于不变性,BigInteger 上的每个操作都会创建一个新操作,这是对大量数字的主要性能损失

问题:

  • 我错过了什么吗?

  • 为什么会这样?

  • 这个问题在最近的 JDK 中修复了吗?

  • 还有其他选择吗?

更新:

我做了进一步的测量,我可以从一些答案中证实这一说法:
似乎BigInteger 已针对后续数值运算进行了优化,但代价是大量数字的更高构建成本,这对我来说似乎是合理的。

【问题讨论】:

  • 看源代码似乎是合理的......
  • 您是否使用非字符串构造函数进行了测量,我不确定操作是否会序列化-反序列化为字符串。

标签: java performance complexity-theory biginteger


【解决方案1】:

source 稍微简化一下,之所以如此,是因为在“传统”字符串解析循环中

for each digit y from left to right:
  x = 10 * x + y

您遇到的问题是,10 * x 的时间与x 的长度呈线性关系,这是不可避免的,而且对于每个数字,该长度或多或少地增长一个常数因子,这也是不可避免的。

(实际的实现比这更聪明一些——它尝试一次解析int的二进制数字,因此循环中的实际乘数更可能是 1 或 20 亿——但是是的,总体上它仍然是二次的。)

也就是说,带有10^6 数字的数字至少是一个googol,这比我听说过的任何用于加密目的的数字都要大。您正在解析一个占用2 MB 内存 的字符串。是的,这需要一段时间,但我怀疑 JDK 的作者没有看到针对这种罕见用例进行优化的意义。

【讨论】:

    【解决方案2】:

    如果将 BigInteger 指定为十进制数字,则 O(n^2) 工作量是由十进制到二进制转换引起的。

    另外,10^7 位是一个非常大的数字。对于像 RSA 这样的典型加密算法,您将处理 10^3 到 10^4 位。大多数BigInteger 操作并未针对如此大量的数字进行优化。

    【讨论】:

    • 也就是说,您的号码长度为10^(10^7)
    【解决方案3】:

    您实际上是在测量解析字符串和创建 BigInteger 所需的时间。涉及 BigIntegers 的数值运算会比这更有效。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-10-03
      • 1970-01-01
      • 1970-01-01
      • 2014-07-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多