Внедрение изменений в .NET Random() - программирование
Подтвердить что ты не робот

Внедрение изменений в .NET Random()

Я переношу метод, который используется для декодирования из .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

Каков наилучший способ решить эту проблему?

4b9b3361

Ответ 1

Вы можете просто использовать Reflector для копирования класса Random из 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);
    }
}

Протестировано и дает желаемый результат.

Ответ 2

Это не проблема с Random, он отлично удовлетворяет документированному интерфейсу. Это проблема, связанная с тем, что ваше программное обеспечение опирается на детали реализации. Изучите эту ошибку и не делайте этого снова.

Что касается устранения проблемы, вы можете реализовать свою собственную версию генерации псевдослучайных чисел 1.1 для декодирования, а затем реализовать новый алгоритм кодирования/декодирования, который не полагается на нестабильное поведение (например, реализация Random или GetHashCode) для вашей новой версии программного обеспечения.

Ответ 3

Если вы абсолютно зависимы от версии .NET 1.1 от Random, то единственное, что я могу придумать, это создать новую сборку, ориентированную на 1.1, и вызывать ее из вашего обновленного приложения .NET 4.

Однако, можете ли вы объяснить, почему так важно сохранить это семя? Возможно, лучший способ.

Ответ 4

Здесь нет ответа, но вопреки многим людям здесь я не думаю, что документирования смешного поведения достаточно, чтобы оправдать его.

Потому, почему вы в первую очередь обеспечиваете механизм посева? Хорошо, я скажу вам, чтобы вы всегда могли воспроизводить случайную последовательность из одного семени, вместо того, чтобы сохранять, возможно, миллионы случайных чисел. Обратите внимание, что я сказал "всегда", а не "пока вы не перейдете к следующей версии .NET". Не будучи согласованными между версиями, текущие генераторы случайных чисел .NET не предоставляют эту функцию. Microsoft должна была бы лучше выполнить это (или вообще не реализовывать), а не просто документировать дефектное поведение.

И, между прочим, хотя алгоритм действительно является деталью реализации, как вы можете назвать результат метода вызовом детализации реализации? Должен ли я действительно проверять документацию каждого метода в .NET Framework, чтобы убедиться, что в следующей версии я не рискую получить другой результат от объединения двух строк или вычисления квадратного корня?

Так что, на мой взгляд, мы имеем здесь просто плохо реализованный генератор случайных чисел. И, конечно, всей проблемы можно было бы легко избежать, предоставив новую функциональность (на основе новой реализации) другое имя.

Ответ 5

В качестве альтернативы, могу ли я предложить использовать класс System.Security.Cryptography.RandomNumberGenerator для генерации криптографически сильных массивов случайных байтов?

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

Я присоединяюсь к остальным комментариям и упоминаю, что использование недокументированной реализации плохое. Более того, если вы на самом деле полагаетесь на предсказуемую "случайность" - если вы используете это для чего-либо, что должно быть "безопасным" - это совершенно неправильно.