BigInteger在合理的时间内串起来

时间:2021-02-23 16:54:17

I am writing a program where time is important, and I just realized through a lot of debugging prints that my big holdup (80% of computing time) is converting a very large BigInteger (50K digits) into a string.
Is this behavior to be expected or how can I change something to make it run faster?

我正在编写一个时间很重要的程序,我刚刚通过大量的调试打印实现了我的大保持(80%的计算时间)将一个非常大的BigInteger(50K位)转换为字符串。这种行为是预期的,或者我如何改变某些东西以使其运行得更快?

2 个解决方案

#1


4  

Converting numbers to strings is an expensive operation even if you use long and double.

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

Normally, the only thing more expensive is the IO you perform when writing the text for a file or the console.

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

It is worth noting that the built in converter a number to text is an O(N^2) operation where N is the number of digits. As such it is not surprising that 50K digit numbers take a very long time to convert to a decimal String.

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


Based on tmyklebu's suggestion I have written this. It is slower for numbers with less than 500 digits, but is much faster in the range of 50,000 digits.

根据tmyklebu的建议,我写了这个。对于数字少于500位的数字,速度较慢,但​​在50,000位数的范围内速度要快得多。

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;
}

prints

版画

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

#2


1  

Check this post about Performance of BigInteger.toString(radix). It could give you an idea.

查看这篇关于BigInteger.toString(radix)的性能的帖子。它可以给你一个想法。

#1


4  

Converting numbers to strings is an expensive operation even if you use long and double.

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

Normally, the only thing more expensive is the IO you perform when writing the text for a file or the console.

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

It is worth noting that the built in converter a number to text is an O(N^2) operation where N is the number of digits. As such it is not surprising that 50K digit numbers take a very long time to convert to a decimal String.

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


Based on tmyklebu's suggestion I have written this. It is slower for numbers with less than 500 digits, but is much faster in the range of 50,000 digits.

根据tmyklebu的建议,我写了这个。对于数字少于500位的数字,速度较慢,但​​在50,000位数的范围内速度要快得多。

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;
}

prints

版画

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

#2


1  

Check this post about Performance of BigInteger.toString(radix). It could give you an idea.

查看这篇关于BigInteger.toString(radix)的性能的帖子。它可以给你一个想法。