
🎲 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:
1 function 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 valorRno 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 * 6escalou 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 + mindeslocou pormin. Pronto: um inteiro aleatório de 5 a 10 inclusive.
A mesma fórmula numa única linha:
1 const 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, oMath.floordescarta a parte fracionária, produzindo o índice 0, maisminresulta 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, maismin→ 2 (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, maismin→ 3 (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:
1 function 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 + 1dá o número de resultados inteiros possíveis, e oMath.floorcombinado com+ mingarante quemaxé alcançável. Por exemplo, commin=1, max=3obté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 valorminsó ocorrerá quandoMath.random()devolver exatamente 0, um evento extremamente raro. OMath.floordá 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.floore+ 1:Math.random() * (max - min) + min. Isto dá um decimal demininclusivo amaxexclusivo. Se precisar que o extremo superior também seja inclusivo, substituamax - minpormax - 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 ocrypto.getRandomValues()faz uma chamada de sistema ao/dev/urandom(Linux) ou equivalente, enquanto oMath.random()é um PRNG puramente interno ao processo. Para gerar milhares de valores num ciclo, useMath.random(); para um único token de segurança, usecrypto.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>.



