I was wondering with the current java 1.7 implementation of the Random class, is it possible for the code below to generate two times the same random long?
Rando
No, getting two identical longs in a row with this algorithm is impossible.
While people were writing long posts about math and other wizardry, I went the code monkey route and brute forced the 2^48 possible seeds over the weekend. No two longs produced in a row were ever equal for any seed.
long int seed = 0;
int next(int bits){
seed = (seed * 0x5DEECE66DL + 0xBL) & ((1L << 48) - 1);
return (int) (seed >> (48 - bits));
}
long int nextLong(){
return ((long int) next(32) << 32) + next(32);
}
int main(int argc, char** argv) {
long int step = atoi(argv[1]);
long int i = step << 32;
long int end = (step+1) << 32;
while(i < end) {
seed = i;
if(nextLong() == nextLong()) {
printf("Found seed %ld\n", i);
return 0;
}
++i;
}
printf("No seed in %ld\n", step);
return 1;
}
then
echo {0..65535} | xargs -n 1 -P 12 ./executable
To come up with an "longer" answer than my previous:
You already linked the implementation, it looks like:
public long nextLong(){
return ((long) next(32) << 32) + next(32);
}
So, obviously, ONE random number calls 2 times next(32)
.
That means, 2 random numbers will be equal, if next(32)
results
in 4 times THE SAME number because the rest of the function is "hardcoded".
Looking at the next()
function, we can see the following:
protected synchronized int next(int bits){
seed = (seed * 0x5DEECE66DL + 0xBL) & ((1L << 48) - 1);
return (int) (seed >>> (48 - bits));
}
The return part can be simply ignored, because again: SAME seed would lead to the SAME return value - otherwhise your CPU is broken.
So, in total: We only need to focus on the line
seed = (seed * 0x5DEECE66DL + 0xBL) & ((1L << 48) - 1);
if that will result in the SAME seed, for four times, 2 random numbers have been generated, that are equal.
(Note: Sequences like a,b,a,b can be excluded to produce the same result. Post is long enough, i skip that part.)
First, lets eliminate the << 48
part. What does that mean? The Number given (1) will be shifted left
48 times. So the binary 0...01
will turn into 1000000000000000000000000000000000000000000000000
(48 zeros)
then, one is subtracted, so what you will get is 0111111111111111111111111111111111111111111111111
(47 ones)
Lets have a look at the first part of that equation:
(seed * 0x5DEECE66D[L] + 0xB[L])
Note, that the ending [L] will only cause it to be a long value instead of a integer.
so, in binary words, that means:
seed * 10111011110111011001110011001101101 + 1011
After all, the function looks like
seed = (seed * 10111011110111011001110011001101101 + 1011) & (0111111111111111111111111111111111111111111111111)
(I left out the leading zeros on the first values)
So, what does & (0111111111111111111111111111111111111111111111111)
do ?
The bitwise-and-operator and basically compares EVERY position of two binary numbers. And only if BOTH of them are "1", the position in the resulting binary number will be 1.
this said, EVERY bit of the equation (seed * 10111011110111011001110011001101101 + 1011)
with a position GREATER than 48 from the RIGHT will be ignored.
The 49th bit equals 2^49
or 562949953421312 decimal
- meaning that & (0111111111111111111111111111111111111111111111111)
basically just says
that the MAXIMUM result can be 562949953421312 - 1
.
So, instead of the result 562949953421312
- it would produce 0 again, 562949953421313
would produce 1 and so on.
All the stuff I wrote above could be easily verified:
While the following code will produce the random seed *11*:
private Long seed = 0L;
protected synchronized int next(int bits){
seed = (seed * 0x5DEECE66DL + 0xBL) & ((1L << 48) - 1);
System.out.println(seed);
return (int) (seed >>> (48 - bits));
}
One can reverse engineer the seed and ALSO gets the seed 11 from a non-0 seed, using the number 562949953421312L
.
private Long seed = 562949953421312L - 0xBL / 0x5DEECE66DL;
protected synchronized int next(int bits){
seed = (seed * 0x5DEECE66DL + 0xBL) & ((1L << 48) - 1);
System.out.println(seed);
return (int) (seed >>> (48 - bits));
}
So, you see: Seed 562949953421312 equals Seed 0.
Easier proof:
Random r = new Random(0L);
Random r2 = new Random(562949953421312L);
if (r.nextLong()==r2.nextLong()){
System.out.println("Equal"); //You WILL get this!
}
it continous of course:
Random r3 = new Random(1L);
Random r4 = new Random(562949953421313L);
if (r3.nextLong()==r4.nextLong()){
System.out.println("Equal");
}
Why is this "magic number" (562949953421312L
) important?
Assuming, we are starting with Seed 0.
The the first new-seed will be: 0 * 10111011110111011001110011001101101 + 1011 = 1011 (dec: 11)
The next seed would be: 1011 * 10111011110111011001110011001101101 + 1011 = 100000010010100001011011110011010111010 (dec: 277363943098)
The next seed (call 3) would be: 100000010010100001011011110011010111010 * 10111011110111011001110011001101101 + 1011 = 10000100101000000010101010100001010100010011100101100100111101 (dec 2389171320405252413)
So, the maximum number of 562949953421312L
is exceeded, which will cause the random number to be SMALLER than the above calculated value.
Also, adding 1011
will cause the result to alternate between odd and even numbers. (Not sure about the real meaning - adding 1 could have worked as well, imho)
So, generating 2 seeds (NOT random numbers) ensures, that they are NOT equal, because a specific "overflow" point has been selected - and adding the MAXIMUM value (562949953421312L) is NOT enough to hit the same number within 2 generations.
And when 2 times the same seed is impossible, 4 times is also impossible, which means, that the nextLong() function could never return the same value for n and n+1 generations.
I have to say, that I wanted to proof the opposite. From a statistical point of view, 2 times the same number is possible - but maybe that's why it's called Pseudorandomness :)
It depends on which question you're asking.
As others have said: The standard formulas for pseudo-random number generators fully explore their value space before repeating.
However: In most applications we don't use the full range of the PRNG's output. We reduce it by dividing or truncating it down to a range that matches the problem we're trying to solve. And most of the ways we do so will, in fact, result in a series of numbers which can include immediate repeats.
And that could be true even if you're simply using an integer random number when the underlying formula is using more bits for its calculations.
So: Theoretically, if you're looking directly at a PRNG's output, the answer is "probably not". Practically, the answer is "don't count on it either way."
I don't think so. I believe that the generator uses the next number as the seed for the subsequent number. So, if you get a value once, and if it were to repeat, your number generator would be stuck in a loop.
However, many applications are looking for a number in a certain range, which makes it possible to repeat since a subsequent number may have the same value modulo the range.
EDIT: Since you included the source code of next
you can see that if ever it returned the same number, it would always return the same number. Hence, you'd be stuck in a loop of one value.