2009-01-26 34 views
7

Quiero simular el dado sesgado por el lado N?¿Cómo simulo un dado sesgado en python?

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 
+0

¿No podría haberle preguntado esto en lugar de su última pregunta? http://stackoverflow.com/questions/477237/how-do-i-simulate-flip-of-biased-coin-in-python –

+4

Creo que te refieres a un dado N-sided. Un dado N-dimensional es bastante diferente. – babbageclunk

+0

Duplicado: http://stackoverflow.com/questions/477237/how-do-i-simulate-flip-of-biased-coin-in-python –

Respuesta

23

Un poco de matemáticas aquí.

Un dado regular le dará a cada número 1-6 la misma probabilidad, es decir, 1/6. Esto se conoce como uniform distribution (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 único papel a continuación X~U[1,6] - lo que significa X se distribuye por igual en contra de todos los posibles resultados de la tirada, 1 a 6.

Esto es igual a la elección de un número en [0,1) mientras dividiéndolo 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).

Solicita una distribución diferente, que es parcial. La forma más fácil de lograr esto es dividir la sección [0,1) en 6 partes, según el sesgo que desee. Entonces, en su caso, deberí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 se toma un vistazo a la wikipedia entry, se verá que en este caso, la función de probabilidad acumulativa no estará compuesta de 6 partes de igual longitud, sino más bien de 6 partes que difieren en longitud de acuerdo con la sesgo tú los diste Lo mismo aplica para la distribución masiva.

Volviendo a la pregunta, dependiendo del idioma que esté utilizando, simplemente traduzca esto de vuelta a la tirada del dado. En Python, aquí es un muy mediocre, aunque trabajo, ejemplo:

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) 
+0

Aquí hay un pequeño problema. Los números de punto flotante tienen una precisión limitada y, por lo tanto, la suma de los pesos no será exactamente 1. No sé cuán importante es este efecto, pero puede ser más seguro usar enteros para los pesos. –

+0

@wcoenen: Son números * al azar *. Ninguna distribución de números aleatorios puede coincidir exactamente con el sesgo dado. Si el conjunto de números coincidiera con el sesgo dado, tendríamos que rechazarlo como no realmente aleatorio. –

+0

Gracias por muy buena explicación matemática. –

8

Más idioma agnóstico, pero se puede usar una tabla de búsqueda.

utilizar un número aleatorio en el rango de 0-1 y buscar 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 
+1

En el contexto de troqueles sesgados, podría ser útil mencionar la distribución uniforme del número aleatorio :) –

4
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 

podría utilizar números enteros también (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 
+0

Gracias por sugerir una solución limpia. –

1

Ver la receta para Walker's alias method de objetos al azar con diferentes probablities.
Un ejemplo, cadenas A B C 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 

aplausos
- Denis

+0

parece genial, pero ¿podría explicarlo? –

+0

David, para una distribución 1d de troquelado/troquelado en algunos puntos, búsqueda de tablas está bien, exageración del método de Walker; para distribuciones de, digamos, muchas estrellas en 3D, use Walker. (¿La receta/sus referencias tienen algún sentido?) – denis

0

Sólo para sugerir una solución más eficiente (y pythonic3), se puede utilizar bisect para buscar en el vector de valores acumulados - que por otra parte puede ser precalculados y almacenado con la esperanza de que las llamadas posteriores a la función se refieran al mismo "sesgo" (para seguir el lenguaje de preguntas).

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 se acumulan, uno puede escribir un bucle sencillo para calcular la suma acumulada.

0
from random import random 
biases = [0.0,0.3,0.5,0.99] 
coins = [1 if random()<bias else 0 for bias in biases] 
1

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

opción
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) 

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

0

he creado un código para un diccionario dando 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 
Cuestiones relacionadas