2011-03-11 17 views
5

Tengo una enumeración de la que me gustaría seleccionar aleatoriamente un valor, pero no realmente aleatorio. Me gustaría que algunos de los valores tengan menos probabilidades de ser seleccionados hasta ahora. Esto es lo que tengo hasta ahora ...Valor aleatorio de enum con probabilidad

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

¿Existe una manera eficiente de asignar probabilidades a cada uno de estos valores?

Código encuentra desde here

Respuesta

6

varias maneras de hacerlo, uno de ellos, de forma similar a su acercarse

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

Creo que has entendido mal. La idea es: sumas todos los pesos. Ahora imagine la regla con max = valor calculado. Ahora apunte cada peso sobre esta regla, por lo que primero será (de acuerdo con mi ejemplo en respuesta) de 0 a 10, del segundo 10 al 11, del 11 al 16 y así sucesivamente. Ahora señale con el dedo el lugar al azar de nuestra regla y vea en qué segmento apunta. Respuesta editada –

+0

Eso es realmente lo que terminé haciendo. ¡Gracias! – tgrosinger

+0

BTW puede mejorar el rendimiento si precalcula los límites de los tipos en el constructor, pero en cinco tipos no es real. –

0

Aquí es un genérico approach a la elección de un valor enum al azar. Puede ajustar las probabilidades según lo sugerido here.

0

Suponiendo que tiene un número finito de valores, podría tener una matriz separada (pesos de flotación [];) de pesos para cada valor. Estos valores estarían entre 0 y 1. Cuando selecciona un valor aleatorio también genera otro número aleatorio entre y solo selecciona el valor si el segundo número generado está por debajo del peso para ese valor.

0

Puede crear una enumeración con los datos asociados bby provding un constructor personalizado y utilizar el constructor para asignar pesos para las probabilidades y luego

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

No entiendo las cosas que se hacen en el método principal. Parece ser mucho más intrincado que el método propuesto por @AlexeySviridov, que simplifiqué un poco más. – tgrosinger

+0

solo un código para ejercitar puts al azar y ver cómo se ve la distribución. –

0

Aquí es otra alternativa que permite la distribución que se especificará en tiempo de ejecución.

Incluye sugerencia de Alexey Sviridov. También el método random() podría incorporar la sugerencia de Ted Dunning cuando hay muchas opciones.

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