关于默认Random by Seed,它会在不同的系统中更改吗?

时间:2012-04-09 20:47:47

标签: c# random

我知道默认c#Random()是一个伪随机数序列。 我只需要这样一个,我不是在寻找这个问题的真实随机。

请允许我提问:

我试过:new Random(100000000).Next(999999999),我得到了145156561,就像一些other people said一样。

我的问题是:同一种子的伪随机数序列会在不同的系统(win2003,win2008,mono等),不同的.net版本(.net 3,.net 4)发生变化,c#2,c#3等),还是会在其他任何环境下更改?

我想知道的是,一旦我编码,我现在和未来的所有地方总是会得到相同种子的伪随机数序列吗?

5 个答案:

答案 0 :(得分:6)

不可靠。一般来说,弄清楚这种事情的方法是查看文档(MSDN for .NET)。如果没有描述算法并且不是其他公布算法的实现,则应将其视为实现细节,并且可能会发生变化。

你需要非常严格地阅读这些文档 - 如果有解释的余地​​,你需要假设最坏的情况。在这种情况下:http://msdn.microsoft.com/en-us/library/system.random.aspx仅表明:

  

为不同的Random对象提供相同的种子值   每个实例产生相同的随机数序列。

它适用于框架:

  

受以下版本支持:4,3.5,3.0,2.0,1.1,1.0 .NET Framework客户端配置文件

     

受支持者:4,3.5 SP1可移植类库

     

支持:可移植类库

没有任何关于它的说法是否保证在未来的任何版本中都能以相同的方式工作,甚至是否能从各种支持的框架中获得相同的结果。事实上,它声明:

  

来电者须知

     

中的随机数发生器的实现   不保证Random类在主要版本中保持不变   .NET Framework的版本。因此,您的应用程序代码   不应该假设相同的种子会产生相同的结果   不同版本的.NET Framework中的伪随机序列。

答案 1 :(得分:3)

实现这一目标的最佳方法是实现自己的随机数生成器。看看http://www.agner.org/random/

答案 2 :(得分:2)

不,这是not guaranteed在不同的.NET版本中保持不变:

  

Random类的当前实现基于Donald E. Knuth的减法随机数生成器算法。

注意使用 current 这个词;这意味着它可以在更高版本的框架中被替换。 (这也在quote mentioned in Chris Shain's post明确说明。

如果您想100%确定您可以在以后的所有版本中复制序列,请包含您自己的PRNG,例如: Mersenne Twister

答案 3 :(得分:2)

根据Random class的MSDN。

  

来电者须知

     

Random类中随机数生成器的实现   不保证在主要版本的.NET中保持不变   框架。因此,您的应用程序代码不应该假设   相同的种子将导致相同的伪随机序列   不同版本的.NET Framework。

所以你不能使用Random并依赖你将得到相同的序列。

答案 4 :(得分:0)

正如Chris Shain's great answer所阐明的那样:你不能依赖于在不同平台上甚至跨主要版本.NET的结果相同

我们在@BlueLineGames遇到了这个确切的问题,因为我们使用MonoGame编写了一个游戏,该游戏在Windows上运行于.NET,但在OSX中运行Mono时生成了不同的值。

史蒂夫建议,编写自己的PRNG是一个很好的解决方案。希望它能节省一些时间,这里有一个类可以代替System.Random和一些快速使用信息。

/// <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);
    }
}

你应该能够安全地删除这个类,而不是使用Random(),但你真的只需要在使用种子构造它的地方(例如:new Random(seedValue))。

一些示例用法:

int seed = 42;
CrossPlatformRandom r = new CrossPlatformRandom(seed);
for (int i = 0; i < 10; ++i)
    Console.WriteLine(r.Next(100));