Como gerar um número aleatório em C ++?


150

Estou tentando fazer um jogo com dados e preciso ter números aleatórios (para simular os lados do dado. Eu sei como fazê-lo entre 1 e 6). Usando

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

using namespace std;

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

não funciona muito bem, porque quando executo o programa algumas vezes, eis a saída que recebo:

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

Então, eu quero um comando que gere um número aleatório diferente a cada vez, não o mesmo 5 vezes seguidas. Existe um comando que fará isso?


54
Questões de distribuição à parte, lembre-se de que, com números aleatórios, existe a possibilidade de obter o mesmo resultado várias vezes seguidas. Se você tivesse a garantia de não obter o mesmo número duas vezes seguidas, os resultados não seriam realmente aleatórios, seriam?
Cdhowie

5
O que faz você pensar que esses números não são aleatórios? Jogue um dado de verdade e você poderá muito bem obter esse resultado. Se eles garantissem que eram diferentes entre cada lançamento, não seria realmente aleatório.
mattjgalloway

2
Leia também eternallyconfuzzled.com/arts/jsw_art_rand.aspx por que usar o operador módulo não costuma ser uma boa idéia.
Benjamin Bannier

4
Você está entendendo mal muito mais do que alguém pode caber em um comentário ou até mesmo em uma resposta. Você precisa aprender, independentemente, sobre geradores de números pseudo-aleatórios, sobre sementes, sobre a importância de escolher uma semente verdadeiramente aleatória e sobre distribuições uniformes.
Kerrek SB

20
Quando você semeia com o tempo. Isso também significa que, se você executar o programa mais de uma vez por segundo, obterá o mesmo número.
Martin York

Respostas:


79

O problema mais fundamental do seu aplicativo de teste é ligar sranduma vez e depoisrand uma vez e sair.

O ponto principal da srandfunção é inicializar a sequência de números pseudo-aleatórios com uma semente aleatória.

Isso significa que, se você passar o mesmo valor para sranddois aplicativos diferentes (com o mesmo srand/ randimplementação), obterá exatamente a mesma sequência derand() valores lida depois nos dois aplicativos.

No entanto, no exemplo de aplicação, a sequência pseudo-aleatória consiste em apenas um elemento - o primeiro elemento de uma sequência pseudo-aleatória gerada a partir da semente, igual ao tempo atual de second precisão. O que você espera ver na saída então?

Obviamente, quando você executa o aplicativo no mesmo segundo - você usa o mesmo valor inicial -, portanto, seu resultado é o mesmo, é claro (como Martin York já mencionou em um comentário à pergunta).

Na verdade, você deve ligar srand(seed)uma vez e depois ligar rand() várias vezes e analisar essa sequência - deve parecer aleatória.

EDITAR:

Oh, entendi. Aparentemente, a descrição verbal não é suficiente (talvez barreira do idioma ou algo assim ... :)).

ESTÁ BEM. Exemplo de código C antiquado com base nas mesmas srand()/rand()/time()funções usadas na pergunta:

#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;
}

^^^ ISSO sequência de uma única execução do programa é suposto para olhar aleatória.

EDIT2:

Ao usar a biblioteca padrão C ou C ++, é importante entender que, a partir de agora, não existe uma única função ou classe padrão produzindo dados realmente aleatórios definitivamente (garantidos pelo padrão). A única ferramenta padrão que aborda esse problema é o std :: random_device que, infelizmente, ainda não oferece garantias de aleatoriedade real.

Dependendo da natureza do aplicativo, você deve primeiro decidir se realmente precisa de dados verdadeiramente aleatórios (imprevisíveis). Caso notável quando você certamente precisa de uma aleatoriedade verdadeira é a segurança da informação - por exemplo, gerar chaves simétricas, chaves privadas assimétricas, valores de sal, tokens de segurança, etc.

No entanto, os números aleatórios com grau de segurança são um setor separado que vale um artigo separado.

Na maioria dos casos, o gerador de números pseudo-aleatórios é suficiente - por exemplo, para simulações ou jogos científicos. Em alguns casos, é necessária uma sequência pseudo-aleatória consistentemente definida - por exemplo, em jogos, você pode optar por gerar exatamente os mesmos mapas em tempo de execução para evitar o armazenamento de muitos dados.

A pergunta original e a multiplicidade recorrente de perguntas idênticas / similares (e até muitas "respostas" equivocadas para elas) indicam que, antes de mais nada, é importante distinguir números aleatórios de números pseudo-aleatórios E entender o que é a sequência numérica pseudo-aleatória. o primeiro lugar E perceber que os geradores de números pseudo-aleatórios NÃO são usados ​​da mesma maneira que você poderia usar geradores de números aleatórios verdadeiros.

