Por que C ++ rand () parece gerar apenas números da mesma ordem de magnitude?

146

Em um aplicativo pequeno escrito em C / C ++, estou enfrentando um problema com a randfunção e talvez a semente:

Eu quero produzir uma sequência de números aleatórios que são de ordens diferentes, ou seja, com diferentes valores de logaritmo (base 2). Mas parece que todos os números produzidos são da mesma ordem, flutuando apenas entre 2 ^ 25 e 2 ^ 30.

É porque rand()é semeado com o tempo do Unix, que agora é um número relativamente grande? O que estou esquecendo? Estou semeando rand()apenas uma vez no início do main().

Tallaron Mathias
fonte
7
FWIW então, é C ou C ++? Se, por C / C ++, você quer dizer que realmente pode usar C ++, e a menção de C foi aleatória, talvez isso en.cppreference.com/w/cpp/numeric/random/binomial_distribution possa ajudar.
R. Martinho Fernandes
9
Infelizmente você estava apostando no cavalo errado. A semente não deve ser seu problema. Seu problema foi a distribuição esperada incorreta. Como o programador imparcial esperaria rand()retornar números uniformemente distribuídos (a documentação com alta classificação no Google diz isso explicitamente), não acho que essa pergunta seja útil para futuros leitores. É por isso que não votar, mas não o desanime de usar o SO.
Imperador Orionii
12
@ doug65536 "... onde nenhum número é repetido" - isso não é aleatório! Eu poderia financiar minha aposentadoria na mesa de craps se meus dados rand () nunca retornassem o mesmo número duas vezes até que todo número possível fosse retornado.
Chris Gregg
6
@GalacticCowboy Não confunda periodicidade com uma repetição de números individuais. No artigo da Wikipedia que você citou: "um resultado repetido não implica que o fim do período tenha sido atingido, pois seu estado interno pode ser maior que sua produção". Seria muito, muito ruim se um PRNG produzisse um valor e, em seguida, fosse garantido que ele não seria produzido novamente até que todos os valores fossem retornados.
Chris Gregg
12
Doug65536, ninguém está brigando. Eles estão apenas afirmando corretamente que você está errado. Um PRNG poderia muito bem produzir o seguinte se eu quisesse um RAND entre 1 e 10: 2 4 7 2 8 1 5 9 7 3 Isso seria totalmente válido, apesar dos múltiplos 2 e 7. Acho que você está confundindo o PRNG com o recurso de reprodução aleatória no seu iPhone.
Relaxando em Chipre

Respostas:

479

Existem apenas 3% dos números entre 1 e 2 30 que NÃO estão entre 2 25 e 2 30 . Então, isso parece bastante normal :)

Porque 2 25 /2 30 = 2 -5 = 1/32 = 0,03125 = 3,125%

C4stor
fonte
36
Sim, bom ponto! Há 31 vezes mais números entre 2 ^ 25 e 2 ^ 30 do que entre 1 e 2 ^ 25 :) obrigado pela resposta rápida. Preciso repensar o programa então. Pergunta respondida.
Tallaron Mathias
1
@TallaronMathias Considere truncar o número através do >>deslocamento de bits - isso fornecerá números menores. (Ou tomar um módulo com %.)
Sean Allred
13
Eu esperaria que isso seja óbvio para a maioria dos programadores: Qualquer inteiro sem sinal de menos de 2 ^ 25 deve ter seus primeiros 7 bits igual a 0- e se cada bit é aleatória ...
BlueRaja - Danny Pflughoeft
118
@ BlueRaja-DannyPflughoeft - se as probabilidades fossem óbvias, os cassinos estariam fora do negócio.
Brett Hale
26
@BrettHale - eu não acho que os programadores sejam o alvo demográfico de um cassino.
EkoostikMartin
272

O verde mais claro é a região entre 0 e 2 25 ; o verde mais escuro é a região entre 2 25 e 2 30 . Os ticks são potências de 2.

distribuição

Casey Chu
fonte
42

Você precisa ser mais preciso: deseja valores diferentes do logaritmo da base 2, mas que distribuição você deseja para isso? As funções padrão rand () geram uma distribuição uniforme; você precisará transformar essa saída usando a função quantil associada à distribuição que deseja.

Se você nos informar a distribuição, podemos informar a quantilefunção que você precisa.

Bathsheba
fonte
13
+1, distribuição é o termo crucial. Realmente não faz sentido falar sobre números aleatórios quando nada se sabe sobre a distribuição. O uniforme é apenas um caso especial, embora importante. Pode ser um bom lugar para apontar várias distribuições da biblioteca padrão do C ++ 11.
leftaroundabout
18

Se você deseja ordens de magnitude diferentes, por que não tentar pow(2, rand())? Ou talvez escolha o pedido diretamente como rand (), como Harold sugeriu?

aspiring_sarge
fonte
3
boa idéia, mas você deve corrigir sua resposta usando pow em vez de ^ (que é o operador lógico xor, não power, na linguagem C).
kriss
6
Desde rand()pode ir até RAND_MAX, você realmente precisa para dimensionar o número aleatório para que o resultado não transborde ...
Floris
@ Florida: mas se você dimensionar um pequeno intervalo contável em um intervalo muito grande, terá MUITOS buracos, o que provavelmente não é o que o OP está esperando.
André Caron
13

