7

So I'm a c# noob. I have a quick question that I can't find an answer to anywhere else.

 [Serializable()]
 public class Dictionary
 {
    private Random m_RandomGenerator = new Random();

    public int GetNext() 
    {
        return m_RandomGenerator.Next(100);
    }
 }

The Dictionary instance is loaded each time the program starts, and this code will return the exact same sequence of numbers each time it is run. By which I mean, each time the executable is run. Surely the time value it's seeded with should be different (DateTime.Now.Ticks I assume?).

A couple of points:

  • There is only one instance of Dictionary, deserialized from a previously-exported file at startup.
  • If I make m_RandomGenerator static then the problem is fixed.

Does anyone know why? I did double check that I'm not creating a new instance of Dictionary each time, so that's not the issue.


Well, colour me embarrassed. As it turns out the culprit was the [Serializable()] attribute. The dictionary class I was using was loaded from a previously exported file, which was obviously loading the seed back into Random(). Changing the variable to static meant that the seed value was no longer loaded from the previously serialised instance - hiding the issue.

Thanks to all the people offering constructive advice!

Shog9
  • 156,901
  • 35
  • 231
  • 235
JBeFat
  • 907
  • 10
  • 20
  • Same question asked 4 hours ago: http://stackoverflow.com/questions/7586613/random-number-in-c – dwonisch Sep 28 '11 at 20:48
  • 1
    Are you sure? I get different answers each time I run the executable? – David Heffernan Sep 28 '11 at 20:50
  • 3
    Actually, are you *sure* the same sequence is returned each time with the *above code*? That would be ... interesting. (How is Dictionary being used?) –  Sep 28 '11 at 20:50
  • 4
    This doesn't seem like a duplicate, Random is being instantiated only once – RichK Sep 28 '11 at 20:52
  • 1
    Are you sure that you're doing `new Random()` and not `new Random(123)` (or whatever number) ? – digEmAll Sep 28 '11 at 20:54
  • 1
    This really should be re-opened if the OP has definitely stated the question correctly – RichK Sep 28 '11 at 20:55
  • http://stackoverflow.com/questions/3792029/trouble-with-c-random-class – Austin Salonen Sep 28 '11 at 20:55
  • @JBeFat Don't worry, we'll soon have this re-opened and then we can concentrate on working out what is going on. – David Heffernan Sep 28 '11 at 20:57
  • 1
    @Austin, I don't see how that question relates. OP says the sequence is the same each time it's run. Unless he's a time lord I don't see how the seed can be the same each time. – RichK Sep 28 '11 at 20:58
  • 1
    @JBeFat Can you provide a larger code snippet? Going off the code you posted there should be no reason why the sequence is the same each time. I'd be interested in seeing more if possible. – CatDadCode Sep 28 '11 at 21:07
  • 1
    Hmm, do you get the same sequences if you manually seed it based on the time? Something like `new Random((int)DateTime.Now.Ticks)` or even `new Random((int)System.Diagnostics.Stopwatch.GetTimestamp())`? – Jeff Mercado Sep 28 '11 at 21:07
  • 2
    @JBeFat: wait, if you can post more of your code, we could help (this is definitely the place with the most skilled programmers ever, after all) – digEmAll Sep 28 '11 at 21:08
  • @digEmAll Very true. Definitely a tough crowd, but an intelligent crowd. You just have to do your best to ignore the arrogance. – CatDadCode Sep 28 '11 at 21:11
  • 1
    Not a dupe, just fantasy code that doesn't reproduce the problem. – H H Sep 28 '11 at 21:14
  • Is the [RNGCryptoServiceProvider](http://msdn.microsoft.com/en-us/library/system.security.cryptography.rngcryptoserviceprovider.aspx) worth a mention here too? – Smudge202 Sep 28 '11 at 21:15
  • 3
    We are given a code that is working fine and produces random sequences, because random is seeded with Environment.TickCount in default constructor. Its also stated that this code produces same output , which is not true. Its as if I posted that true==false returns true and got 9 upvotes for that. I dont understand it. – Valentin Kuzub Sep 28 '11 at 21:24
  • There is something odd going on here. Along with others, I can't recreate this issue. Can you show the full code? Or better yet, isolate it down to just a few calls like Alex Ford and Austin have done? – NotMe Sep 28 '11 at 21:26
  • Hey Guys - Answer is above. I can understand everyone being annoyed at me providing what doesn't seem to be a complete code sample. It totally was except for the serializable attribute, which I really didn't consider (as I said, I am a c coder, new to c#). Totally my mistake - sorry. Thanks for all the suggestions though! – JBeFat Sep 28 '11 at 21:32
  • 2
    "the culprit was the [Serializable()] attribute" - that's not in your repro, and that's too bad since the phenomenon of seeding an rng from a deserialization is interesting. [Lern2ask.](http://www.chiark.greenend.org.uk/~sgtatham/bugs.html) – bzlm Sep 28 '11 at 21:33
  • Removed a bunch of irrelevant comments. Leaving the rest in case future readers are curious as to how this question began with such a mystery. – Shog9 Sep 28 '11 at 22:47

5 Answers5

6

(CW because this is way too big for a comment)

This test will only ever repeat when Random is seeded. You should post the code calling Dictionary because there may be something fishy there (assuming the code posted is the actual code) or even better post your own test that reproduces the issue.

[Test]
public void TestDictionary()
{
    var dictionary = new Dictionary();

    for(int i = 0; i < 10; i++)
    {
        Console.WriteLine(dictionary.GetNext());
    }
}

[Serializable]  // added after the fact
public class Dictionary
{
    //private Random m_RandomGenerator = new Random(12);
    private Random m_RandomGenerator = new Random();

    public int GetNext()
    {
        return m_RandomGenerator.Next(100);
    }
}

This test does repeat your results but that's because of the answer here:

[Test]
public void TestDictionary2()
{
    var alpha = new Dictionary();
    var bravo = new Dictionary();

    for(int i = 0; i < 10; i++)
    {
        Console.WriteLine("{0} - {1}", alpha.GetNext(), bravo.GetNext());
    }
}

For completeness, here's a serialization test:

[Test]
public void SerializationPerhaps()
{
    var charlie = new Dictionary();
    Dictionary delta = null;

    // Borrowed from MSDN:  http://msdn.microsoft.com/en-us/library/system.serializableattribute.aspx

    //Opens a file and serializes the object into it in binary format.
    using (var stream = File.Open("data.xml", FileMode.Create))
    {
        var formatter = new BinaryFormatter();
        formatter.Serialize(stream, charlie);
    }


    //Opens file "data.xml" and deserializes the object from it.
    using (var stream = File.Open("data.xml", FileMode.Open))
    {
        var formatter = new BinaryFormatter();

        delta = (Dictionary) formatter.Deserialize(stream);
        stream.Close();
    }

    for(int i = 0; i < 10; i++)
    {
        Assert.AreEqual(charlie.GetNext(), delta.GetNext());
    }
}
Community
  • 1
  • 1
Austin Salonen
  • 49,173
  • 15
  • 109
  • 139
  • 1
    Well he did say there's only one instance of the dictionary ever so the second test should not be an issue. – Jeff Mercado Sep 28 '11 at 21:15
  • 1
    @JeffMercado: the OP also said that it occurs for each test run... In other words he closes the app and restarts it, then gets the same numbers in a row. – NotMe Sep 28 '11 at 21:22
  • Well I was in the process of testing a serialized class when you edited the question. Protobuf-net wouldn't serialize it otherwise I might've been ahead of you. – Austin Salonen Sep 28 '11 at 21:30
1

The source of your problem must be somwhere else than in the code you posted. Here is the same code, embedded in a test harness:

using System;

namespace RandomTest
{

    public class Dictionary
    {
        private Random m_RandomGenerator = new Random();

        public int GetNext()
        {
            return m_RandomGenerator.Next(100);
        }
    } 


    class Program
    {
        static void Main(string[] args)
        {
            Dictionary d = new Dictionary();

            for (int i=0;i<10;i++)
            {
                int r = d.GetNext();
                Console.Write("{0} ",r);
            }
            Console.WriteLine();

        }
    }
}

It returns a different sequence every time it is run.

Optimax
  • 1,534
  • 2
  • 16
  • 23
1

Well without further code samples the answer to your question is simple.

It is not repetitive.

I plugged your code sample into a console application and the result is completely random.

namespace Sandbox
{
    class Program
    {
        static void Main(string[] args)
        {
            Dictionary dict = new Dictionary();
            for (int count = 0; count < 100; count++)
                Console.WriteLine(dict.GetNext());
            Console.ReadLine();
        }
    }

    public class Dictionary
    {
        private Random m_RandomGenerator = new Random();

        public int GetNext()
        {
            return m_RandomGenerator.Next(100);
        }
    }
}

Result:

http://www.codetunnel.com/content/images/random.jpg

CatDadCode
  • 58,507
  • 61
  • 212
  • 318
  • You got my last upvote of the day. I can't recreate the OP's problem either. – NotMe Sep 28 '11 at 21:24
  • 1
    Hmm Alex Ford, I don't think this example is somehow related to question. you got a random sequence, and it was never questioned that sequence isn't random, he was saying that he gets same SEQUENCES, not same NUMBERS everytime Random.Next(100) is called. – Valentin Kuzub Sep 28 '11 at 21:34
  • 1
    I look at your picture and you ran it once. It shows that Random.Next(100) produces not the same values. – Valentin Kuzub Sep 28 '11 at 21:45
  • @ValentinKuzub The only way to get two sequences is to either do a second foreach loop calling the same get method (which is stupid because it would be the same as running one foreach loop to 200 instead of 100) or to reinstantiate the dictionary class and run the same loop again. Though I didn't show two pictures, the result is completely random no matter what you do. His question was incomplete and you are simply looking for things to pick at. – CatDadCode Sep 28 '11 at 21:49
  • 1
    Well to prove the point that its not reproducable it would be enough to request 1 number and launch program 2 times. By launching program 1 time and getting 100 numbers you are checking different functionality, which is unrelated to question. I am not looking for things to pick at, I am advocate of relevant answers and correctness. Austin Salonen said that seeding Random with Milliseconds is less granular than default constructor , I question that. Cubicle.Jockey answer is completely unrelated , I question that too. – Valentin Kuzub Sep 28 '11 at 21:53
  • This is not correct. OP states that the Dictionary object was only instantiated once. If this is true then this would be the result. If he instantiates it more than once, then it would be a repeated sequence. So instantiating two dictionaries and running them in the same app would not be reproducing his question. – CatDadCode Sep 28 '11 at 22:59
0

I don't know this is what you exactly want but this help me to solve my problem. Your post and other answer, comments bring me to this.

using System;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;

namespace SerializeRandom
{
    [Serializable]  // added after the fact
    public class RandomGenerator
    {
        const string fileName = "random.bin";
        private Random random = new Random();

        public int GetNext()
        {
            return random.Next(100);
        }

        public static void Save(RandomGenerator obj)
        {
            using (var stream = File.Open(fileName, FileMode.Create))
            {
                var formatter = new BinaryFormatter();
                formatter.Serialize(stream, obj);
            }
        }

        public static RandomGenerator Load()
        {
            RandomGenerator randomGenerator = null;

            //create new object if file not exist
            if (!File.Exists(fileName))
            {
                randomGenerator = new RandomGenerator();
            }
            else
            {
                //load from bin file
                using (var stream = File.Open(fileName, FileMode.Open))
                {
                    var formatter = new BinaryFormatter();

                    randomGenerator = (RandomGenerator)formatter.Deserialize(stream);
                    stream.Close();
                }
            }

            return randomGenerator;
        }

    }

    }

and test class

using System.Collections.Generic;
using NUnit.Framework;

namespace SerializeRandom
{
    public class RandomGeneratorTest
    {
        [Test]
        public void TestDictionary1()
        {
            var randomGenerator = RandomGenerator.Load();

            var randomResult1 = new List<int>();
            for (int i = 0; i < 10; i++)
            {
                randomResult1.Add(randomGenerator.GetNext());
            }
            RandomGenerator.Save(randomGenerator);

            randomGenerator = RandomGenerator.Load();
            var randomResult2 = new List<int>();
            for (int i = 0; i < 10; i++)
            {
                randomResult2.Add(randomGenerator.GetNext());
            }

            CollectionAssert.AreNotEqual(randomResult1, randomResult2);

        }

    }
}
embarus
  • 815
  • 3
  • 14
  • 28
-3

Thats because it (Random()) is a pseudorandom number generator.

A seed is required to initialize the random number generator to make it not repetitive.

An acceptable choice is to use the computer time as the seed.

However, 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.

http://msdn.microsoft.com/en-us/library/system.random.aspx

I'd be careful with the default constructor with no parameters.

Jon Raynor
  • 3,804
  • 6
  • 29
  • 43
  • 2
    What? No... using the default constructor (which he is apparently doing), it will be seeded based on the time of the day. – Jeff Mercado Sep 28 '11 at 21:12
  • @Jeff - The default constructor can create the same sets of numbers. The seed is important, that was my only point. Same seed, same numbers. – Jon Raynor Sep 28 '11 at 21:21