Skip to content

Tudo para WordPress, desenvolvimento web — e não só

🎲 Como gerar um número inteiro aleatório num intervalo com JavaScript

🎲 Como gerar um número inteiro aleatório num intervalo com JavaScript

Números aleatórios são necessários em todo o lado. IDs de encomendas em lojas online, códigos de reposição de palavra-passe, lançamentos de dados em jogos de browser, seleção do vencedor de um sorteio. E, quase sempre, os programadores escrevem a mesma linha: Math.random().

Mas entre «obter um decimal aleatório de 0 a 1» e «obter um inteiro aleatório de 5 a 20» existe um abismo de três operações onde os principiantes cometem erros regularmente: Math.floor e Math.ceil, somar um ou não somar, confusão que leva a um overflow do intervalo ou a uma distribuição desigual.

Vamos dissecar a fórmula até ao último parêntesis, escrever um gerador funcional em HTML+CSS+JS puro e ver onde o Math.random() fica aquém.

💡 Visão geral rápida:

  • Dissecar a fórmula Math.floor(Math.random() * (max - min + 1)) + min, compreender cada operação.
  • Aprender porque é que só o Math.floor funciona, e não o Math.ceil ou o Math.round, com exemplos de distorções de intervalo.
  • Copiar um gerador HTML pronto a usar: dois campos, um botão, saída do resultado, JS puro, sem frameworks.
  • Dominar o Crypto.getRandomValues quando o Math.random não é adequado e precisa de uma alternativa criptograficamente segura.

Como o Math.random() funciona

Math.random() é um método estático do objeto integrado Math. Não recebe argumentos e devolve um número pseudoaleatório de vírgula flutuante no intervalo [0, 1), de acordo com a especificação da MDN. O parêntesis reto no zero significa «inclusivo», o parêntesis curvo no um significa «exclusivo». Portanto, pode obter 0.0, um valor por volta de 0.372, ou algo próximo de 0.999, mas nunca obterá exatamente 1.0.

A distribuição é aproximadamente uniforme. A implementação está incorporada no motor (V8, SpiderMonkey, JavaScriptCore) e o programador não pode escolher nem reiniciar o algoritmo específico (normalmente xorshift128+); a semente é definida pelo motor no arranque.

Por si só, o Math.random() é inútil para tarefas práticas. Os jogos precisam de inteiros, as lotarias precisam de inteiros dentro de um intervalo, a criptografia precisa de entropia criptograficamente segura que o Math.random() não tem. É por isso que se envolve sempre uma fórmula de conversão de intervalo à sua volta.

A fórmula: inteiro aleatório no intervalo [min, max]

A fórmula básica que cobre a grande maioria dos casos em desenvolvimento web:

1function randomInt(min, max) {
2 return Math.floor(Math.random() * (max - min + 1)) + min;
3}

Vamos dissecá-la com um exemplo de aprendizagem onde min = 5 e max = 10, com base na documentação do Math.random:

  • Suponha que Math.random() devolveu um valor R no intervalo [0, 1).
  • max - min + 1 = 10 - 5 + 1 = 6, o número de resultados inteiros possíveis: 5, 6, 7, 8, 9, 10.
  • R * 6 escalou o decimal aleatório para a largura do intervalo.
  • Math.floor(R * 6) arredondou para baixo, descartando a parte fracionária. Agora temos um índice de 0 a 5.
  • index + min deslocou por min. Pronto: um inteiro aleatório de 5 a 10 inclusive.

A mesma fórmula numa única linha:

1const roll = Math.floor(Math.random() * (max - min + 1)) + min;

Porquê Math.floor e não Math.ceil ou Math.round

A escolha do método de arredondamento afeta criticamente a distribuição. Vamos demonstrar com um exemplo de aprendizagem usando o intervalo de 1 a 3 (todos os valores, exceto os limites do intervalo, são calculados). A abordagem está descrita na resposta canónica do StackOverflow.

