Genere números aleatorios usando la biblioteca aleatoria C ++ 11


135

Como sugiere el título, estoy tratando de encontrar una manera de generar números aleatorios usando la nueva <random>biblioteca C ++ 11 . Lo he intentado con este código:

std::default_random_engine generator;
std::uniform_real_distribution<double> uniform_distance(1, 10.001);

El problema con el código que tengo es que cada vez que lo compilo y lo ejecuto, siempre genera los mismos números. Entonces, mi pregunta es ¿qué otras funciones en la biblioteca aleatoria pueden lograr esto mientras son verdaderamente aleatorios?

Para mi caso de uso particular, estaba tratando de obtener un valor dentro del rango [1, 10]


3
Esta pregunta raya peligrosamente en "principalmente basada en la opinión". Si puede deshacerse de la solicitud de opinión, puedo ver que esta pregunta es muy útil (si aún no se ha formulado).
John Dibling

44
Sugiero usar a std::mt19937como motor a menos que tenga una buena razón para no hacerlo. Y la distribución es un intervalo cerrado en ambos extremos.
Chris


2
@chris la distribución no está cerrada en ambos extremos, consulte este enlace o este enlace
memo1288

1
@ memo1288, gracias, pensé que el OP estaba usando un std::uniform_int_distribution, que está cerrado en ambos extremos.
Chris

Respuestas:


191

Stephan T. Lavavej (stl) de Microsoft hizo una charla en Going Native sobre cómo usar las nuevas funciones aleatorias de C ++ 11 y por qué no usarlas rand(). En él, incluyó una diapositiva que básicamente resuelve su pregunta. Copié el código de esa diapositiva a continuación.

Puedes ver su charla completa aquí: http://channel9.msdn.com/Events/GoingNative/2013/rand-Considered-Harmful

#include <random>
#include <iostream>

int main() {
    std::random_device rd;
    std::mt19937 mt(rd());
    std::uniform_real_distribution<double> dist(1.0, 10.0);

    for (int i=0; i<16; ++i)
        std::cout << dist(mt) << "\n";
}

Usamos random_deviceuna vez para sembrar el generador de números aleatorios llamado mt. random_device()es más lento que mt19937, pero no necesita ser sembrado porque solicita datos aleatorios de su sistema operativo (que procederá de varias ubicaciones, como RdRand, por ejemplo).


Al mirar esta pregunta / respuesta , parece que uniform_real_distributiondevuelve un número en el rango [a, b), donde lo desee [a, b]. Para hacer eso, en uniform_real_distibutionrealidad debería verse así:

std::uniform_real_distribution<double> dist(1, std::nextafter(10, DBL_MAX));

3
Dado que la pregunta es la forma más general de generar números aleatorios que tal vez quiera usar default_random_engine, de acuerdo con el manual de c ++, es la que la implementación ha considerado más útil
aaronman

2
@aaronman: Voy por la charla de STL, donde explícitamente no le gusta que default_random_engineexista.
Bill Lynch

55
@chris todos sabemos la diferencia entre un vector y un mapa, no todos saben la diferencia entre mt19937 y ranlux24, si alguien logra convertirse en un programador sin saber qué es un vector y un diccionario, tal vez deberían tener un std::default_container, con suerte no hay las personas que se consideran programadores que no conocen las diferencias, muchos lenguajes de secuencias de comandos tienen una estructura de tipo de mapa predeterminada, que podría implementarse en una variedad de formas que el usuario puede no conocer
aaronman

21
La nextafterllamada es exagerada para la mayoría de las aplicaciones. Las posibilidades de un doubleaterrizaje aleatorio exactamente en el punto final son tan minúsculas que no hay una diferencia práctica entre incluirlo y excluirlo.
Mark Ransom

3
@chris Sin relación (pero abrió la puerta), su std::vectoranalogía no funciona aquí porque en realidad std::vector es un buen valor predeterminado debido al almacenamiento en caché de la CPU. Incluso tiene un rendimiento superior std::listpara la inserción en el medio. Eso es cierto incluso si comprende todos los contenedores y podría tomar una decisión informada basada en la complejidad algorítmica.
void.pointer

24

Mi biblioteca 'aleatoria' proporciona un contenedor altamente conveniente alrededor de las clases aleatorias de C ++ 11. Puedes hacer casi todas las cosas con un simple método 'get'.