Intuitivamente, quando você solicita um número aleatório - o resultado retornado não deve depender dos valores retornados anteriormente e não deve depender se alguém já solicitou alguma coisa antes e não deve depender em que momento e por qual processo e em qual computador e de qual gerador e em que que galáxia foi solicitada. Afinal, é isso que a palavra "aleatório" significa - sendo imprevisível e independente de qualquer coisa - caso contrário, não é mais aleatória, certo? Com essa intuição, é natural procurar na Web alguns feitiços para lançar esse número aleatório em qualquer contexto possível.

^^^ Esse tipo de expectativa intuitiva É MUITO ERRADA e prejudicial em todos os casos envolvendo Geradores de Números Pseudo-Aleatórios - apesar de ser razoável para números aleatórios verdadeiros.

Embora exista a noção significativa de "número aleatório" - não existe "número pseudo-aleatório". Um gerador de números pseudo-aleatórios produz realmente uma sequência numérica pseudo-aleatória .

Quando os especialistas falam sobre a qualidade do PRNG, eles realmente falam sobre propriedades estatísticas da sequência gerada (e suas sub sequências notáveis). Por exemplo, se você combinar dois PRNGs de alta qualidade usando-os alternadamente - poderá produzir uma sequência resultante ruim - apesar de gerar boas seqüências cada uma separadamente (essas duas boas sequências podem simplesmente se correlacionar umas com as outras e, portanto, combinar mal).

A sequência pseudo-aleatória é de fato sempre determinística (predeterminada por seu algoritmo e parâmetros iniciais), ou seja, não há realmente nada aleatório nela.

Especificamente rand()/ srand(s)par de funções fornece uma sequência numérica pseudo-aleatória não segura por thread (!) Por processo gerada com o algoritmo definido pela implementação. Função rand()produz valores no intervalo[0, RAND_MAX] .

Citação do padrão C11:

A srandfunção usa o argumento como uma semente para que uma nova sequência de números pseudo-aleatórios seja retornada pelas chamadas subsequentes para rand. Se srandfor então chamado com o mesmo valor inicial, a sequência de números pseudo-aleatórios deve ser repetida. Se randfor chamado antes que qualquer chamada srandseja feita, a mesma sequência será gerada como quando srandé chamada pela primeira vez com um valor inicial de 1.

Muitas pessoas razoavelmente esperam que rand()isso produza uma sequência de números semi-independentes uniformemente distribuídos na faixa 0de RAND_MAX. Bem, definitivamente deveria (caso contrário, é inútil), mas infelizmente não apenas o padrão não exige isso - existe até um aviso explícito de que "não há garantias quanto à qualidade da sequência aleatória produzida" . Em alguns casos históricos rand/ srandimplementação foi de muito má qualidade, de fato. Mesmo em implementações modernas, provavelmente é bom o suficiente - mas a confiança é quebrada e não é fácil de recuperar. Além de sua natureza não-thread-safe, torna seu uso seguro em aplicativos multithread complicado e limitado (ainda possível - você pode apenas usá-los em um thread dedicado).

O novo modelo de classe std :: mersenne_twister_engine <> (e sua conveniência, typedefs - std::mt19937/ std::mt19937_64com boa combinação de parâmetros de modelo) fornece um gerador de números pseudo-aleatórios por objeto, definido no padrão C ++ 11. Com os mesmos parâmetros de modelo e os mesmos parâmetros de inicialização, diferentes objetos gerarão exatamente a mesma sequência de saída por objeto em qualquer computador em qualquer aplicativo criado com a biblioteca padrão compatível com C ++ 11. A vantagem dessa classe é sua sequência de saída previsivelmente alta qualidade e consistência total nas implementações.

Também existem mais mecanismos PRNG definidos no padrão C ++ 11 - std :: linear_congruential_engine <> (historicamente usado como srand/randalgoritmo de qualidade razoável em algumas implementações da biblioteca padrão C) e std :: subtract_with_carry_engine <> . Eles também geram sequências de saída por objeto totalmente dependentes de parâmetros e definidas por objeto.

Substituição moderna de exemplo do C ++ 11 do código C obsoleto acima:

#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;
}

A versão do 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;
}

