C++ генерация случайных чисел с плавающей запятой



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



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

5213   14  

14 ответов:

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)));

отметим, что

C++11 дает вам много новых опций с random. Каноническая статья на эту тему будет N3551, генерация случайных чисел в C++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;
}

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

5Ч 5S КАК 9С 4Д 6Ч Й 6Д Х 2С СМО 9Ч 8Ч 7Ч 2Д 3Д КЦ ТД КС 3С ТС 7Д 4С ДЧ КК КТ ДЖЕЙ ДИ АЙ ЙК АС КД 9Д 2Ч 4Ч 5С 8С 9С И. Н 5Д 4С 7С ОБЪЯВЛЕНИЕ 3С 8С 2С Ц 8Д 3Ч 6С И JS-7С 6С

Boost

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

посмотри импульс.Случайный. Вы могли бы сделать что-то вроде этого:

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

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

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

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

этот синтаксис предлагает возможность выбора генератора (например,мерсенн твистер 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;

современной c++ можно использовать <random> заголовок, который пришел с c++11.
Чтобы получить случайный float's Вы можете использовать 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's в контейнере, например 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

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

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

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

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

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

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

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

Я не был удовлетворен ни одним из ответов до сих пор, поэтому я написал новую случайную функцию 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));
}

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

/ знак (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;

но если вы посмотрите на структуру поплавка вы можете увидеть, что максимальное значение a float равен (приблизительно) 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

Если вы знаете, что ваш формат с плавающей запятой составляет 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;
}

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

для C++ он может генерировать реальные числа с плавающей запятой в диапазоне, указанном 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);
}

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

полностью случайное допустимое число с плавающей запятой генерируется следующим образом: Случайный знак, случайный показатель и случайная мантисса. Вот пример генерации случайных чисел от 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 () для генерации экспоненты и еще 2 вызова rand () для генерации 23 бит мантиссы.

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

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

Comments

    Ничего не найдено.