Gere números aleatórios usando a biblioteca aleatória C ++ 11

135

Como o título sugere, estou tentando descobrir uma maneira de gerar números aleatórios usando a nova <random>biblioteca C ++ 11 . Eu tentei com este código:

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

O problema com o código que tenho é que sempre que eu o compilo e o executo, ele sempre gera os mesmos números. Então, minha pergunta é que outras funções da biblioteca aleatória podem realizar isso enquanto são verdadeiramente aleatórias?

Para meu caso de uso específico, eu estava tentando obter um valor dentro do intervalo [1, 10]

smac89
fonte
3
Esta questão está chegando perigosamente a "principalmente com base em opiniões". Se você pode se livrar da solicitação de opinião, posso ver essa pergunta sendo muito útil (se ainda não tiver sido solicitada).
John Dibling
4
Sugiro usar um std::mt19937como motor, a menos que você tenha um bom motivo para não fazê-lo. E a distribuição é um intervalo fechado nas duas extremidades.
chris
2
@chris a distribuição não está fechada em ambas as extremidades, verifique este link ou este link
memo1288
1
@ memo1288, obrigado, pensei que o OP estivesse usando um std::uniform_int_distribution, que está fechado nas duas extremidades.
Chris #

Respostas:

191

Stephan T. Lavavej (stl) da Microsoft fez uma palestra no Going Native sobre como usar as novas funções aleatórias do C ++ 11 e por que não usá-las rand(). Nele, ele incluiu um slide que basicamente resolve sua pergunta. Copiei o código desse slide abaixo.

Você pode ver a palestra completa aqui: 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_deviceuma vez para propagar o gerador de número aleatório chamado mt. random_device()é mais lento que mt19937, mas não precisa ser propagado porque solicita dados aleatórios do seu sistema operacional (que serão originários de vários locais, como o RdRand, por exemplo).


Olhando para esta pergunta / resposta , parece que uniform_real_distributionretorna um número no intervalo [a, b), onde você deseja [a, b]. Para fazer isso, nosso uniform_real_distibutiondeve realmente parecer com:

std::uniform_real_distribution<double> dist(1, std::nextafter(10, DBL_MAX));
Bill Lynch
fonte
3
Dado que a questão está pedindo a forma mais geral para gerar números aleatórios que você pode quero apenas usar default_random_engine, de acordo com c ++ cartilha é aquele que a implementação considerou mais útil
aaronman
2
@ Aaronman: Estou indo à palestra de STL, onde ele explicitamente não gosta que isso default_random_engineexista.
Bill Lynch
5
@chris todos sabemos a diferença entre um vetor e um mapa, nem todo mundo sabe a diferença entre mt19937 e ranlux24, se alguém conseguisse se tornar um programador sem saber o que é um vetor e um dicionário, talvez devessem ter um std::default_container, espero que não haja pessoas, considerando-se os programadores que não sabem as diferenças, uma grande quantidade de linguagens de script tem uma estrutura padrão tipo de mapa, que pode ser implementado em toda uma variedade de maneiras que o usuário pode não saber
aaronman
21
A nextafterchamada é um exagero para a maioria dos aplicativos. As chances de um doublepouso aleatório exatamente no terminal são tão minúsculas que não há diferença prática entre incluí-lo e excluí-lo.
Mark Ransom
3
@chris Independente (mas você abriu a porta), sua std::vectoranalogia não funciona aqui porque std::vector é realmente um bom padrão devido ao cache da CPU. Ele até supera std::lista inserção no meio. Isso é verdade mesmo se você entender todos os contêineres e puder tomar uma decisão informada com base na complexidade algorítmica.
void.pointer
24

Minha biblioteca 'aleatória' fornece um invólucro altamente conveniente em torno das classes aleatórias C ++ 11. Você pode fazer quase todas as coisas com um simples método 'get'.

Exemplos:

  1. Número aleatório em um intervalo

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

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

    auto val = Random::get( { 1, 3, 5, 7, 9 } ); // val = 1 or 3 or...
  4. Iterador aleatório do intervalo do iterador ou todo o contêiner

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

E ainda mais coisas! Confira a página do github:

https://github.com/effolkronium/random

Ilya Polishchuk
fonte
4

I vermelho todo o material acima, cerca de 40 outras páginas com C ++ no-lo como este e assistiu ao vídeo do Stephan T. Lavavej "STL" e ainda não tinha certeza de como aleatório números obras na práxis por isso, tomei um domingo inteiro para descobrir do que se trata e como funciona e pode ser usado.

Na minha opinião, a STL está certa sobre "não usar mais o srand" e ele explicou bem no vídeo 2 . Ele também recomenda usar:

a) void random_device_uniform()- para geração criptografada, mas mais lenta (do meu exemplo)

b) os exemplos com mt19937- mais rápido, capacidade de criar sementes, não criptografadas


Peguei todos os livros c ++ 11 reivindicados aos quais tenho acesso e descobri que autores alemães como Breymann (2015) ainda usam um clone de

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

apenas em <random>vez de <time> and <cstdlib>#includings - tenha cuidado para aprender apenas com um livro :).

Significado - que não deve ser usado desde c ++ 11 porque:

Os programas geralmente precisam de uma fonte de números aleatórios. Antes do novo padrão, C e C ++ contavam com uma função simples da biblioteca C chamada rand. Essa função produz números inteiros pseudo-aleatórios que são distribuídos uniformemente no intervalo de 0 a um valor máximo dependente do sistema que é pelo menos 32767. A função rand tem vários problemas: Muitos, se não a maioria, programas precisam de números aleatórios em um intervalo diferente do um produzido pela rand. Alguns aplicativos exigem números aleatórios de ponto flutuante. Alguns programas precisam de números que reflitam uma distribuição não uniforme. Os programadores geralmente introduzem a não aleatoriedade quando tentam transformar o intervalo, o tipo ou a distribuição dos números gerados pelo rand. (citação de Lippmans C ++ primer quinta edição de 2012)


Finalmente encontrei a melhor explicação de 20 livros nos livros mais recentes de Bjarne Stroustrups - e ele deve saber tudo - em "Um tour pelo C ++ 2019", "Princípios e práticas de programação usando C ++ 2016" e "Linguagem de programação C ++ 4ª edição 2014 "e também alguns exemplos em" Lippmans C ++ primer quinta edição 2012 ":

E é realmente simples, porque um gerador de números aleatórios consiste em duas partes: (1) um mecanismo que produz uma sequência de valores aleatórios ou pseudo-aleatórios. (2) uma distribuição que mapeia esses valores em uma distribuição matemática em um intervalo.


Apesar da opinião do cara da Microsofts STL, Bjarne Stroustrups escreve:

Em, a biblioteca padrão fornece mecanismos e distribuições de números aleatórios (§24.7). Por padrão, use o default_random_engine, escolhido para ampla aplicabilidade e baixo custo.

O void die_roll()exemplo é de Bjarne Stroustrups - boa ideia gerando mecanismo e distribuição com using (mais sobre isso aqui) .


Para poder fazer uso prático dos geradores de números aleatórios fornecidos pela biblioteca padrão <random> aqui, alguns códigos executáveis ​​com exemplos diferentes são reduzidos ao mínimo necessário, que esperamos ter tempo e dinheiro seguros para vocês:

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

Eu acho que isso acrescenta tudo e, como eu disse, levei um monte de leitura e tempo para dedicar a esses exemplos - se você tiver mais informações sobre geração de números, fico feliz em saber sobre isso via pm ou na seção de comentários e irá adicioná-lo, se necessário, ou editar esta postagem. Bool

Ivanovic
fonte
0

Aqui está algo que eu acabei de escrever nesse 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;
}

~

Bill Moore
fonte
0

Aqui está um recurso que você pode ler sobre o gerador de números pseudo-aleatórios.

https://en.wikipedia.org/wiki/Pseudorandom_number_generator

Basicamente, os números aleatórios no computador precisam de uma semente (esse número pode ser a hora atual do sistema).

Substituir

std::default_random_engine generator;

De

std::default_random_engine generator(<some seed number>);
Ngọc Khánh Nguyễn
fonte
-3

Você tem duas situações comuns. A primeira é que você deseja números aleatórios e não se preocupa com a qualidade ou a velocidade de execução. Nesse caso, use a seguinte macro

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

que fornece p no intervalo de 0 a 1 - epsilon (a menos que RAND_MAX seja maior que a precisão de um duplo, mas se preocupe com isso quando você o encontrar).

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

Agora fornece um número inteiro aleatório de 0 a N -1.

Se você precisar de outras distribuições, precisará transformar p. Ou, às vezes, é mais fácil chamar uniform () várias vezes.

Se você deseja um comportamento repetitivo, propague com uma constante, caso contrário, proponha uma chamada para time ().

Agora, se você se preocupa com a qualidade ou o desempenho em tempo de execução, reescreva uniform (). Mas, caso contrário, não toque no código. Mantenha sempre uniforme () em 0 a 1 menos epsilon. Agora você pode agrupar a biblioteca de números aleatórios C ++ para criar um uniforme melhor (), mas essa é uma espécie de opção de nível médio. Se você se preocupa com as características do RNG, também vale a pena investir um pouco de tempo para entender como os métodos subjacentes funcionam e fornecer um. Portanto, você tem controle total do código e pode garantir que, com a mesma semente, a sequência sempre será exatamente a mesma, independentemente da plataforma ou de qual versão do C ++ você está vinculando.

Malcolm McLean
fonte
3
Exceto que não é uniforme (0 a N-1). O motivo é fácil, vamos supor N = 100 e RAND_MAX = 32758. Não há como mapear uniformemente 32758 elementos (RAND_MAX) para 100 entradas. A única maneira é definir um limite em 32000 e re-executar rand () se fica fora dos limites
amchacon
1
Se N for 100, seu RNG deverá ser extremamente bom para poder detectar o desvio de uma distribuição plana.
Malcolm McLean