Quão aleatório é Math.random do JavaScript?

116

Por 6 anos, tive uma página de gerador de números aleatórios em meu site. Por muito tempo, foi o primeiro ou o segundo resultado no Google para "gerador de números aleatórios" e tem sido usado para decidir dezenas, senão centenas de concursos e desenhos em fóruns de discussão e blogs (eu sei porque vejo os referenciadores em meu web logs e geralmente dá uma olhada).

Hoje, alguém me enviou um e-mail dizendo que pode não ser tão aleatório quanto eu pensava. Ela tentou gerar números aleatórios muito grandes (por exemplo, entre 1 e 10000000000000000000) e descobriu que eles eram quase sempre o mesmo número de dígitos. Na verdade, envolvi a função em um loop para que pudesse gerar milhares de números e, com certeza, para números muito grandes, a variação era de apenas 2 ordens de magnitude.

Por quê?

Aqui está a versão em loop, para que você possa experimentá-la:

http://andrew.hedges.name/experiments/random/randomness.html

Inclui uma implementação direta tirada da Mozilla Developer Network e algum código de 1997 que eu peguei de uma página da web que não existe mais ("Central Randomizer 1.3" de Paul Houle). Veja a fonte para ver como cada método funciona.

Eu li aqui e em outros lugares sobre Mersenne Twister. Estou interessado em saber por que não haveria maior variação nos resultados da função Math.random integrada do JavaScript . Obrigado!

Andrew Hedges
fonte
"sarnath'd" como em, batido com o soco, ou neste caso, a resposta
maetl
5
Se você está procurando a resposta para a pergunta no título, consulte stackoverflow.com/questions/2344312/…
Andrew B.

Respostas:

182

Dados números entre 1 e 100.

  • 9 tem 1 dígito (1-9)
  • 90 têm 2 dígitos (10-99)
  • 1 tem 3 dígitos (100)

Dados números entre 1 e 1000.

  • 9 tem 1 dígito
  • 90 têm 2 dígitos
  • 900 tem 3 dígitos
  • 1 tem 4 dígitos

e assim por diante.

Portanto, se você selecionar alguns aleatoriamente, a grande maioria dos números selecionados terá o mesmo número de dígitos, porque a grande maioria dos valores possíveis terá o mesmo número de dígitos.

Quentin
fonte
11
Sua ideia de aleatoriedade significando perfeitamente e uniformemente distribuída é intrigante ...
19
@ R.Pate - a geração de números aleatórios não é muito útil, a menos que seja uniformemente distribuída em uma escala longa
annakata
3
Leia de novo. @David está apenas declarando que tipo de números existem entre os limites, não o resultado da seleção de N números aleatórios. Eu admito que a titulação é enganosa.
nikc.org
3
Para que conste, votei a favor das respostas desta e de @jwoolard. Escolhi essa como a resposta aceita porque os exemplos deixam claro por que a distribuição dos números é distorcida para números com mais dígitos.
Andrew Hedges
1
@ andrew-hedges - esta é a resposta mais clara, mas obrigado :)
jwoolard
56

Seus resultados são realmente esperados. Se os números aleatórios forem uniformemente distribuídos em um intervalo de 1 a 10 ^ n, você esperaria que cerca de 9/10 dos números tivessem n dígitos e outros 9/100 tivessem n-1 dígitos.

Jwoolard
fonte
8
Exatamente. A distribuição do número de dígitos provavelmente será distorcida. A distribuição do log do número de dígitos deve ser uniforme, entretanto.
Noldorin
45

Existem diferentes tipos de aleatoriedade. Math.random oferece uma distribuição uniforme de números.

Se você quiser diferentes ordens de magnitude, sugiro usar uma função exponencial para criar o que é chamado de distribuição de lei de potência :

function random_powerlaw(mini, maxi) {
    return Math.ceil(Math.exp(Math.random()*(Math.log(maxi)-Math.log(mini)))*mini)
}

Esta função deve fornecer aproximadamente o mesmo número de números de 1 dígito que números de 2 dígitos e números de 3 dígitos.

Existem também outras distribuições para números aleatórios, como a distribuição normal (também chamada de distribuição Gaussiana).

cristão
fonte
Com esse algoritmo, coloquei minimum = 1o maximum = 10e oe às vezes obtive 11 como resultado. Você provavelmente pretendia usar em Math.floorvez deMath.round
Sam Eaton
1
Por que isso funciona? Ele transforma a distribuição uniforme em distribuição exponencial?
shinzou de
@shinzou eu perguntei em math.stackexchange e obtive uma fórmula ligeiramente diferente como resposta. Mudei o código para refletir a fórmula derivada matematicamente de math.stackexchange.
Cristão
20

