Generación aleatoria de números flotantes
¿Cómo genero flotantes aleatorios en C++?
Pensé que podría tomar el rand entero y dividirlo por algo, ¿sería suficiente?
rand()
se puede utilizar para generar números pseudoaleatorios en C++. En combinación con RAND_MAX
un poco de matemáticas, puedes generar números aleatorios en cualquier intervalo arbitrario que elijas. Esto es suficiente para fines de aprendizaje y programas de juguetes. Si necesita números verdaderamente aleatorios con distribución normal, deberá emplear un método más avanzado.
Esto generará un número de 0,0 a 1,0, inclusive.
float r = static_cast <float> (rand()) / static_cast <float> (RAND_MAX);
Esto generará un número desde 0.0 hasta algo arbitrario float
:X
float r2 = static_cast <float> (rand()) / (static_cast <float> (RAND_MAX/X));
Esto generará un número de arbitrario LO
a arbitrario HI
:
float r3 = LO + static_cast <float> (rand()) /( static_cast <float> (RAND_MAX/(HI-LO)));
Tenga en cuenta que la rand()
función a menudo no será suficiente si necesita números verdaderamente aleatorios.
Antes de llamar rand()
, primero debe "sembrar" el generador de números aleatorios llamando a srand()
. Esto debe hacerse una vez durante la ejecución de su programa, no una vez cada vez que llama rand()
. Esto a menudo se hace así:
srand (static_cast <unsigned> (time(0)));
Para poder llamar rand
o srand
debes #include <cstdlib>
.
Para poder llamar time
es necesario #include <ctime>
.
C++ 11 le ofrece muchas opciones nuevas con random
. El artículo canónico sobre este tema sería N3551, Generación de números aleatorios en C++11.
Para ver por qué su uso rand()
puede ser problemático, consulte el material de presentación de rand() considerado dañino de Stephan T. Lavavej durante el evento GoingNative 2013 . Las diapositivas están en los comentarios pero aquí hay un enlace directo .
También cubro boost
el uso, rand
ya que es posible que el código heredado aún requiera su soporte.
El siguiente ejemplo está extraído del sitio cppreference y utiliza el motor std::mersenne_twister_engine y std::uniform_real_distribution que genera números en el [0,10)
intervalo, con otros motores y distribuciones comentados ( véalo en vivo ):
#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';
}
}
La salida será similar a la siguiente:
0 ****
1 ****
2 ****
3 ****
4 *****
5 ****
6 *****
7 ****
8 *****
9 ****
El resultado variará dependiendo de la distribución que elija, por lo que si decidimos usar std::normal_distribution con un valor 2
tanto para la media como para el stddev , por dist(2, 2)
ejemplo, el resultado sería similar a este ( véalo en vivo ):
-6
-5
-4
-3
-2 **
-1 ****
0 *******
1 *********
2 *********
3 *******
4 ****
5 **
6
7
8
9
La siguiente es una versión modificada de parte del código presentado en N3551
( véalo en vivo ):
#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;
}
Los resultados serán similares a:
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 JS 7S 6S
Aumentar
Por supuesto, Boost.Random también es siempre una opción, aquí estoy usando 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()
Si debe usarlo rand()
, podemos ir a las preguntas frecuentes de C para obtener guías sobre ¿Cómo puedo generar números aleatorios de punto flotante? , que básicamente da un ejemplo similar a este para generar un en el intervalo [0,1)
:
#include <stdlib.h>
double randZeroToOne()
{
return rand() / (RAND_MAX + 1.);
}
y para generar un número aleatorio en el rango de [M,N)
:
double randMToN(double M, double N)
{
return M + (rand() / ( RAND_MAX / (N-M) ) ) ;
}
Eche un vistazo a Boost.Random . Podrías hacer algo como esto:
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();
}
Pruebe, podría ser mejor pasar el mismo objeto mt19937 en lugar de construir uno nuevo cada vez, pero con suerte entenderá la idea.
En moderno c++
puedes usar el <random>
encabezado que viene con c++11
.
Para obtener datos aleatorios float
puedes utilizar std::uniform_real_distribution<>
.
Puedes usar una función para generar los números y si no quieres que los números sean los mismos todo el tiempo, configura el motor y la distribución en static
.
Ejemplo:
float get_random()
{
static std::default_random_engine e;
static std::uniform_real_distribution<> dis(0, 1); // range [0, 1)
return dis(e);
}
Es ideal colocar los float
's en un contenedor como 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 << " ";
}
Salida de ejemplo:
0.0518757 0.969106 0.0985112 0.0895674 0.895542
Llame al código con dos float
valores, el código funciona en cualquier rango.
float rand_FloatRange(float a, float b)
{
return ((b - a) * ((float)rand() / RAND_MAX)) + a;
}