Случайное значение из перечисления с вероятностью - PullRequest
6 голосов
/ 11 марта 2011

У меня есть перечисление, из которого я хотел бы выбрать случайное значение, но не случайное. Мне бы хотелось, чтобы некоторые значения были менее вероятными для выбора. Вот что у меня пока ...

private enum Type{
        TYPE_A, TYPE_B, TYPE_C, TYPE_D, TYPE_E;

        private static final List<Type> VALUES =
            Collections.unmodifiableList(Arrays.asList(values()));
          private static final int SIZE = VALUES.size();
          private static final Random RANDOM = new Random();

          public static Type randomType()  {
            return VALUES.get(RANDOM.nextInt(SIZE));
          }
    }

Существует ли эффективный способ присвоения вероятностей каждому из этих значений?

Код найден с здесь

Ответы [ 7 ]

6 голосов
/ 11 марта 2011

несколько способов сделать это, один из них, похожий на ваш подход

private enum Type{
    TYPE_A(10 /*10 - weight of this type*/), TYPE_B(1), TYPE_C(5), TYPE_D(20), TYPE_E(7);

private int weight;

private Type(int weight) {
    this.weight = weight;
}

private int getWeight() {
    return weight;
}


    private static final List<Type> VALUES =
        Collections.unmodifiableList(Arrays.asList(values()));

    private int summWeigts() {
       int summ = 0;
       foreach(Type value: VALUES) 
          summ += value.getWeight();
       return summ;
    }
    private static final int SIZE = summWeigts();
    private static final Random RANDOM = new Random();

    public static Type randomType()  {
        int randomNum = RANDOM.nextInt(SIZE);
        int currentWeightSumm = 0;
        for(Type currentValue: VALUES) {
           if (randomNum > currentWeightSumm && 
               randomNum <= (currentWeightSumm + currentValue.getWeight()) {
             break;
           }
           currentWeightSumm += currentValue.getWeight();
        }

        return currentValue.get();
    }
}
0 голосов
/ 30 сентября 2018

Вы можете использовать EnumeratedDistribution из библиотеки Apache Commons Math .

EnumeratedDistribution<Type> distribution = new EnumeratedDistribution<>(
        RandomGeneratorFactory.createRandomGenerator(new Random()),
        List.of(
                new Pair<>(Type.TYPE_A, 0.2), // get TYPE_A with probability 0.2
                new Pair<>(Type.TYPE_B, 0.5), // get TYPE_B with probability 0.5
                new Pair<>(Type.TYPE_C, 0.3)  // get TYPE_C with probability 0.3
        )
);

Type mySample = distribution.sample();
0 голосов
/ 18 сентября 2012

Вот еще одна альтернатива, которая позволяет указывать дистрибутив во время выполнения.

Включает предложение Алексея Свиридова. Также метод random () может включать предложение от Теда Даннинга, когда есть много вариантов.

     private enum Option {

        OPTION_1, OPTION_2, OPTION_3, OPTION_4;
        static private final Integer OPTION_COUNT = EnumSet.allOf(Option.class).size();
        static private final EnumMap<Option, Integer> buckets = new EnumMap<Option, Integer>(Option.class);
        static private final Random random = new Random();
        static private Integer total = 0;

        static void setDistribution(Short[] distribution) {
           if (distribution.length < OPTION_COUNT) {
              throw new ArrayIndexOutOfBoundsException("distribution too short");
           }
           total = 0;
           Short dist;
           for (Option option : EnumSet.allOf(Option.class)) {
              dist = distribution[option.ordinal()];
              total += (dist < 0) ? 0 : dist;
              buckets.put(option, total);
           }
        }

        static Option random() {
           Integer rnd = random.nextInt(total);
           for (Option option : EnumSet.allOf(Option.class)) {
              if (buckets.get(option) > rnd) {
                 return option;
              }
           }
           throw new IndexOutOfBoundsException();
        }
     }
0 голосов
/ 11 марта 2011
import java.util.*;
enum R {
    a(.1),b(.2),c(.3),d(.4);
    R(final double p) {
        this.p=p;
    }
    private static void init() {
        sums=new double[values().length+1];
        sums[0]=0;
        for(int i=0;i<values().length;i++)
            sums[i+1]=values()[i].p+sums[i];
        once=true;
    }
    static R random() {
        if (!once) init();
        final double x=Math.random();
        for(int i=0;i<values().length;i++)
            if (sums[i]<=x&&x<sums[i+1]) return values()[i];
        throw new RuntimeException("should not happen!");
    }
    static boolean check() {
        double sum=0;
        for(R r:R.values())
            sum+=r.p;
        return(Math.abs(sum-1)<epsilon);
    }
    final double p;
    static final double epsilon=.000001;
    static double[] sums;
    static boolean once=false;
}
public class Main{
    public static void main(String[] args) {
        if (!R.check()) throw new RuntimeException("values should sum to one!");
        final Map<R,Integer> bins=new EnumMap<R,Integer>(R.class);
        for(R r:R.values())
            bins.put(r,0);
        final int n=1000000;
        for(int i=0;i<n;i++) {
            final R r=R.random();
            bins.put(r,bins.get(r)+1);
        }
        for(R r:R.values())
            System.out.println(r+" "+r.p+" "+bins.get(r)/(double)n);
    }
}
0 голосов
/ 11 марта 2011

Вы можете создать перечисление со связанными данными, предоставив пользовательский конструктор, и использовать конструктор для назначения весов для вероятностей, а затем

public enum WeightedEnum {
    ONE(1), TWO(2), THREE(3);
    private WeightedEnum(int weight) {
        this.weight = weight;
    }
    public int getWeight() {
        return this.weight;
    }
    private final int weight;

    public static WeightedEnum randomType()  {
        // select one based on random value and relative weight
    }
}
0 голосов
/ 11 марта 2011

Предполагая, что у вас есть конечное число значений, вы можете иметь отдельный массив (float [] weights;) весов для каждого значения. Эти значения будут между 0 и 1. Когда вы выбираете случайное значение, также генерируйте другое случайное число между и выбирайте значение только в том случае, если второе сгенерированное число меньше веса для этого значения.

0 голосов
/ 11 марта 2011

Вот общий подход к случайному выбору значения enum. Вы можете отрегулировать вероятности, как предложено здесь .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...