简体   繁体   English

实现更改为.NET的Random()

[英]Implementation change to .NET's Random()

I am migrating a method that is used for decoding from .NET Framework 1.1 to .NET Framework 4. I noticed that implementation of Random changed. 我正在迁移一个用于从.NET Framework 1.1解码到.NET Framework 4的方法。我注意到Random的实现已更改。 So given the same seed, Random.NextBytes returns different result. 因此,给定相同的种子, Random.NextBytes返回不同的结果。

So if I run the following code. 所以,如果我运行以下代码。

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

Under .NET Framework 1.1 it returns: 在.NET Framework 1.1下,它返回:

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

Under .NET framework 4 it returns: 在.NET framework 4下,它返回:

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

What is the best way to resolve this problem? 解决此问题的最佳方法是什么?

This is not a problem with Random , it satisfies its documented interface perfectly fine. 这不是Random的问题,它完全可以满足其记录的界面。 This is a problem with your software relying on an implementation detail. 这是您的软件依赖于实现细节的问题。 Learn from this mistake and don't do it again . 从这个错误中吸取教训, 不要再这样做了

As far as fixing the problem, you can implement your own version of 1.1's pseudorandom number generation for decoding and then implement a new encoding/decoding algorithm that doesn't rely on unstable behavior (such as the implementation of Random or GetHashCode ) for your new version of the software. 至于修复问题,您可以实现自己的1.1版伪随机数生成用于解码,然后实现不依赖于不稳定行为的新编码/解码算法(例如RandomGetHashCode的实现)您的新版软件。

You can just use Reflector to copy the Random class from the 1.1 mscorlib. 您可以使用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);
    }
}

Tested and it gives the desired output. 经过测试,它可以提供所需的输出。

No answer here but contrary to many people here I don't think that documenting ridiculous behaviour is enough to justify it. 这里没有答案,但与这里的许多人相反,我认为记录荒谬的行为并不足以证明这一点。

Because why would you provide a seeding mechanism in the first place? 因为你为什么要首先提供播种机制? Well I'll tell you: so that you can always reproduce a random sequence from a single seed rather than having to persist perhaps millions of random numbers. 好吧,我会告诉你:这样你就可以随时从一个种子中重现一个随机序列,而不是必须坚持数百万个随机数。 Note that I said 'always', and not 'until you upgrade to the next version of .NET'. 请注意,我说'总是',而不是'直到你升级到.NET的下一个版本'。 By not being consistent across versions the current .NET random number generators do not provide this functionality. 由于版本不一致,当前的.NET随机数生成器不提供此功能。 Microsoft should have done a better job of implementing this (or not have implemented it at all) instead of just documenting the defective behaviour. Microsoft应该更好地实现它(或者根本没有实现它),而不仅仅是记录有缺陷的行为。

And by the way, although the algorithm is indeed an implementation detail, how on earth can you call the result of a method call an implementation detail? 顺便说一下,虽然算法确实是一个实现细节,但是你怎么能调用方法的结果调用实现细节呢? Should I really have to check the documentation of every method in the .NET framework to be sure that in the next version I do not risk getting a different result from concatenating two strings or calculating a square root? 我是否真的必须检查.NET框架中每个方法的文档,以确保在下一个版本中我不会冒险连接两个字符串或计算平方根的风险?

So in my opinion what we have here is simply a badly implemented random number generator. 所以在我看来,我们这里所拥有的仅仅是一个实施得很糟糕的随机数生成器。 And of course the entire problem could have been easily avoided by giving the new functionality (based on the new implementation) a different name. 当然,通过为新功能(基于新实现)提供不同的名称,可以轻松避免整个问题。

If you're absolutely reliant on the .NET 1.1 version of Random then the only thing I can think of is to create a new assembly that targets 1.1 and call that from your upgraded .NET 4 application. 如果您完全依赖于.NET 1.1版本的Random,那么我唯一能想到的就是创建一个以1.1为目标的新程序集,并从升级后的.NET 4应用程序中调用它。

However, can you detail why it is so essential for you to maintain this seed? 但是,您能详细说明为什么维持这种种子至关重要吗? There might be a better way. 可能有更好的方法。

Alternatively, might I suggest using System.Security.Cryptography.RandomNumberGenerator class to generate cryptographically strong random byte arrays? 或者,我可以建议使用System.Security.Cryptography.RandomNumberGenerator类来生成加密强大的随机字节数组吗?

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

I will join the rest of the comments, and mention that relying on an undocumented implementation is bad. 我将加入其余的评论,并提到依赖于未记录的实现是不好的。 More so, if you're actually relying on a predictable "randomness" - if you're using this for anything that should be "secure" - it is totally wrong. 更重要的是,如果你真的依赖于可预测的“随机性” - 如果你将它用于任何应该是“安全”的东西 - 这是完全错误的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM