223 votes

Générer des nombres aléatoires avec une distribution (numérique) donnée

Je dispose d'un fichier contenant des probabilités pour différentes valeurs, par exemple :

1 0.1
2 0.05
3 0.05
4 0.2
5 0.4
6 0.2

J'aimerais générer des nombres aléatoires à l'aide de cette distribution. Existe-t-il un module qui gère cela ? C'est assez simple à coder soi-même (construire la fonction de densité cumulative, générer une valeur aléatoire [0,1] et choisir la valeur correspondante) mais il semble que ce soit un problème courant et que quelqu'un ait probablement créé une fonction/module pour cela.

J'ai besoin de cela parce que je veux générer une liste d'anniversaires (qui ne suivent aucune distribution dans le modèle standard). random ).

201voto

Sven Marnach Points 133943

scipy.stats.rv_discrete pourrait être ce que vous voulez. Vous pouvez fournir vos probabilités via le formulaire values paramètre. Vous pouvez alors utiliser la fonction rvs() de l'objet distribution pour générer des nombres aléatoires.

Comme l'a fait remarquer Eugene Pakhomov dans les commentaires, vous pouvez également passer un p à l'aide du mot-clé numpy.random.choice() , par exemple

numpy.random.choice(numpy.arange(1, 7), p=[0.1, 0.05, 0.05, 0.2, 0.4, 0.2])

Si vous utilisez Python 3.6 ou plus, vous pouvez utiliser random.choices() à partir de la bibliothèque standard - voir la page réponse de Mark Dickinson .

185voto

Mark Dickinson Points 6780

Depuis Python 3.6, il existe une solution à ce problème dans la bibliothèque standard de Python, à savoir random.choices .

Exemple d'utilisation : établissons une population et des poids correspondant à ceux de la question de l'OP :

>>> from random import choices
>>> population = [1, 2, 3, 4, 5, 6]
>>> weights = [0.1, 0.05, 0.05, 0.2, 0.4, 0.2]

Maintenant choices(population, weights) génère un seul échantillon, contenu dans une liste de longueur 1 :

>>> choices(population, weights)
[4]

L'argument facultatif de mot-clé uniquement k permet de demander plusieurs échantillons à la fois. C'est très utile parce qu'il y a un travail préparatoire à faire. random.choices doit faire à chaque fois qu'il est appelé, avant de générer des échantillons ; en générant de nombreux échantillons à la fois, nous n'avons à faire ce travail préparatoire qu'une seule fois. Ici, nous générons un million d'échantillons et utilisons collections.Counter pour vérifier que la distribution que nous obtenons correspond à peu près aux poids que nous avons donnés.

>>> million_samples = choices(population, weights, k=10**6)
>>> from collections import Counter
>>> Counter(million_samples)
Counter({5: 399616, 6: 200387, 4: 200117, 1: 99636, 3: 50219, 2: 50025})

32voto

sdcvvc Points 14968

L'avantage de générer la liste à l'aide de la FCD est que vous pouvez utiliser la recherche binaire. Bien qu'il faille O(n) de temps et d'espace pour le prétraitement, vous pouvez obtenir k nombres en O(k log n). Comme les listes Python normales sont inefficaces, vous pouvez utiliser array module.

Si vous insistez sur un espace constant, vous pouvez faire ce qui suit : O(n) temps, O(1) espace.

def random_distr(l):
    r = random.uniform(0, 1)
    s = 0
    for item, prob in l:
        s += prob
        if s >= r:
            return item
    return item  # Might occur because of floating point inaccuracies

18voto

Marcelo Cantos Points 91211

(D'accord, je sais que vous demandez des emballages rétractables, mais peut-être que ces solutions maison n'étaient pas assez succinctes à votre goût :-)

pdf = [(1, 0.1), (2, 0.05), (3, 0.05), (4, 0.2), (5, 0.4), (6, 0.2)]
cdf = [(i, sum(p for j,p in pdf if j < i)) for i,_ in pdf]
R = max(i for r in [random.random()] for i,c in cdf if c <= r)

J'ai pseudo-confirmé que cela fonctionne en regardant la sortie de cette expression :

sorted(max(i for r in [random.random()] for i,c in cdf if c <= r)
       for _ in range(1000))

18voto

Ramon Martinez Points 48

Il est peut-être un peu tard. Mais vous pouvez utiliser numpy.random.choice() , en passant le p paramètre :

val = numpy.random.choice(numpy.arange(1, 7), p=[0.1, 0.05, 0.05, 0.2, 0.4, 0.2])

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X