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 mesmo srand
/ 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 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 srand
funçã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
srand
for então chamado com o mesmo valor inicial, a sequência de números pseudo-aleatórios deve ser repetida. Se rand
for chamado antes que qualquer chamada srand
seja 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 0
de 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
/ 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:
#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;
}