Вероятность события, используя проценты

Допустим у меня есть кнопка и 5 событий.

Мне нужно сделать, чтобы при нажатии на кнопку появлялось одно из событий с определенным шансом: обычное 70%, редкое 20%, эпическое 10%, легендарное 1% и мистическое 0.05%

Как это реализовать? И куда закидывать скрипт


Ответы (1 шт):

Автор решения: Alex Rudenko
  1. Создаём enum, в котором прописываем названия событий и их допустимые вероятности
public enum ProbableEvent {
    REGULAR(70),
    RARE(20),
    EPIC(10),
    LEGENDARY(1),
    MYSTICAL(0.05);
    
    private final double probability;

    private ProbableEvent(double percent) {
        this.probability = percent;
    }
    
    public double getProbability() {
        return this.probability;
    }
}
  1. Также пишем статический метод для расчёта суммарной вероятности, чтобы можно было корректно рассчитать частоту генерации событий, кэшируем расчёт суммы.
    private static double total = -1;
    private static double total() {
        if (total < 0) {
            total = Arrays.stream(ProbableEvent.values())
                .mapToDouble(ProbableEvent::getProbability)
                .sum();
        }
        return total;
    }

  1. Пишем статический метод, возвращающий рандомное событие с заданной приведенной вероятностью (например 70% / 1.01005 = 69.3035%, и т.д.)
    private static final Random RANDOM = ThreadLocalRandom.current();

    public static ProbableEvent getRandom() {
        int range = (int)(total() * 100);
        double x = RANDOM.nextInt(range) / 100.0;
        double limit = 0.0;
        for (ProbableEvent pe : ProbableEvent.values()) {
            limit += pe.probability;
            if (x < limit) {
                return pe;
            }
        }
        return ProbableEvent.MYSTICAL;
    }

Тест: вызываем метод ProbableEvent::getRandom 100_000 раз, строим и выводим карту частот:

int runs = 100_000;

Map<ProbableEvent, Integer> freq = Stream
    .generate(ProbableEvent::getRandom)
    .limit(runs)
    .collect(Collectors.groupingBy(
        x -> x,
        Collectors.summingInt(x -> 1)
    ));
freq.entrySet()
    .stream()
    .sorted(Map.Entry.<ProbableEvent, Integer>comparingByValue().reversed())
    .forEach(e -> System.out.printf("%-10s count = %6d %% = %6.3f%n", 
        e.getKey().name(), e.getValue(), 100.0 * e.getValue() / runs
    ));

Вывод:

REGULAR    count =  69082 % = 69.082
RARE       count =  19829 % = 19.829
EPIC       count =  10053 % = 10.053
LEGENDARY  count =    994 % =  0.994
MYSTICAL   count =     42 % =  0.042
→ Ссылка