我想使用RNGCryptoServiceProvider作为随机数的来源.因为它只能将它们作为字节值数组输出,如何将它们转换为0到1的双精度值,同时保持结果的一致性?
byte[] result = new byte[8]; rng.GetBytes(result); return (double)BitConverter.ToUInt64(result,0) / ulong.MaxValue;
我就是这样做的.
private static readonly System.Security.Cryptography.RNGCryptoServiceProvider _secureRng; public static double NextSecureDouble() { var bytes = new byte[8]; _secureRng.GetBytes(bytes); var v = BitConverter.ToUInt64(bytes, 0); // We only use the 53-bits of integer precision available in a IEEE 754 64-bit double. // The result is a fraction, // r = (0, 9007199254740991) / 9007199254740992 where 0 <= r && r < 1. v &= ((1UL << 53) - 1); var r = (double)v / (double)(1UL << 53); return r; }
巧合的
9007199254740991 / 9007199254740992 is ~= 0.99999999999999988897769753748436
是,该Random.NextDouble
方法将返回最大值(参见https://msdn.microsoft.com/en-us/library/system.random.nextdouble(v=vs.110).aspx).
通常,连续均匀分布的标准偏差是(max-min)/ sqrt(12).
样本大小为1000,我可以在2%的误差范围内可靠地获得.
样本大小为10000我可靠地获得1%的误差范围.
以下是我验证这些结果的方法.
[Test] public void Randomness_SecureDoubleTest() { RunTrials(1000, 0.02); RunTrials(10000, 0.01); } private static void RunTrials(int sampleSize, double errorMargin) { var q = new Queue(); while (q.Count < sampleSize) { q.Enqueue(Randomness.NextSecureDouble()); } for (int k = 0; k < 1000; k++) { // rotate q.Dequeue(); q.Enqueue(Randomness.NextSecureDouble()); var avg = q.Average(); // Dividing by n?1 gives a better estimate of the population standard // deviation for the larger parent population than dividing by n, // which gives a result which is correct for the sample only. var actual = Math.Sqrt(q.Sum(x => (x - avg) * (x - avg)) / (q.Count - 1)); // see http://stats.stackexchange.com/a/1014/4576 var expected = (q.Max() - q.Min()) / Math.Sqrt(12); Assert.AreEqual(expected, actual, errorMargin); } }