Реализация собственной криптографической случайной функции .Next (maxValue) - PullRequest
2 голосов
/ 18 сентября 2011

Я работаю над статическим классом для предоставления случайных значений для моей программы, но есть некоторые проблемы с реализацией эквивалента функциональности Random.Next (int maxValue):

public class CRandom {
    static readonly byte[] randomSet;
    static readonly Func<int> closedIndex; 
    static CRandom() {
        randomSet = new byte[60000000];
        closedIndex = _ClosedIndex(0); 
        RNGCryptoServiceProvider Gen = new RNGCryptoServiceProvider();
        Gen.GetBytes(randomSet);
    }
    public static int Next() {
        int index = closedIndex();
        return Convert.ToInt32(randomSet[index]);
    }
    public static int Next(int maxValue) {
        int index = closedIndex();
        byte[] remainingSet = randomSet.Skip(index + 1).ToArray();
        byte next = remainingSet.First(x => Convert.ToInt32(x) < maxValue); 
        return Convert.ToInt32(next);
    }
    public static Func<int> _ClosedIndex(int seed) {
        // seed is the initial value
        int _index = seed - 1; 
        Func<int> del = new Func<int>(() =>
        {  // always returns auto-incremented value
            _index++; 
            return _index; 
        });
        return del; 
    }
}

По сути, он заполняет статический / только для чтения байтовый массив случайных значений, а в случае метода Next (maxValue) просто получает следующее значение, которое находится в диапазоне, но ранее не использовалось. Однако попытка Next (100) в цикле дает следующие результаты, которые, очевидно, не случайны:

53 20 20 34 34 73 73 73 73

Это также очень медленный способ сделать это. Я уверен, что есть лучший способ, но я не совсем понимаю, как работает Random.Next ().

Ответы [ 2 ]

3 голосов
/ 18 сентября 2011

MSDN Magazine опубликовал очень подробную статью на эту тему.
Это сложнее, чем вы думаете.

Он написал следующий класс; Тем не менее, прочитайте статью для заметок о случайности.

public class CryptoRandom : Random
{
    private RNGCryptoServiceProvider _rng =
        new RNGCryptoServiceProvider();
    private byte[] _uint32Buffer = new byte[4];

    public CryptoRandom() { }
    public CryptoRandom(Int32 ignoredSeed) { }

    public override Int32 Next()
    {
        _rng.GetBytes(_uint32Buffer);
        return BitConverter.ToInt32(_uint32Buffer, 0) & 0x7FFFFFFF;
    }

    public override Int32 Next(Int32 maxValue)
    {
        if (maxValue < 0)
            throw new ArgumentOutOfRangeException("maxValue");
        return Next(0, maxValue);
    }

    public override Int32 Next(Int32 minValue, Int32 maxValue)
    {
        if (minValue > maxValue) 
            throw new ArgumentOutOfRangeException("minValue");
        if (minValue == maxValue) return minValue;
        Int64 diff = maxValue - minValue;
        while (true)
        {
            _rng.GetBytes(_uint32Buffer);
            UInt32 rand = BitConverter.ToUInt32(_uint32Buffer, 0);

            Int64 max = (1 + (Int64)UInt32.MaxValue);
            Int64 remainder = max % diff;
            if (rand < max - remainder)
            {
                return (Int32)(minValue + (rand % diff));
            }
        }
    }

    public override double NextDouble()
    {
        _rng.GetBytes(_uint32Buffer);
        UInt32 rand = BitConverter.ToUInt32(_uint32Buffer, 0);
        return rand / (1.0 + UInt32.MaxValue);
    }

    public override void NextBytes(byte[] buffer)
    {
        if (buffer == null) throw new ArgumentNullException("buffer");
        _rng.GetBytes(buffer);
    }
}
0 голосов
/ 04 июня 2013

Попробуйте мое решение:

public sealed class SecureRandom : Random {

    private readonly RandomNumberGenerator _rng;

    public SecureRandom() {
        _rng = new RNGCryptoServiceProvider();            
    }

    public SecureRandom(int seed) {
        var rgb = BitConverter.GetBytes(seed);
        _rng = new RNGCryptoServiceProvider(rgb);
    }

    public override int Next() {
        var data = new byte[sizeof (int)];
        _rng.GetBytes(data);
        return BitConverter.ToInt32(data, 0) & (int.MaxValue - 1);
    }

    public override int Next(int maxValue) {            
        return Next(0, maxValue);
    }

    public override int Next(int minValue, int maxValue) {
        if (minValue > maxValue) {
            throw new ArgumentOutOfRangeException("minValue", minValue, "minValue must be less than or equals to maxValue");
        }            
        return (int) Math.Floor(minValue + (maxValue - minValue) * NextDouble());
    }

    public override double NextDouble() {
        var data = new byte[sizeof (uint)];
        _rng.GetBytes(data);
        var randUint = BitConverter.ToUInt32(data, 0);
        return randUint / (uint.MaxValue + 1.0);
    }

    public override void NextBytes(byte[] data) {
        _rng.GetBytes(data);
    }        

    public override string ToString() {
        return _rng.ToString();
    }

    public override bool Equals(object obj) {
        return _rng.Equals(obj);
    }

    public override int GetHashCode() {
        return _rng.GetHashCode();
    }
}
...