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

150

Estoy tratando de hacer un juego con dados, y necesito tener números aleatorios (para simular los lados del dado. Sé cómo hacerlo entre 1 y 6). Utilizando

#include <cstdlib> 
#include <ctime> 
#include <iostream>

using namespace std;

int main() 
{ 
    srand((unsigned)time(0)); 
    int i;
    i = (rand()%6)+1; 
    cout << i << "\n"; 
}

no funciona muy bien, porque cuando ejecuto el programa varias veces, aquí está el resultado que obtengo:

6
1
1
1
1
1
2
2
2
2
5
2

Entonces, quiero un comando que genere un número aleatorio diferente cada vez, no el mismo 5 veces seguidas. ¿Hay un comando que haga esto?

Previsibilidad
fuente
55
Dejando a un lado los problemas de distribución, tenga en cuenta que con los números aleatorios viene la posibilidad de obtener el mismo resultado varias veces seguidas. Si se garantizara que no obtendría el mismo número dos veces seguidas, los resultados realmente no serían aleatorios, ¿verdad?
cdhowie
55
¿Qué te hace pensar que esos números no son aleatorios? Lanza un dado de verdad y muy bien podrías obtener ese resultado. Si se garantizara que serían diferentes entre cada lanzamiento, entonces no sería realmente aleatorio, ¿verdad?
mattjgalloway
2
Lea también eternallyconfuzzled.com/arts/jsw_art_rand.aspx por qué el uso del operador de módulo no suele ser una buena idea.
Benjamin Bannier
44
Está malinterpretando mucho más de lo que uno puede caber en un comentario o incluso en una respuesta. Debe aprender, independientemente, sobre generadores de números pseudoaleatorios, sobre semillas, sobre la importancia de elegir una semilla verdaderamente aleatoria y sobre distribuciones uniformes.
Kerrek SB
20
Cuando siembras con el tiempo. Esto también significa que si ejecuta su programa más de una vez por segundo obtendrá el mismo número.
Martin York

Respuestas:

79

El problema más fundamental de su aplicación de prueba es que llama sranduna vez y luego llama randuna vez y sale.

El objetivo de la srandfunción es inicializar la secuencia de números pseudoaleatorios con una semilla aleatoria.

Significa que si pasa el mismo valor a sranddos aplicaciones diferentes (con la misma srand/ randimplementación) , obtendrá 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 inicialización igual al tiempo actual de secondprecisión. ¿Qué esperas ver en la salida entonces?

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

En realidad, debe llamar srand(seed)una vez y luego llamar rand() muchas veces y analizar esa secuencia; debe verse al azar.

EDITAR:

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

OKAY. Ejemplo de código C antiguo basado en las mismas srand()/rand()/time()funciones que se usaron en la pregunta:

#include <stdlib.h>
#include <time.h>
#include <stdio.h>

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%d\n", n, n % 6 + 1 );
    }

    return 0;
}

^^^ QUE secuencia de una sola ejecución del programa se supone que se vea al azar.

EDIT2:

Cuando se usa la biblioteca estándar C o C ++, es importante comprender que, a partir de ahora, no hay una sola función estándar o clase que produzca datos realmente aleatorios definitivamente (garantizado por el estándar). La única herramienta estándar que aborda este problema es std :: random_device que desafortunadamente aún 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). Un caso notable cuando realmente necesita una aleatoriedad verdadera es la seguridad de la información, por ejemplo, generar claves simétricas, claves privadas asimétricas, valores de sal, tokens de seguridad, etc.

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

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 puedes elegir 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 los números aleatorios de los números pseudoaleatorios Y comprender qué es una secuencia de números pseudoaleatoria en el 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 generadores de números aleatorios verdaderos.

