Генерация случайных чисел с плавающей точкой

Как создать случайные поплавки в С++?

Я думал, что могу взять целочисленный rand и разделить его на что-то, достаточно ли это было бы?

Ответ 1

rand() может использоваться для генерации псевдослучайных чисел в С++. В сочетании с RAND_MAX и небольшой математикой вы можете генерировать случайные числа в любом произвольном интервале, который вы выберете. Этого достаточно для учебных целей и игрушечных программ. Если вам нужны действительно случайные числа с нормальным распределением, вам нужно использовать более продвинутый метод.


Это приведет к генерации числа от 0.0 до 1.0 включительно.

float r = static_cast <float> (rand()) / static_cast <float> (RAND_MAX);

Это будет генерировать число от 0.0 до некоторого произвольного float, X:

float r2 = static_cast <float> (rand()) / (static_cast <float> (RAND_MAX/X));

Это будет генерировать число от некоторого произвольного LO до некоторого произвольного HI:

float r3 = LO + static_cast <float> (rand()) /( static_cast <float> (RAND_MAX/(HI-LO)));

Обратите внимание, что функция rand() часто бывает недостаточной, если вам нужны действительно случайные числа.


Перед вызовом rand() вы должны сначала "засеять" генератор случайных чисел, вызвав srand(). Это нужно делать один раз во время запуска вашей программы - не каждый раз, когда вы вызываете rand(). Это часто делается следующим образом:

srand (static_cast <unsigned> (time(0)));

Для вызова rand или srand вы должны #include <cstdlib>.

Чтобы позвонить time, вы должны #include <ctime>.

Ответ 2

С++ 11 дает вам множество новых опций с random. Каноническая статья на эту тему была бы N3551, Генерация случайных чисел в С++ 11

Чтобы понять, почему использование rand() может быть проблематичным, см. rand() Рассмотрение вредоносного материала презентации Стефана Т. Лававей, данное во время GoingNative 2013. Слайды в комментариях, но вот прямая ссылка .

Я также охватываю boost, а также используя rand, поскольку устаревший код может по-прежнему нуждаться в его поддержке.

Приведенный ниже пример перегоняется с сайта cppreference и использует механизм std:: mersenne_twister_engine и std:: uniform_real_distribution, который генерирует числа в интервале [0,10), с комментариями других двигателей и дистрибутивов (видеть его вживую):

#include <iostream>
#include <iomanip>
#include <string>
#include <map>
#include <random>

int main()
{
    std::random_device rd;

    //
    // Engines 
    //
    std::mt19937 e2(rd());
    //std::knuth_b e2(rd());
    //std::default_random_engine e2(rd()) ;

    //
    // Distribtuions
    //
    std::uniform_real_distribution<> dist(0, 10);
    //std::normal_distribution<> dist(2, 2);
    //std::student_t_distribution<> dist(5);
    //std::poisson_distribution<> dist(2);
    //std::extreme_value_distribution<> dist(0,2);

    std::map<int, int> hist;
    for (int n = 0; n < 10000; ++n) {
        ++hist[std::floor(dist(e2))];
    }

    for (auto p : hist) {
        std::cout << std::fixed << std::setprecision(1) << std::setw(2)
                  << p.first << ' ' << std::string(p.second/200, '*') << '\n';
    }
}

будет выглядеть примерно так:

0 ****
1 ****
2 ****
3 ****
4 *****
5 ****
6 *****
7 ****
8 *****
9 ****

Выход зависит от выбранного вами дистрибутива, поэтому, если мы решили пойти с std:: normal_distribution со значением 2 для среднего и stddev например dist(2, 2) вместо этого вывод будет похож на этот (увидеть его в прямом эфире):

-6 
-5 
-4 
-3 
-2 **
-1 ****
 0 *******
 1 *********
 2 *********
 3 *******
 4 ****
 5 **
 6 
 7 
 8 
 9 

Ниже приведена модифицированная версия некоторого кода, представленного в N3551 (см. его в прямом эфире):

#include <algorithm>
#include <array>
#include <iostream>
#include <random>

std::default_random_engine & global_urng( )
{
    static std::default_random_engine u{};
    return u ;
}

void randomize( )
{
    static std::random_device rd{};
    global_urng().seed( rd() );
}

int main( )
{
  // Manufacture a deck of cards:
  using card = int;
  std::array<card,52> deck{};
  std::iota(deck.begin(), deck.end(), 0);

  randomize( ) ;  

  std::shuffle(deck.begin(), deck.end(), global_urng());
  // Display each card in the shuffled deck:
  auto suit = []( card c ) { return "SHDC"[c / 13]; };
  auto rank = []( card c ) { return "AKQJT98765432"[c % 13]; };

  for( card c : deck )
      std::cout << ' ' << rank(c) << suit(c);

   std::cout << std::endl;
}

