Saltar al contenido

¿Cómo generar un número aleatorio en C ++?

Solución:

El uso de módulo puede introducir sesgos en los números aleatorios, según el generador de números aleatorios. Consulte esta pregunta para obtener más información. Por supuesto, es perfectamente posible obtener números repetidos en una secuencia aleatoria.

Pruebe algunas características de C ++ 11 para una mejor distribución:

#include 
#include 

int main()

    std::random_device dev;
    std::mt19937 rng(dev());
    std::uniform_int_distribution dist6(1,6); // distribution in range [1, 6]

    std::cout << dist6(rng) << std::endl;

Consulte esta pregunta / respuesta para obtener más información sobre los números aleatorios de C ++ 11. Lo anterior no es la única forma de hacer esto, pero es una forma.

El problema más fundamental de su aplicación de prueba es que llama srand una vez y luego llamar rand una vez y salir.

Todo el punto de srand la función es inicializar la secuencia de números pseudoaleatorios con una semilla aleatoria.

Significa que si pasas el mismo valor para srand en dos aplicaciones diferentes (con el mismo srand/rand implementación) entonces obtendrás exactamente la misma secuencia de rand() valores leídos después de eso en ambas aplicaciones.

Sin embargo, en su aplicación de ejemplo, la secuencia pseudoaleatoria consta solo de un elemento: el primer elemento de una secuencia pseudoaleatoria generada a partir de la semilla igual al tiempo actual de 1 sec precisión. Entonces, ¿qué espera ver en la salida?

Obviamente, cuando ejecuta la aplicación en el mismo segundo, usa el mismo valor de semilla, por lo que el resultado es el mismo, por supuesto (como Martin York ya mencionó en un comentario a la pregunta).

En realidad deberías llamar srand(seed) una vez y luego llamar rand()muchas veces y analizar esa secuencia, debería parecer aleatoria.

EDITAR:

Oh ya entiendo. Aparentemente, la descripción verbal no es suficiente (tal vez la barrera del idioma o algo ... :)).

está bien. Ejemplo de código C anticuado basado en el mismo srand()/rand()/time() funciones que se utilizó en la pregunta:

#include 
#include 
#include 

int main(void)

    unsigned long j;
    srand( (unsigned)time(NULL) );

    for( j = 0; j < 100500; ++j )
    
        int n;

        /* skip rand() readings that would make n%6 non-uniformly distributed
          (assuming rand() itself is uniformly distributed from 0 to RAND_MAX) */
        while( ( n = rand() ) > RAND_MAX - (RAND_MAX-5)%6 )
         /* bad value retrieved so get next one */ 

        printf( "%d,t%dn", n, n % 6 + 1 );
    

    return 0;

^^^ ESE Se supone que la secuencia de una sola ejecución del programa parece aleatoria.

Tenga en cuenta que no recomiendo usar rand/srand funciones en producción por las razones que se explican a continuación y absolutamente no recomiendo utilizar la función time como una semilla aleatoria por las razones por las que la OMI ya debería ser bastante obvia. Esos están bien para fines educativos y para ilustrar el punto a veces, pero para cualquier uso serio son en su mayoría inútiles.

EDIT2:

Al usar la biblioteca estándar C o C ++, es importante comprender que a partir de ahora no existe una sola función o clase estándar que produzca datos realmente aleatorios de manera definitiva (garantizado por el estándar). La única herramienta estándar que aborda este problema es std :: random_device que, lamentablemente, todavía no ofrece garantías de aleatoriedad real.

Dependiendo de la naturaleza de la aplicación, primero debe decidir si realmente necesita datos verdaderamente aleatorios (impredecibles). Caso notable cuando ciertamente lo necesitas true aleatoriedad es la seguridad de la información, por ejemplo, generar simétricos keys, asimétrico privado keys, valores de sal, tokens de seguridad, etc.

Sin embargo, los números aleatorios de grado de seguridad son una industria separada que merece un artículo aparte.

En la mayoría de los casos, el generador de números pseudoaleatorios es suficiente, por ejemplo, para simulaciones científicas o juegos. En algunos casos, incluso se requiere una secuencia pseudoaleatoria definida de manera consistente; por ejemplo, en los juegos, puede optar por generar exactamente los mismos mapas en tiempo de ejecución para evitar almacenar muchos datos.

La pregunta original y la multitud recurrente de preguntas idénticas / similares (e incluso muchas "respuestas" equivocadas) indican que, ante todo, es importante distinguir números aleatorios de números pseudoaleatorios Y comprender qué es la secuencia numérica pseudoaleatoria en en primer lugar Y para darse cuenta de que los generadores de números pseudoaleatorios NO se usan de la misma manera que podría usar true generadores de números aleatorios.

