1

I know default c# Random() is a pseudo-random number sequence. I just need one like this, I am not looking for real-random at this question.

Allow me ask questions:

I tried: new Random(100000000).Next(999999999), I got 145156561, just like some other people said.

My question is: will the pseudo-random number sequence by same seed changes at different system (win2003, win2008, mono etc), at different .net version (.net 3, .net 4, c# 2, c# 3 etc), or will be changed at any other environment?

All I want to know, once I coded, will I always get a same pseudo-random number sequence by same seed everywhere, now and future?

Community
  • 1
  • 1
Eric Yin
  • 8,737
  • 19
  • 77
  • 118
  • 1
    I don't know the answer, but regardless that isn't behaviour I'd ever want my code to depend on... – Dan J Apr 09 '12 at 20:50

5 Answers5

6

Not reliably. In general, the way to figure out this kind of thing is to look at the documentation (MSDN for .NET). If the algorithm is not described and isn't an implementation of an otherwise-published algorithm, it should be considered an implementation detail and subject to change.

You need to read these docs pretty strictly too- if there is room for interpretation, you need to assume worst case scenario. In this case: http://msdn.microsoft.com/en-us/library/system.random.aspx only states:

Providing an identical seed value to different Random objects causes each instance to produce identical sequences of random numbers.

And that it applies to Frameworks:

Supported in: 4, 3.5, 3.0, 2.0, 1.1, 1.0 .NET Framework Client Profile

Supported in: 4, 3.5 SP1 Portable Class Library

Supported in: Portable Class Library

Nothing about that says whether it is guaranteed to work the same way across any future versions, or even whether you'll get the same outcome from the various supported frameworks. In fact, it states:

Notes to Callers

The implementation of the random number generator in the Random class is not guaranteed to remain the same across major versions of the .NET Framework. As a result, your application code should not assume that the same seed will result in the same pseudo-random sequence in different versions of the .NET Framework.

Community
  • 1
  • 1
Chris Shain
  • 50,833
  • 6
  • 93
  • 125
  • 2
    How can you possibly conclude that? My entire answer was saying exactly the opposite! – Chris Shain Apr 09 '12 at 20:58
  • @Chirs, because I also found the algorithm based on Donald E. Knuth's from beginning. – Eric Yin Apr 09 '12 at 20:59
  • 1
    Yes. Microsoft might decide to replace their actual implementation with a better one in future. For instance they are using a better `Dictionary` implementation today as in earlier versions of the .NET Framework. This affects the order of the items returned when enumerating the dictionary. – Olivier Jacot-Descombes Apr 09 '12 at 21:00
  • 1
    @EricYin I strongly suggest you re-read my answer, every other answer to your question, and the documentation. They all say the same thing- you can not rely on this behavior. – Chris Shain Apr 09 '12 at 21:01
  • 2
    @EricYin they **currently** use Donald E. Knuth's but they are saying they could change it at any time between major versions of .NET (so .NET5 could use something totally different) – Scott Chamberlain Apr 09 '12 at 21:04
3

The best way to accomplish that would be to implement your own random number generator. Take a look at http://www.agner.org/random/

Steve
  • 6,334
  • 4
  • 39
  • 67
3

As Chris Shain's great answer illuminated: you can not depend on the results being the same on different platforms or even across Major versions of .NET

We ran into this exact problem at @BlueLineGames because we wrote a game using MonoGame which ran in .NET on Windows, but then produced different values when it ran on Mono in OSX.

As Steve suggested, writing your own PRNG is a great solution. In the hopes that it saves somebody else some time, here is a class that you can drop in place of System.Random and some quick usage info.

/// <summary>
/// This class is a drop-in replacement to use instead of Random(). Its
/// main purpose is to work identically across multiple platforms for a given
/// seed value - whereas the Random class is not guaranteed to give the
/// same results on different platforms even when the seed is the same.
/// 
/// This is an implementation of a Linear Congruential Generator which is
/// a very simple pseudorandom number generator. It is used instead of Random()
/// since Random() is implemented differently in Mono than .NET (and even
/// between different major-versions of .NET).
/// 
/// This is NOT recommended to be used for anything that should be secure such
/// as generating passwords or secret tokens. A "cryptogrpahically secure pseudorandom
/// number generator" (such as Mersenne Twister) should be used for that, instead.
/// 
/// More about Linear Congruential Generators can be found here:
/// http://en.wikipedia.org/wiki/Linear_congruential_generator
/// </summary>
public class CrossPlatformRandom : Random
{
    // To start with, we'll be using the same values as Borland Delphi, Visual Pascal, etc.
    // http://en.wikipedia.org/wiki/Linear_congruential_generator#Parameters_in_common_use
    private const int LCG_MULTIPLIER = 134775813; // 0x08088405
    private const int LCG_INCREMENT = 1;

    private int _seed;

    /// <summary>
    /// Initializes a new instance of the CrossPlatformRandom class, using a time-dependent
    /// default seed value.
    /// 
    /// Please note that this values generated from this are NOT guaranteed to be the same
    /// cross-platform because there is no seed value. In cases where the caller requires
    /// predictable or repeatable results, they MUST specify the seed.
    /// </summary>
    public CrossPlatformRandom()
    {
        // System.Random is time-dependent, so we will just use its first number to generate
        // the seed.
        Random rand = new Random();
        this._seed = rand.Next();
    }

    /// <summary>
    /// Initializes a new instance of the System.Random class, using the specified seed value.
    /// </summary>
    /// <param name="seed">A number used to calculate a starting value for the pseudo-random number sequence. If a negative number is specified, the absolute value of the number is used.</param>
    public CrossPlatformRandom(int seed)
    {
        _seed = seed;
    }

    private int GetNext() // note: returns negative numbers too
    {
        _seed = (_seed * LCG_MULTIPLIER) + LCG_INCREMENT;
        return _seed;
    }

    /// <summary>
    //  Returns a nonnegative random number.
    /// </summary>
    /// <returns>A 32-bit signed integer greater than or equal to zero and less than System.Int32.MaxValue.</returns>
    public override int Next()
    {
        return this.Next(int.MaxValue);
    }

    /// <summary>
    /// Returns a nonnegative random number less than the specified maximum.
    /// </summary>
    /// <param name="maxValue">The exclusive upper bound of the random number to be generated. maxValue must be greater than or equal to zero.</param>
    /// <returns> A 32-bit signed integer greater than or equal to zero, and less than maxValue; that is, the range of return values ordinarily includes zero but not maxValue. However, if maxValue equals zero, maxValue is returned.</returns>
    /// <exception cref="System.ArgumentOutOfRangeException">maxValue is less than zero.</exception>
    public override int Next(int maxValue)
    {
        if (maxValue < 0)
        {
            throw new System.ArgumentOutOfRangeException("maxValue is less than zero.");
        }

        ulong result = (ulong)(uint)GetNext() * (ulong)(uint)maxValue;
        return (int)(result >> 32);
    }

    /// <summary>
    /// Returns a random number within a specified range.
    /// </summary>
    /// <param name="minValue">The inclusive lower bound of the random number returned.</param>
    /// <param name="maxValue">The exclusive upper bound of the random number returned. maxValue must be greater than or equal to minValue.</param>
    /// <returns>A 32-bit signed integer greater than or equal to minValue and less than maxValue; that is, the range of return values includes minValue but not maxValue. If minValue equals maxValue, minValue is returned.</returns>
    /// <exception cref="System.ArgumentOutOfRangeException">minValue is greater than maxValue.</exception>
    public override int Next(int minValue, int maxValue)
    {
        if (minValue > maxValue)
        {
            throw new System.ArgumentOutOfRangeException("minValue is greater than maxValue.");
        }

        return minValue + this.Next(maxValue - minValue);
    }

    /// <summary>
    /// Fills the elements of a specified array of bytes with random numbers.
    /// </summary>
    /// <param name="buffer">An array of bytes to contain random numbers.</param>
    /// <exception cref="System.ArgumentNullException">buffer is null.</exception>
    public override void NextBytes(byte[] buffer)
    {
        if (buffer == null)
        {
            throw new System.ArgumentNullException("buffer is null.");
        }

        for (int index = 0; index < buffer.Length; index++)
        {
            buffer[index] = (byte)this.Next((int)byte.MaxValue);
        }
    }

    /// <summary>
    /// Returns a random number between 0.0 and 1.0.
    /// </summary>
    /// <returns>A double-precision floating point number greater than or equal to 0.0, and less than 1.0.</returns>
    public override double NextDouble()
    {
        return this.Sample();
    }

    /// <summary>
    /// Returns a random number between 0.0 and 1.0.
    /// 
    /// Since System.Random no longer uses this as the basis for all of the other random methods,
    /// this method isn't used widely by this class. It's here for completeness, primarily in case Random
    /// adds new entry points and we are lucky enough that they base them on .Sample() or one of the other existing methods.
    /// </summary>
    /// <returns>A double-precision floating point number greater than or equal to 0.0, and less than 1.0.</returns>
    protected override double Sample()
    {
        return ((double)this.Next() / (double)int.MaxValue);
    }
}

You should be able to safely drop this class in, in place of any use of Random() but you really only need to do it in places that you construct it using a seed (eg: new Random(seedValue)).

Some example usage:

int seed = 42;
CrossPlatformRandom r = new CrossPlatformRandom(seed);
for (int i = 0; i < 10; ++i)
    Console.WriteLine(r.Next(100));
Community
  • 1
  • 1
Sean Colombo
  • 1,459
  • 17
  • 24
2

No, this is not guaranteed to hold true across different .NET versions:

The current implementation of the Random class is based on Donald E. Knuth's subtractive random number generator algorithm.

Notice the use of the word current; that implies it could be replaced in a later version of the framework. (This is also said explicitly in the quote mentioned in Chris Shain's post.

If you want to be 100% certain you can replicate the sequence in all future versions, include your own PRNG, e.g. the Mersenne Twister.

Community
  • 1
  • 1
Michael Madsen
  • 54,231
  • 8
  • 72
  • 83
2

Per the MSDN for the Random class.

Notes to Callers

The implementation of the random number generator in the Random class is not guaranteed to remain the same across major versions of the .NET Framework. As a result, your application code should not assume that the same seed will result in the same pseudo-random sequence in different versions of the .NET Framework.

So you can not use Random and rely you will get the same sequence.

Scott Chamberlain
  • 124,994
  • 33
  • 282
  • 431