Результаты будут выглядеть так:

5H 5S AS 9S 4D 6H TH 6D KH 2S QS 9H 8H 3D KC TD 7H 2D KS 3C TC 7D 4C QH QC QD JD AH JC AC KD 9D 5C 2H 4H 9C 8C JH 5D 4S 7C AD 3S 8S TS 2C 8D 3H 6C JS 7S 6S

подталкивания

Конечно Boost.Random всегда является опцией, здесь я использую повышение:: случайный:: uniform_real_distribution:

#include <iostream>
#include <iomanip>
#include <string>
#include <map>
#include <boost/random/mersenne_twister.hpp>
#include <boost/random/uniform_real_distribution.hpp>

int main()
{
    boost::random::mt19937 gen;
    boost::random::uniform_real_distribution<> dist(0, 10);

    std::map<int, int> hist;
    for (int n = 0; n < 10000; ++n) {
        ++hist[std::floor(dist(gen))];
    }

    for (auto p : hist) {
        std::cout << std::fixed << std::setprecision(1) << std::setw(2)
                  << p.first << ' ' << std::string(p.second/200, '*') << '\n';
    }
}

Rand()

Если вы должны использовать rand(), тогда мы можем перейти к FAQ по C для руководства по Как я могу генерировать случайные числа с плавающей запятой?, в котором в основном приведен пример, подобный этому для генерации a на интервале [0,1):

#include <stdlib.h>

double randZeroToOne()
{
    return rand() / (RAND_MAX + 1.);
}

и создать случайное число в диапазоне от [M,N):

double randMToN(double M, double N)
{
    return M + (rand() / ( RAND_MAX / (N-M) ) ) ;  
}

Ответ 3

Взгляните на Boost.Random. Вы можете сделать что-то вроде этого:

float gen_random_float(float min, float max)
{
    boost::mt19937 rng;
    boost::uniform_real<float> u(min, max);
    boost::variate_generator<boost::mt19937&, boost::uniform_real<float> > gen(rng, u);
    return gen();
}

Играйте вокруг, вы можете лучше пропустить один и тот же объект mt19937, а не конструировать новый каждый раз, но, надеюсь, вы получите эту идею.

Ответ 4

Вызвать код с двумя значениями float, код работает в любом диапазоне.

float rand_FloatRange(float a, float b)
{
    return ((b - a) * ((float)rand() / RAND_MAX)) + a;
}

Ответ 5

Если вы используете С++, а не C, помните, что в техническом отчете 1 (TR1) и в проекте С++ 0x они добавили возможности для генератора случайных чисел в заголовочном файле, я считаю, что он идентичен библиотека Boost.Random и определенно более гибкая и "современная", чем функция библиотеки C, rand.

Этот синтаксис предлагает возможность выбора генератора (например, mersenne twister mt19937), а затем выберите дистрибутив (обычный, bernoulli, binomial и т.д..).

Синтаксис выглядит следующим образом (бесстыдно заимствовано из этого сайта):

  #include <iostream>
  #include <random>

  ...

  std::tr1::mt19937 eng;  // a core engine class 
  std::tr1::normal_distribution<float> dist;     

  for (int i = 0; i < 10; ++i)        
      std::cout << dist(eng) << std::endl;

Ответ 6

В современном c++ вы можете использовать заголовок <random>, поставляемый с c++11.
Для получения случайного числа с float вы можете использовать std::uniform_real_distribution<>.

Вы можете использовать функцию для генерации чисел, и если вы не хотите, чтобы числа были одинаковыми все время, установите движок и распределение static.
Пример:

float get_random()
{
    static std::default_random_engine e;
    static std::uniform_real_distribution<> dis(0, 1); // rage 0 - 1
    return dis(e);
}

Идеально поместить float в контейнер, такой как std::vector:

int main()
{
    std::vector<float> nums;
    for (int i{}; i != 5; ++i) // Generate 5 random floats
        nums.emplace_back(get_random());

    for (const auto& i : nums) std::cout << i << " ";
}

Пример вывода:

0.0518757 0.969106 0.0985112 0.0895674 0.895542

Ответ 7

В некоторых системах (в настоящее время Windows с VC приходит в голову), RAND_MAX смехотворно мал, i. е. всего 15 бит. При делении на RAND_MAX вы создаете мантисс из 15 бит вместо 23 возможных бит. Это может быть или не быть проблемой для вас, но в этом случае вы упускаете некоторые значения.

О, только что заметил, что уже есть комментарий к этой проблеме. Во всяком случае, здесь есть код, который может решить это для вас:

float r = (float)((rand() << 15 + rand()) & ((1 << 24) - 1)) / (1 << 24);

Неподтвержденный, но может работать: -)

Ответ 8

drand48(3) является стандартным способом POSIX. GLibC также предоставляет реентерабельную версию drand48_r(3).

