如何在Java中生成一个随机的BigInteger值?

我需要生成0(含)到n(不含)的任意大的随机整数。 我最初的想法是调用nextDouble并乘以n,但是一旦n变得大于2 53 ,结果将不再是均匀分布的。

BigInteger有以下构造函数可用:

 public BigInteger(int numBits, Random rnd) 

构造一个随机生成的BigInteger,均匀分布在0到(2 numBits – 1)范围内。

这怎么可以用来得到一个在0 – n范围内的随机值,其中n不是2的幂?

使用循环:

 BigInteger r; do { r = new BigInteger(n.bitLength(), rnd); } while (r.compareTo(n) >= 0); 

平均而言,这将需要less于两次迭代,并且select将是统一的。

编辑:如果你的RNG是昂贵的,你可以通过以下方法限制迭代次数:

 int nlen = n.bitLength(); BigInteger nm1 = n.subtract(BigInteger.ONE); BigInteger r, s; do { s = new BigInteger(nlen + 100, rnd); r = s.mod(n); } while (s.subtract(r).add(nm1).bitLength() >= nlen + 100); // result is in 'r' 

在这个版本中,循环不止一次(在2 ^ 100中less于一次的机会,即远低于主机在接下来的秒钟内自发着火的概率)。 另一方面, mod()操作在计算上是很昂贵的,所以这个版本可能比以前慢,除非rnd实例如果特别慢。

以下方法使用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次左右。

最简单的方法是使用指定的构造函数生成一个具有正确位数( floor(log2 n) + 1 )的随机数,如果它大于n,则丢弃它。 在最差的情况下(例如[0,2 n + 1]范围内的数字),平均可以丢掉您创build的值的一半。

为什么不构build一个随机BigInteger,然后从它build立一个BigDecimal? BigDecimal中有一个构造函数: public BigDecimal(BigInteger unscaledVal, int scale) ,在这里看起来相关,不是吗? 给它一个随机BigInteger和一个随机比例int,并且你将有一个随机的BigDecimal。 没有?

下面是我如何在一个名为Generic_BigInteger的类中使用: Andy Turner的通用源代码网页

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

只需使用模块化缩减

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

编译这个F#代码到一个DLL中,你也可以在你的C#/ VB.NET程序中引用它

 type BigIntegerRandom() = static let internalRandom = new Random() /// Returns a BigInteger random number of the specified number of bytes. static member RandomBigInteger(numBytes:int, rand:Random) = let r = if rand=null then internalRandom else rand let bytes : byte[] = Array.zeroCreate (numBytes+1) r.NextBytes(bytes) bytes.[numBytes] <- 0uy bigint bytes /// Returns a BigInteger random number from 0 (inclusive) to max (exclusive). static member RandomBigInteger(max:bigint, rand:Random) = let rec getNumBytesInRange num bytes = if max < num then bytes else getNumBytesInRange (num * 256I) bytes+1 let bytesNeeded = getNumBytesInRange 256I 1 BigIntegerRandom.RandomBigInteger(bytesNeeded, rand) % max /// Returns a BigInteger random number from min (inclusive) to max (exclusive). static member RandomBigInteger(min:bigint, max:bigint, rand:Random) = BigIntegerRandom.RandomBigInteger(max - min, rand) + min