10

long (>10^200) よりもはるかに大きな多数の大きな数値を処理する必要があるため、BigIntegers を使用しています。私が実行する最も一般的な操作は、それらをアキュムレータに追加することです。例:

BigInteger A = new BigInteger("0");
for(BigInteger n : nums) {
    A = A.add(n);
}

もちろん、破壊的なアクションのためにコピーを作成するのは非常に無駄なので (まあ、利用可能な十分なバッファがある限り)、Java がこれを何らかの形で最適化できるかどうか疑問に思っていました (math.java によって公開されていない MutableBigInteger クラスがあると聞きました)。または、独自の BigInteger クラスを作成する必要があるかどうか。

4

4 に答える 4

2

はい、計算集約型の操作にjava.math.MutableBigInteger使用されるクラスがあります。BigInteger残念ながら、package private として宣言されているため、使用できません。Apache Commons ライブラリには「MutableBigInteger」クラスもありますが、これは BigInteger の変更可能なラッパーにすぎず、役に立ちません。

Javaがこれを何らかの形で最適化できるかどうか疑問に思っていました...

いいえ...上記にかかわらず。

または、独自の BigInteger クラスを作成する必要があるかどうか。

それが一つのアプローチです。

もう 1 つは、OpenJDK ソースをダウンロードし、 のソース コードを見つけて、java.math.MutableBigIntegerそのパッケージ名とアクセス権を変更し、それをコード ベースに組み込むことです。唯一の問題は、OpenJDK が GPL (GPL-2 だと思います) の下でライセンスされていることです。これは、変更されたクラスを使用してコードを配布する場合に影響を与えます。

以下も参照してください。

于 2012-05-18T13:44:38.930 に答える
2

より迅速な解決策は、Java パッケージの可視性を回避することです。これを行うには、独自のプロジェクトで java.math という名前のパッケージを作成し、パッケージ private MutableBigInteger を次のように公開するパブリック クラスを作成します。

package java.math;

public class PublicMutableBigInteger extends MutableBigInteger {

}

次に、java.math.PublicMutableBigInteger; をインポートするだけです。他のクラスとして使用します。このソリューションは迅速であり、特定のライセンスを課すことはありません。

于 2012-05-19T09:43:57.050 に答える
2

addメソッドが何をするかを知ることができないため、コンパイラができることはあまりありません。ループ本体の生成コードを次に示します。ご覧のとおり、単純に呼び出しaddて結果を保存します。

   25:  iload   5
   27:  iload   4
   29:  if_icmpge       51
   32:  aload_3
   33:  iload   5
   35:  aaload
   36:  astore  6
   38:  aload_1
   39:  aload   6
   41:  invokevirtual   #5; //Method java/math/BigInteger.add:(Ljava/math/BigInteger;)Ljava/math/BigInteger;
   44:  astore_1
   45:  iinc    5, 1
   48:  goto    25

理論的には、Java 仮想マシンのランタイム システムの方が優れている可能性があります。たとえば、あるオブジェクトが割り当てられたばかりの別のオブジェクトを継続的に上書きしていることを検出し、2 つの割り当てバッファーを交換するだけです。ただし、ガベージ コレクションのログ記録を有効にして次のプログラムを実行するとわかるように、残念ながらそうではありません。

import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Random;

class Test {
    public static void main(String[] args) {
    ArrayList <BigInteger> nums = new ArrayList<BigInteger>();
    final int NBITS = 100;
    final int NVALS = 1000000;

    System.out.println("Filling ArrayList");
    Random r = new Random();
    for (int i = 0; i < NVALS; i++)
        nums.add(new BigInteger(NBITS, r));

    System.out.println("Adding ArrayList values");
    BigInteger A = new BigInteger("0");
    for(BigInteger n : nums) {
        A = A.add(n);
    }

    System.gc();
    }
}

追加プロセス中のガベージ コレクション呼び出しを参照してください。

C:\tmp>java -verbose:gc Test
Filling ArrayList
[GC 16256K->10471K(62336K), 0.0257655 secs]
[GC 26727K->21107K(78592K), 0.0304749 secs]
[GC 53619K->42090K(78592K), 0.0567912 secs]
[Full GC 42090K->42090K(122304K), 0.1019642 secs]
[GC 74602K->65857K(141760K), 0.0601406 secs]
[Full GC 65857K->65853K(182144K), 0.1485418 secs]
Adding ArrayList values
[GC 117821K->77213K(195200K), 0.0381312 secs]
[GC 112746K->77245K(228288K), 0.0111372 secs]
[Full GC 77245K->137K(228288K), 0.0327287 secs]

C:\tmp>java -version
java version "1.6.0_25"
Java(TM) SE Runtime Environment (build 1.6.0_25-b06)
Java HotSpot(TM) 64-Bit Server VM (build 20.0-b11, mixed mode)
于 2012-05-19T10:19:46.893 に答える
0

この場合、Java は特別な最適化を行いません。BigInteger は通常、他のクラスと同様に通常のクラスとして扱われます (たとえば、多くの文字列を連結するときに特別な最適化が行われることがある String とは異なります)。

しかし、ほとんどの場合、BigInteger は十分に高速であるため、問題にはなりません。本当に問題があると思われる場合は、コードをプロファイリングして、時間がかかっている原因を突き止めることをお勧めします。

BigIntegers の追加が本当にボトルネックである場合は、カスタムの変更可能な big-integer クラスを使用してアキュムレータとして機能させることが理にかなっています。しかし、これが本当に主なボトルネックであることを証明する前に、私はこれを行いません.

于 2012-05-19T10:28:56.233 に答える