How do I make my C# random number generator change with each time the constructor is called?

11,357

Solution 1

If you initialize several Random instances using the default constructor, Random(), within a short time period, then you risk that they would end up with the same seed value (which is time-dependant) and, therefore, generate the same random sequence each time.

You can fix this issue by initializing a single static Random instance, and share it among all Student instances. This is perfectly safe as long as you’re not multi-threading.

public class Student
{
    private static readonly Random random = new Random();        
}

From the MSDN page on the Random() constructor:

The default seed value is derived from the system clock and has finite resolution. As a result, different Random objects that are created in close succession by a call to the default constructor will have identical default seed values and, therefore, will produce identical sets of random numbers. This problem can be avoided by using a single Random object to generate all random numbers. You can also work around it by modifying the seed value returned by the system clock and then explicitly providing this new seed value to the Random(Int32) constructor. For more information, see the Random(Int32) constructor.

Edit: Although you state that your random number generator is already a static member, this is still not enough if you’re instantiating it repeatedly (redundantly) during each Student initialization. The following code is still incorrect:

public class Student
{
    private static Random random;

    private int[] numbers;

    public Student()
    {
        random = new Random();
        numbers = new int[10];
        for (int i = 0; i < 10; ++i)
            numbers[i] = random.Next();
    }
}

You need to replace it with a version that only instantiates Random once, like so:

public class Student
{
    private static readonly Random random = new Random();

    private int[] numbers;

    public Student()
    {
        numbers = new int[10];
        for (int i = 0; i < 10; ++i)
            numbers[i] = random.Next();
    }
}

Solution 2

A common pitfall with Random is to create a new instance each time you need a random value.

for (int i = 0; i < 1000; i++) {
   var random = new Random();
   list.Add(random.Next());
}

This does not work as expected, since Random uses the actual time to create the first random value. However, the time clock has a limited resolution. It can be that several succeeding iterations return the same time and thus the same (pseudo) random value!

The solution is to create random only once.

var random = new Random();
for (int i = 0; i < 1000; i++) {
   list.Add(random.Next());
}

You could also declare random as a static class member

public static readonly Random RandomGenerator = new Random();

Solution 3

A random number generator uses the system time as a seed. I wonder if you are possibly declaring a new generator and calling it before the system has time to move on to its next increment. The processor cycles are actually more than often faster than the system clock. Can you post a code example?

I would suggest declaring a Static generator as either a class property or somewhere else accessible, instantiate it as final. This will guarantee that you wont be overwriting the generator with another.

Share:
11,357
ivarlee
Author by

ivarlee

Updated on June 04, 2022

Comments

  • ivarlee
    ivarlee about 2 years

    I have two classes, a Teacher class and a Student class. In my program I create an array of 10 Teacher objects and within each Teacher object is an array of 10 Student Objects. Each Student object also has an array of integers as a member variabe, and when each Student is instantiated, it's own array of integers are filled with numbers from a random number generator. My program goes something like this:

    • An array of type Teacher is created with size 10
    • The array is then filled with 10 actual teacher objects
    • Each Teacher object contains an array of Student objects of size 10 as a member variable
    • The Student array in each Teacher object is filled with actual Student objects
    • Each student object has an array of integers that are filled with random numbers in the Student objects constructor.

    Here is the problem I ran into: It seems that every time the 10 Student objects are created for each Teacher, the random number generator within the Student object constructor does not reset or change even when I call the .Next() function until the next set of 10 Student objects are created for the next Teacher object. What I want is the 10 Teacher objects to each have their own Student Objects which have their own integer arrays filled with randomly generated numbers.

    Any help would be appreciated! I ran into this sort of problem with constructors with my last question, and that was a matter of whether something was to be static or not and I'm not sure that's the case this time. Please ask me questions if I wasn't clear enough about anything!!

    UPDATE** So after looking at MSDN, I found in their sample code "Thread.Sleep(2000)" and stuck it into my Student Constructor just to see what it did. It seemed to have solved the problem although my program runs a lot slower now, is there a minimum sleep value to wait until the Random.Next() uses a new seed from the clock and even if I did solve the problem, is there a better way to do this? My random number generator is already a static member variable of Student.