¿Cómo genero flotantes aleatorios en C ++?
Pensé que podría tomar el rand entero y dividirlo por algo, ¿sería suficiente?
fuente
¿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 usar para generar números pseudoaleatorios en C ++. En combinación con RAND_MAX
un poco de matemática, puede generar números aleatorios en cualquier intervalo arbitrario que elija. 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 cierto arbitraria float
, X
:
float r2 = static_cast <float> (rand()) / (static_cast <float> (RAND_MAX/X));
Esto generará un número de algún arbitrario LO
a otro 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 "inicializar" el generador de números aleatorios llamando srand()
. Esto debe hacerse una vez durante la ejecución de su programa, no una vez cada vez que llame rand()
. Esto a menudo se hace así:
srand (static_cast <unsigned> (time(0)));
Para llamar rand
o srand
debes hacerlo #include <cstdlib>
.
Para llamar time
, debes hacerlo #include <ctime>
.
rand()
. Esta pregunta, y mi respuesta, se centró específicamente en aprender lo básico y no le preocupaban los altos grados de precisión. Tienes que aprender a caminar antes de poder aprender a correr.
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é usar rand()
puede ser problemático, consulte el material de presentación rand () considerado perjudicial por Stephan T. Lavavej dado durante el evento GoingNative 2013 . Las diapositivas están en los comentarios, pero aquí hay un enlace directo .
También cubro boost
y utilizo, rand
ya que el código heredado aún puede requerir su soporte.
El siguiente ejemplo se extrae del sitio cppreference y utiliza el motor std :: mersenne_twister_engine y el std :: uniform_real_distribution que genera números en el [0,10)
intervalo, con otros motores y distribuciones comentadas a cabo ( ver en directo ):
#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 ****
La salida variará dependiendo de la distribución que elija, por lo que si decidimos ir con std :: normal_distribution con un valor de 2
tanto para media y STDDEV por ejemplo, dist(2, 2)
en lugar de la salida sería similar a esta ( ver en directo ):
-6
-5
-4
-3
-2 **
-1 ****
0 *******
1 *********
2 *********
3 *******
4 ****
5 **
6
7
8
9
La siguiente es una versión modificada de algunos de los códigos presentados en N3551
( ver 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 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 una guía sobre ¿Cómo puedo generar números aleatorios de punto flotante? , que básicamente da un ejemplo similar a este para generar un 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) ) ) ;
}
randMToN
pls? tenga en cuenta que es [M,N]
o agregue de nuevo el + 1.
de lo anterior randZeroToOne
. -> piensa en llamarlo así:randMToN(0.0, 1.0);
(N-M)
. Aquí se encuentra una buena manera de lidiar con este error: stackoverflow.com/questions/33058848/…
Echa 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();
}
Juega un poco, es mejor que pases el mismo objeto mt19937 en lugar de construir uno nuevo cada vez, pero espero que entiendas la idea.
max
, pero puede utilizar una participación abierta min
, puede invertir el intervalo fácilmente: return min + max - gen();
.
En moderno c++
, puede usar el <random>
encabezado que viene con c++11
.
Para obtener aleatorios float
, puedes usarstd::uniform_real_distribution<>
.
Puede usar una función para generar los números y, si no desea que los números sean iguales todo el tiempo, configure el motor y la distribución static
.
Ejemplo:
float get_random()
{
static std::default_random_engine e;
static std::uniform_real_distribution<> dis(0, 1); // rage 0 - 1
return dis(e);
}
Es ideal para colocar el 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
std::uniform_real_distribution<> dis(0, 1); // rage 0 - 1
es técnicamente incorrecto, nunca se generará 1.0, consulte en.cppreference.com/w/cpp/numeric/random/… To create a distribution over the closed interval [a,b], std::nextafter(b, std::numeric_limits<RealType>::max()) may be used as the second parameter.
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;
}
fmaf()
(o la fma()
sobrecarga flotante en C ++) en C99 o C ++ 11, lo que podría preservar más precisión. Como en fmaf((float)rand() / RAND_MAX, b - a, a)
.
Si está utilizando C ++ y no C, recuerde que en el informe técnico 1 (TR1) y en el borrador de C ++ 0x han agregado funciones para un generador de números aleatorios en el archivo de encabezado, creo que es idéntico al Boost. Biblioteca aleatoria y definitivamente más flexible y "moderna" que la función de biblioteca C, rand.
Esta sintaxis ofrece la posibilidad de elegir un generador (como el mersenne twister mersenne mt19937) y luego elegir una distribución (normal, bernoulli, binomial, etc.).
La sintaxis es la siguiente (descaradamente prestado de este sitio ):
#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;
En algunos sistemas (Windows con VC me RAND_MAX
viene a la mente, actualmente), es ridículamente pequeño, i. mi. solo 15 bit. Al dividir porRAND_MAX
solo está generando una mantisa de 15 bits en lugar de los 23 bits posibles. Esto puede o no ser un problema para usted, pero está perdiendo algunos valores en ese caso.
Oh, acabo de notar que ya había un comentario para ese problema. De todos modos, aquí hay un código que podría resolver esto por usted:
float r = (float)((rand() << 15 + rand()) & ((1 << 24) - 1)) / (1 << 24);
No probado, pero podría funcionar :-)
drand48(3)
es la forma estándar POSIX. GLibC también proporciona una versión reentrante,drand48_r(3)
.
La función se declaró obsoleta en SVID 3, pero no se proporcionó una alternativa adecuada, por lo que IEEE Std 1003.1-2013 todavía la incluye y no tiene notas de que vaya a ir a ninguna parte pronto.
En Windows, la forma estándar es CryptGenRandom () .
No estaba satisfecho con ninguna de las respuestas hasta ahora, así que escribí una nueva función flotante aleatoria. Hace suposiciones bit a bit sobre el tipo de datos flotante. Todavía necesita una función rand () con al menos 15 bits aleatorios.
//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));
}
En mi opinión, la respuesta anterior da un flotador 'aleatorio', pero ninguno de ellos es realmente un flotador aleatorio (es decir, pierden una parte de la representación del flotador). Antes de apresurarme en mi implementación, primero echemos un vistazo al formato estándar ANSI / IEEE para flotadores:
| signo (1 bit) | e (8 bits) | f (23 bits) |
el número representado por esta palabra es (-1 * signo) * 2 ^ e * 1.f
tenga en cuenta que el número 'e' es un número sesgado (con un sesgo de 127) que oscila entre -127 y 126. La función más simple (y en realidad más aleatoria) es simplemente escribir los datos de un int aleatorio en un flotante, así
int tmp = rand();
float f = (float)*((float*)&tmp);
tenga en cuenta que si lo hace float f = (float)rand();
, convertirá el entero en un flotante (por lo tanto, 10 se convertirá en 10.0).
Entonces, si desea limitar el valor máximo, puede hacer algo como (no estoy seguro si esto funciona)
int tmp = rand();
float f = *((float*)&tmp);
tmp = (unsigned int)f // note float to int conversion!
tmp %= max_number;
f -= tmp;
pero si observa la estructura del flotador, puede ver que el valor máximo de un flotador es (aprox.) 2 ^ 127, que es mucho mayor que el valor máximo de un int (2 ^ 32), lo que descarta una parte significativa de Los números que pueden ser representados por un flotador. Esta es mi implementación 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;
}
El uso de esta función randf(0, 8, 0)
devolverá un número aleatorio entre 0.0 y 255.0
int e = (rand() % (max_exp - min_exp)) + min_exp_mod;
y la mantisa: int f = (int)(frac_mod * (float)rand() / RAND_MAX);
reemplazando sus respectivas líneas arriba. Tenga en cuenta que el error de mantisa es importante: ¡para los RAND_MAX
más pequeños 1 << 23
solo aleatorizaría los bits significativos más bajos y obtendría ceros para los bits más significativos todo el tiempo!
Si sabe que su formato de coma flotante es IEEE 754 (casi todas las CPU modernas, incluidas Intel y ARM), puede construir un número aleatorio de coma flotante a partir de un entero aleatorio utilizando métodos basados en bits. Esto solo debe considerarse si no tiene acceso a C ++ 11 random
o Boost.Random
ambos son mucho mejores.
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;
}
Esto dará una mejor distribución que una que usa división.
return (float)random23 / (1 << 23)
. (Sí, acabo de probar esto , modificando su función para tomarla random32
como parámetro y ejecutándola para todos los valores desde cero hasta (1 << 23)-1
. Y sí, su método realmente da exactamente los mismos resultados que la división por 1 << 23
.)
Para C ++, puede generar números flotantes reales dentro del rango especificado por la dist
variable
#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 () devuelve un int entre 0 y RAND_MAX. Para obtener un número aleatorio entre 0.0 y 1.0, primero arroje el retorno int por rand () a un flotante, luego divida por RAND_MAX.
#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));
...
No pude publicar dos respuestas, así que aquí está la segunda solución. log2 números aleatorios, sesgo masivo hacia 0.0f pero es realmente un flotador aleatorio de 1.0f a 0.0f.
#include <cstdint>
#include <cstdlib>
#include <ctime>
using namespace std;
float getval () {
union UNION {
uint32_t i;
float f;
} r;
/* 3 because it's 0011, the first bit is the float's sign.
* Clearing the second bit eliminates values > 1.0f.
*/
r.i = (rand () & 0xffff) + ((rand () & 0x3fff) << 16);
return r.f;
}
int main ()
{
srand (time (NULL));
...
random
encabezado agregado en C ++ 11 se ve reforzada por el documento estándar N3924: Desalentador rand () en C ++ 14 . Incluyorand()
en mi respuesta por consideraciones principalmente históricas, pero también me doy cuenta de que existen aplicaciones heredadas.<random>
encabezado