Функция была объявлена ​​устаревшей в SVID 3, но адекватная альтернатива не была предоставлена, поэтому IEEE Std 1003.1-2013 по-прежнему включает в себя и не имеет записей, которые он будет куда угодно в ближайшее время.

В Windows стандартный способ CryptGenRandom().

Ответ 9

Я не был удовлетворен ни одним из ответов до сих пор, поэтому я написал новую функцию случайного float. Он делает побитовые предположения о типе данных float. Он по-прежнему нуждается в функции rand() с не менее 15 случайными битами.

//Returns a random number in the range [0.0f, 1.0f).  Every
//bit of the mantissa is randomized.
float rnd(void){
  //Generate a random number in the range [0.5f, 1.0f).
  unsigned int ret = 0x3F000000 | (0x7FFFFF & ((rand() << 8) ^ rand()));
  unsigned short coinFlips;

  //If the coin is tails, return the number, otherwise
  //divide the random number by two by decrementing the
  //exponent and keep going. The exponent starts at 63.
  //Each loop represents 15 random bits, a.k.a. 'coin flips'.
  #define RND_INNER_LOOP() \
    if( coinFlips & 1 ) break; \
    coinFlips >>= 1; \
    ret -= 0x800000
  for(;;){
    coinFlips = rand();
    RND_INNER_LOOP(); RND_INNER_LOOP(); RND_INNER_LOOP();
    //At this point, the exponent is 60, 45, 30, 15, or 0.
    //If the exponent is 0, then the number equals 0.0f.
    if( ! (ret & 0x3F800000) ) return 0.0f;
    RND_INNER_LOOP(); RND_INNER_LOOP(); RND_INNER_LOOP();
    RND_INNER_LOOP(); RND_INNER_LOOP(); RND_INNER_LOOP();
    RND_INNER_LOOP(); RND_INNER_LOOP(); RND_INNER_LOOP();
    RND_INNER_LOOP(); RND_INNER_LOOP(); RND_INNER_LOOP();
  }
  return *((float *)(&ret));
}

Ответ 10

По-моему, вышеприведенный ответ дает некоторый "случайный" float, но ни один из них не является по-настоящему случайным поплавком (т.е. они пропускают часть представления с плавающей точкой). Прежде чем я буду спешить с моей реализацией, сначала взглянем на стандартный формат ANSI/IEEE для float:

| знак (1-бит) | e (8 бит) | f (23-бит) |

число, представленное этим словом, равно (-1 * знак) * 2 ^ e * 1.f

Обратите внимание на то, что число "e" является предвзятым (с смещением 127), таким образом, варьируя от -127 до 126. Самая простая (и фактически самая случайная) функция заключается в том, чтобы просто записать данные случайного int в float, таким образом

int tmp = rand();
float f = (float)*((float*)&tmp);

обратите внимание, что если вы сделаете float f = (float)rand();, он преобразует целое число в float (таким образом, 10 станет 10.0).

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

int tmp = rand();
float f = *((float*)&tmp);
tmp = (unsigned int)f       // note float to int conversion!
tmp %= max_number;
f -= tmp;

но если вы посмотрите на структуру поплавка, вы увидите, что максимальное значение поплавка (приблизительно) 2 ^ 127, которое больше, чем максимальное значение int (2 ^ 32), тем самым исключая значительную часть чисел, которые могут быть представлены поплавком. Это моя окончательная реализация:

/**
 * Function generates a random float using the upper_bound float to determine 
 * the upper bound for the exponent and for the fractional part.
 * @param min_exp sets the minimum number (closest to 0) to 1 * e^min_exp (min -127)
 * @param max_exp sets the maximum number to 2 * e^max_exp (max 126)
 * @param sign_flag if sign_flag = 0 the random number is always positive, if 
 *              sign_flag = 1 then the sign bit is random as well
 * @return a random float
 */
float randf(int min_exp, int max_exp, char sign_flag) {
    assert(min_exp <= max_exp);

    int min_exp_mod = min_exp + 126;

    int sign_mod = sign_flag + 1;
    int frac_mod = (1 << 23);

    int s = rand() % sign_mod;  // note x % 1 = 0
    int e = (rand() % max_exp) + min_exp_mod;
    int f = rand() % frac_mod;

    int tmp = (s << 31) | (e << 23) | f;

    float r = (float)*((float*)(&tmp));

    /** uncomment if you want to see the structure of the float. */
//    printf("%x, %x, %x, %x, %f\n", (s << 31), (e << 23), f, tmp, r);

    return r;
}

используя эту функцию randf(0, 8, 0), вернет случайное число между 0.0 и 255.0

Ответ 11

