web-dev-qa-db-fra.com

Comment choisir un objet par sa probabilité?

J'ai une liste d'articles. Chacun de ces éléments a sa propre probabilité.

Quelqu'un peut-il suggérer un algorithme pour choisir un élément en fonction de sa probabilité?

56
Ruzanna

Ainsi, avec chaque article stocké un nombre qui marque sa probabilité relative, par exemple, si vous avez 3 articles, l'un d'entre eux devrait être deux fois plus susceptible d'être sélectionné que l'un des deux autres, alors votre liste aura:

 [{A,1},{B,1},{C,2}]

Additionnez ensuite les nombres de la liste (c'est-à-dire 4 dans notre cas) . Générez maintenant un nombre aléatoire et choisissez cet index . Int index = Rand.nextInt (4); Renvoyez le nombre tel que index est dans la plage correcte.

Code Java:

class Item {
    int relativeProb;
    String name;

    //Getters Setters and Constructor
}

...

class RandomSelector {
    List<Item> items = new List();
    Random Rand = new Random();
    int totalSum = 0;

    RandomSelector() {
        for(Item item : items) {
            totalSum = totalSum + item.relativeProb;
        }
    }

    public Item getRandom() {

        int index = Rand.nextInt(totalSum);
        int sum = 0;
        int i=0;
        while(sum < index ) {
             sum = sum + items.get(i++).relativeProb;
        }
        return items.get(Math.max(0,i-1));
    }
}
31
Usman Ismail
  1. Générez un nombre aléatoire uniformément distribué.
  2. Parcourez votre liste jusqu'à ce que la probabilité cumulée des éléments visités soit supérieure au nombre aléatoire

Exemple de code:

double p = Math.random();
double cumulativeProbability = 0.0;
for (Item item : items) {
    cumulativeProbability += item.probability();
    if (p <= cumulativeProbability) {
        return item;
    }
}
70
Brent Worden

prétendre que nous avons la liste suivante

Item A 25%
Item B 15%
Item C 35%
Item D 5%
Item E 20%

Supposons que toutes les probabilités soient des entiers et attribuons à chaque élément une "plage" calculée comme suit.

Start - Sum of probability of all items before
End - Start + own probability

Les nouveaux numéros sont les suivants

Item A 0 to 25
Item B 26 to 40
Item C 41 to 75
Item D 76 to 80
Item E 81 to 100

Choisissez maintenant un nombre aléatoire compris entre 0 et 100. Disons que vous en choisissez 32. 32 se situent dans la plage de l'élément B. 

mj

23
mj_

Vous pouvez essayer la Sélection de la roulette .

Tout d’abord, ajoutez toutes les probabilités, puis mettez-les à l’échelle de 1 en les divisant par la somme. Supposons que les probabilités sont A(0.4), B(0.3), C(0.25) and D(0.05). Vous pouvez ensuite générer un nombre à virgule flottante aléatoire compris dans la plage [0, 1]. Maintenant, vous pouvez décider comme ceci:

random number between 0.00 and 0.40 -> pick A
              between 0.40 and 0.70 -> pick B
              between 0.70 and 0.95 -> pick C
              between 0.95 and 1.00 -> pick D

Vous pouvez également le faire avec des entiers aléatoires - par exemple, vous générez un entier aléatoire compris entre 0 et 99 (inclus), puis vous pouvez prendre une décision comme celle décrite ci-dessus.

11
0605002

L'algorithme décrit dans les réponses de Ushman , Brent et @ kaushaya est implémenté dans la bibliothèque Apache commons-math .

Jetez un coup d’œil à la classe EnumeratedDistribution (le code groovy suit):

def probabilities = [
   new Pair<String, Double>("one", 25),
   new Pair<String, Double>("two", 30),
   new Pair<String, Double>("three", 45)]
def distribution = new EnumeratedDistribution<String>(probabilities)
println distribution.sample() // here you get one of your values

Notez que la somme des probabilités n'a pas besoin d'être égale à 1 ou à 100 - elle sera normalisée automatiquement.

9
Dmitry

Ma méthode est assez simple. Générer un nombre aléatoire. Maintenant que les probabilités de vos éléments sont connues, il vous suffit de parcourir la liste de probabilités triée et de choisir l'élément dont la probabilité est inférieure au nombre généré aléatoirement.

Pour plus de détails, lisez ma réponse ici .

5
HackCode

Une méthode lente mais simple consiste à demander à chaque membre de choisir un nombre aléatoire en fonction de sa probabilité et de choisir celui dont la valeur est la plus élevée.

Analogie:

Imaginez qu’une personne sur trois ait besoin d’être choisie, mais leurs probabilités sont différentes. Vous leur donnez la mort avec une quantité différente de visages. Les dés de la première personne ont 4 faces, les 6 de la 2e personne et les 8 de la troisième. Ils lancent leur dé et celui avec le plus grand nombre gagne.

Disons que nous avons la liste suivante:

[{A,50},{B,100},{C,200}]

Pseudocode:

 A.value = random(0 to 50);
 B.value = random(0 to 100);
 C.value = random (0 to 200);

Nous choisissons celui qui a la plus grande valeur.

Cette méthode ci-dessus ne mappe pas exactement les probabilités. Par exemple, 100 n'aura pas deux fois plus de chances que 50. Mais nous pouvons le faire en modifiant légèrement la méthode. 

Méthode 2

Au lieu de choisir un nombre compris entre 0 et le poids, nous pouvons les limiter de la limite supérieure de la variable précédente à l'ajout de la variable actuelle.

[{A,50},{B,100},{C,200}]

Pseudocode:

 A.lowLimit= 0; A.topLimit=50;
 B.lowLimit= A.topLimit+1; B.topLimit= B.lowLimit+100
 C.lowLimit= B.topLimit+1; C.topLimit= C.lowLimit+200 

limites résultantes

A.limits = 0,50
B.limits = 51,151
C.limits = 152,352

Ensuite, nous choisissons un nombre aléatoire de 0 à 352 et le comparons aux limites de chaque variable pour voir si le nombre aléatoire se trouve dans ses limites. 

Je crois que ce Tweak a de meilleures performances puisqu'il n'y a qu'une génération aléatoire.

Il existe une méthode similaire dans d'autres réponses mais cette méthode n'exige pas que le total soit 100 ou 1,00.

4
WVrock

La réponse de Brent est bonne, mais elle ne tient pas compte de la possibilité de choisir à tort un élément avec une probabilité de 0 dans les cas où p = 0. C'est assez facile à gérer en vérifiant la probabilité (ou peut-être en ajoutant élément en premier lieu):

double p = Math.random();
double cumulativeProbability = 0.0;
for (Item item : items) {
    cumulativeProbability += item.probability();
    if (p <= cumulativeProbability && item.probability() != 0) {
        return item;
    }
}
1
Dallas Edwards

Si cela ne vous dérange pas d'ajouter une dépendance tierce dans votre code, vous pouvez utiliser la méthode MockNeat.probabilities ()

Par exemple:

String s = mockNeat.probabilites(String.class)
                .add(0.1, "A") // 10% chance to pick A
                .add(0.2, "B") // 20% chance to pick B
                .add(0.5, "C") // 50% chance to pick C
                .add(0.2, "D") // 20% chance to pick D
                .val();

Déni de responsabilité: je suis l'auteur de la bibliothèque, je peux donc être partial lorsque je le recommande.

0
Andrei Ciobanu