Intuitivamente cuando solicita un número aleatorio: el resultado devuelto no debe depender de los valores devueltos anteriormente y no debe depender si alguien solicitó algo antes y no debería depender en qué momento y por qué proceso y en qué computadora y de qué generador y en qué galaxia fue solicitada. Después de todo, eso es lo que significa la palabra "aleatorio" : 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 para obtener ese número 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 razonables para números aleatorios verdaderos.

Si bien existe la noción significativa de "número aleatorio", no existe tal cosa como "número pseudoaleatorio". Un generador de números pseudoaleatorios en realidad produce una secuencia de números pseudoaleatorios .

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

De hecho, la secuencia pseudoaleatoria siempre es determinista (predeterminada por su algoritmo y parámetros iniciales), es decir, en realidad no tiene nada de aleatorio.

Específicamente rand()/ srand(s)par de funciones proporciona una por proceso singular no thread-safe (!) Secuencia de números pseudo-aleatorio generado con el algoritmo definido por la implementación. La función rand()produce valores en rango [0, RAND_MAX].

Cita del estándar C11:

La srandfunción utiliza el argumento como una semilla para una nueva secuencia de números pseudoaleatorios que serán devueltos por llamadas posteriores a rand. Si srandluego se llama con el mismo valor inicial, se repetirá la secuencia de números pseudoaleatorios. Si randse llama antes de que srandse hayan realizado llamadas , se generará la misma secuencia que cuando srandse llama por primera vez con un valor de inicialización de 1.

Mucha gente espera razonablemente que eso rand()produzca una secuencia de números semi-independientes distribuidos uniformemente en el rango 0de RAND_MAX. Bueno, definitivamente debería (de lo contrario es inútil), pero desafortunadamente no solo el estándar no requiere eso, incluso hay un descargo de responsabilidad explícito que establece que "no hay garantías en cuanto a la calidad de la secuencia aleatoria producida" . En algunos casos históricos rand/ srandaplicación era de muy mala calidad por cierto. Aunque en las implementaciones modernas es 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 posible, puede usarlos desde un subproceso dedicado).

La nueva plantilla de clase std :: mersenne_twister_engine <> (y su conveniencia typedefs - std::mt19937/ std::mt19937_64con una buena combinación de parámetros de plantilla) proporciona un generador de números pseudoaleatorio por objeto 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 creada con una biblioteca estándar compatible con C ++ 11. La ventaja de esta clase es su secuencia de salida predecible de alta calidad y su consistencia total en todas las implementaciones.