Pedi pergunta semelhante aqui ligação , mas ainda não poderia encontrar qualquer resposta clara ainda. Você pode demonstrar "Na verdade, você deve chamar srand (seed) uma vez e depois chamar rand ()" com códigos, porque eu já fiz o que você disse, mas não está funcionando corretamente.
22419 bashburak

2
@bashburak Parece que você perdeu totalmente o objetivo desta resposta. Por que exatamente você cortou minha cotação? Eu disse na minha resposta literalmente: "Na verdade, você deve chamar srand (seed) uma vez e depois chamar rand () várias vezes e analisar essa sequência - deve parecer aleatória". Você notou que deveria ligar para rand () MUITAS VEZES após uma única chamada srand (...)? Sua pergunta no seu link é uma duplicata exata dessa pergunta com exatamente o mesmo mal-entendido.
Serge Dundich

Essa é uma resposta antiga, mas aparece quando você pesquisa no Google "geração de números aleatórios em C ++". É um péssimo conselho para programadores de C ++, porque recomenda o uso de rand()e srand(). Você pode atualizá-lo?
Yakk - Adam Nevraumont 03/06

@ Yakk-AdamNevraumont Na verdade, não aconselha usar rand()e srand(). De fato, apenas responde à pergunta com a descrição fornecida. É evidente a partir da descrição (que usa rand/ srand) que os conceitos básicos da geração de números pseudo-aleatórios devem ser explicados - como o próprio significado da sequência pseudo-aleatória e sua semente. Estou tentando fazer exatamente isso e usar a mais simples e familiar rand/ srandcombinação. O engraçado é que algumas outras respostas - mesmo com uma classificação muito grande - sofrem dos mesmos mal-entendidos que o autor da pergunta.
Serge Dundich

@ Yakk-AdamNevraumont Aceitei seu conselho e alterei minha resposta com algumas informações sobre as mais recentes adições em C ++. Embora eu considere este tópico um pouco desconcertante - mas sua sugestão e algumas outras respostas indicam que os bons e antigos std::rand/std::srandrecursos da biblioteca C ++ std::random_device<>, como std :: mersenne_twister_engine <> e várias distribuições aleatórias, exigem alguma explicação.
Serge Dundich

214

O uso do módulo pode introduzir viés nos números aleatórios, dependendo do gerador de números aleatórios. Veja esta pergunta para mais informações. Obviamente, é perfeitamente possível obter números repetidos em uma sequência aleatória.

Experimente alguns recursos do C ++ 11 para melhor distribuição:

#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 pergunta / resposta para obter mais informações sobre números aleatórios do C ++ 11. O exposto acima não é a única maneira de fazer isso, mas é uma maneira.


7
A quantidade de viés introduzida pelo uso %6é muito pequena. Talvez seja significativo se você estiver escrevendo um jogo de craps para ser usado em Las Vegas, mas sem importância em quase qualquer outro contexto.
Hot Licks

9
HotLicks: concordado, mas se você estiver usando uma versão do C ++ compatível random_devicee mt19937já existente, não há literalmente nenhuma razão para não usar todos os padrões uniform_int_distributiontambém.
Quuxplusone

4
Todos os programadores devem aconselhar as pessoas a evitar módulos como a praga, porque ela usa divisão e custa centenas de ciclos de clock e pode atrapalhar o tempo do aplicativo e / ou queimar muita bateria.

3
É rng para "range"?
Christoffer

4
@ ChristofferHjärtström: É para r Andom n umber g enerator.
Cornstalks

11

Se você estiver usando boost libs, poderá obter um gerador aleatório desta maneira:

#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;
    }
}

Onde a função current_time_nanoseconds()fornece o tempo atual em nanossegundos que é usado como uma semente.


Aqui está uma classe mais geral para obter números e datas aleatórios em um intervalo:

#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);
    }
};

1
Agora que temos o aleatório como parte do padrão, desencorajaria o uso da versão boost, a menos que você esteja usando um compilador verdadeiramente antigo.
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


Qual é a diferença com o código do autor da pergunta? (Exceto que você não usa %6.) E se você decidiu usar a função std::randC ++ API da randbiblioteca C, por que não usar std::timee std::srandem prol da consistência do estilo C ++?
Serge Dundich

4

Pode ficar cheio Randomer código de classe para gerar números aleatórios a partir daqui!

Se você precisar de números aleatórios em diferentes partes do projeto, poderá criar uma classe separada Randomerpara incluir todas as randomcoisas dentro dele.

Algo parecido:

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_);
    }
};

Essa classe seria útil mais tarde:

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

Você pode verificar este link como um exemplo de como eu uso essa Randomerclasse para gerar seqüências aleatórias. Você também pode usar Randomerse desejar.