Com Math.floor e a fórmula (max - min + 1):

  • Se Math.random() cair no primeiro terço do intervalo [0, 1): a multiplicação por 3 dá um valor de 0 a quase 1, o Math.floor descarta a parte fracionária, produzindo o índice 0, mais min resulta em 1 (exatamente um terço dos casos).
  • Se Math.random() estiver no terço do meio: a multiplicação por 3 dá de 1 a quase 2, Math.floor → índice 1, mais min2 (outro terço).
  • Se Math.random() estiver no último terço: a multiplicação por 3 dá de 2 a quase 3, Math.floor → índice 2, mais min3 (o último terço).

Distribuição uniforme.

O que acontece com Math.ceil? Math.ceil(Math.random() * 3) dá 1, 2, 3, mas o 1 tem uma probabilidade insignificante (apenas quando Math.random() é exatamente 0, o que quase nunca acontece). Os valores extremos são distorcidos.

Math.round também não é uniforme: os valores extremos têm metade da probabilidade em comparação com os valores do meio. É por isso que a geração de inteiros aleatórios usa exclusivamente Math.floor.

Exemplo completo: gerador em HTML, CSS e JavaScript

Uma página funcional que pode guardar como .html e abrir num browser. Dois campos de entrada, um botão, resultado num fundo verde.

1<!DOCTYPE html>
2<html lang="ru">
3<head>
4 <meta charset="UTF-8">
5 <title>Генератор случайных чисел</title>
6 <style>
7 body {
8 font-family: system-ui, sans-serif;
9 max-width: 400px;
10 margin: 50px auto;
11 padding: 0 16px;
12 }
13 label {
14 display: block;
15 margin-top: 12px;
16 font-weight: 600;
17 }
18 input {
19 width: 100%;
20 padding: 8px;
21 margin-top: 4px;
22 font-size: 16px;
23 box-sizing: border-box;
24 }
25 button {
26 margin-top: 16px;
27 padding: 10px 24px;
28 font-size: 16px;
29 cursor: pointer;
30 }
31 .result {
32 margin-top: 20px;
33 font-size: 24px;
34 font-weight: 700;
35 color: #2e7d32;
36 }
37 </style>
38</head>
39<body>
40
41 <h1>Случайное число в диапазоне</h1>
42
43 <label for="minInput">От:</label>
44 <input type="number" id="minInput" value="1">
45
46 <label for="maxInput">До:</label>
47 <input type="number" id="maxInput" value="100">
48
49 <button id="generateBtn">Сгенерировать</button>
50
51 <div class="result" id="resultDisplay"></div>
52
53 <script>
54 function randomInt(min, max) {
55 return Math.floor(Math.random() * (max - min + 1)) + min;
56 }
57
58 const minInput = document.getElementById('minInput');
59 const maxInput = document.getElementById('maxInput');
60 const generateBtn = document.getElementById('generateBtn');
61 const resultDisplay = document.getElementById('resultDisplay');
62
63 generateBtn.addEventListener('click', () => {
64 const min = parseInt(minInput.value, 10);
65 const max = parseInt(maxInput.value, 10);
66
67 if (isNaN(min) || isNaN(max)) {
68 resultDisplay.textContent = 'Введите оба числа';
69 return;
70 }
71 if (min > max) {
72 resultDisplay.textContent = '«От» не может быть больше «До»';
73 return;
74 }
75
76 const result = randomInt(min, max);
77 resultDisplay.textContent = `Результат: ${result}`;
78 });
79 </script>
80
81</body>
82</html>

O código é intencionalmente escrito em JavaScript puro, sem frameworks ou empacotadores, para que possa ser copiado e executado instantaneamente sem npm install. A função randomInt recebe min e max, devolvendo um inteiro inclusivo em ambas as extremidades. O manipulador do botão lê os valores dos campos, valida-os e exibe o resultado.

Quando o Math.random() não é suficiente: Crypto.getRandomValues()

Math.random() é um gerador pseudoaleatório. É suficiente para jogos, animações, testes A/B e seleção de um elemento aleatório de um array. Mas para tarefas onde a previsibilidade significa vulnerabilidade, não é adequado: tokens de reposição de palavra-passe, chaves de API, nonces criptográficos, geradores de links de uso único.

Imagine: envia a um utilizador um link de reposição de palavra-passe com um token gerado via Math.random(). Conhecendo a semente e o algoritmo (que são padrão para o motor), um atacante pode reconstruir a sequência e adivinhar o token. É exatamente por isso que qualquer código relacionado com segurança deve usar uma fonte de aleatoriedade criptograficamente segura.

