如何生成一个安全的加密双精度浮点数,其值在0到1之间?

15

我知道如何使用伪随机数生成器的NextDouble方法生成0到1之间的随机数。

var rng1 = new System.Random();
var random1 = rng1.NextDouble(); // generates a random double between 0 and 1.0

我知道如何使用具有加密安全性的随机数生成器填充随机字节数组。

Byte[] bytes = new Byte[8];
var rng2 = new System.Security.Cryptography.RNGCryptoServiceProvider();
rng2.GetBytes(bytes); // generates 8 random bytes

但是我该如何将RNGCryptoServiceProvider的字节数组输出转换为在0(包含)和1(不包含)之间均匀分布的随机数?


你想要哪种分布?均匀分布吗? - AakashM
是的,均匀分布。现在会更新。 - Portman
3个回答

32

在我看来,到目前为止的解决方案由于取倒数而导致分布不均匀。如果要实现均匀分布,我认为你需要像这样做。

// Step 1: fill an array with 8 random bytes
var rng = new RNGCryptoServiceProvider();
var bytes = new Byte[8];
rng.GetBytes(bytes);
// Step 2: bit-shift 11 and 53 based on double's mantissa bits
var ul = BitConverter.ToUInt64(bytes, 0) / (1 << 11);
Double d = ul / (Double)(1UL << 53);

请注意,您不能只将UInt64除以UInt64.MaxValue,因为double类型没有足够的位数,并且没有办法为所有输入获取唯一的输出。因此,您可以/必须丢弃一些位。


太好了,谢谢。只是在BitConverter.ToUInt64中添加了所需的第二个参数和第2行缺失的父级。现在正在测试以确保它与Random.NextDouble()具有相等的分布。 - Portman
1
第三次编辑很好:经过一百万次迭代,最小值为0.0000001,最大值为0.999999,平均值为0.5000003。如果我清理编辑历史记录你介意吗? - Portman
为什么不直接获取53位,而要在倒数第二行做奇怪的事情呢?(说真的,我花了一段时间才明白你具体做了什么)。 - Joey
1
  1. 我很匆忙,那是我首先想到的编码。
  2. 随意展示给我们你更简洁的代码来“获取53位”。
  3. 后来我意识到"BitConverter.ToUInt64(bytes, 0) >> 11"会更简洁,但当时我时间不够。
- Conrad Albrecht

-1

嗯,我不会称一个64位的随机数为“密码学安全” - 你需要比那更多的位数才能达到“密码学安全”的标准。但无论如何,你可以这样做:

var bytes = // assume this contains 8 bytes of random numbers

long l = BitConverter.ToInt64(bytes);
double d = Math.Abs(1 / (double)l);

可能需要添加 Math.Abs( l ) 以确保结果为正数。 - Paul Alexander
8
与NextDouble相比,此函数的分布完全不同。它会生成接近于零的数字,几乎大部分时间都是如此。 - Baffe Boyois
感谢关于NaN的信息,我不知道它是特定的字节序列。 - Thomas

-1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接