Ejemplos:

  1. Número aleatorio en un rango

    auto val = Random::get(-10, 10); // Integer
    auto val = Random::get(10.f, -10.f); // Float point
  2. Booleano aleatorio

    auto val = Random::get<bool>( ) // 50% to generate true
    auto val = Random::get<bool>( 0.7 ) // 70% to generate true
  3. Valor aleatorio de un std :: initilizer_list

    auto val = Random::get( { 1, 3, 5, 7, 9 } ); // val = 1 or 3 or...
  4. Iterador aleatorio del rango de iterador o todos los contenedores

    auto it = Random::get( vec.begin(), vec.end() ); // it = random iterator
    auto it = Random::get( vec ); // return random iterator

¡Y aún más cosas! Echa un vistazo a la página de github:

https://github.com/effolkronium/random


4

Remarqué todas las cosas anteriores, otras 40 páginas con c ++ como esta y vi el video de Stephan T. Lavavej "STL" y todavía no estaba seguro de cómo funcionan los números aleatorios en la praxis, así que me tomé un domingo completo para descubrir de qué se trata y cómo funciona y se puede utilizar.

En mi opinión, STL tiene razón sobre "no usar srand nunca más" y lo explicó bien en el video 2 . También recomienda usar:

a) void random_device_uniform()- para la generación encriptada pero más lenta (de mi ejemplo)

b) los ejemplos con mt19937- más rápido, capacidad de crear semillas, no encriptado


Saqué todos los libros de c ++ 11 reclamados a los que tengo acceso y encontré que los autores alemanes como Breymann (2015) todavía usan un clon de

srand( time( 0 ) );
srand( static_cast<unsigned int>(time(nullptr))); or
srand( static_cast<unsigned int>(time(NULL))); or

solo con en <random>lugar de <time> and <cstdlib>#incluidos, así que tenga cuidado de aprender solo de un libro :).

Significado: eso no debería usarse desde c ++ 11 porque:

Los programas a menudo necesitan una fuente de números aleatorios. Antes del nuevo estándar, tanto C como C ++ dependían de una función de biblioteca C simple llamada rand. Esa función produce enteros pseudoaleatorios que se distribuyen uniformemente en el rango de 0 a un valor máximo dependiente del sistema que es al menos 32767. La función rand tiene varios problemas: muchos, si no la mayoría, los programas necesitan números aleatorios en un rango diferente del uno producido por rand. Algunas aplicaciones requieren números aleatorios de punto flotante. Algunos programas necesitan números que reflejen una distribución no uniforme. Los programadores a menudo introducen la no aleatoriedad cuando intentan transformar el rango, el tipo o la distribución de los números generados por rand. (cita de Lippmans C ++ primer quinta edición 2012)


Finalmente encontré la mejor explicación de 20 libros en los más nuevos de Bjarne Stroustrups, y él debería saber sus cosas, en "Un recorrido por C ++ 2019", "Principios y práctica de programación usando C ++ 2016" y "El lenguaje de programación C ++ 4ta edición 2014 "y también algunos ejemplos en" Lippmans C ++ primer quinta edición 2012 ":

Y es realmente simple porque un generador de números aleatorios consta de dos partes: (1) un motor que produce una secuencia de valores aleatorios o pseudoaleatorios. (2) una distribución que mapea esos valores en una distribución matemática en un rango.


A pesar de la opinión del chico de Microsofts STL, Bjarne Stroustrups escribe:

En, la biblioteca estándar proporciona distribuciones y motores de números aleatorios (§24.7). Por defecto, use default_random_engine, que se elige por su amplia aplicabilidad y bajo costo.

El void die_roll()ejemplo es de Bjarne Stroustrups: buena idea para generar motor y distribución con using (más sobre eso aquí) .


