Java Mutable BigInteger Class

前端 未结 3 1379
既然无缘
既然无缘 2021-02-02 16:27

I am doing calculations with BigIntegers that uses a loop that calls multiply() about 100 billion times, and the new object creation from the BigInteger is making it very slow.

相关标签:
3条回答
  • 2021-02-02 16:32

    I copied MutableBigInteger, then commented out some methods' bodies I dind't need, adding a nice

    throw new UnsupportedOperationException("...");
    

    when invoked.

    here is how it looks.

    In Revisions you can see what's changed from the original java.math.MutableBigInteger.

    I also added some convenience methods,

    public void init(long val) {};
    public MutableBigInteger(long val) {};
    // To save previous value before modifying.
    public void addAndBackup(MutableBigInteger addend) {}
    // To restore previous value after modifying.  
    public void restoreBackup() {}
    

    Here is how I used it:

    private BigInteger traverseToFactor(BigInteger offset, BigInteger toFactorize, boolean forward) {
        MutableBigInteger mbiOffset = new  MutableBigInteger(offset);
        MutableBigInteger mbiToFactorize = new MutableBigInteger(toFactorize);
        MutableBigInteger blockSize = new MutableBigInteger(list.size);
    
        if (! MutableBigInteger.ZERO.equals(mbiOffset.remainder(blockSize))) {
            throw new ArithmeticException("Offset not multiple of blockSize");
        }
    
        LongBigArrayBigList pattern = (LongBigArrayBigList) list.getPattern();
    
        while (true) {
            MutableBigInteger divisor = new MutableBigInteger(mbiOffset);
            for (long i = 0; i < pattern.size64(); i++) {
                long testOperand = pattern.getLong(i);
                MutableBigInteger.UNSAFE_AUX_VALUE.init(testOperand);
                divisor.addAndBackup(MutableBigInteger.UNSAFE_AUX_VALUE);
                if (MutableBigInteger.ZERO.equals(mbiToFactorize.remainder(divisor))) {
                    return divisor.toBigInteger();
                }
                divisor.restoreBackup();
            }
    
            if (forward) {
                mbiOffset.add(blockSize);
            } else {
                mbiOffset.subtract(blockSize);
            }
            System.out.println(mbiOffset);
        }
    }
    
    0 讨论(0)
  • 2021-02-02 16:37

    JScience has a class call LargeInteger, which is also immutable, but which they claim has significantly improved perfomance compared to BigInteger.

    http://jscience.org/

    APFloat's Apint might be worth checking out too. http://www.apfloat.org/apfloat_java/

    0 讨论(0)
  • 2021-02-02 16:41

    Is their any particular reason you cannot use reflection to gain access to the class?

    I was able to do so without any problems, here is the code:

    public static void main(String[] args) throws Exception {       
        Constructor<?> constructor = Class.forName("java.math.MutableBigInteger").getDeclaredConstructor(int.class);
        constructor.setAccessible(true);
        Object x = constructor.newInstance(new Integer(17));
        Object y = constructor.newInstance(new Integer(19));
        Constructor<?> constructor2 = Class.forName("java.math.MutableBigInteger").getDeclaredConstructor(x.getClass());
        constructor2.setAccessible(true);
        Object z = constructor.newInstance(new Integer(0));
        Object w = constructor.newInstance(new Integer(0));
    
        Method m = x.getClass().getDeclaredMethod("multiply", new Class[] { x.getClass(), x.getClass()});
        Method m2 = x.getClass().getDeclaredMethod("mul", new Class[] { int.class, x.getClass()});
        m.setAccessible(true);
        m2.setAccessible(true);
    
        // Slightly faster than BigInteger
        for (int i = 0; i < 200000; i++) {
            m.invoke(x, y, z);
            w = z;
            z = x;
            x = w;
        }
    
        // Significantly faster than BigInteger and the above loop
        for (int i = 0; i < 200000; i++) {
            m2.invoke(x, 19, x);
        }
    
        BigInteger n17 = new BigInteger("17");
        BigInteger n19 = new BigInteger("19");
        BigInteger bigX = n17;
    
        // Slowest
        for (int i = 0; i < 200000; i++) {
            bigX = bigX.multiply(n19);
        }
    }
    

    Edit: I decided to play around with a bit more, it does appear that java.math.MutableBigInteger doesn't behave exactly as you would expect.

    It operates differently when you multiply and it will throw a nice exception when it has to increase the size of the internal array when assigning to itself. Something I guess is fairly expected. Instead I have to swap around the objects so that it is always placing the result into a different MutableBigInteger. After a couple thousand calculations the overhead from reflection becomes negligible. MutableBigInteger does end up pulling ahead and offers increasingly better performance as the number of operations increases. If you use the 'mul' function with an integer primitive as the value to multiply with, the MutableBigInteger runs almost 10 times faster than using BigInteger. I guess it really boils down to what value you need to multiply with. Either way if you ran this calculation "100 billion times" using reflection with MutableBigInteger, it would run faster than BigInteger because there would be "less" memory allocation and it would cache the reflective operations, removing overhead from reflection.

    0 讨论(0)
提交回复
热议问题