6

I have recently been discussing the initialisation of multiple random number generators of the same type in the comments of another post and in that discussion we asked the following questions:

1) Is it a good idea to create multiple instances of the same random number generator with different seeds and use these random number generators in different parts of the program?

2) In particular, can the technique of creating random number generators using the .Net Random class, seeded as below, and using each RNG in different program contexts cause problems:

int size = 64;  // The number of RNGs to use
int seed;       // Get seed using some normal technique
Random[] r = new Random[size];

for (int i = 0; i < size; i++)
{
    r[i] = new Random(seed + i);
}

3) What would you recommend instead if multiple streams of random numbers are required?

4) How would you recommend generating random numbers when thread safety is required?

Andrew
  • 702
  • 7
  • 24
  • This question has been created as a reply to comments in an answer for the following other question: [how-do-i-seed-a-random-class-to-avoid-getting-duplicate-random-values](http://stackoverflow.com/questions/1785744/how-do-i-seed-a-random-class-to-avoid-getting-duplicate-random-values/1785821) – Andrew Apr 02 '16 at 18:03

2 Answers2

13

1) Is it a good idea to create multiple instances of the same random number generator with different seeds and use these random number generators in different parts of the program?

No. The above scheme is in general not recommended.

In his book, The Art of Computer Programming, Volume 2: Seminumerical Algorithms. Addison-Wesley, Reading, MA, third edition, 1997, Dr. Knuth states that

It is not easy to invent a foolproof source of random numbers.

In this case, I point out that taking subsequences from a random sequence may be less random than the original sequence of random numbers:

Notice that Micosoft's Random implementation is based on a subractive lagged-fibonacci generator:

This kind of random number generator is known for an inbuilt three-point correlation, after all, we're generating the next random number: Subtractive Lagged-Fibonacci Generator

These kinds of Random Number Generators also depend heavily on the initialisation of their initial 55 number state. Poor initialisation may lead to poor random numbers. In the above case, similar states, may result in correlated random numbers from each of the different random number generators. Microsoft even recommends against this in their MSDN post about System.Random: MSDN The System.Random class and thread safety:

Instead of instantiating individual Random objects, we recommend that you create a single Random instance to generate all the random numbers needed by your app.

We shall look at an example where a particular initialisation creates strong correlation between the different random number generators and look for alternatives.

2) I have implemented a program that attempts to initialise 64 instances of Random as described above so that we observe any visible flaws. I chose a particular initialisation as a proof of concept:

int size = 64;    // The number of random numbers generators
int length = 20;  // The number of random numbers from each generator
int steps = 18;   // Move 18 steps forward in the beginning to show a particular phenomenon

Random[] r = new Random[size];

for (int i = 0; i < size; i++)
{
     r[i] = new Random(i + 1);

     // move RNG forward 18 steps
     for (int j = 0; j < steps; j++)
     {
          r[i].Next(3);
     }
}


for (int i = 0; i < size; i++)
{
     for (int j = 0; j < length; j++)
     {
          Console.Write(r[i].Next(3) + ", ");  // Generate a random number, 0 represents a small number, 1 a medium number and 2 a large number
     }

     Console.WriteLine();
}

This program generates the output shown here, each row represents the output from another RNG:

Output of the program

Notice that the highlighted columns: at particular places the RNGs seem to synchronise and produce output that does not look independent from each other.

I also wish to add another note, that creating a single list of random numbers and taking one random number from the list of each row also produces poor looking random numbers (the RNG being used here is known to have fail some statistical after all!).

3) The type of RNG used depends on your context. Some may be happy with the above output. In other cases, the RNG used may be unusable (Monte Carlo Simulation and Cryptography are two scenarios where System.Random should never be used, even for one stream of random numbers).

If you need to extract multiple subsequences of Random Numbers, find an RNG that has been designed for that purpose:

4) Finally, what if I want to use System.Random in multiple threads? Microsoft MSDN has the answer in the same link I referred to above:

