Java Language
Génération de nombres aléatoires
Recherche…
Remarques
Rien n'est vraiment aléatoire et le javadoc appelle donc ces nombres de manière pseudo-aléatoire. Ces nombres sont créés avec un générateur de nombres pseudo-aléatoires .
Nombres aléatoires
Java fournit, dans le cadre du package utils
, un générateur de nombres pseudo-aléatoires de base, nommé de manière appropriée Random
. Cet objet peut être utilisé pour générer une valeur pseudo-aléatoire comme n'importe quel type de données numérique intégré ( int
, float
, etc.). Vous pouvez également l'utiliser pour générer une valeur booléenne aléatoire ou un tableau aléatoire d'octets. Un exemple d'utilisation est le suivant:
import java.util.Random;
...
Random random = new Random();
int randInt = random.nextInt();
long randLong = random.nextLong();
double randDouble = random.nextDouble(); //This returns a value between 0.0 and 1.0
float randFloat = random.nextFloat(); //Same as nextDouble
byte[] randBytes = new byte[16];
random.nextBytes(randBytes); //nextBytes takes a user-supplied byte array, and fills it with random bytes. It returns nothing.
REMARQUE: cette classe ne produit que des nombres pseudo-aléatoires de qualité assez faible et ne devrait jamais être utilisée pour générer des nombres aléatoires pour des opérations cryptographiques ou d'autres situations où un caractère aléatoire de qualité supérieure est critique (pour cela, vous souhaitez utiliser la classe SecureRandom
, comme indiqué ci-dessous). Une explication de la distinction entre le caractère aléatoire "sécurisé" et "aléatoire" dépasse le cadre de cet exemple.
Nombres aléatoires dans une plage spécifique
La méthode nextInt(int bound)
de Random
accepte une limite exclusive supérieure, c'est-à-dire un nombre que la valeur aléatoire renvoyée doit être inférieure à. Cependant, seule la méthode nextInt
accepte une borne; nextLong
, nextDouble
etc. ne le font pas.
Random random = new Random();
random.nextInt(1000); // 0 - 999
int number = 10 + random.nextInt(100); // number is in the range of 10 to 109
À partir de Java 1.7, vous pouvez également utiliser ThreadLocalRandom
( source ). Cette classe fournit un générateur de nombres pseudo-aléatoires (PRNG). Notez que la méthode nextInt
de cette classe accepte les limites supérieure et inférieure.
import java.util.concurrent.ThreadLocalRandom;
// nextInt is normally exclusive of the top value,
// so add 1 to make it inclusive
ThreadLocalRandom.current().nextInt(min, max + 1);
Notez que la documentation officielle indique que nextInt(int bound)
peut faire des choses étranges lorsque la bound
est proche de 2 30 +1 (emphase ajoutée):
L'algorithme est un peu délicat. Il rejette les valeurs qui entraîneraient une distribution inégale (due au fait que 2 ^ 31 n'est pas divisible par n). La probabilité qu'une valeur soit rejetée dépend de n. Le pire des cas est n = 2 ^ 30 + 1, pour lequel la probabilité d'un rejet est 1/2, et le nombre attendu d'itérations avant que la boucle ne se termine est 2.
En d'autres termes, spécifier une limite diminuera (légèrement) les performances de la méthode nextInt
, et cette diminution de la performance deviendra plus prononcée à mesure que la bound
approche la moitié de la valeur max int.
Génération de nombres pseudo-aléatoires sécurisés par cryptographie
Random
et ThreadLocalRandom
sont assez bons pour un usage quotidien, mais ils ont un gros problème: ils sont basés sur un générateur de congruence linéaire , un algorithme dont la sortie peut être prédite assez facilement. Ainsi, ces deux classes ne conviennent pas aux utilisations cryptographiques (telles que la génération de clés).
On peut utiliser java.security.SecureRandom
dans les situations où un PRNG avec une sortie très difficile à prévoir est requis. Prédire les nombres aléatoires créés par les instances de cette classe est suffisamment difficile pour étiqueter la classe comme étant sécurisée sur le plan cryptographique .
import java.security.SecureRandom;
import java.util.Arrays;
public class Foo {
public static void main(String[] args) {
SecureRandom rng = new SecureRandom();
byte[] randomBytes = new byte[64];
rng.nextBytes(randomBytes); // Fills randomBytes with random bytes (duh)
System.out.println(Arrays.toString(randomBytes));
}
}
En plus d'être sécurisé sur le plan cryptographique, SecureRandom
dispose d'une période gigantesque de 2 160 , contre 2 48 pour la période Random
. Il a cependant l’inconvénient d’être beaucoup plus lent que Random
et d’autres PRNG linéaires tels que Mersenne Twister et Xorshift .
Notez que la mise en œuvre de SecureRandom dépend à la fois de la plate-forme et du fournisseur. Le SecureRandom
par défaut (fourni par le fournisseur SUN
dans sun.security.provider.SecureRandom
):
- sur des systèmes de type Unix, dotés de données provenant de
/dev/random
et / ou/dev/urandom
. - sur Windows, doté d'appels à
CryptGenRandom()
dans CryptoAPI .
Sélectionner des nombres aléatoires sans doublons
/**
* returns a array of random numbers with no duplicates
* @param range the range of possible numbers for ex. if 100 then it can be anywhere from 1-100
* @param length the length of the array of random numbers
* @return array of random numbers with no duplicates.
*/
public static int[] getRandomNumbersWithNoDuplicates(int range, int length){
if (length<range){
// this is where all the random numbers
int[] randomNumbers = new int[length];
// loop through all the random numbers to set them
for (int q = 0; q < randomNumbers.length; q++){
// get the remaining possible numbers
int remainingNumbers = range-q;
// get a new random number from the remainingNumbers
int newRandSpot = (int) (Math.random()*remainingNumbers);
newRandSpot++;
// loop through all the possible numbers
for (int t = 1; t < range+1; t++){
// check to see if this number has already been taken
boolean taken = false;
for (int number : randomNumbers){
if (t==number){
taken = true;
break;
}
}
// if it hasnt been taken then remove one from the spots
if (!taken){
newRandSpot--;
// if we have gone though all the spots then set the value
if (newRandSpot==0){
randomNumbers[q] = t;
}
}
}
}
return randomNumbers;
} else {
// invalid can't have a length larger then the range of possible numbers
}
return null;
}
La méthode fonctionne en boucle à travers un tableau qui a la taille de la longueur demandée et trouve la longueur restante des nombres possibles. Il définit un nombre aléatoire de ces nombres possibles newRandSpot
et trouve ce nombre dans le nombre restant non pris. Cela se fait en parcourant la plage et en vérifiant si ce nombre a déjà été pris.
Par exemple, si la plage est 5 et que la longueur est 3 et que nous avons déjà choisi le nombre 2. Nous avons alors 4 nombres restants, nous obtenons donc un nombre aléatoire compris entre 1 et 4 et nous parcourons la plage (5). que nous avons déjà utilisé (2).
Maintenant, disons que le nombre suivant choisi entre 1 et 4 est 3. Sur la première boucle, nous obtenons 1 qui n'a pas encore été pris, nous pouvons donc en retirer 1 de 3, ce qui fait 2. Maintenant, sur la deuxième boucle, nous obtenons 2 donc nous ne faisons rien. Nous suivons ce schéma jusqu'à ce que nous arrivions à 4 où une fois que nous supprimons 1, il devient 0 et nous définissons donc le nouveau randomNumber sur 4.
Génération de nombres aléatoires avec une graine spécifiée
//Creates a Random instance with a seed of 12345.
Random random = new Random(12345L);
//Gets a ThreadLocalRandom instance
ThreadLocalRandom tlr = ThreadLocalRandom.current();
//Set the instance's seed.
tlr.setSeed(12345L);
L'utilisation de la même graine pour générer des nombres aléatoires renverra les mêmes nombres à chaque fois, donc définir une graine différente pour chaque instance Random
est une bonne idée si vous ne voulez pas vous retrouver avec des numéros en double.
System.currentTimeMillis()
est une bonne méthode pour obtenir un Long
différent pour chaque appel:
Random random = new Random(System.currentTimeMillis());
ThreadLocalRandom.current().setSeed(System.currentTimeMillis());
Générer un nombre aléatoire en utilisant apache-common lang3
Nous pouvons utiliser org.apache.commons.lang3.RandomUtils
pour générer des nombres aléatoires en utilisant une seule ligne.
int x = RandomUtils.nextInt(1, 1000);
La méthode nextInt(int startInclusive, int endExclusive)
prend une plage.
En dehors de int, nous pouvons générer des nombres long
, double
, float
et bytes
aléatoires en utilisant cette classe.
RandomUtils
classe RandomUtils
contient les méthodes suivantes:
static byte[] nextBytes(int count) //Creates an array of random bytes.
static double nextDouble() //Returns a random double within 0 - Double.MAX_VALUE
static double nextDouble(double startInclusive, double endInclusive) //Returns a random double within the specified range.
static float nextFloat() //Returns a random float within 0 - Float.MAX_VALUE
static float nextFloat(float startInclusive, float endInclusive) //Returns a random float within the specified range.
static int nextInt() //Returns a random int within 0 - Integer.MAX_VALUE
static int nextInt(int startInclusive, int endExclusive) //Returns a random integer within the specified range.
static long nextLong() //Returns a random long within 0 - Long.MAX_VALUE
static long nextLong(long startInclusive, long endExclusive) //Returns a random long within the specified range.