Intuitivamente cuando solicita un número aleatorio: el resultado devuelto no debe depender de los valores devueltos previamente y no debe depender de si alguien solicitó algo antes y no debe depender de en qué momento y por qué proceso y en qué computadora y de qué generador y en qué galaxia se solicitó. Esa es la palabra "aleatorio" significa, después de todo, ser impredecible e independiente de cualquier cosa, de lo contrario ya no es aleatorio, ¿verdad? Con esta intuición, es natural buscar en la web algunos hechizos mágicos que lanzar para obtener un número tan aleatorio en cualquier contexto posible.

^^^ ESE tipo de expectativas intuitivas ES MUY INCORRECTO y dañino en todos los casos que involucran generadores de números pseudoaleatorios, a pesar de ser razonable para true números al azar.

Si bien existe la noción significativa de "número aleatorio" (más o menos), no existe tal cosa como "número pseudoaleatorio". Un generador de números pseudoaleatorios en realidad produce números pseudoaleatorios secuencia.

La secuencia pseudoaleatoria es, de hecho, siempre determinista (predeterminado por su algoritmo y parámetros iniciales), es decir, no hay nada aleatorio al respecto.

Cuando los expertos hablan de la calidad de PRNG, en realidad se refieren a las propiedades estadísticas de la secuencia generada (y sus subsecuencias notables). Por ejemplo, si combina dos PRNG de alta calidad utilizando ambos en turnos, puede producir una secuencia resultante incorrecta, a pesar de que generen secuencias buenas cada una por separado (esas dos secuencias buenas pueden simplemente correlacionarse entre sí y, por lo tanto, combinarse mal).

Específicamente rand()/srand(s) Un par de funciones proporcionan una secuencia numérica pseudoaleatoria singular por proceso no segura para subprocesos (!) generada con un algoritmo definido por la implementación. Función rand() produce valores en el rango [0, RAND_MAX].

Cita de la norma C11 (ISO / IEC 9899: 2011):

los srand La función utiliza el argumento como semilla para una nueva secuencia de números pseudoaleatorios que serán devueltos por llamadas posteriores a rand. Si
srand luego se llama con el mismo valor semilla, se repetirá la secuencia de números pseudoaleatorios. Si rand se llama antes de cualquier llamada a srand se han realizado, se generará la misma secuencia que cuando srand se llama primero con un valor inicial de 1.

Mucha gente espera razonablemente que rand() produciría una secuencia de números semiindependientes distribuidos uniformemente en el rango 0 para RAND_MAX. Bueno, ciertamente debería (de lo contrario, es inútil), pero desafortunadamente no solo el estándar no lo requiere, incluso hay una exención de responsabilidad explícita que dice "no hay garantías en cuanto a la calidad de la secuencia aleatoria producida". En algunos casos históricos rand/srand la implementación fue de muy mala calidad. Aunque en las implementaciones modernas es muy probable que sea lo suficientemente bueno, pero la confianza está rota y no es fácil de recuperar. Además, su naturaleza no segura para subprocesos hace que su uso seguro en aplicaciones de subprocesos múltiples sea complicado y limitado (aún es posible, puede usarlos desde un subproceso dedicado).

Nueva plantilla de clase std :: mersenne_twister_engine <> (y su conveniencia typedefs - std::mt19937/std::mt19937_64 con una buena combinación de parámetros de plantilla) proporciona por objeto generador de números pseudoaleatorios definido en el estándar C ++ 11. Con los mismos parámetros de plantilla y los mismos parámetros de inicialización, diferentes objetos generarán exactamente la misma secuencia de salida por objeto en cualquier computadora en cualquier aplicación construida con la biblioteca estándar compatible con C ++ 11. La ventaja de esta clase es su secuencia de salida de alta calidad predecible y su total coherencia en todas las implementaciones.

También hay más motores PRNG definidos en el estándar C ++ 11 - std :: linear_congruential_engine <> (históricamente usado como calidad justa srand/rand algoritmo en algunas implementaciones de bibliotecas estándar de C) y std :: subtract_with_carry_engine <>. También generan secuencias de salida por objeto dependientes de parámetros completamente definidas.

Reemplazo de ejemplo de C ++ 11 de hoy en día para el código C obsoleto anterior:

#include 
#include 
#include 

int main()

    std::random_device rd;
    // seed value is designed specifically to make initialization
    // parameters of std::mt19937 (instance of std::mersenne_twister_engine<>)
    // different across executions of application
    std::mt19937::result_type seed = rd() ^ (
            (std::mt19937::result_type)
            std::chrono::duration_cast(
                std::chrono::system_clock::now().time_since_epoch()
                ).count() +
            (std::mt19937::result_type)
            std::chrono::duration_cast(
                std::chrono::high_resolution_clock::now().time_since_epoch()
                ).count() );

    std::mt19937 gen(seed);

    for( unsigned long j = 0; j < 100500; ++j )
    /* ^^^Yes. Generating single pseudo-random number makes no sense
       even if you use std::mersenne_twister_engine instead of rand()
       and even when your seed quality is much better than time(NULL) */    
    
        std::mt19937::result_type n;
        // reject readings that would make n%6 non-uniformly distributed
        while( ( n = gen() ) > std::mt19937::max() -
                                    ( std::mt19937::max() - 5 )%6 )
         /* bad value retrieved so get next one */ 

        std::cout << n << 't' << n % 6 + 1 << 'n';
    

    return 0;

