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?
Respostas:
O problema mais fundamental do seu aplicativo de teste é ligar
srand
uma vez e depoisrand
uma vez e sair.O ponto principal da
srand
funçã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
srand
dois aplicativos diferentes (com o mesmosrand
/rand
implementaçã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 ligarrand()
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:^^^ 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.
^^^ 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çãorand()
produz valores no intervalo[0, RAND_MAX]
.Citação do padrão C11:
Muitas pessoas razoavelmente esperam que
rand()
isso produza uma sequência de números semi-independentes uniformemente distribuídos na faixa0
deRAND_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óricosrand
/srand
implementaçã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_64
com 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/rand
algoritmo 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:
A versão do código anterior que usa std :: uniform_int_distribution <>
fonte
rand()
esrand()
. Você pode atualizá-lo?rand()
esrand()
. De fato, apenas responde à pergunta com a descrição fornecida. É evidente a partir da descrição (que usarand
/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 familiarrand
/srand
combinaçã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.std::rand/std::srand
recursos da biblioteca C ++std::random_device<>
, como std :: mersenne_twister_engine <> e várias distribuições aleatórias, exigem alguma explicação.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:
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.
fonte
%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.random_device
emt19937
já existente, não há literalmente nenhuma razão para não usar todos os padrõesuniform_int_distribution
também.Se você estiver usando boost libs, poderá obter um gerador aleatório desta maneira:
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:
fonte
http://en.cppreference.com/w/cpp/numeric/random/rand
fonte
%6
.) E se você decidiu usar a funçãostd::rand
C ++ API darand
biblioteca C, por que não usarstd::time
estd::srand
em prol da consistência do estilo C ++?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
Randomer
para incluir todas asrandom
coisas dentro dele.Algo parecido:
Essa classe seria útil mais tarde:
Você pode verificar este link como um exemplo de como eu uso essa
Randomer
classe para gerar seqüências aleatórias. Você também pode usarRandomer
se desejar.fonte
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 com5
a 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..loop
para escanear o pacote de índices e um contador para contar quando umindex
éfalse
chamadoNumberOfMoves
.NumberOfMoves
é usado para escolher o próximo número disponível.NumberOfMoves
é definido primeiro como um valor aleatório entre0
e5
, 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
e4
, 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 usamosrand() % (RemainingNumberCount + 1)
para calcular o próximo valor paraNumberOfMoves
.Quando o
NumberOfMoves
contador chega a zero, ofor..loop
seguinte deve:for..loop
do índice.false
.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)
Uma classe de console
Crio essa classe de console porque facilita o redirecionamento da saída.
Abaixo no código ...
... pode ser substituído por ...
... e, em seguida, essa
Console
classe pode ser excluída, se desejado.Método principal
Exemplo de uso da seguinte maneira:
Saída de exemplo
Quando executei o programa, obtive a seguinte saída:
Declaração de encerramento
Este programa foi escrito usando o Visual Studio 2017 e eu escolhi torná-lo um
Visual C++ Windows Console Application
projeto 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.
fonte
Sempre que você faz uma pesquisa básica na Web
random number generation
na 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 generator
você 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!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
cstdlib
arquivo 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 omodulo operator '%'
. A seguir, é comum o uso desse PRNG, com relação à pergunta original feita pelo @Predictability:O uso comum do PRNG de C abriga uma série de questões, como:
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.std::rand()
elimina a possibilidade de uma distribuição uniforme de números pseudo-aleatórios, devido ao Princípio do Buraco de Pombo .std::rand()
propagaçãostd::srand( ( unsigned int )std::time( nullptr ) )
tecnicamente não é correto, porquetime_t
é considerado um tipo restrito . Portanto, a conversão detime_t
paraunsigned 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
random
biblioteca 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 darandom
biblioteca, com relação a @ pergunta original de Previsibilidade: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_device
para propagar o mecanismo, que obtém seu valor do sistema operacional (se você estiver usando um sistema Linux,std::random_device
retornará um valor de/dev/urandom
).Observe que você não precisa usar
std::random_device
para propagar nenhum mecanismo . Você pode usar constantes ou até achrono
biblioteca! Você também não precisa usar a versão de 32 bits dostd::mt19937
mecanismo, existem outras opções ! Para mais informações sobre os recursos darandom
biblioteca, consulte cplusplus.comEm 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 Webgenerating random numbers in c++
!fonte
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
fonte
Este código produz números aleatórios de
n
am
.exemplo:
fonte
srand(time(0))
à função principal antesrandom(n, m)
?srand(time(0))
à função principal não no loop for ou dentro da implementação da função.aleatoriamente todos os arquivos RUN
fonte
Aqui está um gerador aleatório simples com aprox. probabilidade igual de gerar valores positivos e negativos em torno de 0:
fonte