2011-03-11 96 views
5

我有一个枚举,我想随机选择一个值,但不是真正的随机。我希望一些价值观被选中的可能性较小。以下是我迄今为止...随机的值从枚举的概率

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

回答

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

我认为你误解了。想法是 - 你收到所有的重量。现在想象一下max =计算值的标尺。现在在这个规则中突破每一个重量,所以首先将(根据我的答案)0到10,第二个10到11,第11到第16个等等。现在将你的手指指向我们的统治者的随机位置,并看看你指向哪一段。编辑答案。 – 2011-03-11 07:05:17

+0

这实际上是我最终做的。谢谢! – tgrosinger 2011-03-11 07:16:58

+0

顺便说一句,如果在构造函数中预先计算你的边界类型,你可以提高性能,但是在五种类型中它不是实际的。 – 2011-03-11 11:11:14

0

发现这里是一个通用approach来选择随机的enum值。您可以按照建议here调整概率。

0

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

0

您可以创建相关数据枚举BBY provding一个自定义的构造函数,并使用构造函数指定的概率权重,然后

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

我不明白在主要方法中正在做的东西。似乎比@AlexeySviridov提出的方法更加复杂,我甚至简化了一些。 – tgrosinger 2011-03-11 23:00:44

+0

只是一些代码来锻炼随机放入并看看分布的外观。 – 2011-10-06 00:21:00

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