Генерация случайных чисел с плавающей точкой - PullRequest
246 голосов
/ 26 марта 2009

Как генерировать случайные числа с плавающей точкой в ​​C ++?

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

Ответы [ 14 ]

345 голосов
/ 26 марта 2009

rand() может использоваться для генерации псевдослучайных чисел в C ++. В сочетании с 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>.

116 голосов
/ 23 июля 2013

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

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

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

Приведенный ниже пример извлечен из сайта cppreference и использует механизм std :: mersenne_twister_engine и std ::iform_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 КАК 9S 4D 6H TH 6D KH 2S QS 9H 8H 3D KC TD 7H 2D KS 3C TC 7D 4C QH QC 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 тоже всегда вариант, здесь я использую boost :: random ::iform_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(), тогда мы можем перейти к C FAQ для руководств по Как я могу генерировать случайные числа с плавающей точкой? , который в основном дает пример, подобный этому, для генерации на интервале [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) ) ) ;  
}
60 голосов
/ 26 марта 2009

Взгляните на 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, вместо того чтобы каждый раз создавать новый, но, надеюсь, вы поняли идею.

23 голосов
/ 26 марта 2009

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

float rand_FloatRange(float a, float b)
{
    return ((b - a) * ((float)rand() / RAND_MAX)) + a;
}
19 голосов
/ 10 апреля 2016

В современном 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
19 голосов
/ 05 декабря 2009

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

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

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

  #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;
5 голосов
/ 26 марта 2009

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

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

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

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

4 голосов
/ 03 сентября 2013

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

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

В Windows стандартным способом является CryptGenRandom () .

2 голосов
/ 20 марта 2015

Если вы знаете, что ваш формат с плавающей запятой - IEEE 754 (почти все современные процессоры, включая Intel и ARM), то вы можете построить случайное число с плавающей запятой из случайного целого числа, используя побитовые методы. Это следует учитывать, только если у вас нет доступа к C ++ 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;
}

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

2 голосов
/ 03 июля 2013

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

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

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

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

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

обратите внимание, что если вы сделаете float f = (float)rand();, оно преобразует целое число в число с плавающей точкой (таким образом, 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, что намного больше максимального значения целого числа (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

...