Skip to content

Tutto per WordPress, lo sviluppo web — e non solo

🎲 Come generare un numero intero casuale in un intervallo con JavaScript

🎲 Come generare un numero intero casuale in un intervallo con JavaScript

I numeri casuali servono ovunque. ID ordine negli shop online, codici di reset password, tiri di dado nei giochi browser, estrazione del vincitore di un concorso. E quasi sempre gli sviluppatori scrivono la stessa riga: Math.random().

Ma tra "ottenere un decimale casuale da 0 a 1" e "ottenere un intero casuale da 5 a 20" c'è un abisso di tre operazioni in cui i principianti sbagliano regolarmente: Math.floor e Math.ceil, più uno o non più uno, confusione che porta a sforamento dell'intervallo o a distribuzione non uniforme.

Scomponiamo la formula fino all'ultima parentesi, scriviamo un generatore funzionante in HTML+CSS+JS puro e vediamo dove Math.random() non basta.

💡 Panoramica rapida:

  • Scomposizione della formula Math.floor(Math.random() * (max - min + 1)) + min, comprensione di ogni operazione.
  • Perché funziona solo Math.floor, non Math.ceil o Math.round, con esempi di distorsione dell'intervallo.
  • Copia un generatore HTML già pronto: due campi, un pulsante, output del risultato, JS puro, nessun framework.
  • Padroneggia Crypto.getRandomValues quando Math.random non è adatto e ti serve un'alternativa crittograficamente sicura.

Come funziona Math.random()

Math.random() è un metodo statico dell'oggetto nativo Math. Non accetta argomenti e restituisce un numero a virgola mobile pseudo-casuale nell'intervallo [0, 1), secondo la specifica MDN. La parentesi quadra sullo zero significa "inclusivo", la parentesi tonda sull'uno significa "esclusivo". Quindi potresti ottenere 0.0, un valore intorno a 0.372 o qualcosa vicino a 0.999, ma non otterrai mai esattamente 1.0.

La distribuzione è approssimativamente uniforme. L'implementazione è integrata nel motore (V8, SpiderMonkey, JavaScriptCore) e lo sviluppatore non può scegliere o reimpostare l'algoritmo specifico (di solito xorshift128+); il seed viene impostato dal motore all'avvio.

Di per sé, Math.random() è inutile per compiti pratici. I giochi hanno bisogno di interi, le lotterie di interi entro un intervallo, la crittografia ha bisogno di entropia crittograficamente sicura che Math.random() non possiede. Ecco perché ci si avvolge sempre intorno una formula di conversione in intervallo.

La formula: intero casuale nell'intervallo [min, max]

La formula base che copre la stragrande maggioranza dei casi nello sviluppo web:

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

Scomponiamola con un esempio didattico dove min = 5 e max = 10, basato sulla documentazione di Math.random:

  • Supponiamo che Math.random() abbia restituito un valore R nell'intervallo [0, 1).
  • max - min + 1 = 10 - 5 + 1 = 6, il numero di possibili risultati interi: 5, 6, 7, 8, 9, 10.
  • R * 6 ha scalato il decimale casuale all'ampiezza dell'intervallo.
  • Math.floor(R * 6) ha arrotondato per difetto scartando la parte frazionaria. Ora abbiamo un indice da 0 a 5.
  • index + min ha traslato di min. Fatto: un intero casuale da 5 a 10 inclusi.

La stessa formula in una riga:

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

Perché Math.floor, non Math.ceil o Math.round

La scelta del metodo di arrotondamento incide in modo critico sulla distribuzione. Dimostriamolo con un esempio didattico usando l'intervallo da 1 a 3 (tutti i valori tranne i confini dell'intervallo sono calcolati). L'approccio è descritto nella risposta canonica su StackOverflow.

Con Math.floor e la formula (max - min + 1):

  • Se Math.random() cade nel primo terzo dell'intervallo [0, 1): la moltiplicazione per 3 dà un valore da 0 a quasi 1, Math.floor scarta la parte frazionaria, producendo l'indice 0, più min1 (esattamente un terzo dei casi).
  • Se Math.random() è nel terzo centrale: la moltiplicazione per 3 dà da 1 a quasi 2, Math.floor → indice 1, più min2 (un altro terzo).
  • Se Math.random() è nell'ultimo terzo: la moltiplicazione per 3 dà da 2 a quasi 3, Math.floor → indice 2, più min3 (l'ultimo terzo).

Distribuzione uniforme.

Cosa succede con Math.ceil? Math.ceil(Math.random() * 3) dà 1, 2, 3, ma 1 ha una probabilità trascurabile (solo quando Math.random() è esattamente 0, cosa che quasi non accade mai). I valori estremi sono distorti.

Anche Math.round non è uniforme: i valori estremi hanno metà della probabilità rispetto a quelli centrali. Ecco perché la generazione di interi casuali usa esclusivamente Math.floor.

Esempio completo: generatore in HTML, CSS e JavaScript

Una pagina funzionante che puoi salvare come .html e aprire nel browser. Due campi di input, un pulsante, risultato su sfondo 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>

Il codice è scritto intenzionalmente in JavaScript puro, senza framework o bundler, così da poter essere copiato ed eseguito all'istante senza npm install. La funzione randomInt prende min e max, restituendo un intero inclusivo su entrambi gli estremi. Il gestore del pulsante legge i valori dai campi, li valida e mostra il risultato.

Quando Math.random() non basta: Crypto.getRandomValues()

Math.random() è un generatore pseudo-casuale. È sufficiente per giochi, animazioni, test A/B e selezione di un elemento casuale da un array. Ma per compiti in cui la prevedibilità significa vulnerabilità, non è adatto: token di reset password, chiavi API, nonce crittografici, generatori di link monouso.

Immagina: invii a un utente un link di reset password con un token generato via Math.random(). Conoscendo il seed e l'algoritmo (che sono standard per il motore), un attaccante può ricostruire la sequenza e indovinare il token. È esattamente per questo che qualsiasi codice legato alla sicurezza deve usare una fonte di casualità crittograficamente sicura.

Per questi scenari esiste la Web Crypto API: crypto.getRandomValues(). Usa la fonte di entropia crittograficamente sicura del sistema operativo e riempie l'array tipizzato fornito con valori casuali.

Generare un intero casuale crittograficamente sicuro in un intervallo:

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}

Qui si usa il campionamento per rigetto: la funzione richiede un valore casuale a 32 bit e scarta quelli che cadono fuori dall'intervallo uniformemente divisibile. Questo elimina il modulo bias, cioè quando alcuni valori occorrono più frequentemente di altri.

In pratica, Math.random() è sufficiente per la maggior parte delle applicazioni web. Ma se stai scrivendo un generatore di link monouso o codici di conferma, usa crypto.getRandomValues().

Video: generare un numero casuale in JavaScript

Una breve dimostrazione dal canale dcode, che scompone Math.random() e la formula dell'intervallo con un esempio dal vivo nella console del browser:

⁉️🤔 Domande frequenti

La formula Math.floor(Math.random() * (max - min + 1)) + min include il limite superiore?

Sì, lo include. max - min + 1 dà il numero di possibili risultati interi, e Math.floor combinato con + min garantisce che max sia raggiungibile. Ad esempio, con min=1, max=3 ottieni 1, 2 o 3; tutti e tre i valori sono possibili.

Si può usare Math.ceil invece di Math.floor?

Tecnicamente sì, ma la distribuzione diventa non uniforme. Con Math.ceil(Math.random() * range), il valore min si verificherà solo quando Math.random() restituisce esattamente 0, un evento di probabilità infinitesima. Math.floor dà a ogni intero nell'intervallo la stessa probabilità, ed è per questo che la formula standard lo usa.

Math.random() è davvero un numero casuale?

No, è un numero pseudo-casuale. Il motore JavaScript usa un algoritmo deterministico (di solito xorshift128+) che produce una sequenza statisticamente uniforme, ma conoscere il seed permette di prevedere tutti i valori successivi. Per la crittografia questo è inaccettabile; usa crypto.getRandomValues().

Come si genera un numero a virgola mobile casuale in un intervallo?

Rimuovi Math.floor e + 1: Math.random() * (max - min) + min. Questo dà un decimale da min inclusivo a max esclusivo. Se ti serve inclusivo anche il limite superiore, sostituisci max - min con max - min + Number.EPSILON, ma in pratica gli intervalli decimali raramente richiedono l'inclusività del limite superiore.

Qual è più veloce: Math.random() o crypto.getRandomValues()?

Math.random() è ordini di grandezza più veloce. Nei benchmark V8 la differenza raggiunge un divario di cento volte o più, perché crypto.getRandomValues() effettua una chiamata di sistema a /dev/urandom (Linux) o equivalente, mentre Math.random() è un PRNG puramente in-process. Per generare migliaia di valori in un ciclo, usa Math.random(); per un singolo token di sicurezza, usa crypto.getRandomValues().

Numero casuale in 30 secondi: cosa ricordare

Il cavallo di battaglia è Math.floor(Math.random() * (max - min + 1)) + min. Ricorda tre regole: sempre Math.floor (non ceil, non round), sempre + 1 nel moltiplicatore (così max è raggiungibile), sempre + min alla fine (per traslare l'intervallo). Queste tre operazioni coprono tutto, dai tiri di dado alla selezione di un vincitore casuale.

Per codici di reset password e token, usa crypto.getRandomValues(). Più lento, ma crittograficamente sicuro.

Se vuoi solo provarlo, copia l'esempio HTML qui sopra, salvalo come file e aprilo nel browser. Nessuna dipendenza, nessun bundler. Funziona ovunque ci sia un <script>.