Parece perfeitamente aleatório para mim! (Dica: Depende do navegador.)

Pessoalmente, acho que minha implementação seria melhor, embora eu tenha roubado do XKCD , que SEMPRE deve ser reconhecido:

function random() {
  return 4; // Chosen by a fair dice throw. Guaranteed to be random.
}
Arafangion
fonte
19
+1 para mencionar que depende do navegador, -1 para emprestar xkcd sem vincular.
Obrigatório ou não, por ser xkcd, está sendo atribuído. :)
Arafangion
2
OT: Estou surpreso e feliz que "XKCD" foi a resposta a uma pergunta do Desafio Universitário esta semana: D
Matt Sach
2
Bergi: Um link direto não é suficiente?
Arafangion
Acho que querem dizer que a piada não foi citada corretamente ("random = 4;" em vez de "return 4;")
Eren Tantekin
18

O artigo a seguir explica como math.random () nos principais navegadores da Web é (não) seguro: "Rastreamento temporário do usuário nos principais navegadores e vazamento e ataques de informações entre domínios" por Amid Klein (2008) . Não é mais forte do que as funções PRNG integradas de Java ou Windows típicas.

Por outro lado, a implementação do SFMT do período 2 ^ 19937-1 requer 2.496 bytes do estado interno mantido para cada sequência PRNG. Algumas pessoas podem considerar isso um custo imperdoável.

jj1bdx
fonte
1
+1: O artigo mencionado é ótimo, muito além do que tratava a pergunta original.
Roland Illig
6

Se você usar um número como 10000000000000000000, estará ultrapassando a precisão do tipo de dados que o Javascript está usando. Observe que todos os números gerados terminam em "00".

Greg
fonte
1
Esse não é o problema dele neste caso, no entanto.
Joey
3
@Johannes - é um dos problemas dele :)
annakata
A distribuição do IEE754 não é uniforme. Talvez você possa representar de 0 a 999 em incrementos de dois e ter precisão suficiente para isso, de modo que observe uma distribuição uniforme ao longo desse intervalo se escolher um número muitas vezes. 10% terá dois dígitos e 90% três dígitos. Porém, quando você começar a atingir números realmente altos, o incremento excederá 1. Você só poderá passar de um trilhão de bilhões para um trilhão de bilhões de mil e não um trilhão de bilhões e um. Embora para números / escalas pequenos, este efeito será insignificante ou inexistente. O efeito de escala terá muito mais impacto.
jgmjgm
5

Eu tentei o gerador de números pseudoaleatórios JS no Chaos Game .

Meu triângulo Sierpiński diz que é bastante aleatório: Fractal

zie1ony
fonte
2
Você se importaria de compartilhar o código do triângulo aqui e jsfiddle / jsbin para que possamos verificá-lo facilmente na prática para diferentes navegadores?
Fabrício Matté
1
OK, mas me dê alguns dias, porque preciso traduzir o código para o inglês. Agora é polonês-inglês e tenho muito trabalho.
zie1ony
1
@ zie1ony, alguns dias se passaram.
trusktr de
1
usp :( work, work, work Link: kubaplas.vot.pl/green/fractal O primeiro parâmetro é o nr do vértice. O segundo é um ponto de intersecção (de 0 a 1) do segmento de linha. Experimente.
zie1ony
4
Link morto - talvez um repositório Github em vez disso?
Mark K Cowan
3

Bem, se você está gerando números até, digamos, 1e6, com sorte obterá todos os números com probabilidade aproximadamente igual. Isso também significa que você só tem uma chance em dez de obter um número com um dígito a menos. Uma chance em cem de obter dois dígitos a menos, etc. Duvido que você veja muita diferença ao usar outro RNG, porque você tem uma distribuição uniforme entre os números, não seu logaritmo.

Joey
fonte
0

Números não aleatórios uniformemente distribuídos de 1 a N têm a mesma propriedade. Observe que (em certo sentido) é uma questão de precisão. Uma distribuição uniforme em 0-99 (como inteiros) tem 90% dos seus números com dois dígitos. Uma distribuição uniforme em 0-999999 tem 905 de seus números com cinco dígitos.

Qualquer conjunto de números (sob algumas condições não muito restritivas) tem uma densidade. Quando alguém deseja discutir números "aleatórios", a densidade desses números deve ser especificada (conforme observado acima). Uma densidade comum é a densidade uniforme. Existem outras: a densidade exponencial, a densidade normal, etc. Deve-se escolher qual densidade é relevante antes de propor um gerador de números aleatórios. Além disso, os números provenientes de uma densidade podem ser facilmente transformados em outra densidade por meios cariados.

ttw
fonte