1. Overview
1.概述
In this tutorial, we’ll look at a few examples of how we can implement probability with Java.
在本教程中,我们将看几个例子,看看如何用Java实现概率。
2. Simulating Basic Probability
2.模拟基本概率
To simulate probability in Java, the first thing we need to do is to generate random numbers. Fortunately, Java provides us with plenty of random numbers generators.
要在Java中模拟概率,我们需要做的第一件事就是生成随机数。幸运的是,Java为我们提供了大量的随机数字生成器。
In this case, we’ll use the SplittableRandom class because it provides high-quality randomness and is relatively fast:
在这种情况下,我们将使用SplittableRandom类,因为它提供了高质量的随机性,并且相对较快。
SplittableRandom random = new SplittableRandom();
Then we need to generate a number in a range and compare it to another number chosen from that range. Every number in the range has an equal chance of being drawn. As we know the range, we know the probability of drawing our chosen number. That way we’re controlling probability:
然后我们需要在一个范围内生成一个数字,并将其与从该范围内选择的另一个数字进行比较。范围内的每个数字都有相同的机会被抽中。当我们知道范围时,我们就知道抽到我们所选数字的概率。这样一来,我们就控制了概率。
boolean probablyFalse = random.nextInt(10) == 0
In this example, we drew numbers from 0 to 9. Therefore, the probability of drawing 0 is equal to 10%. Now, let’s get a random number and test if the chosen number is lower than the drawn one:
在这个例子中,我们抽到的数字是0到9。因此,抽到0的概率等于10%。现在,让我们得到一个随机数,并测试所选的数字是否比抽到的数字低。
boolean whoKnows = random.nextInt(1, 101) <= 50
Here, we drew numbers from 1 to 100. The chance for our random number to be lesser or equal to 50 is exactly 50%.
在这里,我们从1到100抽出了数字。我们的随机数小于或等于50的机会正好是50%。
3. Uniform Distribution
3.均匀分布
Values generated up to this point fall into the uniform distribution. This means that every event, for example rolling some number on a dice, has an equal chance of happening.
在这一点上产生的数值属于均匀分布。这意味着每一个事件,例如在骰子上掷出某个数字,都有相同的机会发生。
3.1. Invoking a Function With a Given Probability
3.1.用给定的概率调用一个函数
Now, let’s say we want to perform a task from time to time and control its probability. For example, we operate an e-commerce site and we want to give a discount to 10% of our users.
现在,假设我们想不时地执行一项任务并控制其概率。例如,我们经营一个电子商务网站,我们想给10%的用户提供折扣。
To do so, let’s implement a method that will take three parameters: a supplier to invoke in some percentage of cases, a second supplier to invoke in the rest of the cases, and the probability.
要做到这一点,让我们实现一个方法,该方法将接受三个参数:在某些百分比的情况下调用的供应商,在其余情况下调用的第二个供应商,以及概率。
First, we declare our SplittableRandom as Lazy using Vavr. This way we’ll instantiate it only once, on a first request:
首先,我们将我们的SplittableRandom声明为Lazy使用Vavr。这样,我们将只在第一次请求时将其实例化一次。
private final Lazy<SplittableRandom> random = Lazy.of(SplittableRandom::new);
Then, we’ll implement the probability-managing function:
然后,我们将实现概率管理的功能。
public <T> withProbability(Supplier<T> positiveCase, Supplier<T> negativeCase, int probability) {
SplittableRandom random = this.random.get();
if (random.nextInt(1, 101) <= probability) {
return positiveCase.get();
} else {
return negativeCase.get();
}
}
3.2. Sampling Probability With the Monte Carlo Method
3.2.用蒙特卡洛方法取样的概率
Let’s reverse the process we saw in the previous section. To do so, we’ll measure the probability using the Monte Carlo method. It generates a high volume of random events and counts how many of them satisfy the provided condition. It’s useful when the probability is hard or impossible to compute analytically.
让我们把我们在上一节看到的过程倒过来。为此,我们将使用Monte Carlo方法来测量概率。它产生大量的随机事件,并计算其中有多少事件满足所提供的条件。当概率很难或不可能通过分析计算时,它就很有用。
For example, if we look at six-sided dice we know that the probability of rolling a certain number is 1/6. But, if we have a mysterious dice with an unknown number of sides, it’d be hard to tell what the probability would be. Instead of analyzing the dice we could just roll it numerous times and count how many times certain events are occurring.
例如,如果我们看一下六面骰子,我们知道掷出某个数字的概率是1/6。但是,如果我们有一个未知面数的神秘骰子,就很难知道概率会是多少。与其分析骰子,我们不如直接掷出无数次,数一数某些事件发生了多少次。
Let’s see how we can implement this approach. First, we’ll try to generate the number 1 with the probability of 10% for a million times and count them:
让我们看看如何实现这种方法。首先,我们将尝试以10%的概率生成数字1,进行100万次计数。
int numberOfSamples = 1_000_000;
int probability = 10;
int howManyTimesInvoked =
Stream.generate(() -> randomInvoker.withProbability(() -> 1, () -> 0, probability))
.limit(numberOfSamples)
.mapToInt(e -> e)
.sum();
Then, the sum of generated numbers divided by the number of samples will be an approximation of the probability of the event:
然后,生成的数字之和除以样本数将是事件概率的近似值。
int monteCarloProbability = (howManyTimesInvoked * 100) / numberOfSamples;
Mind that, the computed probability is approximated. The higher the number of samples, the better the approximation will be.
注意,计算出来的概率是近似的。样本的数量越多,近似值就越高。
4. Other Distributions
4.其他分配
The uniform distribution works well for modeling things like games. For the game to be fair, all the events often need to have the same probability of happening.
均匀分布对游戏等事物的建模很有效。为了使游戏公平,所有的事件往往需要有相同的发生概率。
However, in real life, distributions are usually more complicated. The chances are not equal for different things to happen.
然而,在现实生活中,分布情况通常更加复杂。不同事情发生的机会并不相等。
For example, there are very few extremely short people and very few extremely tall. Most people are of average height, which means that the height of people follows the normal distribution. If we need to generate random human heights, then it won’t suffice to generate a random number of feet.
例如,极短的人很少,极高的人也很少。大多数人都是平均身高,这意味着人们的身高遵循正常分布。如果我们需要生成随机的人的高度,那么生成一个随机的脚数是不够的。
Fortunately, we don’t need to implement the underlying mathematical model ourselves. We need to know which distribution to use and how to configure it, for example, using statistical data.
幸运的是,我们不需要自己实现基础数学模型。我们需要知道使用哪种分布以及如何配置它,例如,使用统计数据。
The Apache Commons library provides us with implementations for several distributions. Let’s implement the normal distribution with it:
Apache Commons库为我们提供了几个分布的实现。让我们用它来实现正态分布。
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);
Using this API is very straightforward – the sample method draws a random number from the distribution:
使用这个API是非常直接的 – 样本方法从分布中抽取一个随机数。
public static double generateNormalHeight() {
return distribution.sample();
}
Finally, let’s invert the process:
最后,让我们把这个过程倒过来。
public static double probabilityOfHeightBetween(double heightLowerExclusive, double heightUpperInclusive) {
return distribution.probability(heightLowerExclusive, heightUpperInclusive);
}
As a result, we’ll get the probability of a person having a height between two bounds. In this case, the lower and the upper heights.
因此,我们将得到一个人的身高在两个界限之间的概率。在这种情况下,就是下限和上限的身高。
5. Conclusion
5.总结
In this article, we learned how to generate random events and how to compute the probability of them happening. We used uniform and normal distributions to model different situations.
在这篇文章中,我们学习了如何生成随机事件以及如何计算它们发生的概率。我们用均匀分布和正态分布来模拟不同的情况。
The full example can be found over on GitHub.
完整的例子可以在GitHub上找到over。