我想使用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 约等于 0.99999999999999988897769753748436
,这也是 Random.NextDouble
方法将返回的最大值(请参阅 https://msdn.microsoft.com/en-us/library/system.random.nextdouble(v=vs.110).aspx)。[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<double>();
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);
}
}
v &= ((1UL << 53) - 1)
和 v >> 11
是一样的,对吗? - Yellowfivev &= ((1UL << 53) - 1)
是一种常见的位掩码模式。我从未见过使用位移来进行位掩码。此外,格式中包含53,这将告诉您在掩码操作完成后还剩下确切的53个有效位信息。这就是为什么我更喜欢这种掩码方法的原因。 - John Leidegrenbyte[] random_bytes = new byte[8]; // BitConverter will expect an 8-byte array
new RNGCryptoServiceProvider().GetBytes(random_bytes);
double my_random_double = BitConverter.ToDouble(random_bytes, 0);
ulong.MaxValue
是分子中最大的数字。第二个问题:不,因为分子始终为正(它是无符号的)。第三个问题:我不知道你在说什么...3
与任何事情有什么关系?2^64
比double.MaxValue
小得多,所以没有Infinity
,它唯一可能变成NaN
的方式是分母为零,但它不是。编辑:也许你认为将其转换为double
是位拷贝?它不是。 - AnorZakenx / ulong.MaxValue
你就会遇到一种情况,即你可能会返回一个值为1.0
的结果,而通常NextDouble
API 应该返回小于1.0
的值,这样你就可以安全地使用它来生成数组的索引,并且也不必担心越界的问题。例如,arr[(int)(r * arr.Length)]
其中r
在范围[0, 1)
内 ,即0.0 <= r && r < 1.0
。 - John Leidegren(double)(ulong.MaxValue - 1) / ulong.MaxValue
由于强制转换时的截断,仍将导致1.0!这是仅使用54位随机数的更强烈原因!使用超过54位的任何数字都会使正确防止1.0变得棘手(续...) - AnorZaken