Generazione di numeri casuali in Java

1. Panoramica

In questo tutorial, esploreremo diversi modi per generare numeri casuali in Java.

2. Utilizzo dell'API Java

L'API Java ci fornisce diversi modi per raggiungere il nostro scopo. Vediamone alcuni.

2.1. java.lang.Math

Il metodo casuale della classe Math restituirà un valore double in un intervallo da 0,0 (incluso) a 1,0 (esclusivo). Vediamo come lo useremmo per ottenere un numero casuale in un dato intervallo definito da min e max :

int randomWithMathRandom = (int) ((Math.random() * (max - min)) + min);

2.2. java.util.Random

Prima di Java 1.7, il modo più diffuso di generare numeri casuali era usare nextInt . C'erano due modi per utilizzare questo metodo, con e senza parametri. La chiamata senza parametro restituisce uno qualsiasi dei valori int con probabilità approssimativamente uguale. Quindi, è molto probabile che otterremo numeri negativi:

Random random = new Random(); int randomWithNextInt = random.nextInt();

Se usiamo l' invocazione netxInt con il parametro bound , otterremo numeri all'interno di un intervallo:

int randomWintNextIntWithinARange = random.nextInt(max - min) + min;

Questo ci darà un numero compreso tra 0 (incluso) e parametro (esclusivo). Quindi, il parametro associato deve essere maggiore di 0. In caso contrario, otterremo un'eccezione java.lang.IllegalArgumentException .

Java 8 ha introdotto i nuovi metodi ints che restituiscono java.util.stream.IntStream. Vediamo come usarli.

Il metodo ints senza parametri restituisce un flusso illimitato di valori int :

IntStream unlimitedIntStream = random.ints();

Possiamo anche passare un singolo parametro per limitare la dimensione del flusso:

IntStream limitedIntStream = random.ints(streamSize);

E, naturalmente, possiamo impostare il massimo e il minimo per l'intervallo generato:

IntStream limitedIntStreamWithinARange = random.ints(streamSize, min, max);

2.3. java.util.concurrent.ThreadLocalRandom

Il rilascio Java 1.7 ci ha portato un modo nuovo e più efficiente di generare numeri casuali tramite la classe ThreadLocalRandom . Questo ha tre importanti differenze rispetto alla classe Random :

  • Non è necessario avviare esplicitamente una nuova istanza di ThreadLocalRandom . Questo ci aiuta a evitare errori di creare molte istanze inutili e sprecare tempo da garbage collector
  • Non possiamo impostare il seme per ThreadLocalRandom , il che può portare a un vero problema. Se abbiamo bisogno di impostare il seme, allora dovremmo evitare questo modo di generare numeri casuali
  • La classe casuale non funziona bene negli ambienti multi-thread

Ora vediamo come funziona:

int randomWithThreadLocalRandomInARange = ThreadLocalRandom.current().nextInt(min, max);

Con Java 8 o versioni successive, abbiamo nuove possibilità. Innanzitutto, abbiamo due varianti per il metodo nextInt :

int randomWithThreadLocalRandom = ThreadLocalRandom.current().nextInt(); int randomWithThreadLocalRandomFromZero = ThreadLocalRandom.current().nextInt(max);

In secondo luogo, e soprattutto, possiamo usare il metodo ints :

IntStream streamWithThreadLocalRandom = ThreadLocalRandom.current().ints();

2.4. java.util.SplittableRandom

Java 8 ci ha anche portato un generatore molto veloce: la classe SplittableRandom .

Come possiamo vedere nel JavaDoc, questo è un generatore da utilizzare in calcoli paralleli. È importante sapere che le istanze non sono thread-safe. Quindi, dobbiamo fare attenzione quando usiamo questa classe.

Abbiamo a disposizione il nextInt e ints metodi. Con nextInt possiamo impostare direttamente il range superiore e inferiore utilizzando i due parametri di invocazione:

SplittableRandom splittableRandom = new SplittableRandom(); int randomWithSplittableRandom = splittableRandom.nextInt(min, max);

