15

Possible Duplicates:
c# - getting the same random number repeatedly
Random number generator not working the way I had planned (C#)

I have a method that builds a queue of ints:

public Queue<int> generateTrainingInts(int count = 60)
    {
        Queue<int> retval = new Queue<int>();

        for (int i = 0; i < count; i++)
        {
            retval.Enqueue(JE_Rand.rInt(2001, 100));
        }

        return retval;
    }

JE_Rand.rInt() is just a function that delegates to a function of the Random class:

public static int rInt(int exclUB, int incLB = 0)
    {
        Random rand = new Random(DateTime.Now.Millisecond);
        int t = rand.Next(incLB, exclUB);
        rand = null;            
        return t;
    }

But when I call generateTrainingInts, the same number is enqueued each time. However, if I change rInt to use a static instance of the Random class, instead of a local instance (with function scope as it is defined above), then it appears to work correctly (enqueue random integers). Does anybody know why this happens?

Edit: Dear Answerers who didn't read my question thoroughly, Like some of you pointed out, I am looking for a good explanation of why this happens. I am not looking for a solution to the same-number-generated problem, because I already fixed that like I said above. Thanks for your enthusiasm though :) I really just want to understand things like this, because my first implementation made more sense conceptually to me.

Community
  • 1
  • 1
JeffE
  • 794
  • 2
  • 9
  • 15
  • Seems duplicate... Look at realted questions like -http://stackoverflow.com/questions/1437825/random-number-generation-in-c – Alexei Levenkov Jan 31 '11 at 20:35
  • It happens because the `Random` object is initialized from `Environment.GetTickCount`, which is a millisecond timer. So if you call the `Random` constructor twice within the same millisecond, you're going to get the same initial value. – Jim Mischel Jan 31 '11 at 20:36
  • When rand is instantiated with the same seed (e.g. DateTime.Now.Millisecond in your case), it will return the same sequences of values. You should instantiate it once (and store it in a static variable), or with different seeds at each time. – AFract Jan 31 '11 at 20:41
  • Regarding your edit: I think the reason this throws off so many developers is the name itself, `Random`. They think of it as "a random number." It should really be called `RandomSequence` or something of that nature. Then it would seem more obvious that there's no need to keep instantiating new ones. – Dan Tao Jan 31 '11 at 20:49
  • @Dan Tao That's an excellent point. Although I always knew random meant pseudo-random, there was never a separation of these notions in practice i.e. using a random number generator is always treated as truly random even though it isn't because the implementation is hidden from developers... – JeffE Jan 31 '11 at 21:08
  • So, if this thread was closed as a duplicate, why does it pull up first in a Google search? –  Aug 12 '13 at 20:51

3 Answers3

28

You need to keep the same Random object. Put it outside your static method as a static member

private static Random rand = new Random();

public static int rInt(int exclUB, int incLB = 0)
{
    int t = rand.Next(incLB, exclUB);
    return t;
}

Edit
The reason is the finite resolution of the clock used to initialize Random. Subsequent initializations of Random will get the same starting position in the random sequence. When reusing the same Random the next value in the random sequence is always generated.

Albin Sunnanbo
  • 46,430
  • 8
  • 69
  • 108
  • The OP already tried that and was asking *why*. – Dan Tao Jan 31 '11 at 20:37
  • Why assign rand to null after generation ?? As rand's will be instantiated in a very short time range, the both instances will get the same seed and return the same value, exactly like in the author's version. You should put it static and keep always the same instance. It is also possible to use multiple instance with different seeds, by example using a "Guid.NewGuid().GetHashCode()" as seed, but you'll get awful performances. – AFract Jan 31 '11 at 20:39
  • I see you've corrected your code sample. It was boggus but it should work now... – AFract Jan 31 '11 at 20:45
  • I use this var seed = (int) DateTime.Now.Ticks; var random = new Random(seed); – Gary Davies Aug 05 '15 at 16:53
7

Try out the following code and I think you'll see why:

void PrintNowAHundredTimes()
{
    for (int i = 0; i < 100; ++i)
    {
        Console.WriteLine(DateTime.Now);
    }
}

The Random objects are getting the same seed over and over. This is because the granularity of the system time returned by DateTime.Now is, quite simply, finite. On my machine for example the value only changes every ~15 ms. So consecutive calls within that time period return the same time.

And as I suspect you already know, two Random objects initialized with the same seed value will generate identical random sequences. (That's why it's called pseudorandom, technically.)

You should also be aware that even if it made sense to instantiate a new Random object locally within your method, setting it to null would still serve no purpose (once the method exits there will be no more references to the object anyway, so it will be garbage collected regardless).

Dan Tao
  • 125,917
  • 54
  • 300
  • 447
2
public class JE_Rand
{
   private static Random rand= new Random(DateTime.Now.Millisecond);

    public static int rInt(int exclUB, int incLB = 0)
    {
        int t = rand.Next(incLB, exclUB);
        return t;
    }
}
BFree
  • 102,548
  • 21
  • 159
  • 201
  • why rand = null? it will dump object, and next call will throw an exception – walter Aug 11 '11 at 05:05
  • @walter: This is an old answer, but I was just copying and pasting his code and moving the new Random(....) outside of the method call. You're 100% right though. I'll edit my answer. – BFree Aug 11 '11 at 14:11