Comment générer des nombres basés sur une distribution discrète arbitraire?
Par exemple, j'ai un ensemble de nombres que je veux générer. Disons qu'ils sont étiquetés de 1 à 3 comme suit.
1: 4%, 2: 50%, 3: 46%
Fondamentalement, les pourcentages sont des probabilités d'apparaître dans la sortie du générateur de nombres aléatoires. J'ai un générateur de nombres pesudorandom qui générera une distribution uniforme dans l'intervalle [0, 1]. Y a-t-il une manière de faire ça?
Il n'y a pas de limites sur le nombre d'éléments que je peux avoir, mais le% totalisera 100%.
distributions
FurtiveFelon
la source
la source
Réponses:
L'un des meilleurs algorithmes d'échantillonnage à partir d'une distribution discrète est la méthode des alias .
La méthode des alias pré-calcule (efficacement) une structure de données bidimensionnelle pour partitionner un rectangle en zones proportionnelles aux probabilités.
Dans ce schéma à partir du site référencé, un rectangle de hauteur de l' unité a été divisée en quatre types de régions - comme différenciées par la couleur - dans les proportions , 1 / trois , 1 / 12 et 1 / 12 , en afin d'échantillonner à plusieurs reprises à partir d'une distribution discrète avec ces probabilités. Les bandes verticales ont une largeur (unité) constante. Chacun est divisé en une ou deux pièces seulement. Les identités des pièces et les emplacements des divisions verticales sont stockés dans des tableaux accessibles via l'index des colonnes.1/2 1/3 1/12 1/12
Le tableau peut être échantillonné en deux étapes simples (une pour chaque coordonnée) nécessitant de générer seulement deux valeurs uniformes indépendantes et un calcul . Cela améliore le calcul O ( log ( n ) ) nécessaire pour inverser le CDF discret comme décrit dans d'autres réponses ici.O(1) O(log(n))
la source
Vous pouvez le faire facilement dans R, spécifiez simplement la taille dont vous avez besoin:
la source
Dans votre exemple, disons que vous dessinez votre valeur uniforme pseudo-aléatoire [0,1] et appelez-la U. Puis sortez:
1 si U <0,04
2 si U> = 0,04 et U <0,54
3 si U> = 0,54
Si les% spécifiés sont a, b, ..., sortez simplement
valeur 1 si U
valeur 2 si U> = a et U <(a + b)
etc.
Essentiellement, nous mappons le% en sous-ensembles de [0,1], et nous savons que la probabilité qu'une valeur aléatoire uniforme tombe dans n'importe quelle plage est simplement la longueur de cette plage. La mise en ordre des plages semble la façon la plus simple, sinon unique, de le faire. Cela suppose que vous posez des questions sur les distributions discrètes uniquement; pour continu, peut faire quelque chose comme "échantillonnage de rejet" ( entrée Wikipedia ).
la source
Supposons qu'il y ait résultats discrets possibles. Vous divisez l'intervalle [ 0 , 1 ] en sous-intervalles en fonction de la fonction de masse de probabilité cumulative, F , pour donner l' intervalle partitionné ( 0 , 1 )m [0,1] F (0,1)
où et F ( 0 ) ≡ 0 . Dans votre exemple, m = 3 etIj=(F(j−1),F(j)) F(0)≡0 m=3
puisque et F ( 2 ) = 0,54 et F ( 3 ) = 1 .F(1)=.04 F(2)=.54 F(3)=1
Ensuite, vous pouvez générer avec la distribution F en utilisant l'algorithme suivant:X F
(1) générerU∼Uniform(0,1)
(2) Si , alors X = j .U∈Ij X=j
TRUE
FALSE
FALSE
Notez que sera dans exactement l'un des intervalles I j car ils sont disjoints et partitionnent [ 0 , 1 ] .U Ij [0,1]
la source
min(which(u < cp))
? Il serait bon d'éviter également de recalculer la somme cumulée à chaque appel. Avec ce calcul préalable, l'algorithme entier est réduit àmin(which(runif(1) < cp))
. Ou mieux, parce que l'OP demande de générer des nombres ( pluriel ), vectorisez-le commen<-10; apply(matrix(runif(n),1), 2, function(u) min(which(u < cp)))
.Un algorithme simple consiste à commencer par votre nombre aléatoire uniforme et, dans une boucle, soustrayez d'abord la première probabilité, si le résultat est négatif, vous retournez la première valeur, s'il est toujours positif, vous passez à l'itération suivante et soustrayez la probabilité suivante , vérifiez s'il est négatif, etc.
C'est bien car le nombre de valeurs / probabilités peut être infini mais vous n'avez besoin de calculer les probabilités que lorsque vous vous approchez de ces nombres (pour quelque chose comme générer à partir d'une distribution de Poisson ou d'une distribution binomiale négative).
Si vous avez un ensemble fini de probabilités, mais que vous en générerez de nombreux nombres, il pourrait être plus efficace de trier les probabilités de sorte que vous soustrayiez d'abord la plus grande, puis la deuxième plus grande ensuite et ainsi de suite.
la source
Tout d'abord, permettez-moi d'attirer votre attention sur une bibliothèque python avec des classes prêtes à l'emploi pour la génération de nombres aléatoires entiers ou à virgule flottante qui suivent une distribution arbitraire.
D'une manière générale, il existe plusieurs approches à ce problème. Certains sont linéaires dans le temps, mais nécessitent une grande mémoire, certains s'exécutent en O (n log (n)). Certains sont optimisés pour les nombres entiers et certains sont définis pour les histogrammes circulaires (par exemple: générer des points temporels aléatoires pendant une journée). Dans la bibliothèque mentionnée ci-dessus, j'ai utilisé cet article pour les cas de nombres entiers et cette recette pour les nombres à virgule flottante. Il manque (encore) de support d'histogramme circulaire et est généralement désordonné, mais cela fonctionne bien.
la source
J'ai eu le même problème. Étant donné un ensemble où chaque élément a une probabilité et dont les probabilités des éléments se résument à un, je voulais tirer un échantillon efficacement, c'est-à-dire sans trier quoi que ce soit et sans itérer de manière répétée sur l'ensemble .
La fonction suivante tire le plus petit de nombres aléatoires uniformément répartis dans l'intervalle [ a , 1 ) . Soit r un nombre aléatoire de [ 0 , 1 ) .N [a,1) r [0,1)
la source