Existe `Math.random()`, e depois existe `Math.random()`
Math.random()
retorna um valor do tipoNumber
com sinal positivo, maior ou igual a0
, mas menor que1
, escolhido aleatoriamente ou pseudoaleatoriamente com distribuição aproximadamente uniforme dentro desse intervalo, usando um algoritmo ou estratégia dependente da implementação. Esta função não aceita argumentos.
Math.random()
é a fonte de aleatoriedade mais conhecida e usada frequentemente em JavaScript. No V8 e na maioria dos outros motores JavaScript, é implementado usando um gerador de números pseudoaleatórios (PRNG). Como todos os PRNGs, o número aleatório é derivado de um estado interno, que é alterado por um algoritmo fixo para cada novo número aleatório. Portanto, para um estado inicial específico, a sequência de números aleatórios é determinística. Como o tamanho de bits n do estado interno é limitado, os números que um PRNG gera eventualmente se repetirão. O limite superior para o comprimento do período desse ciclo de permutação é 2n.
Existem muitos algoritmos PRNG diferentes; entre os mais conhecidos estão Mersenne-Twister e LCG. Cada um tem suas características particulares, vantagens e desvantagens. Idealmente, ele usaria o mínimo de memória possível para o estado inicial, seria rápido de executar, teria um grande comprimento de período e ofereceria uma distribuição aleatória de alta qualidade. Enquanto o uso de memória, desempenho e comprimento de período podem ser facilmente medidos ou calculados, a qualidade é mais difícil de determinar. Existe muita matemática por trás dos testes estatísticos para verificar a qualidade dos números aleatórios. O conjunto de testes padrão PRNG, TestU01, implementa muitos desses testes.
Até final de 2015 (até a versão 4.9.40), a escolha de PRNG do V8 era MWC1616 (multiplicar com transporte, combinando duas partes de 16 bits). Ele usa 64 bits de estado interno e se parece aproximadamente com isso:
uint32_t state0 = 1;
uint32_t state1 = 2;
uint32_t mwc1616() {
state0 = 18030 * (state0 & 0xFFFF) + (state0 >> 16);
state1 = 30903 * (state1 & 0xFFFF) + (state1 >> 16);
return state0 << 16 + (state1 & 0xFFFF);
}
O valor de 32 bits é então transformado em um número de ponto flutuante entre 0 e 1, de acordo com a especificação.
MWC1616 usa pouca memória e é bastante rápido para computar, mas infelizmente oferece qualidade inferior:
- O número de valores aleatórios que pode gerar está limitado a 232, em oposição aos 252 números entre 0 e 1 que a ponto flutuante de precisão dupla pode representar.
- A metade superior mais significativa do resultado depende quase totalmente do valor de state0. O comprimento do período seria no máximo 232, mas em vez de poucos ciclos de permutação grandes, existem muitos ciclos curtos. Com um estado inicial mal escolhido, o comprimento do ciclo pode ser inferior a 40 milhões.
- Ele falha em muitos testes estatísticos no conjunto de testes TestU01.
Isso foi apontado para nós, e após entender o problema e realizar algumas pesquisas, decidimos reimplementar o Math.random
com base em um algoritmo chamado xorshift128+. Ele usa 128 bits de estado interno, tem um comprimento de período de 2128 - 1 e passa em todos os testes do conjunto TestU01.
A implementação foi integrada no V8 v4.9.41.0 dentro de alguns dias após tomarmos conhecimento do problema. Tornou-se disponível com o Chrome 49. Tanto Firefox quanto Safari também mudaram para xorshift128+.
No V8 v7.1, a implementação foi ajustada novamente CL, confiando apenas em state0. Por favor, encontre mais detalhes sobre a implementação no código fonte.
No entanto, não se engane: embora o xorshift128+ seja uma grande melhoria em relação ao MWC1616, ele ainda não é criptograficamente seguro. Para casos de uso como hashing, geração de assinaturas e criptografia/descriptografia, PRNGs comuns são inadequados. A API de Criptografia da Web introduz window.crypto.getRandomValues
, um método que retorna valores aleatórios criptograficamente seguros, com um custo de desempenho.
Por favor, lembre-se de que, se você encontrar áreas de melhoria no V8 e no Chrome, mesmo aquelas que — como esta — não afetam diretamente a conformidade com as especificações, estabilidade ou segurança, registre um problema em nosso rastreador de bugs.