4

I had a lot of issues with randomizing lists. I am talking about a list of 200 elements, where I want to shuffle the list. Don't get me wrong, I read a lot of examples, and on first glance there are pretty nice things, like this:

Randomize a List<T>

But in my experience, at least on a fast machine, this is basically worthless. The shuffling works so fast that there is NOT a MS delay between two calls to Random.NEXT() which results in not nearly random behaviour.

I am NOT talking about super secure stuff, just a basic game random. I know I can add a delay of 1 MS, but this means "wasting" 200 MS just to randomize a list.

Now I found this approach: http://www.codinghorror.com/blog/2007/12/shuffling.html

It looks nice, using GUIDs to sort. BUT aren't they created the same way? Lets step it up a notch, lets assume I want to create 1000 numbers, between 0 - 5. This code is basically useless:

        var resultA = new List<int>();
        for (int i = 0; i < 1000; i++)
        {
            resultA.Add(new Random().Next(5));
        }


        var resultB = new List<int>();
        for (int i = 0; i < 1000; i++)
        {
            resultB.Add(new Random().Next(5));
            Thread.Sleep(1);
        }

A does not work at all, at least not im my environment in Windows Phone 7. B is fine, but it takes a second, which is also stupid. Any comments or thoughts, it can't be that hard to create a random list of integers :-)

Community
  • 1
  • 1
Christian Ruppert
  • 3,749
  • 5
  • 47
  • 72
  • Oh, I REALLY hate myself right now :-) Ok, that explains it.. Well there were some issues where it made sense to use a new random (each question was created, and it should place the item in 1-4, so the question took care of the whole thing) BUT they were created to fast.. Ok, thanks anyway, got my stupid mistake!! – Christian Ruppert Dec 03 '10 at 17:17
  • Use the shuffle extension on the question you referenced -- or see if you can find Jon Skeet's implementation, it's on a different question. Successive calls to Next() on the same RNG should typically return different values (though it is "random" so you might get duplicates periodically). – tvanfosson Dec 03 '10 at 17:19
  • Yeah, and with the questions, I just move the random generator to the parent object (the game), so there is no problem anymore. But still, it funny how long you can search for this stupid mistake, and I was sure that "random numbers" can't be THAT hard. Should have asked earlier :-) – Christian Ruppert Dec 03 '10 at 17:22
  • Or just RTFM of the random function... – Christian Ruppert Dec 03 '10 at 17:24
  • 1
    Possible duplicate of [Random number generator only generating one random number](https://stackoverflow.com/questions/767999/random-number-generator-only-generating-one-random-number) – Peter O. Aug 05 '17 at 23:34

3 Answers3

22

Don't keep initializing a new instance of Random; make just one and continually reference it.

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

You are correct that repeatedly creating new instances of Random within the same "timestamp" will result in the same seed; but calling .Next on an instance of Random "advances" the seed so that the next number you retrieve is (most likely) different.

This is also covered in the documentation on Random:

... because the clock has finite resolution, using the parameterless constructor to create different Random objects in close succession creates random number generators that produce identical sequences of random numbers.

...

This problem can be avoided by creating a single Random object rather than multiple ones.

Mark Rushakoff
  • 249,864
  • 45
  • 407
  • 398
  • The question's pretty much covered here by Mark and Bronumski, so I'll just add this as additional info. Think of pseudo random number generators as a mathematical sequence. When you instantiate it, it is seeded, subsequent calls will produce psuedo random numbers from the sequence. They are refered to as "psuedo random" because they only have the appearence of being random when in fact are just numbers returned from said sequence. Pseudo random number generators are sometimes evaluated against each other for their capability to make an appearance of being random. – Mick N Dec 04 '10 at 03:05
6

You need to keep hold of the same instance of Random.

    var random = new Random();

    var resultA = new List<int>();
    for (int i = 0; i < 1000; i++)
    {
        resultA.Add(random.Next(5));
    }


    var resultB = new List<int>();
    for (int i = 0; i < 1000; i++)
    {
        resultB.Add(random.Next(5));
        Thread.Sleep(1);
    }

This is because when Random initializes it uses the system clock to get a point in time. When you call next it can use the difference in time to get the next number. If you keep initializing a Random object you will keep getting the same number most of the time.

Bronumski
  • 14,009
  • 6
  • 49
  • 77
4

The shuffling works so fast that there is NOT a MS delay between two calls to Random.NEXT() which results in not nearly random behaviour.

What makes you think that there needs to be a ms delay between two calls to Random.Next?

Your bog standard random number generator is going to take some initial seed (say the system clock) and then repeatedly some algorithm to that seed to produce a sequence of numbers that appears to be random. Most of these algorithms don't take the clock as an input other than for the seed and so it doesn't matter how quickly two consecutive calls are executed.

The reason your code fails is because you keep instantiating a new random number generator on each iteration. This is where the clock can kill you because you end up with the same seed twice. You are not calling Random.Next consecutively on the same random number generator. You are calling Random.Next on a new random number generator on every iteration and sometimes these random number generators are seeded with the same value because you are seeding them by the system clock.

Move the instantiation of the random number generator outside of your loop.

var resultA = new List<int>();
Random rg = new Random();
for (int i = 0; i < 1000; i++) {
    resultA.Add(rg.Next(5));
}
jason
  • 236,483
  • 35
  • 423
  • 525