También hay más motores PRNG definidos en el estándar C ++ 11: std :: linear_congruential_engine <> (utilizado históricamente como srand/randalgoritmo de calidad razonable en algunas implementaciones de biblioteca estándar 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 moderno para el código C obsoleto anterior:

#include <iostream>
#include <chrono>
#include <random>

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::seconds>(
                std::chrono::system_clock::now().time_since_epoch()
                ).count() +
            (std::mt19937::result_type)
            std::chrono::duration_cast<std::chrono::microseconds>(
                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 <iostream>
#include <chrono>
#include <random>

int main()
{
    std::random_device rd;
    std::mt19937::result_type seed = rd() ^ (
            (std::mt19937::result_type)
            std::chrono::duration_cast<std::chrono::seconds>(
                std::chrono::system_clock::now().time_since_epoch()
                ).count() +
            (std::mt19937::result_type)
            std::chrono::duration_cast<std::chrono::microseconds>(
                std::chrono::high_resolution_clock::now().time_since_epoch()
                ).count() );

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

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

    std::cout << '\n';
    return 0;
}
Serge Dundich
fuente
He hecho una pregunta similar en este enlace, pero todavía no he podido encontrar ninguna respuesta clara. ¿Puede por favor demostrar "En realidad, debe llamar a srand (seed) una vez y luego llamar a rand ()" con códigos porque ya hice lo que usted dice pero no funciona correctamente.
bashburak
2
@bashburak Parece que te perdiste totalmente el punto de esta respuesta. ¿Por qué exactamente cortaste mi cita? Dije en mi respuesta literalmente: "En realidad, deberías llamar a srand (seed) una vez y luego llamar a rand () muchas veces y analizar esa secuencia; debería parecer aleatorio". ¿Notó que debería llamar a rand () MUCHAS VECES después de una sola llamada (...)? Su pregunta en su enlace es un duplicado exacto de esta pregunta con exactamente el mismo malentendido.
Serge Dundich
Esta es una respuesta anterior, pero aparece cuando buscas en Google "generación de números aleatorios C ++". Es un mal consejo para los programadores de C ++, porque aconseja que uses rand()y srand(). ¿Puedes actualizarlo?
Yakk - Adam Nevraumont
@ Yakk-AdamNevraumont En realidad no aconseja usar rand()y srand(). De hecho, solo responde la pregunta con la descripción proporcionada. Es evidente por la descripción (que usa rand/ srand) que los conceptos básicos de generación de números pseudoaleatorios deben explicarse, como el significado mismo de la secuencia pseudoaleatoria y su semilla. Estoy tratando de hacer exactamente eso y utilizar el más simple y familiar rand/ srandcombinación. Lo curioso es que algunas otras respuestas, incluso con una calificación muy alta, sufren los mismos malentendidos que el autor de la pregunta.
Serge Dundich
@ Yakk-AdamNevraumont Tomé su consejo y modifiqué mi respuesta con información sobre las nuevas adiciones de C ++. Aunque considero esto un poco fuera de tema - pero su sugerencia, así como algunas otras respuestas indica que tanto los buenos viejos std::rand/std::srandcaracterísticas y nuevos C ++ biblioteca como std::random_device<>, std :: mersenne_twister_engine <> y multitud de distribuciones aleatorias requieren alguna explicación.
Serge Dundich
216

El uso de módulo puede introducir sesgo en los números aleatorios, dependiendo del generador de números aleatorios. Vea esta pregunta para 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 <random>
#include <iostream>

int main()
{
    std::random_device dev;
    std::mt19937 rng(dev());
    std::uniform_int_distribution<std::mt19937::result_type> 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.

Tallos de maiz
fuente
77
La cantidad de sesgo introducido mediante el uso %6es muy pequeña. Quizás sea significativo si estás escribiendo un juego de dados para ser usado en Las Vegas, pero no tiene consecuencias en casi cualquier otro contexto.
Hot Licks
9
HotLicks: de acuerdo, pero si está utilizando una versión de C ++ que es compatible random_devicey mt19937ya no existe, literalmente, ninguna razón para no utilizar todo el estándar uniform_int_distribution.
Quuxplusone
44
Todos los programadores deben aconsejar a las personas que eviten módulos como la peste porque usan la división y eso cuesta cientos de ciclos de reloj y puede estropear el tiempo de su aplicación y / o quemar mucha batería.
3
¿Es rng para "rango"?
Christoffer el
44
@ ChristofferHjärtström: Es para r Andom n umber g TenG.
Cornstalks
11

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

#include <iostream>
#include <string>

// Used in randomization
#include <ctime>
#include <boost/random/mersenne_twister.hpp>
#include <boost/random/uniform_int_distribution.hpp>
#include <boost/random/variate_generator.hpp>

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<dice_rolls; i++){
        cout << six(rng) << endl;
    }
}

Donde la función current_time_nanoseconds()proporciona el tiempo actual en nanosegundos que se utiliza como semilla.


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

#include <iostream>
#include <ctime>
#include <boost/random/mersenne_twister.hpp>
#include <boost/random/uniform_int_distribution.hpp>
#include <boost/random/variate_generator.hpp>
#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);
    }
};
madx
fuente
1
Ahora que tenemos aleatorio como parte del estándar, desalentaría el uso de la versión de refuerzo a menos que esté utilizando un compilador verdaderamente antiguo.
Martin York
9
#include <iostream>
#include <cstdlib>
#include <ctime>