Para estes cenários, existe a Web Crypto API: crypto.getRandomValues(). Esta usa a fonte de entropia criptograficamente segura do sistema operativo e preenche o array tipificado fornecido com valores aleatórios.

Gerar um inteiro aleatório criptograficamente seguro num intervalo:

1function secureRandomInt(min, max) {
2 const range = max - min + 1;
3 const maxSafeVal = Math.floor(256 ** 4 / range) * range;
4 const buffer = new Uint32Array(1);
5
6 do {
7 crypto.getRandomValues(buffer);
8 } while (buffer[0] >= maxSafeVal);
9
10 return min + (buffer[0] % range);
11}

Isto usa amostragem por rejeição: a função solicita um valor aleatório de 32 bits e rejeita aqueles que ficam fora do intervalo uniformemente divisível. Isto elimina o viés de módulo, quando alguns valores ocorrem com mais frequência do que outros.

Na prática, o Math.random() é suficiente para a maioria das aplicações web. Mas se estiver a escrever um gerador de links de uso único ou códigos de confirmação, use crypto.getRandomValues().

Vídeo: gerar um número aleatório em JavaScript

Uma demonstração curta do canal dcode, dissecando o Math.random() e a fórmula de intervalo com um exemplo ao vivo na consola do browser:

⁉️🤔 Perguntas frequentes

A fórmula Math.floor(Math.random() * (max - min + 1)) + min inclui o limite superior?

Sim, inclui. max - min + 1 dá o número de resultados inteiros possíveis, e o Math.floor combinado com + min garante que max é alcançável. Por exemplo, com min=1, max=3 obtém 1, 2 ou 3; todos os três valores são possíveis.

Pode usar-se Math.ceil em vez de Math.floor?

Tecnicamente sim, mas a distribuição torna-se não uniforme. Com Math.ceil(Math.random() * range), o valor min só ocorrerá quando Math.random() devolver exatamente 0, um evento extremamente raro. O Math.floor dá a cada inteiro no intervalo uma probabilidade igual, razão pela qual a fórmula padrão o utiliza.

O Math.random() é verdadeiramente um número aleatório?

Não, é um número pseudoaleatório. O motor de JavaScript usa um algoritmo determinístico (normalmente xorshift128+) que produz uma sequência estatisticamente uniforme, mas conhecer a semente permite prever todos os valores subsequentes. Para criptografia, isto é inaceitável; use crypto.getRandomValues().

Como gerar um número de vírgula flutuante aleatório num intervalo?

Remova Math.floor e + 1: Math.random() * (max - min) + min. Isto dá um decimal de min inclusivo a max exclusivo. Se precisar que o extremo superior também seja inclusivo, substitua max - min por max - min + Number.EPSILON, mas, na prática, os intervalos decimais raramente exigem inclusividade do limite superior.

O que é mais rápido: Math.random() ou crypto.getRandomValues()?

Math.random() é ordens de magnitude mais rápido. Nos benchmarks do V8, a diferença atinge uma diferença de cem vezes ou mais, porque o crypto.getRandomValues() faz uma chamada de sistema ao /dev/urandom (Linux) ou equivalente, enquanto o Math.random() é um PRNG puramente interno ao processo. Para gerar milhares de valores num ciclo, use Math.random(); para um único token de segurança, use crypto.getRandomValues().

Número aleatório em 30 segundos: o que reter

O cavalo de batalha é Math.floor(Math.random() * (max - min + 1)) + min. Lembre-se de três regras: sempre Math.floor (não ceil, não round), sempre + 1 no multiplicador (para que max seja alcançável), sempre + min no final (para deslocar o intervalo). Estas três operações cobrem tudo, desde lançamentos de dados até à seleção de um vencedor aleatório.

Para códigos de reposição de palavra-passe e tokens, use crypto.getRandomValues(). Mais lento, mas criptograficamente seguro.

Se quiser apenas experimentar, copie o exemplo HTML acima, guarde-o como um ficheiro e abra-o num browser. Sem dependências, sem empacotadores. Funciona em qualquer lugar onde exista uma <script>.