2011-03-11 126 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(); 
     } 
    }