Генерация случайного числа между [-1, 1] в C? - PullRequest
14 голосов
/ 13 октября 2009

Я видел много вопросов на SO по этой конкретной теме, но ни один из них не дал мне никакого ответа, поэтому я подумал задать этот вопрос.

Я хотел сгенерировать случайное число между [-1, 1]. Как я могу это сделать?

Ответы [ 7 ]

18 голосов
/ 13 октября 2009

Использование -1+2*((float)rand())/RAND_MAX

rand() генерирует целые числа в диапазоне [0,RAND_MAX] включительно, поэтому ((float)rand())/RAND_MAX возвращает число с плавающей точкой в ​​[0,1]. Мы получаем случайные числа из [-1,1], добавляя его к -1.

РЕДАКТИРОВАТЬ: (добавление соответствующих частей раздела комментариев)

Об ограничениях этого метода:

((float)rand())/RAND_MAX возвращает процент (часть от 0 до 1). Так как диапазон от -1 до 1 составляет 2 целых числа, я умножаю эту дробь на 2, а затем добавляю ее к минимальному числу, которое вы хотите, -1. Это также говорит вам о качестве ваших случайных чисел, поскольку у вас будет только RAND_MAX уникальных случайных чисел.

9 голосов
/ 13 октября 2009

Если все, что у вас есть, это библиотека Standard C, то ответы других людей будут разумными. Если у вас есть доступная функциональность POSIX, рассмотрите возможность использования семейства функций drand48 () . В частности:

#define _XOPEN_SOURCE 600  /* Request non-standard functions */
#include <stdlib.h>

double f = +1.0 - 2.0 * drand48();
double g = -1.0 + 2.0 * drand48();

Обратите внимание, что в руководстве написано:

Функции drand48 () и erand48 () должны возвращать неотрицательные значения двойной точности с плавающей точкой, равномерно распределенные по интервалу [0.0,1.0).

Если вам строго необходимо [-1.0,+1.0] (в отличие от [-1.0,+1.0)), тогда вы столкнетесь с очень деликатной проблемой, связанной с расширением диапазона.

Функции drand48() дают вам значительно больше случайности, чем типичная реализация rand(). Однако, если вам нужна криптографическая случайность, ни один из них не подходит; вам нужно искать «криптографически сильный PRNG» (PRNG = генератор псевдослучайных чисел).

8 голосов
/ 13 октября 2009

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

float sfrand( void )
{
    unsigned int a=(rand()<<16)|rand();  //we use the bottom 23 bits of the int, so one
                                         //16 bit rand() won't cut it.
    a=(a&0x007fffff) | 0x40000000;  

    return( *((float*)&a) - 3.0f );
}

Первая часть генерирует случайное число с плавающей точкой из [2 ^ 1,2 ^ 2), вычтите 3, и вы получите [-1, 1). Это, конечно, может быть слишком интимно для некоторых приложений / разработчиков, но это было именно то, что я искал. Этот механизм хорошо работает для любого диапазона шириной 2 степени.

6 голосов
/ 13 октября 2009

Для начала вам понадобится функция библиотеки C rand(). Это в заголовочном файле stdlib.h, поэтому вы должны указать:

#include <stdlib.h>

в начале вашего кода. rand() сгенерирует случайное целое число от нуля до RAND_MAX, поэтому, поделив его на RAND_MAX / 2, вы получите число от нуля до 2 включительно. Вычтите одно, и вы попадете в целевой диапазон от -1 до 1.

Однако, если вы просто сделаете int n = rand() / (RAND_MAX / 2), вы обнаружите, что не получите ответ, который ожидаете. Это потому, что оба rand() и RAND_MAX / 2 являются целыми числами, поэтому используется целочисленная арифметика. Чтобы этого не происходило, некоторые люди используют бросок с плавающей точкой, но я бы рекомендовал избегать приведения умножением на 1.0.

Вы также должны заполнить свой генератор случайных чисел, используя функцию srand(). Чтобы каждый раз получать разные результаты, люди часто запускают генератор на основе времени, выполняя srand(time(0)).

Итак, в целом имеем:

#include <stdlib.h>
srand(time(0);
double r = 1.0 * rand() / (RAND_MAX / 2) - 1;
3 голосов
/ 13 октября 2009

Несмотря на то, что принятый ответ во многих случаях подходит, он пропускает «все остальные числа», поскольку расширяет диапазон уже дискретных значений на 2, чтобы охватить интервал [-1, 1]. Аналогичным образом, если у вас был генератор случайных чисел, который мог генерировать целое число из [0, 10], и вы хотели сгенерировать [0, 20], простое умножение на 2 будет охватывать диапазон, но не сможет охватить диапазон (было бы пропущено все нечетные числа).

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

Метод, который способен генерировать любое представимое число с плавающей запятой от -1 до 1 включительно, должен опираться на генерацию последовательности a1.a2 a3 a4 a5 ... до предела точности с плавающей запятой, которая является единственным способом чтобы иметь возможность генерировать любое возможное плавание в диапазоне. (т.е. после определения действительных чисел)

1 голос
/ 13 октября 2009

Из "Стандартной библиотеки C"

int rand(void) - возвращает псевдослучайное число в диапазоне от 0 до RAND_MAX

RAND_MAX - максимальное значение, возвращаемое rand().

Итак:

rand() вернет псевдослучайное число в диапазоне от 0 до RAND_MAX

rand() / RANDMAX вернет псевдослучайное число в диапазоне от 0 до 1

2*( rand() / RANDMAX ) вернет псевдослучайное число в диапазоне от 0 до 2

2*( rand() / RANDMAX ) -1 вернет псевдослучайное число в диапазоне от -1 до 1

0 голосов
/ 13 октября 2009

Как уже отмечали другие, любые попытки просто преобразовать диапазон функции 'rand ()' из [0, RAND_MAX] в желаемый [-1, +1] приведут к генератору случайных чисел, который может генерировать только дискретноенабор значений с плавающей точкой.Для генератора с плавающей запятой плотность этих значений может быть недостаточной в некоторых приложениях (если значение RAND_MAX, определяемое реализацией, недостаточно велико).Если это проблема, можно увеличить экспоненциальную плотность выше экспоненциально, используя два или более вызовов rand () вместо одного.

Например, комбинируя результаты двух последовательных вызовов к rand ()'можно получить псевдослучайное число в диапазоне [0, (RAND_MAX + 1) ^ 2 - 1]

#define RAND_MAX2 ((RAND_MAX + 1ul) * (RAND_MAX + 1) - 1)

unsigned long r2 = (unsigned long) rand() * (RAND_MAX + 1) + rand();

, а затем использовать тот же метод для преобразования его в число с плавающей точкой в ​​[-1, +1] range

double dr2 = r2 * 2.0 / RAND_MAX2 - 1;

Используя этот метод, можно накапливать столько вызовов rand (), сколько необходимо, конечно, следя за целочисленным переполнением.

В качестве примечания: этот метод объединения последовательных вызовов 'rand ()' не дает генераторов псевдослучайных чисел очень высокого качества, но может отлично работать во многих целях.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...