Reputation: 115
I discovered the hard way that this method seems to fail miserably when you feed it a power of 2. Given two Random objects of different seeds, it seems that the first integer they return when asked to return an integer between 0 (inclusive) and and a power of two (exclusive) is always the same; the seeds don't matter. For example:
public static void main(String[] args) {
Random mRandom;
for (int i = 0; i < 10; i++) {
mRandom = new Random(i);
System.out.println(mRandom.nextInt((int) Math.pow(2, 4)));
}
}
Console:
11
11
11
11
11
11
11
11
11
11
I chose 2^4 arbitrarily, but it seems to work for any power of two. What's going on? Furthermore, how can I avoid this?
Upvotes: 9
Views: 821
Reputation: 21961
This problem occurs due to 2 reasons.
Random
class.nextInt(int n)
, if n is power of 2Because, you have initiated new Random
instance with new seed
value which has influence on the nextInt
value generation. According to Java docs of Random(long seed).
Creates a new random number generator using a single long seed. The seed is the initial value of the internal state of the pseudorandom number generator which is maintained by method next(int).
The invocation new Random(seed) is equivalent to: Random rnd = new Random(); rnd.setSeed(seed);
If you try to generate random value, without new seed
than it will generate real random value, even though new instance of Random
class.
for (int i = 0; i < 10; i++) {
mRandom = new Random(); // Without seed
System.out.println(mRandom.nextInt((int) Math.pow(2, 4)));
}
Output: 2 1 12 4 3 9 9 8 2 9
Besides this, Random#nextInt has effect of power of 2. If n is a power of 2 it will return (int)((n * (long)next(31)) >> 31)
which will always same for same n. According to nextInt
Algorithm,
public int nextInt(int n) {
if (n <= 0)
throw new IllegalArgumentException("n must be positive");
if ((n & -n) == n) // i.e., n is a power of 2
return (int)((n * (long)next(31)) >> 31);
int bits, val;
do {
bits = next(31);
val = bits % n;
} while (bits - val + (n-1) < 0);
return val;
}
Upvotes: 6
Reputation: 340
You could also use Math.random and Math.pow together for more simplicity if you wanted.
for (int i = 0; i < 10; i++) {
int pows = (int) Math.pow(2, 4);
int random = (int)(Math.random()*pows);
System.out.println(""+random);
}
Upvotes: 0