35

Possible Duplicate:
Random number generator only generating one random number

A beginner question. I have a very simple program that draws a line and I want to randomize the locations, but each time I create a new instance of Random it returns the same value. Where is the problem? Thank you.

private void Draw()
{
    Random random1 = new Random();
    int randomNumber1 = random1.Next(0, 300);
    Random random2 = new Random();
    int randomNumber2 = random2.Next(0, 300);
    Random random3 = new Random();
    int randomNumber3 = random3.Next(0, 300);
    Random random4 = new Random();
    int randomNumber4 = random4.Next(0, 300);
    System.Drawing.Graphics g = this.CreateGraphics();
    Pen green = new Pen(Color.Green, 5);
    g.DrawLine(green, new Point(randomNumber1, randomNumber2), 
                      new Point(randomNumber3, randomNumber4));
}

private void btndraw1_Click(object sender, EventArgs e)
{
    Draw();
}
Community
  • 1
  • 1
Nejc Ucman
  • 361
  • 1
  • 3
  • 5

7 Answers7

64

The reason this happens is that every time you do a new Random it is initialized using the clock. So in a tight loop (or many calls one after the other) you get the same value lots of times since all those random variables are initialized with the same seed.

To solve this: Create only one Random variable, preferably outside your function and use only that one instance.

Random random1 = new Random();
private void Draw()
{
    int randomNumber1 = random1.Next(0, 300);
    int randomNumber2 = random1.Next(0, 300);
    int randomNumber3 = random1.Next(0, 300);
    int randomNumber4 = random1.Next(0, 300);
    System.Drawing.Graphics g = this.CreateGraphics();
    Pen green = new Pen(Color.Green, 5);
    g.DrawLine(green, new Point(randomNumber1, randomNumber2), new Point(randomNumber3, randomNumber4));
}
Blachshma
  • 17,097
  • 4
  • 58
  • 72
10

Simply use the same instance:

Random random = new Random();
int randomNumber1 = random.Next(0, 300);
int randomNumber2 = random.Next(0, 300);
//...

Random numbers in programming are not really random; they are based on some unique seed that is taken and manipulated to generate what appears to be set of random numbers. Using the same seed will result in same set of numbers.

The default constructor of the Random class is using the number of milliseconds elapsed since the system started as the seed, so what actually happened is the same seed was used.

There is really no reason to create more than once Random instance; the single instance will generate random set of numbers on each execution of the code.

To prove my above statement of default seed, I used reflection:

// System.Random
/// <summary>Initializes a new instance of the <see cref="T:System.Random" /> class, using a time-dependent default seed value.</summary>
public Random() : this(Environment.TickCount)
{
}

And the Environment.TickCount:

