Implementing my own cryptographically random .Next(maxValue) function
我正在研究一个静态类来为我的程序提供随机值,但是在实现Random.Next(int maxvalue)功能时存在一些问题:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | 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; } } |
基本上,它所做的就是填充随机值的静态/只读字节数组,在下一个(maxvalue)方法的情况下,只获取范围内但以前从未使用过的下一个值。然而,在一个循环中尝试下一个(100)会得到这些结果,这显然不是随机的:
五十三二十二十三十四三十四七十三七十三七十三七十三
这也是一种非常缓慢的方法。我确信有更好的方法,但我不太清楚random.next()在引擎盖下的工作原理。
《MSDN》杂志有一篇关于这个确切主题的非常详细的文章。这比你想象的要复杂。
他写了下节课;但是,阅读这篇文章可以获得关于随机性的重要注释。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | 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); } } |
尝试我的解决方案:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | 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(); } } |