Você não gostaria de reutilizar o gerador para todos os seus objetos Randomer? Especialmente porque é relativamente caro criar inicializar e manter seu estado.
Martin York

3

Gere um número aleatório diferente a cada vez, não o mesmo seis vezes seguidas.

Cenário de caso de uso

Comparei o problema da previsibilidade a um saco de seis pedaços de papel, cada um com um valor de 0 a 5 escrito. Um pedaço de papel é retirado da sacola cada vez que um novo valor é necessário. Se a bolsa estiver vazia, os números serão colocados de volta na bolsa.

... a partir disso, eu posso criar um tipo de algoritmo.

Algoritmo

Uma bolsa é geralmente a Collection. Eu escolhi umbool[] (também conhecido como matriz booleana, plano de bits ou mapa de bits) para assumir o papel da bolsa.

A razão pela qual escolhi um bool[]é porque o índice de cada item já é o valor de cada pedaço de papel. Se os papéis exigissem mais alguma coisa escrita neles, eu teria usado umDictionary<string, bool> em seu lugar. O valor booleano é usado para acompanhar se o número já foi desenhado ou não.

Um contador chamado RemainingNumberCounté inicializado com 5a contagem regressiva quando um número aleatório é escolhido. Isso nos impede de contar quantos pedaços de papel restam cada vez que desejamos desenhar um novo número.

Para selecionar o próximo valor aleatório, estou usando um for..looppara escanear o pacote de índices e um contador para contar quando umindex é falsechamado NumberOfMoves.

NumberOfMovesé usado para escolher o próximo número disponível. NumberOfMovesé definido primeiro como um valor aleatório entre 0e 5, porque existem 0..5 etapas disponíveis que podemos fazer através do pacote. Na próxima iteração, NumberOfMovesé definido como um valor aleatório entre0 e 4, porque agora existem 0..4 etapas que podemos fazer através da bolsa. À medida que os números são usados, os números disponíveis são reduzidos, pelo que usamos rand() % (RemainingNumberCount + 1)para calcular o próximo valor para NumberOfMoves.

Quando o NumberOfMoves contador chega a zero, o for..loopseguinte deve:

  1. Defina o valor atual como o mesmo for..loopdo índice.
  2. Defina todos os números na bolsa para false .
  3. Quebre do for..loop.

Código

O código para a solução acima é o seguinte:

(coloque os três blocos a seguir no arquivo .cpp principal, um após o outro)

#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;

    }

};

Uma classe de console

Crio essa classe de console porque facilita o redirecionamento da saída.

Abaixo no código ...

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

... pode ser substituído por ...

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

... e, em seguida, essa Consoleclasse pode ser excluída, se desejado.

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

    }

};

Método principal

Exemplo de uso da seguinte maneira:

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");

}

Saída de exemplo

Quando executei o programa, obtive a seguinte saída:

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 . . .

Declaração de encerramento

Este programa foi escrito usando o Visual Studio 2017 e eu escolhi torná-lo um Visual C++ Windows Console Applicationprojeto usando .Net 4.6.1.

Como não estou fazendo nada de especial aqui, o código também deve funcionar em versões anteriores do Visual Studio.


Se for o VS 2017, você deve usar a versão mais recente da biblioteca padrão: en.cppreference.com/w/cpp/numeric/random . Atualmente, este exemplo usa as funções da biblioteca aleatória C e "Não há garantias quanto à qualidade da sequência aleatória produzida".
Robert Andrzejuk 13/01/19

3

Sempre que você faz uma pesquisa básica na Web random number generationna linguagem de programação C ++, essa pergunta geralmente é a primeira a aparecer! Quero jogar meu chapéu no ringue para esclarecer melhor o conceito de geração de números pseudo-aleatórios em C ++ para futuros codificadores que inevitavelmente pesquisarão essa mesma pergunta na Web!

O básico

A geração de números pseudo-aleatórios envolve o processo de utilização de um algoritmo determinístico que produz uma sequência de números cujas propriedades se assemelham aproximadamente a números aleatórios . Digo aproximadamente se assemelham , porque a verdadeira aleatoriedade é um mistério bastante ilusório em matemática e ciência da computação. Portanto, por que o termo pseudo-aleatório é utilizado para ser mais pedanticamente correto!

Antes que você possa realmente usar um PRNG, ou seja, pseudo-random number generatorvocê deve fornecer ao algoritmo um valor inicial frequentemente chamado também de semente . No entanto, a semente deve ser definida apenas uma vez antes de usar o próprio algoritmo!

