Geração aleatória de números float C ++

Como faço para gerar flutuações aleatórias em C ++?

Eu pensei que poderia pegar o inteiro rand e dividi-lo por algo, isso seria adequado o suficiente?

rand() pode ser usado para gerar números pseudo-randoms em C ++. Em combinação com RAND_MAX e um pouco de matemática, você pode gerar números randoms em qualquer intervalo arbitrário escolhido. Isso é suficiente para fins de aprendizado e programas de brinquedos. Se você precisar de números verdadeiramente randoms com distribuição normal, precisará empregar um método mais avançado.


Isso gerará um número de 0,0 a 1,0, inclusive.

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

Isto irá gerar um número de 0.0 a algum float arbitrário, X :

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

Isso irá gerar um número de algum LO arbitrário para algum HI arbitrário:

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

Observe que a function rand() geralmente não será suficiente se você precisar de números verdadeiramente randoms.


Antes de chamar rand() , você deve primeiro “propagar” o gerador de números randoms chamando srand() . Isso deve ser feito uma vez durante a execução do programa – não uma vez toda vez que você telefonar para rand() . Isso geralmente é feito assim:

 srand (static_cast  (time(0))); 

Para chamar rand ou srand você deve #include .

Para chamar a time , você deve #include .

O C ++ 11 oferece muitas novas opções com random . O documento canônico sobre este tópico seria N3551, Geração de Números Aleatórios em C ++ 11

Para ver por que usar rand() pode ser problemático, consulte o material de apresentação considerado nocivo por Stephan T. Lavavej durante o evento GoingNative 2013 . Os slides estão nos comentários, mas aqui está um link direto .

Eu também abro o boost , assim como uso de rand já que o código legado ainda pode exigir seu suporte.

O exemplo abaixo é destilado do site cppreference e usa o mecanismo std :: mersenne_twister_engine e std :: uniform_real_distribution que gera números no intervalo [0,10) , com outros mecanismos e distribuições comentados ( veja ao vivo ):

 #include  #include  #include  #include  #include  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 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'; } } 

a saída será semelhante à seguinte:

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

A saída irá variar dependendo de qual distribuição você escolher, então se nós decidimos ir com std :: normal_distribution com um valor de 2 para ambos mean e stddev eg dist(2, 2) invés disso a saída seria similar a esta ( veja ao vivo ):

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

A seguir está uma versão modificada de alguns dos códigos apresentados no N3551 ( veja ao vivo ):

 #include  #include  #include  #include  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 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; } 

Os resultados serão semelhantes a:

5h 5s como 9 s 4d 6h th 6d kh 2s qs 9 h 8 h 3d KC TD 7 H 2D KS 3C TC 7D 4C QH QD JD AH JC AC KD 9D 5C 2 H 4 H 9C 8C JH 5D 4 S 7C AD 3 S 8 S TS 2C 8D 3 H 6C JS 7S 6S

Impulso

Claro Boost.Random é sempre uma opção também, aqui estou usando boost :: random :: uniform_real_distribution :

 #include  #include  #include  #include  #include  #include  int main() { boost::random::mt19937 gen; boost::random::uniform_real_distribution<> dist(0, 10); std::map 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 ()

Se você deve usar rand() então podemos ir para o C FAQ para obter um guia sobre Como eu posso gerar números randoms de ponto flutuante? , que basicamente dá um exemplo semelhante a este para gerar um no intervalo [0,1) :

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

e gerar um número random no intervalo de [M,N) :

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

Dê uma olhada no Boost.Random . Você poderia fazer algo assim:

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

Brincar, você pode fazer melhor passando o mesmo object mt19937 ao invés de construir um novo a cada vez, mas esperamos que você entenda a idéia.

Chame o código com dois valores float , o código funciona em qualquer intervalo.

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

Se você estiver usando C ++ e não C, lembre-se de que no relatório técnico 1 (TR1) e no rascunho C ++ 0x eles adicionaram resources para um gerador de números randoms no arquivo de header, acredito que seja idêntico ao Boost. Biblioteca aleatória e definitivamente mais flexível e “moderna” do que a function da biblioteca C, rand.

Esta syntax oferece a capacidade de escolher um gerador (como o mersenne twister mt19937) e depois escolher uma distribuição (normal, bernoulli, binomial etc.).

A syntax é a seguinte (sem vergonha emprestada deste site ):

  #include  #include  ... std::tr1::mt19937 eng; // a core engine class std::tr1::normal_distribution dist; for (int i = 0; i < 10; ++i) std::cout << dist(eng) << std::endl; 

No c++ moderno, você pode usar o header que vem com o c++11 .
Para obter float randoms, você pode usar std::uniform_real_distribution<> .

Você pode usar uma function para gerar os números e, se não quiser que os números sejam iguais o tempo todo, defina o mecanismo e a distribuição como static .
Exemplo:

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

