Alors que la réponse sélectionnée fonctionne, il est malheureusement asymptotiquement lent pour votre cas d'utilisation. Au lieu de cela, vous pouvez utiliser quelque chose qui s'appelle l'Alias de l'Échantillonnage. Alias échantillonnage (ou l'alias de la méthode) est une technique utilisée pour la sélection d'éléments avec une distribution pondérée. Si le poids de choisir ces éléments ne permet pas de modifier vous pouvez faire la sélection en O(1) le temps!. Si ce n'est pas le cas, vous pouvez toujours obtenir amorti O(1) fois si le rapport entre le nombre de sélections que vous faites et les modifications que vous apportez à la table d'alias (changement de poids) est élevé. Le courant sélectionné réponse suggère un algorithme O(N), la meilleure chose à faire est de O(log(N)), étant donné triés probabilités et de la recherche binaire, mais rien ne se passe de battre le O(1) fois ai-je suggéré.
Ce site fournit un bon aperçu de l'Alias de la méthode qui est la plupart du temps la langue agnostique. Essentiellement, vous créez un tableau, où chaque entrée représente l'aboutissement de deux probabilités. Il y a un seuil unique pour chaque entrée de la table, en dessous du seuil que vous obtenez une valeur, ci-dessus, vous obtenez une autre valeur. Vous étalez les plus grandes probabilités à travers de multiples valeurs de la table afin de créer une probabilité graphique avec une superficie de un pour toutes les probabilités combinées.
Dire que vous avez l'probabilités A, B, C, et D, qui ont les valeurs de 0.1, 0.1, 0.1 et 0.7 respectivement. Alias méthode de propagation de la probabilité de 0,7 à tous les autres. Un index correspondent à chaque probabilité, où vous auriez l'0,1 et 0,15 pour ABC, et de 0,25 pour D index. Avec cela, vous normaliser chaque probabilité de sorte que vous vous retrouvez avec de 0,4 chance d'obtenir Un et 0,6 chance de se D dans Un index (0.1/(0.1 + 0.15) et 0,15/(0.1 + 0.15) respecively) ainsi que B et C de l'index, et 100% de chance de tomber D au D de l'index (de 0,25/0,25 1).
Compte tenu de l'impartialité uniforme PRNG (Math.Random()) pour l'indexation, vous obtenez une probabilité égale de choix de chaque indice, mais vous pouvez également faire une pièce de monnaie par l'index qui fournit le pondérée de la probabilité. Vous avez 25% de chance de se poser sur l'Un ou D de la fente, mais à l'intérieur que vous n'avez 40% de chance de choisir Un, et 60% de D. .40 * .25 = 0.1, a l'origine, notre probabilité, et si vous additionnez tous D probabilités éparpillés à travers les autres indices, vous obtiendrez .70 à nouveau.
Afin de faire la sélection au hasard, vous avez besoin seulement de générer de façon aléatoire un indice de 0 à N, puis faire un pile ou face, peu importe combien d'articles que vous ajoutez, c'est très rapide et à coût constant. Faire une table d'alias ne prend pas beaucoup de lignes de code, ma version de python prend 80 lignes y compris les déclarations d'importation et les sauts de ligne, et la version présentée dans les Pandas de l'article est de la même taille (et C++)
Pour votre java mise en œuvre pourrait correspondre entre probabilités et tableau liste les indices de vos fonctions, vous devez exécuter, la création d'un éventail de fonctions qui sont exécutées comme vous l'index de chaque, vous pouvez également utiliser la fonction des objets (foncteurs) qui ont une méthode qui vous permet de passer des paramètres à exécuter.
ArrayList<(YourFunctionObject)> function_list;
// add functions
AliasSampler aliassampler = new AliasSampler(listOfProbabilities);
// somewhere later with some type T and some parameter values.
int index = aliassampler.sampleIndex();
T result = function_list[index].apply(parameters);
EDIT:
J'ai créé une version en java de la AliasSampler méthode, à l'aide de classes, il utilise l'exemple de l'indice de la méthode et doit pouvoir être utilisé comme ci-dessus.
import java.util.ArrayList;
import java.util.Collections;
import java.util.Random;
public class AliasSampler {
private ArrayList<Double> binaryProbabilityArray;
private ArrayList<Integer> aliasIndexList;
AliasSampler(ArrayList<Double> probabilities){
// java 8 needed here
assert(DoubleStream.of(probabilities).sum() == 1.0);
int n = probabilities.size();
// probabilityArray is the list of probabilities, this is the incoming probabilities scaled
// by the number of probabilities. This allows us to figure out which probabilities need to be spread
// to others since they are too large, ie [0.1 0.1 0.1 0.7] = [0.4 0.4 0.4 2.80]
ArrayList<Double> probabilityArray;
for(Double probability : probabilities){
probabilityArray.add(probability);
}
binaryProbabilityArray = new ArrayList<Double>(Collections.nCopies(n, 0.0));
aliasIndexList = new ArrayList<Integer>(Collections.nCopies(n, 0));
ArrayList<Integer> lessThanOneIndexList = new ArrayList<Integer>();
ArrayList<Integer> greaterThanOneIndexList = new ArrayList<Integer>();
for(int index = 0; index < probabilityArray.size(); index++){
double probability = probabilityArray.get(index);
if(probability < 1.0){
lessThanOneIndexList.add(index);
}
else{
greaterThanOneIndexList.add(index);
}
}
// while we still have indices to check for in each list, we attempt to spread the probability of those larger
// what this ends up doing in our first example is taking greater than one elements (2.80) and removing 0.6,
// and spreading it to different indices, so (((2.80 - 0.6) - 0.6) - 0.6) will equal 1.0, and the rest will
// be 0.4 + 0.6 = 1.0 as well.
while(lessThanOneIndexList.size() != 0 && greaterThanOneIndexList.size() != 0){
//https://stackoverflow.com/questions/16987727/removing-last-object-of-arraylist-in-java
// last element removal is equivalent to pop, java does this in constant time
int lessThanOneIndex = lessThanOneIndexList.remove(lessThanOneIndexList.size() - 1);
int greaterThanOneIndex = greaterThanOneIndexList.remove(greaterThanOneIndexList.size() - 1);
double probabilityLessThanOne = probabilityArray.get(lessThanOneIndex);
binaryProbabilityArray.set(lessThanOneIndex, probabilityLessThanOne);
aliasIndexList.set(lessThanOneIndex, greaterThanOneIndex);
probabilityArray.set(greaterThanOneIndex, probabilityArray.get(greaterThanOneIndex) + probabilityLessThanOne - 1);
if(probabilityArray.get(greaterThanOneIndex) < 1){
lessThanOneIndexList.add(greaterThanOneIndex);
}
else{
greaterThanOneIndexList.add(greaterThanOneIndex);
}
}
//if there are any probabilities left in either index list, they can't be spread across the other
//indicies, so they are set with probability 1.0. They still have the probabilities they should at this step, it works out mathematically.
while(greaterThanOneIndexList.size() != 0){
int greaterThanOneIndex = greaterThanOneIndexList.remove(greaterThanOneIndexList.size() - 1);
binaryProbabilityArray.set(greaterThanOneIndex, 1.0);
}
while(lessThanOneIndexList.size() != 0){
int lessThanOneIndex = lessThanOneIndexList.remove(lessThanOneIndexList.size() - 1);
binaryProbabilityArray.set(lessThanOneIndex, 1.0);
}
}
public int sampleIndex(){
int index = new Random().nextInt(binaryProbabilityArray.size());
double r = Math.random();
if( r < binaryProbabilityArray.get(index)){
return index;
}
else{
return aliasIndexList.get(index);
}
}
}