int main() {
    srand(time(NULL));
    int random_number = std::rand(); // rand() return a number between ​0​ and RAND_MAX
    std::cout << random_number;
    return 0;
}

http://en.cppreference.com/w/cpp/numeric/random/rand

Arley
fuente
¿Cuál es la diferencia con el código del autor de la pregunta? (Excepto que no usa %6). Y si decidió usar la std::randAPI de randC ++ de la función de biblioteca C, ¿por qué no usar std::timey std::sranden aras de la coherencia de estilo C ++?
Serge Dundich
4

¡Puede obtener un Randomercódigo de clase completo para generar números aleatorios desde aquí!

Si necesita números aleatorios en diferentes partes del proyecto, puede crear una clase separada Randomerpara encapsular todo lo que contiene random.

Algo como eso:

class Randomer {
    // random seed by default
    std::mt19937 gen_;
    std::uniform_int_distribution<size_t> dist_;

public:
    /*  ... some convenient ctors ... */ 

    Randomer(size_t min, size_t max, unsigned int seed = std::random_device{}())
        : gen_{seed}, dist_{min, max} {
    }

    // if you want predictable numbers
    void SetSeed(unsigned int seed) {
        gen_.seed(seed);
    }

    size_t operator()() {
        return dist_(gen_);
    }
};

Tal clase sería útil más adelante:

int main() {
    Randomer randomer{0, 10};
    std::cout << randomer() << "\n";
}

Puede consultar este enlace como ejemplo de cómo uso dicha Randomerclase para generar cadenas aleatorias. También puede usar Randomersi lo desea.

Gusev Slava
fuente
¿No le gustaría reutilizar el generador para todos sus objetos Randomer? Especialmente porque es relativamente costoso crear, inicializar y mantener su estado.
Martin York
3

Genere un número aleatorio diferente cada vez, no el mismo seis veces seguidas.

Escenario de caso de uso

Comparé el problema de la previsibilidad con una bolsa de seis bits de papel, cada uno con un valor de 0 a 5 escrito en él. Se saca un pedazo de papel de la bolsa cada vez que se requiere un nuevo valor. Si la bolsa está vacía, los números se vuelven a poner en la bolsa.

... a partir de esto, puedo crear una especie de algoritmo.

Algoritmo

Una bolsa suele ser a Collection. Elegí un bool[](también conocido como matriz booleana, plano de bits o mapa de bits) para asumir el papel de la bolsa.

La razón por la que elegí a bool[]es porque el índice de cada elemento ya es el valor de cada hoja de papel. Si los documentos requirieran algo más escrito en ellos, entonces habría usado un Dictionary<string, bool>en su lugar. El valor booleano se usa para realizar un seguimiento de si el número se ha extraído o no.

Se RemainingNumberCountinicializa un contador llamado 5que cuenta atrás a medida que se elige un número aleatorio. Esto nos evita tener que contar cuántos trozos de papel quedan cada vez que deseamos dibujar un nuevo número.

Para seleccionar el siguiente valor aleatorio, estoy usando un for..looppara escanear la bolsa de índices y un contador para contar cuando indexse falsellama a NumberOfMoves.

NumberOfMovesse usa para elegir el siguiente número disponible. NumberOfMovesse establece primero como un valor aleatorio entre 0y 5, porque hay 0..5 pasos disponibles que podemos realizar a través de la bolsa. En la próxima iteración NumberOfMovesse establece un valor aleatorio entre 0y 4, porque ahora hay 0..4 pasos que podemos hacer a través de la bolsa. A medida que se usan los números, los números disponibles se reducen, por lo que en su lugar los usamos rand() % (RemainingNumberCount + 1)para calcular el siguiente valor de NumberOfMoves.

Cuando el NumberOfMovescontador llega a cero, for..loopdebería ser el siguiente:

  1. Configure el valor actual para que sea el mismo que for..loopel índice de.
  2. Establezca todos los números en la bolsa a false.
  3. Romper de la for..loop.

