Pregunta

¿Quiero simular un troquel sesgado del 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
¿Fue útil?

Solución

Un poco de matemática aquí.

Un dado regular dará a cada número 1-6 con la misma probabilidad, es decir, 1/6. Esto se conoce como distribución uniforme (la versión discreta del mismo, en oposición a 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 [0,1) se distribuye equitativamente contra todos los resultados posibles de la tirada del dado, del 1 al 6.

Esto equivale a elegir un número en [0,1/6) mientras lo divide en 6 secciones: [1/6,2/6), [2/6,3/6), [3/6,4/6), [4/6,5/6), [5/6,1), [0,0.2).

Está solicitando una distribución diferente, que está sesgada. La forma más fácil de lograr esto es dividir la sección [0.2,0.4) en 6 partes según el sesgo que desee. Entonces, en su caso, desearía dividirlo en lo siguiente: [0.4,0.55), 0.55,0.7), [0.7,0.84), [0.84,1), <=>, <=>.

Si echa un vistazo a la entrada de wikipedia , 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 el sesgo que usted les dio. Lo mismo ocurre con la distribución masiva.

Volviendo a la pregunta, dependiendo del idioma que esté utilizando, simplemente traduzca esto a su tirada de dados. En Python, aquí hay un ejemplo muy incompleto, aunque funciona,

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)

Otros consejos

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

Use 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 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

It is a little surprising that the np.random.choice answer hasn't been given here.

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)

The p option gives "the probabilities associated with each entry in a", where a is np.range(N) for us. "If not given the sample assumes a uniform distribution over all entries in a".

See the recipe for Walker's alias method for random objects with different probablities.
An example, strings A B C or D with probabilities .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

cheers
-- denis

Just to suggest a more efficient (and pythonic3) solution, one can use bisect to search in the vector of accumulated values — that can moreover be precomputed and stored in the hope that subsequent calls to the function will refer to the same "bias" (to follow the question parlance).

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

In absence of Python 3 accumulate, one can just write a simple loop to compute the cumulative sum.

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

i have created a code for a dictionary giving a event and corresponding probability, it gives back the corresponding key ie the event of that probability.

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
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top