La versión del código anterior que usa std :: uniform_int_distribution <>

#include 
#include 
#include 

int main()

    std::random_device rd;
    std::mt19937::result_type seed = rd() ^ (
            (std::mt19937::result_type)
            std::chrono::duration_cast(
                std::chrono::system_clock::now().time_since_epoch()
                ).count() +
            (std::mt19937::result_type)
            std::chrono::duration_cast(
                std::chrono::high_resolution_clock::now().time_since_epoch()
                ).count() );

    std::mt19937 gen(seed);
    std::uniform_int_distribution distrib(1, 6);

    for( unsigned long j = 0; j < 100500; ++j )
    
        std::cout << distrib(gen) << ' ';
    

    std::cout << 'n';
    return 0;

Si está utilizando boost libs, puede obtener un generador aleatorio de esta manera:

#include 
#include 

// Used in randomization
#include 
#include 
#include 
#include 

using namespace std;
using namespace boost;

int current_time_nanoseconds()
    struct timespec tm;
    clock_gettime(CLOCK_REALTIME, &tm);
    return tm.tv_nsec;


int main (int argc, char* argv[]) 
    unsigned int dice_rolls = 12;
    random::mt19937 rng(current_time_nanoseconds());
    random::uniform_int_distribution<> six(1,6);

    for(unsigned int i=0; i

Donde la funcion current_time_nanoseconds() da la hora actual en nanosegundos que se utiliza como semilla.


Aquí hay una clase más general para obtener números enteros y fechas aleatorios en un rango:

#include 
#include 
#include 
#include 
#include 
#include "boost/date_time/posix_time/posix_time.hpp"
#include "boost/date_time/gregorian/gregorian.hpp"


using namespace std;
using namespace boost;
using namespace boost::posix_time;
using namespace boost::gregorian;


class Randomizer 
private:
    static const bool debug_mode = false;
    random::mt19937 rng_;

    // The private constructor so that the user can not directly instantiate
    Randomizer() 
        if(debug_mode==true)
            this->rng_ = random::mt19937();
        else
            this->rng_ = random::mt19937(current_time_nanoseconds());
        
    ;

    int current_time_nanoseconds()
        struct timespec tm;
        clock_gettime(CLOCK_REALTIME, &tm);
        return tm.tv_nsec;
    

    // C++ 03
    // ========
    // Dont forget to declare these two. You want to make sure they
    // are unacceptable otherwise you may accidentally get copies of
    // your singleton appearing.
    Randomizer(Randomizer const&);     // Don't Implement
    void operator=(Randomizer const&); // Don't implement

public:
    static Randomizer& get_instance()
        // The only instance of the class is created at the first call get_instance ()
        // and will be destroyed only when the program exits
        static Randomizer instance;
        return instance;
    
    bool method()  return true; ;

    int rand(unsigned int floor, unsigned int ceil)
        random::uniform_int_distribution<> rand_ = random::uniform_int_distribution<> (floor,ceil);
        return (rand_(rng_));
    

    // Is not considering the millisecons
    time_duration rand_time_duration()
        boost::posix_time::time_duration floor(0, 0, 0, 0);
        boost::posix_time::time_duration ceil(23, 59, 59, 0);
        unsigned int rand_seconds = rand(floor.total_seconds(), ceil.total_seconds());
        return seconds(rand_seconds);
    


    date rand_date_from_epoch_to_now()
        date now = second_clock::local_time().date();
        return rand_date_from_epoch_to_ceil(now);
    

    date rand_date_from_epoch_to_ceil(date ceil_date)
        date epoch = ptime(date(1970,1,1)).date();
        return rand_date_in_interval(epoch, ceil_date);
    

    date rand_date_in_interval(date floor_date, date ceil_date)
        return rand_ptime_in_interval(ptime(floor_date), ptime(ceil_date)).date();
    

    ptime rand_ptime_from_epoch_to_now()
        ptime now = second_clock::local_time();
        return rand_ptime_from_epoch_to_ceil(now);
    

    ptime rand_ptime_from_epoch_to_ceil(ptime ceil_date)
        ptime epoch = ptime(date(1970,1,1));
        return rand_ptime_in_interval(epoch, ceil_date);
    

    ptime rand_ptime_in_interval(ptime floor_date, ptime ceil_date)
        time_duration const diff = ceil_date - floor_date;
        long long gap_seconds = diff.total_seconds();
        long long step_seconds = Randomizer::get_instance().rand(0, gap_seconds);
        return floor_date + seconds(step_seconds);
    
;

Aquí tienes las comentarios y puntuaciones

Si para ti ha sido provechoso este artículo, agradeceríamos que lo compartas con otros entusiastas de la programación así contrubuyes a difundir este contenido.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)


Tags : / /

Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *