Как создать криптографически безопасное двойное от 0 до 1?

Я знаю, как генерировать случайное число между 0 и 1, используя метод NextDouble генератора псевдослучайных чисел.

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 ( эксклюзив)?

Ответ 1

Мне кажется, что решения до сих пор будут иметь неравномерное распределение из-за принятия обратного. Для равномерного распределения я бы подумал, что вы хотите что-то вроде этого.

// 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 mantissa bits
var ul = BitConverter.ToUInt64(bytes, 0) / (1 << 11);
Double d = ul / (Double)(1UL << 53);

Обратите внимание, что вы не можете просто разделить UInt64 на UInt64.MaxValue, потому что у двойника недостаточно битов, и нет никакого способа получить уникальные выходы для всех ваших входов. Таким образом, вы можете/должны отбросить несколько бит.

Ответ 2

Ну, я бы не назвал 64-битное случайное число "криптографически безопасным" - вам нужно было бы гораздо больше бит, чем это "криптографически безопасно". Но в любом случае вы можете сделать что-то вроде этого:

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

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