Java Mutable Класс BigInteger - PullRequest
       36

Java Mutable Класс BigInteger

13 голосов
/ 23 октября 2011

Я делаю вычисления с BigIntegers, который использует цикл, который вызывает multiply () около 100 миллиардов раз, а создание нового объекта из BigInteger делает его очень медленным. Я надеялся, что кто-то написал или нашел класс MutableBigInteger. Я нашел MutableBigInteger в пакете java.math, но он закрытый, и когда я копирую код в новый класс, возникает много ошибок, большинство из которых я не знаю, как исправить.

Какие существуют реализации класса Java, такого как MutableBigInteger, который позволяет изменять значение на месте?

Ответы [ 3 ]

8 голосов
/ 21 декабря 2011

По какой-то определенной причине вы не можете использовать отражение, чтобы получить доступ к классу?

Я смог сделать это без проблем, вот код:

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

Править: Я решил немного поиграть, похоже, что java.math.MutableBigInteger не ведет себя точно так, как вы ожидаете.

Он работает по-другому, когда вы умножаетесь, и выдает приятное исключение, когдаон должен увеличивать размер внутреннего массива при присваивании себе.Что-то, я думаю, вполне ожидаемо.Вместо этого я должен поменять объекты так, чтобы результат всегда помещался в другой MutableBigInteger.После нескольких тысяч расчетов накладные расходы на отражение становятся незначительными.MutableBigInteger в конечном итоге выходит вперед и предлагает все более высокую производительность по мере увеличения количества операций.Если вы используете функцию 'mul' с целочисленным примитивом в качестве значения для умножения, MutableBigInteger работает почти в 10 раз быстрее, чем при использовании BigInteger.Я предполагаю, что это действительно сводится к тому, какое значение нужно умножить.В любом случае, если вы выполняете это вычисление «100 миллиардов раз», используя отражение с MutableBigInteger, оно будет выполняться быстрее, чем BigInteger, потому что будет «меньше» выделения памяти и будет кэшировать отражающие операции, удаляя накладные расходы из отражения.

2 голосов
/ 23 октября 2011

JScience имеет вызов класса LargeInteger , который также неизменен, но, по их утверждению, значительно улучшил производительность по сравнению с BigInteger.

http://jscience.org/

APFloat Apint, возможно, тоже стоит проверить.http://www.apfloat.org/apfloat_java/

0 голосов
/ 22 марта 2016

Я скопировал MutableBigInteger, затем прокомментировал тела некоторых методов, которые мне не нужны, добавив хороший

throw new UnsupportedOperationException("...");

при вызове.

здесь выглядит так.

В Revisions вы можете увидеть, что изменилось по сравнению с оригинальным java.math.MutableBigInteger.

Я также добавил несколько удобных методов,

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() {}

Вот как я это использовал:

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