10

While making a map generator in Java I found a rather unnerving problem with their random number generator, to specify, when two RNGs have very similar seeds (differing in small integers) their first output value will become very similar!

Example code:

Random r = new Random();
long n = 100000; //Choose any number
r.setSeed(n);  
System.out.println(r.nextInt());
r.setSeed(n+1);
System.out.println(r.nextInt());

This pretty much broke my faith in the original Java RNG, since I use coordinates to seed a map generator. Could someone suggest either a redefinition for the Random.next(int bits) method, or some other fix for this problem?

Thank you for your help!

Szoltomi
  • 193
  • 1
  • 10
  • 4
    Why are you calling `nextInt()` on `r3`? Where does that RNG come from? Is it somehow related to `r`? – David Harkness May 28 '11 at 19:51
  • 1
    .. and why change seed after each value? if r3 only is a typo – Kaj May 28 '11 at 19:54
  • @Kaj, to illustrate that the outputs are similar. – aioobe May 28 '11 at 20:04
  • So? That's the wrong way to use it. Use it correctly and it works as documented. – user207421 May 28 '11 at 23:36
  • Sorry, I copied the code in wrong. It's supposed to be `System.out.println(r.nextInt());` Also, I have to set the seed each time in my application because the dynamic map generator uses the coordinates to seed. Inspiration shamelessly stolen from minecraft :P – Szoltomi May 29 '11 at 11:23

4 Answers4

9

did you compare the sequence of the first ~20 values you get from 100000 and 100001?

these are the first 20 nextInts of seeds 100000 and 100001 resp. with in the third column the amount of different bits (bitcount of the xor between the 2)

this last column should remain around 16

-1986972922 -1987357671 13
-1760380366 -604895790  16
-1057894078 -329706441  15
-363772240  -1218064509 15
1545317691  -300240831  14
271304166   -900428132  21
1208561582  273461468   16
-1257783052 1069490639  16
-1549884799 40157720    15
-1514737808 -1818800021 17
-1030569735 1859508545  15
1310070992  880402584   18
-1513092400 971613287   19
-1993219517 354161779   16
-10847170   -204018237  15
-965377044  1488135032  14
802471291   1094582308  22
-539776032  -1021376555 15
2088199751  2070302462  12
-1271582124 64627614    19

not so similar after 3-5 iterations he

besides the standard Random implements a linear congruential RNG which is known not to be the best pseudo-random implementation in existence but the most efficient with memory (only one 64bit word for a period of 2^48)

for the interested the multiplier is 0x5deece66dL and c is 0xbL

ratchet freak
  • 47,288
  • 5
  • 68
  • 106
  • Just so that everyone here is clear - the "not the best pseudo-random implementation" in that context just means that after 2^48 different numbers we'll get the same number again. In other - usually more important regards - the java Random implementation is quite good. After all if you just increment the seed by one every time we want to generate a number we'll get trivially a period of 2^64 for a long, but nobody would think that the generator is better ;) – Voo May 28 '11 at 20:51
  • 1
    actually there are other issues with the LCG from the all-knowing wiki: http://en.wikipedia.org/wiki/Linear_congruential_generator#Advantages_and_disadvantages_of_LCGs I'm talking about the correlation between successive numbers – ratchet freak May 28 '11 at 20:59
2

Your two seeds (PRNG states) differ only by the least significant bit. Considering that PRNGs usually just do some xor-ing and shifting this shouldn't be too surprising.

You shouldn't use Random like this anyway. The state of the PRNG will be updated (state / seed will change by about 50 % of the 48 available bits) upon each nextInt method. That's all you should care about.

aioobe
  • 413,195
  • 112
  • 811
  • 826
2

As I understand, you want a sequence of random numbers that depends on some computed seed, such that you can re-generate the sequence any time when given the same seed. Is that right?

The random number sequence generated by similar seeds starts similar, but soon diverges. You might get results that better fit your need, if you just skip over the first k values. Here, k is a number you have to determine, according to your need of dissimilarity of the sequence and speed of computation.

Christian Semrau
  • 8,913
  • 2
  • 32
  • 39
0

java.security.SecureRandom was introduced to deal with problems in java.util.Random such as the one described in the question. SecureRandom does not exhibit the same predictability (at least, it is not as blatantly obvious). You can fix the problem by using SecureRandom in your code instead of Random as the former is a subclass of the latter.

One might wonder why Sun didn't just fix Random after this problem was discovered. The reason is backward compatibility -- the behaviour of Random could not be changed because it would break existing code that depended upon the particular pseudo-random sequence generated by any given seed.

WReach
  • 18,098
  • 3
  • 49
  • 93
  • 1
    But he's not using a 'sequence generated by any given seed'. He's using a sequence of length 1, then changing the seed slightly. He's misusing the API. This does not exhibit a problem in Random that's solved by using SecureRandom. – user207421 May 29 '11 at 01:04
  • 1
    @EJP His question was not about the randomness of the generated stream, but rather the lack of same for the initial element after reseeding with a nearby seed. This can happen when initial seeds are generated, say, using the time of day. This predictability is not only poor from a pseudo-random point of view, but can be harmful to security. This very problem afflicted early TCP/IP stacks. That is why `SecureRandom` has "secure" in its name, and why any effort was made to fix this behaviour in later JDK implementations. – WReach May 29 '11 at 01:21