Если вы знаете, что ваш формат с плавающей запятой IEEE 754 (почти все современные процессоры, включая Intel и ARM), тогда вы можете построить случайное плавающее номер точки из случайного целого с использованием битовых методов. Это следует учитывать только в том случае, если у вас нет доступа к С++ 11 random или Boost.Random, которые намного лучше.

float rand_float()
{
    // returns a random value in the range [0.0-1.0)

    // start with a bit pattern equating to 1.0
    uint32_t pattern = 0x3f800000;

    // get 23 bits of random integer
    uint32_t random23 = 0x7fffff & (rand() << 8 ^ rand());

    // replace the mantissa, resulting in a number [1.0-2.0)
    pattern |= random23;

    // convert from int to float without undefined behavior
    assert(sizeof(float) == sizeof(uint32_t));
    char buffer[sizeof(float)];
    memcpy(buffer, &pattern, sizeof(float));
    float f;
    memcpy(&f, buffer, sizeof(float));

    return f - 1.0;
}

Это даст лучшее распределение, чем использование деления.

Ответ 12

Для С++ он может генерировать реальные числа с плавающей запятой в пределах диапазона, указанного переменной dist

#include <random>  //If it doesnt work then use   #include <tr1/random>
#include <iostream>

using namespace std;

typedef std::tr1::ranlux64_base_01 Myeng; 
typedef std::tr1::normal_distribution<double> Mydist;

int main() { 
       Myeng eng; 
       eng.seed((unsigned int) time(NULL)); //initializing generator to January 1, 1970);
       Mydist dist(1,10); 

       dist.reset(); // discard any cached values 
       for (int i = 0; i < 10; i++)
       {
           std::cout << "a random value == " << (int)dist(eng) << std::endl; 
       }

       return (0);
}

Ответ 13

rand() возвращает int между 0 и RAND_MAX. Чтобы получить случайное число между 0.0 и 1.0, сначала введите int return by rand() в float, а затем разделите на RAND_MAX.

Ответ 14

#include <cstdint>
#include <cstdlib>
#include <ctime>

using namespace std;

/* single precision float offers 24bit worth of linear distance from 1.0f to 0.0f */
float getval() {
    /* rand() has min 16bit, but we need a 24bit random number. */
    uint_least32_t r = (rand() & 0xffff) + ((rand() & 0x00ff) << 16);
    /* 5.9604645E-8 is (1f - 0.99999994f), 0.99999994f is the first value less than 1f. */
    return (double)r * 5.9604645E-8;
}

int main()
{
    srand(time(NULL));
...

Я не мог опубликовать два ответа, так что вот второе решение. log2 случайные числа, массивный уклон в сторону 0,0f, но это действительно случайное смещение от 1,0f до 0,0f.

#include <cstdint>
#include <cstdlib>
#include <ctime>

using namespace std;

float getval () {
    union UNION {
        uint32_t i;
        float f;
    } r;
    /* 3 because it 0011, the first bit is the float sign.
     * Clearing the second bit eliminates values > 1.0f.
     */
    r.i = (rand () & 0xffff) + ((rand () & 0x3fff) << 16);
    return r.f;
}

int main ()
{
    srand (time (NULL));
...

Ответ 15

Полностью случайное действительное число с плавающей точкой генерируется следующим образом: Случайный знак, случайный показатель и случайная мантисса. Ниже приведен пример генерации случайных чисел из 0..MAXFLOAT с равномерным распределением:

static float frand(){
    float f;
    UINT32 *fi = (UINT32*)&f;
    *fi = 0;
    const int minBitsRandGives  = (1<<15);          //  RAND_MAX is at least (1<<15)    
    UINT32 randExp              = (rand()%254)+1;   //  Exponents are in range of [1..254]
    UINT32 randMantissa         = ((rand() % minBitsRandGives) << 8) | (rand()%256);
    *fi                         = randMantissa | (randExp<<23);                 // Build a float with random exponent and random mantissa
    return f;
}

Важное примечание: RAND_MAX по умолчанию равен 2 ^ 16 (в 32-битных системах), поэтому rand() может генерировать не более 15 случайных бит. Поскольку с плавающей запятой имеется всего 32 бита, мы должны активировать rand() не менее 3 раз для генерации случайных 32 бит. Я использовал 8 бит rand() для генерации Exponent и еще 2 обращения к rand() для генерации 23 бит мантиссы.

Общая ошибка:. Если вы используете (float)rand()/MAX_RAND для получения плавающей запятой в диапазоне [0..1], вы все равно получите случайные числа в равномерном распределении, но с низким точность. Например, ваш случайный генератор может генерировать 0,00001 и 0,00002, но не может генерировать 0,000017. Такое случайное число в 256 раз меньше, чем фактическое представление с плавающей запятой.

Оптимизация: Моя функция не оптимизирована для скорости. Вы можете улучшить его, заменив разделение "%" на побитовые логические операции. Например, вместо %256 используйте &0xFF