/// 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...

Portanto, se você deseja uma boa sequência de números, deve fornecer uma semente ampla ao PRNG!

The Old C Way

A biblioteca padrão compatível com versões anteriores de C que C ++ possui, usa o que é chamado de gerador congruencial linear encontrado no cstdlibarquivo de cabeçalho! Esse PRNG funciona através de uma função descontínua por partes que utiliza aritmética modular, ou seja, um algoritmo rápido que gosta de usar o modulo operator '%'. A seguir, é comum o uso desse PRNG, com relação à pergunta original feita pelo @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;
}

O uso comum do PRNG de C abriga uma série de questões, como:

  1. A interface geral de std::rand()não é muito intuitiva para a geração adequada de números pseudo-aleatórios entre um determinado intervalo, por exemplo, produzindo números entre [1, 6] da maneira que o @Predictability queria.
  2. O uso comum de std::rand()elimina a possibilidade de uma distribuição uniforme de números pseudo-aleatórios, devido ao Princípio do Buraco de Pombo .
  3. O modo comum de std::rand()propagação std::srand( ( unsigned int )std::time( nullptr ) )tecnicamente não é correto, porque time_té considerado um tipo restrito . Portanto, a conversão de time_tpara unsigned int não é garantida!

Para obter informações mais detalhadas sobre os problemas gerais do uso do PRNG de C e sobre como contorná-los, consulte Usando rand () (C / C ++): conselhos para a função rand () da biblioteca padrão C !

A maneira C ++ padrão

Desde que o padrão ISO / IEC 14882: 2011 foi publicado, ou seja, C ++ 11, a randombiblioteca separa a linguagem de programação C ++ há algum tempo. Esta biblioteca vem equipado com vários PRNGs, e diferentes tipos de distribuição , tais como: distribuição uniforme , distribuição normal , a distribuição binomial , etc. O seguinte exemplo de código fonte demonstra um uso muito básico da randombiblioteca, com relação a @ pergunta original de Previsibilidade:

#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;
}

O mecanismo Mersenne Twister de 32 bits , com uma distribuição uniforme de valores inteiros , foi utilizado no exemplo acima. (O nome do mecanismo no código-fonte parece estranho, porque o nome vem do período de 2 ^ 19937-1). O exemplo também usa std::random_devicepara propagar o mecanismo, que obtém seu valor do sistema operacional (se você estiver usando um sistema Linux, std::random_deviceretornará um valor de/dev/urandom ).

Observe que você não precisa usar std::random_devicepara propagar nenhum mecanismo . Você pode usar constantes ou até a chronobiblioteca! Você também não precisa usar a versão de 32 bits do std::mt19937mecanismo, existem outras opções ! Para mais informações sobre os recursos da randombiblioteca, consulte cplusplus.com

Em suma, os programadores de C ++ não devem std::rand()mais usar , não porque é ruim , mas porque o padrão atual fornece melhores alternativas que são mais diretas e confiáveis . Felizmente, muitos de vocês acham isso útil, especialmente aqueles que pesquisaram recentemente na Web generating random numbers in c++!


2

Aqui está uma solução. Crie uma função que retorne o número aleatório e coloque-o fora da função principal para torná-lo global. Espero que isto ajude

#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;
}

2

Este código produz números aleatórios de na m.

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

exemplo:

int main(){
    srand(time(0));
    cout << random(0, 99) << "\n";
}

2
Isso realmente não responde à pergunta.
9298 HolyBlackCat

1
Você não consertou. O ponto principal da pergunta é que, se você executar o programa várias vezes por segundo, ele gerará os mesmos valores aleatórios. Seu código também faz isso.
9298 HolyBlackCat

1
@HolyBlackCat Verifiquei se há várias execuções, está funcionando. Você já adicionou srand(time(0))à função principal antes random(n, m)?
Amir Fo

1
Você deve adicionar srand(time(0))à função principal não no loop for ou dentro da implementação da função.
Amir Fo

1
Copiei seu código literalmente. Você executou várias vezes por segundo ?
9298 HolyBlackCat 07/07

1

aleatoriamente todos os arquivos 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);
}

1
Você não deve criar o gerador várias vezes. Ele mantém um monte de estados para gerar uma sequência de números aleatórios com a distribuição apropriada (para torná-la aleatória).
Martin York

-2

Aqui está um gerador aleatório simples com aprox. probabilidade igual de gerar valores positivos e negativos em torno 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;
   }
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.