1. 개요

이 사용방법(예제)에서는 Java로 확률을 구현할 수있는 방법에 대한 몇 가지 예를 살펴 보겠습니다.

2. 기본 확률 시뮬레이션

자바에서 확률을 시뮬레이션하기 위해 가장 먼저해야 할 일은 난수 를 생성 하는 것 입니다. 다행히 Java는 많은 난수 생성기를 제공 합니다.

이 경우 높은 품질의 임의성을 제공하고 상대적으로 빠르기 때문에 SplittableRandom 클래스를 사용합니다 .

SplittableRandom random = new SplittableRandom();

그런 다음 범위에서 숫자를 생성하고 해당 범위에서 선택한 다른 숫자와 비교해야합니다. 범위의 모든 숫자는 똑같이 그려 질 가능성이 있습니다. 범위를 알기 때문에 선택한 숫자를 그릴 확률을 알고 있습니다. 그런 식으로 우리는 확률을 제어합니다 .

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

이 예에서는 0에서 9까지의 숫자를 그렸습니다. 따라서 0을 그릴 확률은 10 %와 같습니다. 이제 난수를 가져 와서 선택한 숫자가 그려진 숫자보다 낮은 지 테스트 해 봅시다.

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

여기서 우리는 1에서 100까지의 숫자를 그렸습니다. 랜덤의 숫자가 50보다 작거나 같을 확률은 정확히 50 %입니다.

3. 균일 한 분포

이 시점까지 생성 된 값은 균등 분포에 속합니다. 이것은 예를 들어 주사위에서 어떤 숫자를 굴리는 것과 같은 모든 이벤트가 발생할 확률이 동일하다는 것을 의미합니다 .

3.1. 주어진 확률로 함수 호출

이제 우리가 때때로 작업을 수행하고 그 확률을 제어하고 싶다고 가정 해 봅시다. 예를 들어, 우리는 전자 상거래 사이트를 운영하고 있으며 사용자의 10 %에게 할인을 제공하고자합니다.

이를 위해 세 가지 매개 변수를 사용하는 메서드를 구현해 보겠습니다. 일부 경우에 호출 할 공급자, 나머지 경우에 호출 할 두 번째 공급자, 확률입니다.

첫째, 우리는 우리의 선언 SplittableRandom 같은 Lazy가 사용 Vavr을 . 이렇게하면 첫 번째 요청에서 한 번만 인스턴스화합니다.

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

그런 다음 확률 관리 기능을 구현합니다.

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. Monte Carlo 방법을 사용한 샘플링 확률

이전 섹션에서 본 과정을 반대로 해보겠습니다. 이를 위해 Monte Carlo 방법을 사용하여 확률을 측정합니다 . 대량의 임의 이벤트를 생성하고 제공된 조건을 충족하는 이벤트의 수를 계산합니다. 확률이 분석적으로 계산하기 어렵거나 불가능할 때 유용합니다.

예를 들어 6면 주사위를 보면 특정 숫자를 굴릴 확률이 1/6이라는 것을 알 수 있습니다. 하지만 변의 수를 알 수없는 신비한 주사위가 있다면 그 확률이 ​​무엇인지 말하기 어려울 것입니다. 주사위를 분석하는 대신 여러 번 굴려서 특정 이벤트가 발생하는 횟수를 계산할 수 있습니다.

이 접근 방식을 어떻게 구현할 수 있는지 살펴 보겠습니다. 먼저, 10 % 확률로 백만 번의 숫자 1을 생성하고 세어 보겠습니다.

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

그런 다음 생성 된 수의 합계를 샘플 수로 나눈 값이 이벤트 확률의 근사치가됩니다.

int monteCarloProbability = (howManyTimesInvoked * 100) / numberOfSamples;

계산 된 확률은 근사치입니다. 샘플 수가 많을수록 근사치가 더 좋아집니다.

4. 기타 배포

균일 분포는 게임과 같은 모델링에 적합합니다. 게임이 공정 해지려면 모든 이벤트가 동일한 발생 확률을 가져야하는 경우가 많습니다.

그러나 실제 생활에서는 일반적으로 배포가 더 복잡합니다. 다른 일이 일어날 확률은 같지 않습니다.

예를 들어, 극히 키가 작은 사람은 거의없고 키가 큰 사람은 거의 없습니다. 대부분의 사람들은 평균 신장이며, 이는 사람의 신장이 정규 분포를 따른다는 것을 의미합니다 . 랜덤의 인간 키를 생성해야한다면 랜덤의 피트 수를 생성하는 것으로는 충분하지 않습니다.

다행히도 기본 수학적 모델을 직접 구현할 필요는 없습니다. 예를 들어 통계 데이터 를 사용하여 사용할 배포판과 구성 방법을 알아야 합니다.

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);

이 API를 사용하는 것은 매우 간단합니다. 샘플 방법은 분포에서 난수를 가져옵니다.

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

마지막으로 프로세스를 뒤집어 보겠습니다.

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

결과적으로 우리는 사람이 두 경계 사이의 키를 가질 확률을 얻습니다. 이 경우 낮은 높이와 높은 높이입니다.

5. 결론

이 기사에서는 랜덤 이벤트를 생성하는 방법과 이벤트 발생 확률을 계산하는 방법을 배웠습니다. 다양한 상황을 모델링하기 위해 균일 분포와 정규 분포를 사용했습니다.

전체 예제는 GitHub 에서 찾을 수 있습니다  .