Java 在合理时间内将BigInteger转换为字符串

Java 在合理时间内将BigInteger转换为字符串,java,string,performance,biginteger,Java,String,Performance,Biginteger,我正在编写一个时间很重要的程序,通过大量的调试打印,我意识到我的大延迟(80%的计算时间)正在将一个非常大的BigInteger(50K位)转换成字符串。 这种行为是预期的吗?或者我如何更改某些内容以使其运行更快?即使使用long和double,将数字转换为字符串也是一项昂贵的操作 通常,唯一更昂贵的是在为文件或控制台写入文本时执行的IO 值得注意的是,内置的数字到文本的转换器是一个O(N^2)操作,其中N是位数。因此,50K位数字转换为十进制字符串需要很长时间也就不足为奇了 根据tmykle

我正在编写一个时间很重要的程序,通过大量的调试打印,我意识到我的大延迟(80%的计算时间)正在将一个非常大的BigInteger(50K位)转换成字符串。

这种行为是预期的吗?或者我如何更改某些内容以使其运行更快?

即使使用
long
double
,将数字转换为字符串也是一项昂贵的操作

通常,唯一更昂贵的是在为文件或控制台写入文本时执行的IO

值得注意的是,内置的数字到文本的转换器是一个O(N^2)操作,其中N是位数。因此,50K位数字转换为十进制字符串需要很长时间也就不足为奇了


根据tmyklebu的建议,我写了这篇文章。对于小于500位的数字,速度较慢,但对于50000位的数字,速度要快得多

public static void main(String... args) {
    BigInteger bi = BigInteger.valueOf(11).pow(48100);
    System.out.println(bi.toString());
    System.out.println(toString(bi));
    System.out.println("bi.length=" + bi.toString().length() + ", toString(bi).length=" + toString(bi).length());
    for (int i = 0; i < 10; i++) {
        long start = System.nanoTime();
        String s = bi.toString();
        long mid = System.nanoTime();
        String s2 = toString(bi);
        long end = System.nanoTime();
        System.out.printf("time1 %.3f ms, time2 %.3f ms%n", (mid - start) / 1e6, (end - mid) / 1e6);
        if (!s.equals(s2))
            throw new AssertionError();
    }
}

public static String toString(BigInteger bi) {
    StringBuilder sb = new StringBuilder();
    int i = 16;
    while (bi.compareTo(powerOfTen(i)) > 0)
        i *= 2;
    toString(bi, sb, i);
    int start = 0;
    while (sb.charAt(start) == '0')
        start++;
    return sb.substring(start);
}

private static void toString(BigInteger bi, StringBuilder sb, int digits) {
    if (digits < 18) {
        int start = sb.length();
        for (int i = 0; i < digits; i++)
            sb.append('0');
        long l = bi.longValue();
        for (int i = digits - 1; i >= 0; i--, l /= 10)
            sb.setCharAt(start + i, (char) ('0' + l % 10));
    } else {
        int digits2 = digits / 2;
        BigInteger[] parts = bi.divideAndRemainder(powerOfTen(digits2));
        toString(parts[0], sb, digits - digits2);
        toString(parts[1], sb, digits2);
    }
}

private static final Map<Integer, BigInteger> powersOfTen = new HashMap<Integer, BigInteger>();

private static BigInteger powerOfTen(int digits2) {
    BigInteger tens = powersOfTen.get(digits2);
    if (tens == null)
        powersOfTen.put(digits2, tens = BigInteger.TEN.pow(digits2));
    return tens;
}

查看这篇文章。它可以给你一个主意。

你真的用那个大整数计算吗?指数、除法和乘法?你需要结果是字符串吗?我需要把它放进一个文件中。你能把它作为二进制转储,避免转换成十进制吗?我想你不需要自己阅读50K数字?这是一个家庭作业,其结果需要准确。所以我可能应该找到一个更好的算法?我假设作业需要一个特定的格式。如果它必须是十进制的,没有简单的解决方案可以使它更快。不要忘记,内置代码是由资深开发人员多年来开发的。我想大多数简单的解决方案都已经被考虑过了,我指的是一些不需要我使用BigInteger的快捷方式。尽管如此,帕文在这个问题上的回答指出,toString()并没有针对任何特定的基数进行优化。我只需要以其他方式对其进行优化。@PeterLawrey:不,进行基数转换不需要二次时间。时间足够了。将您要转换的基数除以源基数的上半部分和下半部分。把两半都转换。(在目标基数中)将上半部分乘以正确的量(因为它是上半部分)。添加结果(以目标基数)。我认为GMP有一些类似的东西,在实践中不那么可怕。那是十六进制的,而不是十进制的。
973096948397248203274473625697464617461138859359846077811290536......
973096948397248203274473625697464617461138859359846077811290536......
bi.length=50091, toString(bi).length=50091
time1 525.892 ms, time2 67.260 ms
time1 458.559 ms, time2 98.178 ms
time1 441.275 ms, time2 92.902 ms
time1 399.339 ms, time2 98.448 ms
time1 518.761 ms, time2 97.804 ms
time1 396.884 ms, time2 65.651 ms
time1 363.945 ms, time2 98.827 ms