具有概率的枚举的随机值

时间:2011-03-11 05:19:54

标签: java enums

我有一个枚举,我想从中随机选择一个值,但不是真正随机的。我希望到目前为止,某些值不太可能被选中。这是我到目前为止所拥有的......

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

是否有一种有效的方法为每个值分配概率?

here

找到的代码

7 个答案:

答案 0 :(得分:6)

有几种方法可以做到,其中一种方法与您的方法类似

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

答案 1 :(得分:0)

以下是随机选择enum值的通用approach。您可以按建议here调整概率。

答案 2 :(得分:0)

假设您有一个有限数量的值,您可以为每个值设置一个单独的数组(float []权重;)。这些值介于0和1之间。当您选择随机值时,还会在其间生成另一​​个随机数,并且仅在第二个生成的数字低于该值的权重时选择该值。

答案 3 :(得分:0)

您可以通过提供自定义构造函数来创建包含关联数据的枚举,并使用构造函数为概率分配权重,然后

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
    }
}

答案 4 :(得分:0)

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

答案 5 :(得分:0)

这是另一种允许在运行时指定分发的替代方法。

包括Alexey Sviridov的建议。当有很多选项时,方法random()也可以包含来自Ted Dunning的建议。

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

答案 6 :(得分:0)

您可以使用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();