【发布时间】:2008-12-31 17:34:54
【问题描述】:
我想使用 RNGCryptoServiceProvider 作为我的随机数来源。由于它只能将它们作为字节值数组输出,我如何将它们转换为 0 到 1 的双精度值,同时保持结果的一致性?
【问题讨论】:
我想使用 RNGCryptoServiceProvider 作为我的随机数来源。由于它只能将它们作为字节值数组输出,我如何将它们转换为 0 到 1 的双精度值,同时保持结果的一致性?
【问题讨论】:
byte[] result = new byte[8];
rng.GetBytes(result);
return (double)BitConverter.ToUInt64(result,0) / ulong.MaxValue;
【讨论】:
ulong.MaxValue 是分子中可以得到的最大数字。 第二个问题: 否,因为分子总是正数(无符号)。 第三个问题: 不知道你在说什么……3 与什么有什么关系? 2^64 比double.MaxValue 小得多,所以没有Infinity,它可能变成NaN 的唯一方法是分母是否为零,而事实并非如此。 编辑: 也许您认为double 的演员表是在传送?不是。
这就是我要这样做的方式。
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<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 相同,不是吗?
v &= ((1UL << 53) - 1) 是一种用于屏蔽位的常见模式。我还没有看到使用位移位来屏蔽位。此外,该格式的结构中有 53 个,这将告诉您在完成掩码操作后,还剩下 53 个有效位的信息。这就是为什么我更喜欢这种掩蔽方法。
您可以使用 BitConverter.ToDouble(...) 方法。它接受一个字节数组并将返回一个 Double。其他大多数基元类型都有相应的方法,以及从基元到字节数组的方法。
【讨论】:
使用 BitConverter 将随机字节序列转换为 Double:
byte[] 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);
【讨论】: