Come generare numeri casuali in Java

Generare numeri casuali è un’operazione fondamentale nella programmazione, con molteplici applicazioni come giochi, simulazioni, crittografia e molto altro.

Java mette a disposizione diversi modi per generare numeri pseudo-casuali, ovvero sequenze di numeri che appaiono casuali ma sono in realtà generate da un algoritmo.

Panoramica sui numeri casuali in Java

In Java, la generazione di numeri casuali avviene principalmente tramite la classe java.util.Random.

Questa classe incapsula un generatore di numeri pseudo-casuali che può essere utilizzato per generare vari tipi di numeri casuali come interi, float, double ecc.

Altre classi correlate ai numeri casuali in Java sono:

  • java.util.concurrent.ThreadLocalRandom: versione thread-safe di Random
  • java.security.SecureRandom: genera numeri crittografici sicuri
  • java.util.RandomGenerator: interfaccia aggiunta in Java 17

I passaggi di base per generare numeri casuali in Java sono:

  1. Creare un’istanza di Random o ThreadLocalRandom
  2. Chiamare metodi come nextInt(), nextDouble() ecc. per generare i numeri
  3. Se necessario, impostare un seed per il generatore di numeri casuali

Vediamo ora più in dettaglio come implementare la generazione di vari tipi di numeri casuali in Java.

Generare numeri interi casuali

Per generare numeri interi casuali in Java, si utilizza il metodo nextInt() della classe Random. Questo metodo restituisce un numero intero con segno compreso tra Integer.MIN_VALUE e Integer.MAX_VALUE.

Ecco un esempio di come generare un intero casuale:

import java.util.Random;

public class Main {

  public static void main(String[] args) {

    Random random = new Random();

    int randomInt = random.nextInt();

    System.out.println(randomInt);
  }

}

Possiamo anche specificare i valori minimo e massimo desiderati passando un argomento a nextInt():

int randomInt = random.nextInt(10); // numeri da 0 a 9

nextInt(int bound) restituisce un numero casuale da 0 al bound passato meno 1.

Generare numeri double casuali

Per i numeri in virgola mobile, possiamo utilizzare i metodi nextDouble() e nextFloat().

nextDouble() restituisce un double casuale compreso tra 0.0 e 1.0:

Random random = new Random();

double randomDouble = random.nextDouble();

Per ottenere un double in un range diverso, ad esempio tra 10.0 e 20.0, basta moltiplicare per l’ampiezza del range e sommare il minimo:

double min = 10.0;
double max = 20.0;

double randomDouble = min + (max - min) * random.nextDouble();

nextFloat() funziona in modo analogo per i float.

Generare booleani casuali

Per ottenere valori booleani casuali, possiamo confrontare nextInt() con una soglia:

Random random = new Random();

boolean randomBoolean = random.nextInt(2) == 0;

Questo confronterà il risultato di nextInt(2) con 0, restituendo vero o falso a caso con uguale probabilità.

In alternativa, si può utilizzare nextBoolean():

boolean randomBoolean = random.nextBoolean();

Impostare il seed

I generatori di numeri pseudo-casuali come Random producono sempre la stessa sequenza a partire da uno stesso seed iniziale.

Se non specifichiamo un seed, Java ne genera automaticamente uno basato sul tempo di esecuzione.

Per ottenere la stessa sequenza di numeri casuali, possiamo impostare manualmente il seed:

Random random = new Random(10);

In questo modo otterremo sempre la stessa sequenza partendo dal seed 10.

ThreadLocalRandom

La classe Random non è thread-safe: utilizzarla da più thread contemporaneamente porta a risultati imprevedibili.

Per ovviare a questo problema esiste la classe ThreadLocalRandom che incapsula istanze separate di Random per ogni thread, rendendo sicuro l’utilizzo da code multi-thread.

Può essere utilizzata al posto di Random:

ThreadLocalRandom.current().nextInt(10);

SecureRandom

Per applicazioni crittografiche o di sicurezza, la classe SecureRandom genera numeri non prevedibili e criptograficamente sicuri, adatti per password, chiavi, sale e vettoriali di inizializzazione.

Si può utilizzare allo stesso modo di Random:

SecureRandom secureRandom = new SecureRandom();

secureRandom.nextInt();

SecureRandom è più lento di Random ma garantisce una qualità superiore di casualità.

Testare la qualità di casualità

Per testare se una sequenza di numeri è sufficientemente casuale, si possono utilizzare test statistici come il test chi-quadro o il test Kolmogorov-Smirnov.

In Java sono disponibili classi come org.apache.commons.math3.random.RandomDataGenerator che includono metodi per effettuare questi test.

Ad esempio, il test chi-quadro può rilevare eventuali deviazioni dall’equidistribuzione attesa per una sequenza casuale.