Comment puis-je simuler mourir biaisé en python?
-
20-08-2019 - |
Question
Je veux simuler mourir biaisé N-face?
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
La solution
Un peu de mathématiques ici.
Une matrice régulière donnera à chaque nombre de 1 à 6 avec la même probabilité, à savoir 1/6
. Ceci est appelé distribution uniforme (la version discrète de celui-ci, par opposition à la version continue). Ce qui signifie que si une variable est X
aléatoire décrivant le résultat d'un rôle unique alors X~U[1,6]
-. Signifiant est réparti à parts égales [0,1)
contre tous les résultats possibles du jet de dé, 1 à 6
Il est égal à choisir un nombre en divisant [0,1/6)
en 6 sections:. [1/6,2/6)
, [2/6,3/6)
, [3/6,4/6)
, [4/6,5/6)
, [5/6,1)
, [0,0.2)
Vous demandez une distribution différente, qui est biaisée.
La meilleure façon d'y parvenir est de diviser la section 6 pièces [0.2,0.4)
en fonction du biais que vous voulez. Donc, dans votre cas, vous voulez le diviser en ce qui suit:
[0.4,0.55)
, 0.55,0.7)
, [0.7,0.84)
, [0.84,1)
, <=>, <=>.
Si vous jetez un oeil à la wikipedia entrée, vous verrez que dans ce cas, la fonction de probabilité cumulée ne sera pas composé de 6 parties de longueur égale, mais plutôt de 6 parties qui diffèrent en longueur selon le biais vous leur avez donné. Même chose pour la distribution de masse.
Retour à la question, en fonction de la langue que vous utilisez, simplement traduire à votre jet de dé. En Python, voici un très sommaire, bien que travaillant, par exemple:
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)
Autres conseils
Plus la langue agnostique, mais vous pouvez utiliser une table de consultation.
A l'aide d'un nombre aléatoire dans la plage 0-1 et rechercher la valeur dans un tableau:
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
Le biais sera proportionnelle.
>>> 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
On pourrait utiliser des entiers trop (mieux):
>>> 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