Questo modo di utilizzare verifica che il parametro max sia maggiore di min . In caso contrario, otterremo un'eccezione IllegalArgumentException . Tuttavia, non controlla se lavoriamo con numeri positivi o negativi. Quindi, uno qualsiasi dei parametri può essere negativo. Inoltre, sono disponibili invocazioni a uno e zero parametri. Questi funzionano nello stesso modo che abbiamo descritto prima.

Abbiamo a disposizione anche i metodi ints . Ciò significa che possiamo facilmente ottenere un flusso di valori int . Per chiarire, possiamo scegliere di avere uno streaming limitato o illimitato. Per un flusso limitato, possiamo impostare la parte superiore e inferiore per l'intervallo di generazione del numero:

IntStream limitedIntStreamWithinARangeWithSplittableRandom = splittableRandom.ints(streamSize, min, max);

2.5. java.security.SecureRandom

Se disponiamo di applicazioni sensibili alla sicurezza, dovremmo considerare l'utilizzo di SecureRandom . Questo è un generatore crittograficamente potente. Le istanze costruite per impostazione predefinita non utilizzano seed crittograficamente casuali. Quindi, dovremmo:

  • Imposta il seme - di conseguenza, il seme sarà imprevedibile
  • Impostare la proprietà di sistema java.util.secureRandomSeed su true

Questa classe eredita da java.util.Random . Quindi, abbiamo a disposizione tutti i metodi che abbiamo visto sopra. Ad esempio, se dobbiamo ottenere uno qualsiasi dei valori int , chiameremo nextInt senza parametri:

SecureRandom secureRandom = new SecureRandom(); int randomWithSecureRandom = secureRandom.nextInt();

D'altra parte, se dobbiamo impostare l'intervallo, possiamo chiamarlo con il parametro bound :

int randomWithSecureRandomWithinARange = secureRandom.nextInt(max - min) + min;

We must remember that this way of using it throws IllegalArgumentException if the parameter is not bigger than zero.

3. Using Third-Party APIs

As we have seen, Java provides us with a lot of classes and methods for generating random numbers. However, there are also third-party APIs for this purpose.

We're going to take a look at some of them.

3.1. org.apache.commons.math3.random.RandomDataGenerator

There are a lot of generators in the commons mathematics library from the Apache Commons project. The easiest, and probably the most useful, is the RandomDataGenerator. It uses the Well19937c algorithm for the random generation. However, we can provide our algorithm implementation.

Let’s see how to use it. Firstly, we have to add dependency:

 org.apache.commons commons-math3 3.6.1 

The latest version of commons-math3 can be found on Maven Central.

Then we can start working with it:

RandomDataGenerator randomDataGenerator = new RandomDataGenerator(); int randomWithRandomDataGenerator = randomDataGenerator.nextInt(min, max);

3.2. it.unimi.dsi.util.XoRoShiRo128PlusRandom

Certainly, this is one of the fastest random number generator implementations. It has been developed at the Information Sciences Department of the Milan University.

The library is also available at Maven Central repositories. So, let's add the dependency:

 it.unimi.dsi dsiutils 2.6.0 

Questo generatore eredita da java.util.Random . Tuttavia, se diamo uno sguardo al JavaDoc, ci rendiamo conto che c'è solo un modo per usarlo: attraverso il metodo nextInt . Soprattutto, questo metodo è disponibile solo con le invocazioni di un parametro e di uno zero. Qualsiasi altra chiamata utilizzerà direttamente i metodi java.util.Random .

Ad esempio, se vogliamo ottenere un numero casuale all'interno di un intervallo, scriveremmo:

XoRoShiRo128PlusRandom xoroRandom = new XoRoShiRo128PlusRandom(); int randomWithXoRoShiRo128PlusRandom = xoroRandom.nextInt(max - min) + min;

4. Conclusione

Esistono diversi modi per implementare la generazione di numeri casuali. Tuttavia, non esiste un modo migliore. Di conseguenza, dovremmo scegliere quello più adatto alle nostre esigenze.

L'esempio completo può essere trovato su GitHub.