// System.Environment
/// <summary>Gets the number of milliseconds elapsed since the system started.</summary>
/// <returns>A 32-bit signed integer containing the amount of time in milliseconds that has passed since the last time the computer was started.</returns>
/// <filterpriority>1</filterpriority>
public static extern int TickCount
{
    [SecuritySafeCritical]
    [MethodImpl(MethodImplOptions.InternalCall)]
    get;
}
Shadow The GPT Wizard
  • 66,030
  • 26
  • 140
  • 208
  • 1
    Just... how do you know that `Random` uses the Unix epoch? The [documentation](http://msdn.microsoft.com/en-us/library/h343ddh9.aspx) only states that it `derived from the system clock` but never mentioned the actual implementation. – Alvin Wong Feb 03 '13 at 15:16
  • @Alvin good point! That's just what I always **thought**, being the most reasonable thing in my opinion. Using ILSpy I found out I was plain wrong and the real seed is number of milliseconds since the system was started. – Shadow The GPT Wizard Feb 03 '13 at 15:26
5

You shouldn't create a new Random object for each number. Instead, use the same object:

Random r = new Random();

private void Draw()
{
    // Create 4 random numbers
    int[] numbers = Enumerable.Range(0, 4).Select(x => r.Next(0, 300)).ToArray();

    System.Drawing.Graphics g = this.CreateGraphics();
    Pen green = new Pen(Color.Green, 5);
    g.DrawLine(green, new Point(numbers[0], numbers[1]),
                      new Point(numbers[2], numbers[3]));
}
Adi Lester
  • 24,731
  • 12
  • 95
  • 110
4

A random number generator (RNG) does not actually generate random numbers. Instead, it uses an algorithm to define a sequence of numbers, that appear to be random. This sequence depends on the seed that is run through said algorithm at the time you RNG is created.

By default, RNGs are created using the system's clock as a seed, since the clock will usually vary every time the program is run, making it extremely difficult to predict the "random" sequence.

In your case, it is very likely, that the clock didn't change between creating a random object and another; possibly due to CPU-internal re-ordering of instructions.

As Blachshma states, it is best to create only a single random object and use only that.

public static Random MyRNG = new Random(); // create a single static random object, that you can use across all classes
private void Draw()
{
    randomNumber1 = MyRNG.Next(0, 300);
    randomNumber2 = MyRNG.Next(0, 300);
    // and so forth
}

Keep in mind that any instance of System.Random are not guaranteed to be thread-safe, meaning that if you plan on having multiple threads share the same random object, you must lock it.

lock (MyRNG)
{
    randomNumber = MyRNG.Next(0, 300);
}

Failure to do so might break your random object, leading to consequent calls returning only 0 as a result.

Nolonar
  • 5,962
  • 3
  • 36
  • 55
3

You only need one instance of the Random class.

private void Draw()
    {
        Random random1 = new Random();
        int randomNumber1 = random1.Next(0, 300);

        int randomNumber2 = random1.Next(0, 300);

        int randomNumber3 = random1.Next(0, 300);

        int randomNumber4 = random1.Next(0, 300);

        System.Drawing.Graphics g = this.CreateGraphics();
        Pen green = new Pen(Color.Green, 5);
        g.DrawLine(green, new Point(randomNumber1, randomNumber2), new Point(randomNumber3, randomNumber4));
    }


    private void btndraw1_Click(object sender, EventArgs e)
    {
        Draw();
    }
alu
  • 759
  • 7
  • 20
3
    private static readonly Random Random1 = new Random();

    private void Draw()
    {

        int randomNumber1 = Random1.Next(0, 300);
        int randomNumber2 = Random1.Next(0, 300);
        int randomNumber3 = Random1.Next(0, 300);
        int randomNumber4 = Random1.Next(0, 300);
        System.Drawing.Graphics g = this.CreateGraphics();
        Pen green = new Pen(Color.Green, 5);
        g.DrawLine(green, new Point(randomNumber1, randomNumber2), new Point(randomNumber3, randomNumber4));
    }


    private void btndraw1_Click(object sender, EventArgs e)
    {
        Draw();
    }
Parimal Raj
  • 20,189
  • 9
  • 73
  • 110
-4

What random class of .Net needs is a seed value you can use a date value as a seed and it would work.

private void Draw()
    {
        Random random1 = new Random(unchecked((int)DateTime.Now.Ticks << (int)100));
        int randomNumber1 = random1.Next(0, 300);
        Random random2 = new Random(unchecked((int)DateTime.Now.Ticks << (int)200));
        int randomNumber2 = random2.Next(0, 300);
        Random random3 = new Random(unchecked((int)DateTime.Now.Ticks << (int)300));
        int randomNumber3 = random3.Next(0, 300);
        Random random4 = new Random(unchecked((int)DateTime.Now.Ticks << (int)400));
        int randomNumber4 = random4.Next(0, 300);
        System.Drawing.Graphics g = this.CreateGraphics();
        Pen green = new Pen(Color.Green, 5);
        g.DrawLine(green, new Point(randomNumber1, randomNumber2), new Point(randomNumber3, randomNumber4));
    }


private void btndraw1_Click(object sender, EventArgs e)
{
    Draw();
}
Nikshep
  • 2,117
  • 4
  • 21
  • 30
  • 1
    Why do you need a seed value like the one you used? Random() uses something like that by default. – Vladislav Rastrusny Feb 03 '13 at 15:22
  • That is true it does but they always tend to generate the same sent of number it is safer to add a unique seed number which would guarantee random number being generated for different random class objects. – Nikshep Feb 03 '13 at 15:24
  • @Nikshep It's still a workaround. What if you have different functions generating `Random`s? Will you keep a tally? If so, why not have a static random anyway? And you realize that shifting the `Ticks` left by 100 - 400 places is ridiculous, right? – antonijn Feb 03 '13 at 15:35
  • @Antonijn that is true but I just wanted to iterate the behavior of .Net's Random class. As I found if the question asked was for a particular behavior or wanted the code problem to be solved which has been in the first question. – Nikshep Feb 03 '13 at 15:40
  • 1
    Firstly: why are you doing `(int)100` if `100` is already an `int`? Secondly: `DateTime.Ticks` is a `long`, so it's 64 bit large. Doing `Ticks << 64` in that case returns the same as `Ticks << 0` or `Ticks`; so `Ticks << 100` is the same as `Ticks << 36`, `Ticks << 200 == Ticks << 4`, `Ticks << 300 == Ticks << 40` and `Ticks << 400 == Ticks << 12`. Thirdly: `randomNumber1 = random.Next(0, 300); randomNumber2 = random.Next(0, 300)` is definitely easier and better than creating tons of `Random` objects. It's no wonder you're getting all these downvotes. – Nolonar Jun 06 '13 at 15:48
  • Most people make the mistake of creating Random rnd = new Random inside the method, Create it out side and the result will be much different, Example I create 2000 request almost instantaniosly in Excel remove duplicates and no duplicates found. Just - for if someone stumbles on this as it is common error – user3731074 Dec 13 '16 at 13:49