¿Cómo simulo un dado sesgado en python?

Quiero simular un dado sesgado de lado N?

def roll(N,bias): '''this function rolls N dimensional die with biasing provided''' # do something return result >> N=6 >> bias=( 0.20,0.20,0.15,0.15,0.14,0.16,) >> roll(N,bias) 2 

Un poco de matemáticas aquí.

Un dado regular dará a cada número 1-6 con la misma probabilidad, a saber, 1/6 . Esto se conoce como distribución uniforme (la versión discreta de la misma, a diferencia de la versión continua). Lo que significa que si X es una variable aleatoria que describe el resultado de un solo rol, entonces X~U[1,6] , lo que significa que X se distribuye por igual contra todos los resultados posibles de la tirada del dado, 1 a 6.

Esto es igual a elegir un número en [0,1) y dividirlo en 6 secciones: [0,1/6) , [1/6,2/6) , [2/6,3/6) , [3/6,4/6) , [4/6,5/6) , [5/6,1) .

Usted está solicitando una distribución diferente, que está sesgada. La forma más fácil de lograr esto es dividir la sección [0,1) en 6 partes, dependiendo del sesgo que desee. Entonces, en su caso, querría dividirlo en lo siguiente: [0,0.2) , [0.2,0.4) , [0.4,0.55) , 0.55,0.7) , [0.7,0.84) , [0.84,1) .

Si observa la entrada de wikipedia , verá que, en este caso, la función de probabilidad acumulada no estará compuesta de 6 partes de igual longitud sino de 6 partes que difieren en longitud de acuerdo con el sesgo que le dio. Lo mismo ocurre con la distribución masiva.

De vuelta a la pregunta, dependiendo del idioma que esté utilizando, simplemente tradúzcalo de nuevo a su tirada. En Python, aquí hay un ejemplo muy incompleto, aunque en funcionamiento:

 import random sampleMassDist = (0.2, 0.1, 0.15, 0.15, 0.25, 0.15) # assume sum of bias is 1 def roll(massDist): randRoll = random.random() # in [0,1) sum = 0 result = 1 for mass in massDist: sum += mass if randRoll < sum: return result result+=1 print roll(sampleMassDist) 

Más lenguaje agnóstico, pero podría usar una tabla de búsqueda.

Utilice un número aleatorio en el rango 0-1 y busque el valor en una tabla:

 0.00 - 0.20 1 0.20 - 0.40 2 0.40 - 0.55 3 0.55 - 0.70 4 0.70 - 0.84 5 0.84 - 1.00 6 
 import random def roll(sides, bias_list): assert len(bias_list) == sides number = random.uniform(0, sum(bias_list)) current = 0 for i, bias in enumerate(bias_list): current += bias if number <= current: return i + 1 

El sesgo será proporcional.

 >>> print roll(6, (0.20, 0.20, 0.15, 0.15, 0.14, 0.16)) 6 >>> print roll(6, (0.20, 0.20, 0.15, 0.15, 0.14, 0.16)) 2 

También podría usar números enteros (mejor):

 >>> print roll(6, (10, 1, 1, 1, 1, 1)) 5 >>> print roll(6, (10, 1, 1, 1, 1, 1)) 1 >>> print roll(6, (10, 1, 1, 1, 1, 1)) 1 >>> print roll(6, (10, 5, 5, 10, 4, 8)) 2 >>> print roll(6, (1,) * 6) 4 

Es un poco sorprendente que la respuesta np.random.choice no se haya dado aquí.

 from numpy import random def roll(N,bias): '''this function rolls N dimensional die with biasing provided''' return random.choice(np.range(N),p=bias) 

La opción p da “las probabilidades asociadas con cada entrada en a “, donde a es np.range(N) para nosotros. “Si no se proporciona, la muestra asume una distribución uniforme sobre todas las entradas en un “.

Vea la receta del método de alias de Walker para objetos aleatorios con diferentes probabilidades.
Un ejemplo, cadenas ABC o D con probabilidades .1 .2 .3 .4 –

 abcd = dict( A=1, D=4, C=3, B=2 ) # keys can be any immutables: 2d points, colors, atoms ... wrand = Walkerrandom( abcd.values(), abcd.keys() ) wrand.random() # each call -> "A" "B" "C" or "D" # fast: 1 randint(), 1 uniform(), table lookup 

aclamaciones
– Denis

Solo para sugerir una solución más eficiente (y pythonic3), se puede usar bisect para buscar en el vector de valores acumulados, que además se puede calcular previamente y almacenar con la esperanza de que las llamadas posteriores a la función se refieran al mismo “sesgo” ( seguir el lenguaje de la pregunta).

 from bisect import bisect from itertools import accumulate from random import uniform def pick( amplitudes ): if pick.amplitudes != amplitudes: pick.dist = list( accumulate( amplitudes ) ) pick.amplitudes = amplitudes return bisect( pick.dist, uniform( 0, pick.dist[ -1 ] ) ) pick.amplitudes = None 

En ausencia de Python 3 acumulado, uno puede escribir un simple bucle para calcular la sum acumulada.

 from random import random biases = [0.0,0.3,0.5,0.99] coins = [1 if random() 

He creado un código para un diccionario que da un evento y la probabilidad correspondiente, devuelve la clave correspondiente, es decir, el evento de esa probabilidad.

 import random def WeightedDie(Probabilities): high_p = 0 rand = random.uniform(0,1) for j,i in Probabilities.items(): high_p = high_p + i if rand< high_p: return j