来自枚举的随机值的概率

发布于 2024-10-21 05:59:12 字数 665 浏览 2 评论 0原文

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

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

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

此处找到的代码

I have an enum that I would like to randomly select a value from, but not truly random. I would like some of the values to be less likely of being selected so far. Here is what I have so far...

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

Is there an efficient way of assigning probabilities to each of these values?

Code found from here

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

友谊不毕业 2024-10-28 05:59:12

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

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

several ways to do it, one of them, similar to your approach

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;
       for(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();
    }
}
拥醉 2024-10-28 05:59:12

这是随机选择枚举值的通用方法。您可以按照此处的建议调整概率。

Here's a generic approach to choosing an enum value at random. You can adjust the probabilities as suggested here.

我喜欢麦丽素 2024-10-28 05:59:12

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

Assuming you have a finite number of values you could have a separate array (float[] weights;) of weights for each value. These values would be between 0 and 1. When you select a random value also generate another random number between and only select the value if the second generated number is below the weight for that value.

⒈起吃苦の倖褔 2024-10-28 05:59:12

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

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

You can create an enum with associated data bby provding a custom constructor, and use the constructor to assign weightings for the probabilities and then

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
    }
}
去了角落 2024-10-28 05:59:12
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);
    }
}
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);
    }
}
蛮可爱 2024-10-28 05:59:12

这是另一种选择,它允许在运行时指定分发。

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

Here is another alternative which allows the distribution to be specified at runtime.

Includes suggestion from Alexey Sviridov. Also method random() could incorporate suggestion from Ted Dunning when there are many options.

     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();
        }
     }
葬花如无物 2024-10-28 05:59:12

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

You can use EnumeratedDistribution from the Apache Commons Math library.

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();
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文