web-dev-qa-db-fra.com

Python aléatoire pondéré

Je dois renvoyer des valeurs différentes basées sur un tournoi à la ronde pondéré de telle sorte que 1 sur 20 obtient A, 1 sur 20 obtient B, et le reste passe à C.

Alors:

A => 5%
B => 5%
C => 90%

Voici une version de base qui semble fonctionner:

import random

x = random.randint(1, 100)

if x <= 5:
    return 'A'
Elif x > 5 and x <= 10:
    return 'B'
else:
    return 'C'

Cet algorithme est-il correct? Si oui, peut-il être amélioré?

25
doremi

Votre algorithme est correct, que diriez-vous de quelque chose de plus élégant:

import random
my_list = ['A'] * 5 + ['B'] * 5 + ['C'] * 90
random.choice(my_list)
50
jurgenreza

c'est très bien. plus généralement, vous pouvez définir quelque chose comme:

from collections import Counter
from random import randint

def weighted_random(pairs):
    total = sum(pair[0] for pair in pairs)
    r = randint(1, total)
    for (weight, value) in pairs:
        r -= weight
        if r <= 0: return value

results = Counter(weighted_random([(1,'a'),(1,'b'),(18,'c')])
                  for _ in range(20000))
print(results)

qui donne

Counter({'c': 17954, 'b': 1039, 'a': 1007})

qui est aussi proche de 18: 1: 1 que vous pouvez vous y attendre.

32
andrew cooke

Si vous souhaitez utiliser un aléatoire pondéré et non un aléatoire centile, vous pouvez créer votre propre classe Randomizer:

import random

class WeightedRandomizer:
    def __init__ (self, weights):
        self.__max = .0
        self.__weights = []
        for value, weight in weights.items ():
            self.__max += weight
            self.__weights.append ( (self.__max, value) )

    def random (self):
        r = random.random () * self.__max
        for ceil, value in self.__weights:
            if ceil > r: return value

w = {'A': 1.0, 'B': 1.0, 'C': 18.0}
#or w = {'A': 5, 'B': 5, 'C': 90}
#or w = {'A': 1.0/18, 'B': 1.0/18, 'C': 1.0}
#or or or

wr = WeightedRandomizer (w)

results = {'A': 0, 'B': 0, 'C': 0}
for i in range (10000):
    results [wr.random () ] += 1

print ('After 10000 rounds the distribution is:')
print (results)
9
Hyperboreus

Cela semble correct puisque vous utilisez une variable aléatoire uniform avec des tirages indépendants, la probabilité pour chaque nombre sera 1/n (n = 100).

Vous pouvez facilement vérifier votre algorithme en l'exécutant par exemple 1000 fois et voir la fréquence de chaque lettre.

Un autre algorithme que vous pourriez envisager est de générer un tableau avec vos lettres en fonction de la fréquence que vous souhaitez pour chaque lettre et de générer uniquement un nombre aléatoire unique qui est l'indice dans le tableau

Il sera moins efficace en mémoire mais devrait mieux fonctionner

Modifier:

Pour répondre au commentaire de @Joel Cornett, un exemple sera très similaire à @jurgenreza mais plus efficace en mémoire

import random
data_list = ['A'] + ['B'] + ['C'] * 18
random.choice(data_list )
0
iTech