É ideal colocar o float em um container como std::vector :

 int main() { std::vector nums; for (int i{}; i != 5; ++i) // Generate 5 random floats nums.emplace_back(get_random()); for (const auto& i : nums) std::cout < < i << " "; } 

Exemplo de saída:

 0.0518757 0.969106 0.0985112 0.0895674 0.895542 

Em alguns sistemas (o Windows com o VC vem à mente, atualmente), o RAND_MAX é ridiculamente pequeno, i. e. apenas 15 bits. Ao dividir por RAND_MAX você está gerando apenas uma mantissa de 15 bits em vez dos 23 bits possíveis. Isso pode ou não ser um problema para você, mas você está perdendo alguns valores nesse caso.

Ah, só notei que já havia um comentário para esse problema. De qualquer forma, aqui está um código que pode resolver isso para você:

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

Não testado, mas pode funcionar 🙂

drand48(3) é o caminho padrão POSIX. O GLibC também fornece uma versão reentrante, drand48_r(3) .

A function foi declarada obsoleta no SVID 3, mas nenhuma alternativa adequada foi fornecida, de modo que a IEEE Std 1003.1-2013 ainda a inclui e não tem annotations de que irá para qualquer lugar em breve.

No Windows, a maneira padrão é CryptGenRandom () .

Eu não estava satisfeito com nenhuma das respostas até agora, então escrevi uma nova function de flutuação aleatória. Ele faz suposições bit a bit sobre o tipo de dados float. Ainda precisa de uma function rand () com pelo menos 15 bits randoms.

 //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, aka '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)); } 

Na minha opinião, a resposta acima dá um float “random”, mas nenhum deles é verdadeiramente um float random (ou seja, eles perdem uma parte da representação float). Antes de entrar na minha implementação, vamos primeiro dar uma olhada no formato padrão ANSI / IEEE para floats:

| sinal (1 bit) | e (8 bits) | f (23 bits) |

o número representado por esta palavra é (sinal -1 *) * 2 ^ e * 1.f

note que o número ‘e’ é um número tendencioso (com um viés de 127), portanto variando de -127 a 126. A function mais simples (e na verdade mais aleatória) é simplesmente escrever os dados de um int random em um float, portanto

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

note que se você float f = (float)rand(); converterá o inteiro em um float (assim 10 se tornará 10.0).

Então, agora se você quiser limitar o valor máximo, você pode fazer algo como (não tenho certeza se isso funciona)

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

mas se você olhar para a estrutura do flutuador você pode ver que o valor máximo de um float é (aproximadamente) 2 ^ 127, que é muito maior que o valor máximo de um int (2 ^ 32) descartando assim uma parte significante de um float. os números que podem ser representados por um float. Esta é minha implementação final:

 /** * 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; } 

usando esta function randf(0, 8, 0) retornará um número random entre 0.0 e 255.0

Se você sabe que seu formato de ponto flutuante é IEEE 754 (quase todas as CPUs modernas, incluindo Intel e ARM), então você pode construir um número de ponto flutuante random a partir de um inteiro random usando methods bit-wise. Isso só deve ser considerado se você não tiver access ao random ou ao Boost.Random do C ++ 11, ambos muito melhores.

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

Isso dará uma distribuição melhor do que uma usando a divisão.

Para C ++, ele pode gerar números flutuantes reais dentro do intervalo especificado pela variável dist

 #include  //If it doesnt work then use #include  #include  using namespace std; typedef std::tr1::ranlux64_base_01 Myeng; typedef std::tr1::normal_distribution 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 () retorna um int entre 0 e RAND_MAX. Para obter um número random entre 0.0 e 1.0, primeiro lance o retorno int por rand () para um float e divida por RAND_MAX.

O número flutuante válido completamente random é gerado da seguinte maneira: Sinal random, expoente random e mantissa aleatória. Aqui está um exemplo de geração de números randoms de 0..MAXFLOAT com distribuição uniforme:

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

Nota importante: RAND_MAX é por padrão igual a 2 ^ 16 (em sistemas 32bits), de modo que rand () pode gerar no máximo 15 bits randoms. Como o ponto flutuante tem um total de 32 bits, devemos ativar o rand () pelo menos 3 vezes para gerar 32 bits randoms. Eu usei 8 bits de rand () para gerar o Exponent e outras 2 chamadas para rand () para gerar 23 bits de mantissa.

Erro comum a ser evitado: Se você usar (float)rand()/MAX_RAND para obter um ponto flutuante no intervalo [0..1], você ainda obterá números randoms em distribuição uniforme, mas de baixa precisão . Por exemplo, seu gerador random pode gerar 0,00001 e 0,00002, mas não pode gerar 0,000017. Tal random é 256 vezes menos preciso que a representação real do ponto flutuante.

Otimização: Minha function não é otimizada para velocidade. Você pode melhorá-lo substituindo a divisão '%' por operações lógicas bit a bit. Por exemplo, em vez de %256 use &0xFF