@ C4stor fez um grande ponto. Mas, para um caso mais geral e mais fácil de entender para humanos (base 10): para o intervalo de 1 a 10 ^ n, ~ 90% dos números são de 10 ^ (n-1) a 10 ^ n, portanto, ~ 99% dos números vão de 10 ^ (n-2) a 10 ^ n. Continue adicionando quantas casas decimais desejar.

Matemática engraçada, se você continuar fazendo isso por n, poderá ver que de 1 a 10 ^ n, 99.9999 ...% = 100% dos números são de 10 ^ 0 a 10 ^ n com esse método.

Agora, sobre o código, se você deseja um número aleatório com ordens aleatórias de magnitude, de 0 a 10 ^ n, você pode fazer:

  1. Gere um pequeno número aleatório de 0 a n

  2. Se você souber o intervalo que n possui, gere um grande número aleatório de ordem 10 ^ k, onde k> max {n}.

  3. Corte o número aleatório mais longo para obter os n dígitos desse grande número aleatório.

Francisco Presencia
fonte
46
Você está completamente correto, mas para uma resposta REALMENTE fácil de entender, o OP deve se perguntar por que 90% dos números aleatórios entre 1 e 100 são dois dígitos.
Pergunte sobre Monica
13

A resposta básica (e correta) já foi dada e aceita acima: existem 10 números entre 0 e 9, 90 números entre 10 e 99, 900 entre 100 e 999, etc.

Para uma maneira computacionalmente eficiente de obter uma distribuição com distribuição aproximadamente logarítmica, você deseja deslocar seu número aleatório à direita por um número aleatório:

s = rand() & 31; // a random number between 0 and 31 inclusive, assuming RAND_MAX = 2^32-1
r = rand() >> s; // right shift

Não é perfeito, mas é muito mais rápido que a computação pow(2, rand()*scalefactor). Será "irregular" no sentido de que a distribuição será uniforme para números dentro de um fator 2 (uniforme para 128 a 255, metade da densidade para 256 a 1023, etc.).

Aqui está um histograma da frequência dos números de 0 a 31 (em 1 milhão de amostras):

insira a descrição da imagem aqui

Floris
fonte
nitpick: isso incentiva números muito pequenos mais do que se poderia esperar. A probabilidade de obter um zero é significativamente maior que um 10. #
Mooing Duck
Bem - o objetivo disso é incentivar pequenos números, então estou feliz que esteja funcionando! Fiz uma simulação de Monte Carlo, e isso está me dando uma queda no fator 2 na probabilidade, pois os números dobram - não muito diferente de uma distribuição de log. Resposta atualizada com uma imagem.
Floris
não, quero dizer, com rand()>>(rand()&31);, seria de esperar intuitivamente 1/32 dos números com 32 bits, 1/32 dos números com 31 bits e 1/32 dos números com 30 bits etc. Mas isso é não os resultados que você está obtendo, apenas cerca de 1/64 dos números resultaria em 32 bits, enquanto quase metade deve ser 0. Como minha matemática mental discorda de suas medidas, terei que fazer minhas próprias medidas para descobrir isso.
Mooing Duck
2
Não quero dizer que seu código está errado. Provavelmente é o que eu faria. Apenas merece um aviso de que os resultados não são bem distribuídos como se poderia esperar.
precisa
1
Eu acho que o problema vem de pensar em 0 como um número de 1 bit ... esse é o tipo de enigma que você encontra quando mistura números inteiros e logaritmos. Foi um bom exercício e você me deu algo para pensar. "Teste os limites do seu algoritmo" - ele nunca envelhece.
Floris
5

Há um número exatamente igual de números entre 0 e 2 ^ 29 e 2 ^ 29 e 2 ^ 30.

Outra maneira de analisar o problema: considere a representação binária do número aleatório gerado, a probabilidade de que o bit mais alto seja 1 é igual a 1/2 e, portanto, você recebe a ordem 29 pela metade. O que você deseja é ver um número que esteja abaixo de 2 ^ 25, mas isso significa que os 5 bits mais altos são todos zero, o que acontece com uma baixa probabilidade de 1/32. As chances são de que, mesmo que você o execute por um longo período, você nunca verá a ordem abaixo de 15 (a probabilidade é algo como rolar 6 6 vezes seguidas).

Agora, a parte da sua pergunta sobre a semente. Não, a semente não pode determinar o intervalo a partir do qual os números são gerados, apenas determina o primeiro elemento inicial. Pense em rand () como uma sequência de todos os números possíveis no intervalo (permutação predeterminada). A semente determina onde você começa a desenhar números da sequência. É por isso que se você deseja (pseudo) aleatoriedade, usa o tempo atual para inicializar a sequência: você não se importa que a posição de onde você começa não seja distribuída uniformemente, tudo o que importa é que você nunca comece da mesma posição.

Vadim
fonte
2

usá- pow(2,rand()) lo dará as respostas em ordem da magnitude desejada !!

Shivendra
fonte
2

Se você deseja usar números aleatórios em um serviço online, pode usar o wget para isso, pode querer ver que também pode usar serviços como o random.org para sua geração de números aleatórios, pode capturá-los usando o wget e depois ler os números em o arquivo baixado

wget -q https://www.random.org/integers/?num=100&min=1&max=100&col=5&base=10&format=html&rnd=new -O new.txt

http://programmingconsole.blogspot.in/2013/11/a-better-and-different-way-to-generate.html

Namit Sinha
fonte
Bem-vindo ao SO. abstenha-se de postar links como respostas. Você pode fornecer um esboço detalhado de uma resposta, deixando os detalhes para serem lidos por meio de links.
Shai 23/11