The class BigInteger provides the constructor
public BigInteger(int numBits, Random rnd)
Since Java 17 the use of RandomGenerator
is encouraged. How can I generate a random BigInteger in the range 0...(2^n)-1 using only an instance of RandomGenerator
.
Just using Random
& SecureRandom
Why do you need this? Because the new Random()
gets you a relatively fast non-secure random number generator and new SecureRandom()
provides a CSPRNG (Cryptographically Secure Pseudo Random Number Generator). Both those instances are instances of the RandomGenerator
interface. So the only time you'd need this is if you want a specific Random generator defined in the random package.
If you don't need speed and you haven't any specific needs regarding the distribution - you'd probably know if you'd have any - then please just use new BigInteger(n, new SecureRandom())
.
Implementing Random
TL;DR: don't
As RandomGenerator
is an interface. The Random
class has been refactored into a class that implements the interface. That means you can use the old Random
wherever an interface specifies RandomGenerator
. However, that doesn't work the other way around.
Random
itself is not a final
class, so it can be inherited from. So you could, in principle, create an adapter class by simply implementing all methods in Random
and calling the similarly named methods of a wrapped RandomGenerator
. This is however very dangerous practice as future extensions of Random
may break everything. In the worst case it will show inconsistent behavior by mixing the state of the parent Random
object and the wrapped RandomGenerator
.
Move to Java 19
In Java 19 (build b21) there is an adapter method provided for RandomGenerator
named Random#from(RandomGenerator)
. You can see the feature request here. Note that this is an intermediate step; preferably the BigInteger
method should be retrofitted to use RandomGenerator
(as stated in the feature request).
Beware that you should never use this wrapper class if you suspect that setSeed
can ever be called, e.g. when generating a large prime in the BigInteger
class. If it is called it will generate an UnsupportedOperationException
.
These kind of classes go through all kinds of testing so the Java provided method can be trusted. However, the fact that a method can be retrofitted to use setSeed
could be a problem regarding forward compatibility - you have been warned.
The special case of [0, n^2)
However, you've got one out: the range 0..2^n-1
or [0, 2^n-1)
is a number consisting of a set of bits. So what you can do is use your RandomGenerator
, fill an array of bytes, remove the most significant bits from the first byte (as Java is big endian), and then convert the array into a signed integer:
/**
* Mimics the {@link BigInteger#BigInteger(int, Random)} function using a
* {@link RandomGenerator} instance.
*
* @param numBits maximum bitLength of the new BigInteger.
* @param rnd source of randomness to be used in computing the new
* BigInteger.
* @throws IllegalArgumentException {@code numBits} is negative.
* @see #bitLength()
*/
public static BigInteger generateRandomBigInteger(int numBits, RandomGenerator rng) {
if (numBits < 0) {
throw new IllegalArgumentException("numBits must be non-negative");
}
if (numBits == 0) {
return BigInteger.ZERO;
}
int bytes = (numBits + Byte.SIZE - 1) / Byte.SIZE;
// mask bits that we need the value of to 1, the others - if any -- will be set
// to zero
byte bitMask = (byte) ((1 << ((numBits - 1) % Byte.SIZE + 1)) - 1);
byte[] randomBytes = new byte[bytes];
rng.nextBytes(randomBytes);
randomBytes[0] &= bitMask;
return new BigInteger(1, randomBytes);
}
numBits
is of course the same as your n
variable.
The case for [0, max)
Actually, you can combine the comparison function and the random bit generator in such a way that you can create large random values in the range 0..max with max having any value, with a minimum amount of random bits and no nasty BigInteger
operations (such as division) at all.
You can find my implementation of that here but you may get weirded out. Sorry in advance, and yes, this is a shameless plug of my RNGBC algorithm :)