Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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. So given the same seed, Random.NextBytes returns different result.

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:

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

Under .NET framework 4 it returns:

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

What is the best way to resolve this problem?

like image 841
Nejchy Avatar asked Mar 18 '12 12:03

Nejchy


People also ask

Is the random number generator implementation the same across versions of net?

The implementation of the random number generator in the Random class isn't guaranteed to remain the same across major versions of the .NET Framework. As a result, you shouldn't assume that the same seed will result in the same pseudo-random sequence in different versions of the .NET Framework.

What is the current implementation of the random class?

The current implementation of the Random class is based on a modified version of Donald E. Knuth's subtractive random number generator algorithm. For more information, see D. E. Knuth. "The Art of Computer Programming, volume 2: Seminumerical Algorithms". Addison-Wesley, Reading, MA, second edition, 1981. All calculations are based on seed.

Is initialization of random class in NET Framework faster than core?

The results are interesting: initialization of Random class instance on .NET Framework takes about three times faster than on .NET Core. It implies that reading Environment.TickCount is still faster than using random generators to get the seed. Both platforms, .NET Framework and .NET Core, have different ways to implement Random class.

What happened to the random class in NET Core?

With the introduction of .NET Core 2.0, the internal implementation of the System.Random class was revised slightly compared to the legacy .NET Framework. From a usage perspective, things remained virtually unchanged - but there are some minor nuances that may help to be aware of.


2 Answers

This is not a problem with Random, it satisfies its documented interface perfectly fine. 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.

like image 173
Greg D Avatar answered Oct 04 '22 08:10

Greg D


You can just use Reflector to copy the Random class from the 1.1 mscorlib.

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.

like image 34
Will Avatar answered Oct 04 '22 10:10

Will