Código

El código para la solución anterior es el siguiente:

(coloque los siguientes tres bloques en el archivo principal .cpp uno tras otro)

#include "stdafx.h"
#include <ctime> 
#include <iostream>
#include <string>

class RandomBag {
public:
    int Value = -1;

    RandomBag() {
        ResetBag();

    }

    void NextValue() {
        int BagOfNumbersLength = sizeof(BagOfNumbers) / sizeof(*BagOfNumbers);

        int NumberOfMoves = rand() % (RemainingNumberCount + 1);

        for (int i = 0; i < BagOfNumbersLength; i++)            
            if (BagOfNumbers[i] == 0) {
                NumberOfMoves--;

                if (NumberOfMoves == -1)
                {
                    Value = i;

                    BagOfNumbers[i] = 1;

                    break;

                }

            }



        if (RemainingNumberCount == 0) {
            RemainingNumberCount = 5;

            ResetBag();

        }
        else            
            RemainingNumberCount--; 

    }

    std::string ToString() {
        return std::to_string(Value);

    }

private:
    bool BagOfNumbers[6]; 

    int RemainingNumberCount;

    int NumberOfMoves;

    void ResetBag() {
        RemainingNumberCount = 5;

        NumberOfMoves = rand() % 6;

        int BagOfNumbersLength = sizeof(BagOfNumbers) / sizeof(*BagOfNumbers);

        for (int i = 0; i < BagOfNumbersLength; i++)            
            BagOfNumbers[i] = 0;

    }

};

Una clase de consola

Creo esta clase de consola porque hace que sea fácil redirigir la salida.

Abajo en el código ...

Console::WriteLine("The next value is " + randomBag.ToString());

...puede ser reemplazado por...

std::cout << "The next value is " + randomBag.ToString() << std::endl; 

... y luego esta Consoleclase se puede eliminar si lo desea.

class Console {
public:
    static void WriteLine(std::string s) {
        std::cout << s << std::endl;

    }

};

Método principal

Ejemplo de uso de la siguiente manera:

int main() {
    srand((unsigned)time(0)); // Initialise random seed based on current time

    RandomBag randomBag;

    Console::WriteLine("First set of six...\n");

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    Console::WriteLine("\nSecond set of six...\n");

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    Console::WriteLine("\nThird set of six...\n");

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    Console::WriteLine("\nProcess complete.\n");

    system("pause");

}

Salida de ejemplo

Cuando ejecuté el programa, obtuve el siguiente resultado:

First set of six...

The next value is 2
The next value is 3
The next value is 4
The next value is 5
The next value is 0
The next value is 1

Second set of six...

The next value is 3
The next value is 4
The next value is 2
The next value is 0
The next value is 1
The next value is 5

Third set of six...

The next value is 4
The next value is 5
The next value is 2
The next value is 0
The next value is 3
The next value is 1

Process complete.

Press any key to continue . . .

Frase de cierre

Este programa fue escrito usando Visual Studio 2017 , y elegí convertirlo en un Visual C++ Windows Console Applicationproyecto usando .Net 4.6.1.

No estoy haciendo nada particularmente especial aquí, por lo que el código también debería funcionar en versiones anteriores de Visual Studio.

WonderWorker
fuente
Si se trata de VS 2017, debería usar la versión más reciente de la biblioteca estándar: en.cppreference.com/w/cpp/numeric/random . Actualmente, este ejemplo utiliza las funciones de biblioteca aleatoria C y "No hay garantías en cuanto a la calidad de la secuencia aleatoria producida".
Robert Andrzejuk el
3

Siempre que realice una búsqueda web básica random number generationen el lenguaje de programación C ++, esta pregunta suele ser la primera en aparecer. ¡Quiero lanzar mi sombrero al ring para poder aclarar mejor el concepto de generación de números pseudoaleatorios en C ++ para futuros codificadores que inevitablemente buscarán esta misma pregunta en la web!

