Generación aleatoria de números flotantes

Resuelto hasen asked hace 15 años • 15 respuestas

¿Cómo genero flotantes aleatorios en C++?

Pensé que podría tomar el rand entero y dividirlo por algo, ¿sería suficiente?

hasen avatar Mar 26 '09 22:03 hasen
Aceptado

rand()se puede utilizar para generar números pseudoaleatorios en C++. En combinación con RAND_MAXun 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 LOa 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 rando sranddebes #include <cstdlib>.

Para poder llamar timees necesario #include <ctime>.

John Dibling avatar Mar 26 '2009 15:03 John Dibling

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 boostel uso, randya 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 2tanto 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) ) ) ;  
}
Shafik Yaghmour avatar Jul 22 '2013 22:07 Shafik Yaghmour

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.

rlbond avatar Mar 26 '2009 15:03 rlbond

En moderno c++puedes usar el <random>encabezado que viene con c++11.
Para obtener datos aleatorios floatpuedes 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
Andreas DM avatar Apr 10 '2016 07:04 Andreas DM

Llame al código con dos floatvalores, el código funciona en cualquier rango.

float rand_FloatRange(float a, float b)
{
    return ((b - a) * ((float)rand() / RAND_MAX)) + a;
}
Ivan Prodanov avatar Mar 26 '2009 15:03 Ivan Prodanov