Para poder hacer un uso práctico de los generadores de números aleatorios proporcionados por la biblioteca estándar <random> aquí, algunos códigos ejecutables con diferentes ejemplos se reducen a los menos necesarios que con suerte ahorren tiempo y dinero para ustedes:

    #include <random>     //random engine, random distribution
    #include <iostream>   //cout
    #include <functional> //to use bind

    using namespace std;


    void space() //for visibility reasons if you execute the stuff
    {
       cout << "\n" << endl;
       for (int i = 0; i < 20; ++i)
       cout << "###";
       cout << "\n" << endl;
    }

    void uniform_default()
    {
    // uniformly distributed from 0 to 6 inclusive
        uniform_int_distribution<size_t> u (0, 6);
        default_random_engine e;  // generates unsigned random integers

    for (size_t i = 0; i < 10; ++i)
        // u uses e as a source of numbers
        // each call returns a uniformly distributed value in the specified range
        cout << u(e) << " ";
    }

    void random_device_uniform()
    {
         space();
         cout << "random device & uniform_int_distribution" << endl;

         random_device engn;
         uniform_int_distribution<size_t> dist(1, 6);

         for (int i=0; i<10; ++i)
         cout << dist(engn) << ' ';
    }

    void die_roll()
    {
        space();
        cout << "default_random_engine and Uniform_int_distribution" << endl;

    using my_engine = default_random_engine;
    using my_distribution = uniform_int_distribution<size_t>;

        my_engine rd {};
        my_distribution one_to_six {1, 6};

        auto die = bind(one_to_six,rd); // the default engine    for (int i = 0; i<10; ++i)

        for (int i = 0; i <10; ++i)
        cout << die() << ' ';

    }


    void uniform_default_int()
    {
       space();
       cout << "uniform default int" << endl;

       default_random_engine engn;
       uniform_int_distribution<size_t> dist(1, 6);

        for (int i = 0; i<10; ++i)
        cout << dist(engn) << ' ';
    }

    void mersenne_twister_engine_seed()
    {
        space();
        cout << "mersenne twister engine with seed 1234" << endl;

        //mt19937 dist (1234);  //for 32 bit systems
        mt19937_64 dist (1234); //for 64 bit systems

        for (int i = 0; i<10; ++i)
        cout << dist() << ' ';
    }


    void random_seed_mt19937_2()
    {
        space();
        cout << "mersenne twister split up in two with seed 1234" << endl;

        mt19937 dist(1234);
        mt19937 engn(dist);

        for (int i = 0; i < 10; ++i)
        cout << dist() << ' ';

        cout << endl;

        for (int j = 0; j < 10; ++j)
        cout << engn() << ' ';
    }



    int main()
    {
            uniform_default(); 
            random_device_uniform();
            die_roll();
            random_device_uniform();
            mersenne_twister_engine_seed();
            random_seed_mt19937_2();
        return 0;
    }

Creo que eso lo suma todo y, como dije, me tomó un montón de lectura y tiempo para destilarlo a esos ejemplos; si tiene más información sobre la generación de números, me alegra escuchar eso a través de la tarde o en la sección de comentarios y lo agregará si es necesario o editará esta publicación. Bool


0

Aquí hay algo que acabo de escribir en ese sentido:

#include <random>
#include <chrono>
#include <thread>

using namespace std;

//==============================================================
// RANDOM BACKOFF TIME
//==============================================================
class backoff_time_t {
  public:
    random_device                      rd;
    mt19937                            mt;
    uniform_real_distribution<double>  dist;

    backoff_time_t() : rd{}, mt{rd()}, dist{0.5, 1.5} {}

    double rand() {
      return dist(mt);
    }
};

thread_local backoff_time_t backoff_time;


int main(int argc, char** argv) {
   double x1 = backoff_time.rand();
   double x2 = backoff_time.rand();
   double x3 = backoff_time.rand();
   double x4 = backoff_time.rand();
   return 0;
}

~



-3

Tienes dos situaciones comunes. La primera es que desea números aleatorios y no está demasiado preocupado por la calidad o la velocidad de ejecución. En ese caso, use la siguiente macro

#define uniform() (rand()/(RAND_MAX + 1.0))

eso le da p en el rango de 0 a 1 - epsilon (a menos que RAND_MAX sea más grande que la precisión de un doble, pero preocúpese por eso cuando lo haga).

int x = (int) (uniforme () * N);

Ahora da un entero aleatorio en 0 a N -1.

Si necesita otras distribuciones, debe transformar p. O a veces es más fácil llamar a uniform () varias veces.

Si desea un comportamiento repetible, siembre con una constante, de lo contrario siembre con un llamado al tiempo ().

Ahora, si le preocupa la calidad o el rendimiento del tiempo de ejecución, vuelva a escribir uniform (). Pero de lo contrario no toque el código. Mantenga siempre uniform () en 0 a 1 menos épsilon. Ahora puede ajustar la biblioteca de números aleatorios de C ++ para crear un mejor uniforme (), pero esa es una especie de opción de nivel medio. Si le preocupan las características del RNG, entonces también vale la pena invertir un poco de tiempo para comprender cómo funcionan los métodos subyacentes, luego proporcione uno. Por lo tanto, tiene el control completo del código y puede garantizar que con la misma semilla, la secuencia siempre será exactamente la misma, independientemente de la plataforma o la versión de C ++ a la que se vincule.


3
Excepto que no es uniforme (0 a N-1). La razón es fácil, supongamos que N = 100 y RAND_MAX = 32758. No hay una forma de asignar uniformemente elementos 32758 (RAND_MAX) a 100 entradas. La forma única es establecer un límite en 32000 y volver a ejecutar rand () si se sale de los límites
amchacon el

1
Si N es 100, entonces su RNG debe ser extremadamente bueno para poder detectar la desviación de una distribución plana.
Malcolm McLean
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.