Los basicos

La generación de números pseudoaleatorios implica el proceso de utilizar un algoritmo determinista que produce una secuencia de números cuyas propiedades se parecen aproximadamente a números aleatorios . Digo que se parecen aproximadamente , porque la verdadera aleatoriedad es un misterio bastante difícil de alcanzar en matemáticas y ciencias de la computación. Por lo tanto, ¡por qué el término pseudoaleatorio se utiliza para ser más pedante correcto!

Antes de poder usar un PRNG, es decir, pseudo-random number generatordebe proporcionar al algoritmo un valor inicial que a menudo se denomina semilla . Sin embargo, ¡la semilla solo debe establecerse una vez antes de usar el algoritmo mismo!

/// Proper way!
seed( 1234 ) /// Seed set only once...
for( x in range( 0, 10) ):
  PRNG( seed ) /// Will work as expected

/// Wrong way!
for( x in rang( 0, 10 ) ):
  seed( 1234 ) /// Seed reset for ten iterations!
  PRNG( seed ) /// Output will be the same...

Por lo tanto, si desea una buena secuencia de números, ¡debe proporcionar una semilla amplia al PRNG!

El viejo camino de C

La biblioteca estándar de C compatible con versiones anteriores que tiene C ++, utiliza lo que se llama un generador congruencial lineal que se encuentra en el cstdlibarchivo de encabezado. Este PRNG funciona a través de una función discontinua discontinua que utiliza aritmética modular, es decir, un algoritmo rápido que le gusta usar modulo operator '%'. El siguiente es el uso común de este PRNG, con respecto a la pregunta original hecha por @Predictability:

#include <iostream>
#include <cstdlib>
#include <ctime>

int main( void )
{
  int low_dist  = 1;
  int high_dist = 6;
  std::srand( ( unsigned int )std::time( nullptr ) );
  for( int repetition = 0; repetition < 10; ++repetition )
    std::cout << low_dist + std::rand() % ( high_dist - low_dist ) << std::endl;
  return 0;
}

El uso común de PRNG de C alberga una gran cantidad de problemas como:

  1. La interfaz general de std::rand()no es muy intuitiva para la generación adecuada de números pseudoaleatorios entre un rango dado, por ejemplo, producir números entre [1, 6] de la manera que quería @Predictability.
  2. El uso común de std::rand()elimina la posibilidad de una distribución uniforme de números pseudoaleatorios, debido al Principio de Pigeonhole .
  3. La forma común de ser std::rand()sembrada std::srand( ( unsigned int )std::time( nullptr ) )técnicamente no es correcta, porque time_tse considera un tipo restringido . Por lo tanto, la conversión de time_ta unsigned int no está garantizada.

Para obtener información más detallada sobre los problemas generales del uso de PRNG de C y cómo evitarlos, consulte Uso de rand () (C / C ++): ¡Consejos para la función rand () de la biblioteca estándar de C !

La forma estándar de C ++

Desde que se publicó el estándar ISO / IEC 14882: 2011, es decir, C ++ 11, la randombiblioteca ha estado separada del lenguaje de programación C ++ por un tiempo. Esta biblioteca está equipada con múltiples PRNG, y diferentes tipos de distribución , tales como: distribución uniforme , distribución normal , distribución binomial , etc. El siguiente ejemplo de código fuente demuestra un uso muy básico de la randombiblioteca, en lo que respecta a la pregunta original de @ Previsibilidad:

#include <iostream>
#include <cctype>
#include <random>

using u32    = uint_least32_t; 
using engine = std::mt19937;

int main( void )
{
  std::random_device os_seed;
  const u32 seed = os_seed();

  engine generator( seed );
  std::uniform_int_distribution< u32 > distribute( 1, 6 );

  for( int repetition = 0; repetition < 10; ++repetition )
    std::cout << distribute( generator ) << std::endl;
  return 0;
}

