2011-03-11 2 views
5

J'ai une énumération que je voudrais sélectionner aléatoirement, mais pas vraiment aléatoire. J'aimerais que certaines valeurs soient moins susceptibles d'être sélectionnées jusqu'à maintenant. Voici ce que j'ai jusqu'à présent ...Valeur aléatoire d'enum avec probabilité

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-t-il un moyen efficace d'attribuer des probabilités à chacune de ces valeurs?

code trouvé de here

Répondre

6

plusieurs façons de le faire, l'un d'entre eux, semblable à votre approche

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

Je pense que vous avez mal compris. L'idée est - vous somm tous les poids. Maintenant, imaginez une règle avec max = valeur calculée. Maintenant, dash chaque poids sur cette règle, donc d'abord sera (selon mon exemple en réponse) 0 à 10, deuxième 10 à 11, troisième 11 à 16 et ainsi de suite. Maintenant pointez votre doigt vers l'endroit aléatoire de notre règle et voyez dans quel segment vous pointez. Réponse éditée –

+0

C'est en fait ce que j'ai fini par faire. Merci! – tgrosinger

+0

BTW vous pouvez améliorer perfomance si précalculer les limites de vous tapez dans le constructeur, mais sur cinq types ce n'est pas réel. –

0

Voici un approach générique de choisir une valeur enum au hasard. Vous pouvez ajuster les probabilités comme suggéré here.

0

En supposant que vous avez un nombre fini de valeurs, vous pourriez avoir un tableau séparé (poids float [];) de poids pour chaque valeur. Ces valeurs seraient comprises entre 0 et 1. Lorsque vous sélectionnez une valeur aléatoire, générez également un autre nombre aléatoire entre et sélectionnez uniquement la valeur si le second nombre généré est inférieur au poids de cette valeur.

0

Vous pouvez créer un ENUM avec des données associées BBY provding un constructeur personnalisé et utiliser le constructeur pour attribuer des pondérations pour les probabilités et

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

Je ne comprends pas ce qui se passe dans la méthode principale. Semble être beaucoup plus compliqué que la méthode proposée par @AlexeySviridov que j'ai simplifié un peu plus même. – tgrosinger

+0

juste un peu de code pour exercer des puts aléatoires et voir à quoi ressemble la distribution. –

0

Voici une autre alternative qui permet la distribution à préciser lors de l'exécution.

Comprend une suggestion d'Alexey Sviridov. Aussi la méthode random() pourrait incorporer la suggestion de Ted Dunning quand il y a beaucoup d'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(); 
     } 
    } 
Questions connexes