Каков наилучший способ создания случайного двойного значения в POSIX?

StackOverflow https://stackoverflow.com/questions/146387

Вопрос

Я хотел бы получить равномерное распределение в диапазоне [0,0, 1,0)

Если возможно, позвольте реализации использовать случайные байты из /dev/urandom.

Также было бы неплохо, если бы ваше решение было потокобезопасный.Если вы не уверены, пожалуйста, укажите это.

Видеть какое-то решение Я подумал об этом после прочтения других ответов.

Это было полезно?

Решение 2

Кажется, это довольно хороший способ:

unsigned short int r1, r2, r3;
// let r1, r2 and r3 hold random values
double result = ldexp(r1, -48) + ldexp(r2, -32) + ldexp(r3, -16);

Это основано на реализации drand48 в NetBSD.

Другие советы

Простой:Double имеет точность 52 бита, если предположить, что IEEE.Итак, сгенерируйте 52-битное (или больше) случайное целое число без знака (например, прочитав байты из dev/urandom), преобразуйте его в двойное и разделите на 2^(количество битов).

Это дает численно однородное распределение (в том смысле, что вероятность нахождения значения в заданном диапазоне пропорциональна диапазону) вплоть до 52-й двоичной цифры.

Сложный:Однако в диапазоне [0,1) есть много двойных значений, которые приведенное выше не может сгенерировать.Точнее, половина значений в диапазоне [0,0,5) (те, у которых установлен младший значащий бит) не может возникнуть.Три четверти значений в диапазоне [0,0,25) (те, у которых установлен хотя бы один из двух битов) не могут встречаться и т. д., вплоть до того, что возможно только одно положительное значение меньше 2 ^ -51, несмотря на то, что двойник способен представлять сквиллоны таких значений.Поэтому нельзя сказать, что он действительно однороден во всем указанном диапазоне с полной точностью.

Конечно, мы не хотим выбирать один из этих двойников с равной вероятностью, потому что тогда результирующее число в среднем будет слишком маленьким.Нам по-прежнему нужно, чтобы вероятность попадания результата в заданный диапазон была пропорциональна диапазону, но с более высокой точностью определения того, для каких диапазонов это работает.

я думать следующие работы.Я особо не изучал и не тестировал этот алгоритм (как вы, вероятно, можете заметить по тому, что в нем нет кода), и лично я бы не стал использовать его, не найдя подходящих ссылок, подтверждающих его правильность.Но вот:

  • Начните экспоненту с 52 и выберите 52-битное случайное целое число без знака (при условии, что мантисса 52 бита).
  • Если старший бит целого числа равен 0, увеличьте показатель степени на единицу, сдвиньте целое число на единицу влево и заполните младший бит новым случайным битом.
  • Повторяйте до тех пор, пока либо вы не наберете 1 в самом значимом месте, либо показатель степени не станет слишком большим для вашего двойного значения (1023.Или, возможно, 1022).
  • Если вы нашли 1, разделите полученное значение на 2^экспонента.Если вы получили все нули, верните 0 (я знаю, что на самом деле это не особый случай, но следует подчеркнуть, насколько маловероятно возвращение 0 [Редактировать:на самом деле это может быть особый случай - это зависит от того, хотите ли вы генерировать денормы или нет.Если нет, то как только у вас будет достаточно нулей подряд, вы отбрасываете все оставшееся и возвращаете 0.Но на практике это настолько маловероятно, что им можно пренебречь, если только случайный источник не является случайным).

Заметьте, я не знаю, есть ли вообще практическая польза от такого случайного двойника.Ваше определение случайности должно в некоторой степени зависеть от того, для чего она нужна.Но если вы можете извлечь выгоду из того, что все 52 его значащих бита случайны, это действительно может быть полезно.

Чтение из файлов является потокобезопасным AFAIK, поэтому использование fopen() для чтения из /dev/urandom даст «действительно случайные» байты.

Хотя могут быть потенциальные ошибки, я думаю, что любой набор таких байтов, доступный как целое число, разделенный на максимальное целое число этого размера, даст значение с плавающей запятой от 0 до 1 с примерно таким распределением.

Например:

FILE* f = fopen("/dev/urandom", "r");
int32_t int;
fread(&int, sizeof(int32_t), 1, f);
fclose(f);
double theRandomValue = int / (double) (2 ** 32 - 1);

Хитрость в том, что вам нужен 54-битный рандомизатор, отвечающий вашим требованиям.Несколько строк кода с объединением, чтобы вставить эти 54 бита в мантиссу, и вы получите свой номер.Хитрость не в двойном плавании, а в желаемом рандомизаторе.

#include <stdlib.h>
printf("%f\n", drand48());

/Дев/случайный:

double c;
fd = open("/dev/random", O_RDONLY);
unsigned int a, b;
read(fd, &a, sizeof(a));
read(fd, &b, sizeof(b));
if (a > b)
   c = fabs((double)b / (double)a);
else
    c = fabs((double)a / (double)b);

c — ваше случайное значение

/dev/urandom не является POSIX и обычно недоступен.

Стандартный способ создания двойного числа равномерно в [0,1) — создать целое число в диапазоне [0,2^N) и разделить его на 2^N.Так что выберите свой любимый генератор случайных чисел и используйте его.Для моделирования я использую Мерсенн Твистер, поскольку он чрезвычайно быстр, но все еще плохо коррелирован.На самом деле, он может сделать это за вас и даже имеет версию, обеспечивающую большую точность для меньших чисел.Обычно для начала вы даете ему начальное значение, что помогает обеспечить повторяемость при отладке или показе другим ваших результатов.Конечно, вы можете использовать свой код для получения случайного числа из /dev/urandom в качестве начального числа, если оно не указано.

Вместо этого для криптографических целей вам следует использовать одну из стандартных криптографических библиотек, например OpenSSL), который действительно будет использовать /dev/urandom, если он доступен.

Что касается потокобезопасности, то большинство из них не будет, по крайней мере, со стандартными интерфейсами, поэтому вам нужно будет создать слой поверх или использовать их только в одном потоке.Те, которые являются потокобезопасными, заставляют вас предоставлять состояние, которое они изменяют, так что вместо этого вы эффективно запускаете несколько невзаимодействующих генераторов случайных чисел, что может быть не тем, что вы ищете.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top