Los pesos definen una función de distribución de probabilidad (pdf). Los números aleatorios a partir de cualquiera de tales pdf pueden ser generados por applying its associated inverse cumulative distribution function a números aleatorios uniformes entre 0 y 1.
Ver también este SO explanation, o, como se explica por Wikipedia:
If Y has a U[0,1] distribution then F⁻¹(Y) is distributed as F. This is used in random number generation using the inverse transform sampling-method.
import random
import bisect
import collections
def cdf(weights):
total = sum(weights)
result = []
cumsum = 0
for w in weights:
cumsum += w
result.append(cumsum/total)
return result
def choice(population, weights):
assert len(population) == len(weights)
cdf_vals = cdf(weights)
x = random.random()
idx = bisect.bisect(cdf_vals, x)
return population[idx]
weights=[0.3, 0.4, 0.3]
population = 'ABC'
counts = collections.defaultdict(int)
for i in range(10000):
counts[choice(population, weights)] += 1
print(counts)
# % test.py
# defaultdict(<type 'int'>, {'A': 3066, 'C': 2964, 'B': 3970})
enter code here
La función choice
anteriormente usa bisect.bisect
, por lo que la selección de una variable aleatoria ponderada se realiza en O(log n)
donde n
es la longitud de weights
.
Nota que a partir de la versión 1.7.0, NumPy tiene una Cythonized np.random.choice function. Por ejemplo, esto genera 1000 muestras de la población [0,1,2,3]
con pesos [0.1, 0.2, 0.3, 0.4]
:
import numpy as np
np.random.choice(4, 1000, p=[0.1, 0.2, 0.3, 0.4])
np.random.choice
también tiene un parámetro replace
para el muestreo con o sin sustitución.
Un teóricamente mejor algoritmo es el Alias Method. Construye una tabla que requiere O(n)
tiempo, pero después de eso, las muestras se pueden dibujar en O(1)
vez. Entonces, si necesita dibujar muchas muestras, en teoría, el Método Alias puede ser más rápido. Hay una implementación de Python del Método Walker Alias here y un numpy version here.
La búsqueda encontró varias preguntas similares/idénticas [aquí] (http://stackoverflow.com/questions/526255/probability-distribution-in-python) y [aquí] (http://stackoverflow.com/questions/1056151/random-python-dictionary-key-weighted-by-values) – snapshoe
@ ma3: Uno de los puntos débiles de este sitio es que, como las viejas preguntas tienden a ignorarse, no existe un mecanismo o motivación para mejorarlas. Creo que mi respuesta es significativamente mejor que al menos las respuestas más altas a esas preguntas, no he leído las más bajas, pero nadie lo vería si las publicara en esas. –