Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does an instance of Random in a serialized class repeatedly produce the same set of numbers?

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!

like image 672
JBeFat Avatar asked Sep 28 '11 20:09

JBeFat


People also ask

How do you prevent a variable from being serialized in a serializable class?

You can prevent member variables from being serialized by marking them with the NonSerialized attribute as follows. If possible, make an object that could contain security-sensitive data nonserializable. If the object must be serialized, apply the NonSerialized attribute to specific fields that store sensitive data.

How does Random class work in c#?

The current implementation of the Random class is based on a modified version of Donald E. Knuth's subtractive random number generator algorithm. Every time you do new Random() it is initialized using the clock. This means that in a tight loop you get the same value lots of times.

What is a seed in c#?

Seed Int32. A number used to calculate a starting value for the pseudo-random number sequence.


2 Answers

(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());
    }
}
like image 125
3 revs Avatar answered Sep 30 '22 05:09

3 revs


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.

like image 29
Optimax Avatar answered Sep 30 '22 05:09

Optimax