实现更改为.NET的Random()

时间:2012-03-18 12:56:04

标签: c# .net random .net-1.1

我正在迁移一个用于从.NET Framework 1.1解码到.NET Framework 4的方法。 我注意到Random的实现发生了变化。因此,给定相同的种子,Random.NextBytes会返回不同的结果。

所以,如果我运行以下代码。

byte[] bytes = new byte[4];
System.Random random = new System.Random(50);
random.NextBytes(bytes);

for(int i=0; i< bytes.Length; i++)
{
  Console.WriteLine("bytes[" + i + "] = " + bytes[i]);
}

在.NET Framework 1.1下,它返回:

bytes[0] = 216
bytes[1] = 124
bytes[2] = 183
bytes[3] =  58

在.NET framework 4下,它返回:

bytes[0] = 154
bytes[1] =  49
bytes[2] = 183
bytes[3] =  48

解决此问题的最佳方法是什么?

5 个答案:

答案 0 :(得分:27)

这不是Random的问题,它完全可以满足其记录的界面。这是您的软件依赖于实现细节的问题。从这个错误中学习,不要再做了

就解决问题而言,您可以实现自己的1.1版伪随机数生成用于解码,然后实现不依赖于不稳定行为的新编码/解码算法(例如{{的实现1}}或Random)用于您的新版软件。

答案 1 :(得分:17)

您可以使用Reflector从1.1 mscorlib中复制Random类。

public class Random1_1
{
    // Fields
    private int inext;
    private int inextp;
    private const int MBIG = 0x7fffffff;
    private const int MSEED = 0x9a4ec86;
    private const int MZ = 0x0;
    private int[] SeedArray;

    // Methods
    public Random1_1()
        : this(Environment.TickCount)
    {
    }

    public Random1_1(int Seed)
    {
        this.SeedArray = new int[0x38];
        int num2 = 0x9a4ec86 - Math.Abs(Seed);
        this.SeedArray[0x37] = num2;
        int num3 = 0x1;
        for (int i = 0x1; i < 0x37; i++)
        {
            int index = (0x15 * i) % 0x37;
            this.SeedArray[index] = num3;
            num3 = num2 - num3;
            if (num3 < 0x0)
            {
                num3 += 0x7fffffff;
            }
            num2 = this.SeedArray[index];
        }
        for (int j = 0x1; j < 0x5; j++)
        {
            for (int k = 0x1; k < 0x38; k++)
            {
                this.SeedArray[k] -= this.SeedArray[0x1 + ((k + 0x1e) % 0x37)];
                if (this.SeedArray[k] < 0x0)
                {
                    this.SeedArray[k] += 0x7fffffff;
                }
            }
        }
        this.inext = 0x0;
        this.inextp = 0x15;
        Seed = 0x1;
    }

    public virtual int Next()
    {
        return (int)(this.Sample() * 2147483647.0);
    }

    public virtual int Next(int maxValue)
    {
        if (maxValue < 0x0)
        {
            throw new ArgumentOutOfRangeException("maxValue");
        }
        return (int)(this.Sample() * maxValue);
    }

    public virtual int Next(int minValue, int maxValue)
    {
        if (minValue > maxValue)
        {
            throw new ArgumentOutOfRangeException("minValue");
        }
        int num = maxValue - minValue;
        if (num < 0x0)
        {
            long num2 = maxValue - minValue;
            return (((int)((long)(this.Sample() * num2))) + minValue);
        }
        return (((int)(this.Sample() * num)) + minValue);
    }

    public virtual void NextBytes(byte[] buffer)
    {
        if (buffer == null)
        {
            throw new ArgumentNullException("buffer");
        }
        for (int i = 0x0; i < buffer.Length; i++)
        {
            buffer[i] = (byte)(this.Sample() * 256.0);
        }
    }

    public virtual double NextDouble()
    {
        return this.Sample();
    }

    protected virtual double Sample()
    {
        int inext = this.inext;
        int inextp = this.inextp;
        if (++inext >= 0x38)
        {
            inext = 0x1;
        }
        if (++inextp >= 0x38)
        {
            inextp = 0x1;
        }
        int num = this.SeedArray[inext] - this.SeedArray[inextp];
        if (num < 0x0)
        {
            num += 0x7fffffff;
        }
        this.SeedArray[inext] = num;
        this.inext = inext;
        this.inextp = inextp;
        return (num * 4.6566128752457969E-10);
    }
}

经过测试,它可以提供所需的输出。

答案 2 :(得分:3)

如果您完全依赖于.NET 1.1版本的Random,那么我唯一能想到的就是创建一个以1.1为目标的新程序集,并从升级后的.NET 4应用程序中调用它。

但是,您能否详细说明为什么保持这种种子至关重要?可能有更好的方法。

答案 3 :(得分:3)

这里没有答案,但与此处的许多人相反,我并不认为记录荒谬行为足以证明这一点。

因为你为什么要首先提供播种机制?好吧,我会告诉你:这样你就可以随时从单个种子中重现一个随机序列,而不必持续存在数百万个随机数。请注意,在您升级到.NET的下一个版本之前,我总是说&#39;而不是&#39;由于版本不一致,当前的.NET随机数生成器不提供此功能。 Microsoft应该更好地实现它(或者根本没有实现它),而不仅仅是记录有缺陷的行为。

顺便说一下,虽然算法确实是一个实现细节,但是你怎么能调用方法的结果调用实现细节呢?我是否真的必须检查.NET框架中每个方法的文档,以确保在下一个版本中我不会冒险连接两个字符串或计算平方根的风险?

所以在我看来,我们这里所拥有的仅仅是一个执行得很糟糕的随机数生成器。当然,通过为新功能(基于新实现)提供不同的名称,可以轻松避免整个问题。

答案 4 :(得分:0)

或者,我可以建议使用System.Security.Cryptography.RandomNumberGenerator类来生成加密强大的随机字节数组吗?

RandomNumberGenerator rng = RandomNumberGenerator.Create();
byte[] bytes = new byte[128];
rng.GetBytes(bytes);

我将加入其余的评论,并提到依赖于未记录的实现是不好的。更重要的是,如果你真的依赖于可预测的“随机性” - 如果你将它用于任何应该“安全”的东西 - 这是完全错误的。