web-dev-qa-db-ja.com

JavaでランダムなBigInteger値を生成する方法は?

0(包括的)からn(包括的)の範囲の任意の大きなランダム整数を生成する必要があります。私の最初の考えは、 nextDouble を呼び出してnを掛けることでしたが、nが2より大きくなると53、結果はもはや均一に分布しません。

BigInteger には次のコンストラクターがあります。

public BigInteger(int numBits, Random rnd)

ランダムに生成されたBigIntegerを構築し、範囲0から(2numBits -1)、包括的。

これを使用して0〜nの範囲のランダムな値を取得するにはどうすればよいですか(nは2のべき乗ではありません)。

62
Bill the Lizard

ループを使用します。

_BigInteger randomNumber;
do {
    randomNumber = new BigInteger(upperLimit.bitLength(), randomSource);
} while (randomNumber.compareTo(upperLimit) >= 0);
_

平均して、これは2回未満の反復で済み、選択は均一になります。

編集: RNGが高価な場合は、次の方法で反復回数を制限できます。

_int nlen = upperLimit.bitLength();
BigInteger nm1 = upperLimit.subtract(BigInteger.ONE);
BigInteger randomNumber, temp;
do {
    temp = new BigInteger(nlen + 100, randomSource);
    randomNumber = temp.mod(upperLimit);
} while (s.subtract(randomNumber).add(nm1).bitLength() >= nlen + 100);
// result is in 'randomNumber'
_

このバージョンでは、ループが複数回(2 ^ 100で1回未満、つまり確率よりはるかに少ない)実行される可能性は非常に低いホストマシンが次の2秒で自発的に発火すること)。一方、mod()操作は計算コストが高いため、randomSourceインスタンスが非常に遅い場合を除き、このバージョンはおそらく以前のものよりも遅くなります。

45
Thomas Pornin

次のメソッドはBigInteger(int numBits, Random rnd)コンストラクターを使用し、指定されたnよりも大きい場合、結果を拒否します。

public BigInteger nextRandomBigInteger(BigInteger n) {
    Random Rand = new Random();
    BigInteger result = new BigInteger(n.bitLength(), Rand);
    while( result.compareTo(n) >= 0 ) {
        result = new BigInteger(n.bitLength(), Rand);
    }
    return result;
}

これの欠点は、コンストラクターが指定されていない回数呼び出されることですが、最悪の場合(nは2のべき乗よりもわずかに大きい)、コンストラクターの予想される呼び出し回数は約2回である必要があります。

18
Bill the Lizard

最も単純なアプローチ(かなり長い方法で)は、指定されたコンストラクターを使用して適切なビット数(floor(log2 n) + 1)の乱数を生成し、nより大きい場合は破棄します。最悪の場合(例:[0、2n + 1)作成した値の半分以下を平均して捨てます。

13
Jon Skeet

ランダムなBigIntegerを構築し、それからBigDecimalを構築しないのはなぜですか? BigDecimalにはコンストラクターがあります:public BigDecimal(BigInteger unscaledVal, int scale)これはここでは関連しているように見えますが、いいえ?ランダムなBigIntegerとランダムなスケールintを与えると、ランダムなBigDecimalが得られます。いや?

0
Riduidel

モジュラーリダクションを使用するだけ

new BigInteger(n.bitLength(), new SecureRandom()).mod(n)
0
lpsun

以下に、Generic_BigIntegerというクラスで利用できる方法を示します。 Andy TurnerのGeneric Source Code Web Page

/**
 * There are methods to get large random numbers. Indeed, there is a
 * constructor for BigDecimal that allows for this, but only for uniform
 * distributions over a binary power range.
 * @param a_Random
 * @param upperLimit
 * @return a random integer as a BigInteger between 0 and upperLimit
 * inclusive
 */
public static BigInteger getRandom(
        Generic_Number a_Generic_Number,
        BigInteger upperLimit) {
    // Special cases
    if (upperLimit.compareTo(BigInteger.ZERO) == 0) {
        return BigInteger.ZERO;
    }
    String upperLimit_String = upperLimit.toString();
    int upperLimitStringLength = upperLimit_String.length();
    Random[] random = a_Generic_Number.get_RandomArrayMinLength(
        upperLimitStringLength);
    if (upperLimit.compareTo(BigInteger.ONE) == 0) {
        if (random[0].nextBoolean()) {
            return BigInteger.ONE;
        } else {
            return BigInteger.ZERO;
        }
    }
    int startIndex = 0;
    int endIndex = 1;
    String result_String = "";
    int digit;
    int upperLimitDigit;
    int i;
    // Take care not to assign any digit that will result in a number larger
    // upperLimit
    for (i = 0; i < upperLimitStringLength; i ++){
        upperLimitDigit = new Integer(
                upperLimit_String.substring(startIndex,endIndex));
        startIndex ++;
        endIndex ++;
        digit = random[i].nextInt(upperLimitDigit + 1);
        if (digit != upperLimitDigit){
            break;
        }
        result_String += digit;
    }
    // Once something smaller than upperLimit guaranteed, assign any digit
    // between zero and nine inclusive
    for (i = i + 1; i < upperLimitStringLength; i ++) {
        digit = random[i].nextInt(10);
        result_String += digit;
    }
    // Tidy values starting with zero(s)
    while (result_String.startsWith("0")) {
        if (result_String.length() > 1) {
            result_String = result_String.substring(1);
        } else {
            break;
        }
    }
    BigInteger result = new BigInteger(result_String);
    return result;
}
0
Andy Turner