Java 概率枚举中的随机值

Java 概率枚举中的随机值,java,enums,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 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));
          }
    }
是否有一种有效的方法为每个值分配概率


中找到的代码这里有一个随机选择枚举值的通用方法。您可以根据建议调整概率。

假设值的数量有限,您可以使用单独的数组float[]weights;每个值的权重值。这些值将介于0和1之间。当您选择一个随机值时,还将生成另一个介于之间的随机数,并且仅当第二个生成的数低于该值的权重时才选择该值。

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

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

有几种方法,其中一种与您的方法类似

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

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

包括亚历克赛·斯维里多夫的建议。另外,当有很多选择时,随机方法可以结合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();
        }
     }
你可以从图书馆使用


我想你误解了。这个想法是——你把所有的重量加起来。现在想象一下最大值=计算值的标尺。现在,在这条规则上划掉每一个砝码,第一个砝码将根据我在答案0到10中的例子,第二个砝码是10到11,第三个砝码是11到16,依此类推。现在把你们的手指指向我们的尺子的任意位置,看看你们指的是哪一段。编辑后的答案。这就是我最后要做的。谢谢顺便说一句,如果在构造函数中预先计算类型的边界,您可以提高性能,但在五种类型上它不是实际的。当只有几个值可供选择时,此算法是标准算法。当有许多选项时,可以使用基于树的选择算法来加速算法。要执行此操作,请拆分[0,1]将区间划分为若干段,例如100。然后,对于每个区间,您确定样本是否恰好有一个可能的结果。如果有,请在树中用该值标记该叶。对于具有多个可能结果的任何段,您将构建另一个仅跨该段的树。根据r限制递归E关于概率的解决方案。我不理解主方法中所做的事情。似乎比@AlexeySviridov提出的方法复杂得多,我将其简化了一点。只需一些代码来练习随机看跌期权,看看分布情况如何。
     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();
        }
     }
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();