我想知道使用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
针对后续的数值运算进行了优化,但是对于庞大的数字建设成本更高,这似乎是合理的对我来说。
答案 0 :(得分:6)
从source稍微简化,就是这种情况,因为在“传统的”字符串解析循环中
for each digit y from left to right:
x = 10 * x + y
你有一个问题10 * x
在x
的长度上需要时间线性,这是不可避免的,并且这个长度或多或少是每个数字的常数因素,也是不可避免的。
(实际的实现比这更聪明 - 它试图一次解析一个int
的二进制数字,因此循环中的实际乘数更可能是1或20亿 - - 但是,它总体上仍然是二次方。)
那就是说,10^6
位的数字至少是一个googol,并且比我听说甚至用于加密目的的任何数字都要大。您正在解析一个带有两兆内存的字符串。是的,这需要一段时间,但我怀疑JDK的作者没有看到优化这种罕见用例的重点。
答案 1 :(得分:2)
如果将BigInteger
指定为十进制数字,则O(n ^ 2)工作由十进制到二进制转换引起。
此外,10 ^ 7位数是一个非常庞大的数字。对于像RSA这样的典型加密算法,您可以处理10 ^ 3到10 ^ 4个数字。大多数BigInteger
操作都没有针对如此大量的数字进行优化。
答案 2 :(得分:1)
实际上,您正在测量解析字符串并创建BigInteger所需的时间。涉及BigIntegers的数字操作比这更有效。