Probabilité en Java

1. Vue d'ensemble

Dans ce didacticiel, nous examinerons quelques exemples de la façon dont nous pouvons implémenter la probabilité avec Java.

2. Simulation de la probabilité de base

Pour simuler la probabilité en Java, la première chose à faire est de générer des nombres aléatoires. Heureusement, Java nous fournit de nombreux générateurs de nombres aléatoires .

Dans ce cas, nous utiliserons la classe SplittableRandom car elle fournit un caractère aléatoire de haute qualité et est relativement rapide:

SplittableRandom random = new SplittableRandom();

Ensuite, nous devons générer un nombre dans une plage et le comparer à un autre nombre choisi dans cette plage. Chaque numéro de la plage a une chance égale d'être tiré. Comme nous connaissons la plage, nous connaissons la probabilité de tirer le nombre choisi. De cette façon, nous contrôlons la probabilité :

boolean probablyFalse = random.nextInt(10) == 0

Dans cet exemple, nous avons dessiné des nombres de 0 à 9. Par conséquent, la probabilité de dessiner 0 est égale à 10%. Maintenant, obtenons un nombre aléatoire et testons si le nombre choisi est inférieur à celui tiré:

boolean whoKnows = random.nextInt(1, 101) <= 50

Ici, nous avons dessiné des nombres de 1 à 100. La chance que notre nombre aléatoire soit inférieur ou égal à 50 est exactement de 50%.

3. Distribution uniforme

Les valeurs générées jusqu'à ce point tombent dans la distribution uniforme. Cela signifie que chaque événement, par exemple lancer un certain nombre sur un dé, a une chance égale de se produire.

3.1. Appel d'une fonction avec une probabilité donnée

Maintenant, disons que nous voulons effectuer une tâche de temps en temps et contrôler sa probabilité. Par exemple, nous exploitons un site de commerce électronique et nous souhaitons accorder une réduction à 10% de nos utilisateurs.

Pour ce faire, implémentons une méthode qui prendra trois paramètres: un fournisseur à invoquer dans un certain pourcentage des cas, un deuxième fournisseur à invoquer dans le reste des cas, et la probabilité.

Tout d'abord, nous déclarons notre SplittableRandom comme Lazy en utilisant Vavr. De cette façon, nous ne l'instancierons qu'une seule fois, sur une première requête:

private final Lazy random = Lazy.of(SplittableRandom::new); 

Ensuite, nous implémenterons la fonction de gestion des probabilités:

public  withProbability(Supplier positiveCase, Supplier negativeCase, int probability) { SplittableRandom random = this.random.get(); if (random.nextInt(1, 101) <= probability) { return positiveCase.get(); } else { return negativeCase.get(); } }

3.2. Probabilité d'échantillonnage avec la méthode de Monte Carlo

Inversons le processus que nous avons vu dans la section précédente. Pour ce faire, nous mesurerons la probabilité en utilisant la méthode de Monte Carlo. Il génère un volume élevé d'événements aléatoires et compte combien d'entre eux satisfont à la condition fournie. C'est utile lorsque la probabilité est difficile ou impossible à calculer analytiquement.

Par exemple, si nous regardons des dés à six faces, nous savons que la probabilité de lancer un certain nombre est de 1/6. Mais, si nous avons un dé mystérieux avec un nombre inconnu de côtés, il serait difficile de dire quelle serait la probabilité. Au lieu d'analyser les dés, nous pourrions simplement les lancer plusieurs fois et compter combien de fois certains événements se produisent.

Voyons comment nous pouvons mettre en œuvre cette approche. Tout d'abord, nous allons essayer de générer le nombre 1 avec la probabilité de 10% pour un million de fois et de les compter:

int numberOfSamples = 1_000_000; int probability = 10; int howManyTimesInvoked = Stream.generate(() -> randomInvoker.withProbability(() -> 1, () -> 0, probability)) .limit(numberOfSamples) .mapToInt(e -> e) .sum();

Ensuite, la somme des nombres générés divisée par le nombre d'échantillons sera une approximation de la probabilité de l'événement:

int monteCarloProbability = (howManyTimesInvoked * 100) / numberOfSamples; 

Gardez à l'esprit que la probabilité calculée est approximée. Plus le nombre d'échantillons est élevé, meilleure sera l'approximation.

4. Autres distributions

La distribution uniforme fonctionne bien pour modéliser des choses comme les jeux. Pour que le jeu soit juste, tous les événements doivent souvent avoir la même probabilité de se produire.

Cependant, dans la vraie vie, les distributions sont généralement plus compliquées. Les chances ne sont pas égales que des choses différentes se produisent.

Par exemple, il y a très peu de personnes extrêmement petites et très peu extrêmement grandes. La plupart des personnes sont de taille moyenne, ce qui signifie que la taille des personnes suit la distribution normale. Si nous devons générer des hauteurs humaines aléatoires, il ne suffira pas de générer un nombre aléatoire de pieds.

Heureusement, nous n'avons pas besoin d'implémenter nous-mêmes le modèle mathématique sous-jacent. Nous devons savoir quelle distribution utiliser et comment la configurer , par exemple à l'aide de données statistiques.

La bibliothèque Apache Commons nous fournit des implémentations pour plusieurs distributions. Implémentons la distribution normale avec:

private static final double MEAN_HEIGHT = 176.02; private static final double STANDARD_DEVIATION = 7.11; private static NormalDistribution distribution = new NormalDistribution(MEAN_HEIGHT, STANDARD_DEVIATION); 

L'utilisation de cette API est très simple - la méthode d'exemple tire un nombre aléatoire de la distribution:

public static double generateNormalHeight() { return distribution.sample(); }

Enfin, inversons le processus:

public static double probabilityOfHeightBetween(double heightLowerExclusive, double heightUpperInclusive) { return distribution.probability(heightLowerExclusive, heightUpperInclusive); }

En conséquence, nous obtiendrons la probabilité qu'une personne ait une taille entre deux bornes. Dans ce cas, les hauteurs inférieure et supérieure.

5. Conclusion

Dans cet article, nous avons appris à générer des événements aléatoires et à calculer la probabilité qu'ils se produisent. Nous avons utilisé des distributions uniformes et normales pour modéliser différentes situations.

L'exemple complet peut être trouvé sur GitHub.