El motor Mersenne Twister de 32 bits , con una distribución uniforme de valores enteros , se utilizó en el ejemplo anterior. (El nombre del motor en el código fuente suena extraño, porque su nombre proviene de su período de 2 ^ 19937-1). El ejemplo también se utiliza std::random_devicepara inicializar el motor, que obtiene su valor del sistema operativo (si está utilizando un sistema Linux, std::random_devicedevuelve un valor de /dev/urandom).

Tenga en cuenta que no tiene que usar std::random_devicepara sembrar ningún motor . ¡Puedes usar constantes o incluso la chronobiblioteca! Tampoco tiene que usar la versión de 32 bits del std::mt19937motor, ¡hay otras opciones ! Para obtener más información sobre las capacidades de la randombiblioteca, consulte cplusplus.com

En general, los programadores de C ++ ya no deberían usarlo std::rand(), no porque sea malo , sino porque el estándar actual ofrece mejores alternativas que son más directas y confiables . Con suerte, muchos de ustedes encuentran esto útil, ¡especialmente aquellos de ustedes que recientemente buscaron en la web generating random numbers in c++!

Peluche cómodo
fuente
2

Aquí hay una solución. Cree una función que devuelva el número aleatorio y colóquelo fuera de la función principal para que sea global. Espero que esto ayude

#include <iostream>
#include <cstdlib>
#include <ctime>
int rollDie();
using std::cout;
int main (){
    srand((unsigned)time(0));
    int die1;
    int die2;
    for (int n=10; n>0; n--){
    die1 = rollDie();
    die2 = rollDie();
    cout << die1 << " + " << die2 << " = " << die1 + die2 << "\n";
}
system("pause");
return 0;
}
int rollDie(){
    return (rand()%6)+1;
}
HDSSNET
fuente
2

Este código produce números aleatorios desde nhasta m.

int random(int from, int to){
    return rand() % (to - from + 1) + from;
}

ejemplo:

int main(){
    srand(time(0));
    cout << random(0, 99) << "\n";
}
Amir Fo
fuente
2
Esto realmente no responde la pregunta.
HolyBlackCat
1
No lo arreglaste. El punto de la pregunta es que si ejecuta el programa varias veces por segundo, genera los mismos valores aleatorios. Tu código hace eso también.
HolyBlackCat
1
@HolyBlackCat Lo he comprobado para varias ejecuciones, está funcionando. ¿Has agregado srand(time(0))a la función principal antes random(n, m)?
Amir Fo
1
Debe agregar srand(time(0))a la función principal no a for loop o dentro de la implementación de la función.
Amir Fo
1
He copiado su código literalmente. ¿Lo ejecutaste varias veces por segundo ?
HolyBlackCat
1

para aleatorio cada archivo RUN

size_t randomGenerator(size_t min, size_t max) {
    std::mt19937 rng;
    rng.seed(std::random_device()());
    //rng.seed(std::chrono::high_resolution_clock::now().time_since_epoch().count());
    std::uniform_int_distribution<std::mt19937::result_type> dist(min, max);

    return dist(rng);
}
Nehigienix
fuente
1
Se supone que no debe crear el generador varias veces. Mantiene un conjunto de estados para que genere una secuencia de números aleatorios que tenga la distribución adecuada (para que parezca aleatorio).
Martin York
-2

Aquí hay un generador aleatorio simple con aprox. igual probabilidad de generar valores positivos y negativos alrededor de 0:

  int getNextRandom(const size_t lim) 
  {
        int nextRand = rand() % lim;
        int nextSign = rand() % lim;
        if (nextSign < lim / 2)
            return -nextRand;
        return nextRand;
  }


   int main()
   {
        srand(time(NULL));
        int r = getNextRandom(100);
        cout << r << endl;
        return 0;
   }
Andrushenko Alexander
fuente