Andrew
  • 702
  • 7
  • 24
  • This is much more convincing than any of your comments... although with only 3 numbers to choose from at any time, it's not *quite* as surprising. (If you'd shown the same pattern with `Next(100)` for example, it would be even more surprising. – Jon Skeet Apr 02 '16 at 18:05
  • @JonSkeet it's surprising enough. The idea is to show high, medium and low numbers. The RNGs are deciding to "at the same time" output large numbers. There is another column visible that I missed, after the 2nd one. The probability of 13 numbers being "small" at the same time (shown above in the 2nd column) is about 1 in 1.5 million. Often you use similar random numbers to choose between few actions or simulate a dice roll. (Also, I never could have added as much references and information in a comment - they are in fact, comments.) – Andrew Apr 02 '16 at 18:09
  • I've added a link to this post from my answer. This feels like an issue with using consecutive seeds as much as anything else. As I've posted in my answer, another alternative would be to use a "master" `Random`, locked once per thread in order to generate the seed for that thread's `Random`. Trying that approach with your test code doesn't show the same pattern for me. Would you agree that's an improvement, or do you still fundamentally object to the approach? – Jon Skeet Apr 02 '16 at 18:12
  • I believe that there are multiple items at play. The consecutive seeds is a problem, yes. Microsoft (see link above) recommends against the approach (due to possible correlation in the output probably) and recommends something like your "master" random to avoid causing issues like above. For your final question, this approach is theoretically sound (if you have a good RNG (perhaps through polymorphism?), you get good results), but Random is not a very good RNG, so you might also notice some poor behaviour unfortunately. An RNG that fails the spectral test will generate poor results here. – Andrew Apr 02 '16 at 18:20
  • @JonSkeet Take a look at [this old answer of mine](http://stackoverflow.com/questions/25390301/net-framework-random-number-generator-produces-repeating-pattern/25390969#25390969), it does that exact pattern of using one random to seed a second, if you use a unconstrained first then constrain the second's output to 0-200 it gives a very, ***very***, predictable 3rd value of `rnd.Next(200)` – Scott Chamberlain Apr 02 '16 at 18:20
  • Righto, I'm suitably convinced :) Will delete my answer in the other question... and edit an article which suggested the same approach. Will refer to this answer. – Jon Skeet Apr 02 '16 at 18:21
  • @JonSkeet Why not instead modify the code to initialise just one Random instance and use that (with the appropriate semaphore/locking)? I'd also comment that the quality of the RNG used might affect the "randomness" of the actual results. – Andrew Apr 02 '16 at 18:24
  • @Andrew: That's what I'll do in the article (I think I already give that as one option), but it didn't seem worth editing the SO answer to be so radically different. Can do so if you think it's worth doing. – Jon Skeet Apr 02 '16 at 18:25
  • @JonSkeet: the code at MSDN seems OK, so you might refer to that. – Andrew Apr 02 '16 at 18:28
  • @Andrew: Yup, will do so from the article. – Jon Skeet Apr 02 '16 at 18:30
  • I did know that the .NET RNG was not good but this is really quite bad. Not sure why they picked this design. XorShift+ is superior in every regard including quality and speed. – usr Apr 03 '16 at 12:54
  • @JonSkeet: I see that as of .NET Core 3.0 the `RandomNumberGenerator` class has a `GetInt32` static method, which returns a random integer between two bounds. If we're using .NET Core 3.0 or later should we be using this method to generate random numbers instead of `Random`? – Simon Elms Sep 12 '22 at 07:35
  • 1
    @SimonTewsi: It depends on your requirements. `Random` can be useful if you want to be able to get a repeatable sequence of pseudo-random numbers. It may be faster than `RandomNumberGenerator`, too. But `RNG` is definitely more appropriate for anything security-related. – Jon Skeet Sep 12 '22 at 07:40
1

Not sure what "multiple streams of random numbers" means. In random numbers there is no relationship between any two random numbers, there is no order, each is a standalone instance.

If you use a cryptographic PRNG no seeding is necessary. Consider the .net RNGCryptoServiceProvider Class.

zaph
  • 111,848
  • 21
  • 189
  • 228
  • Cryptographic PRNGs are in general continually seeded with essentially random data such as CPU interrupt timings and other realtime occurrences that in some cases include the variations in a hard drive platter speed due to air turbulence. The next number is not entirely deterministic by any test and the output is not discernible from true randomness. These are not just fancy linear congruential generators. There is simple not a better manor of generating more randomness in user code. – zaph Apr 03 '16 at 10:10