Aucune de ces réponses n'est particulièrement claire ou simple.
Voici une méthode claire et simple dont l'efficacité est garantie.
accumuler_normaliser_les_probabilités prend un dictionnaire p
qui associe des symboles à des probabilités OU fréquences. Il produit une liste utilisable de tuples à partir desquels il est possible d'effectuer une sélection.
def accumulate_normalize_values(p):
pi = p.items() if isinstance(p,dict) else p
accum_pi = []
accum = 0
for i in pi:
accum_pi.append((i[0],i[1]+accum))
accum += i[1]
if accum == 0:
raise Exception( "You are about to explode the universe. Continue ? Y/N " )
normed_a = []
for a in accum_pi:
normed_a.append((a[0],a[1]*1.0/accum))
return normed_a
Rendement :
>>> accumulate_normalize_values( { 'a': 100, 'b' : 300, 'c' : 400, 'd' : 200 } )
[('a', 0.1), ('c', 0.5), ('b', 0.8), ('d', 1.0)]
Pourquoi ça marche
Les l'accumulation transforme chaque symbole en un intervalle entre lui-même et la probabilité ou la fréquence des symboles précédents (ou 0 dans le cas du premier symbole). Ces intervalles peuvent être utilisés pour effectuer une sélection (et donc échantillonner la distribution fournie) en parcourant simplement la liste jusqu'à ce que le nombre aléatoire dans l'intervalle 0,0 -> 1,0 (préparé plus tôt) soit inférieur ou égal à l'extrémité de l'intervalle du symbole actuel.
Les normalisation nous libère de la nécessité de nous assurer que chaque chose a une valeur. Après normalisation, le "vecteur" de probabilités est égal à 1,0.
Les reste du code pour la sélection et la génération d'un échantillon arbitrairement long de la distribution est inférieur à :
def select(symbol_intervals,random):
print symbol_intervals,random
i = 0
while random > symbol_intervals[i][1]:
i += 1
if i >= len(symbol_intervals):
raise Exception( "What did you DO to that poor list?" )
return symbol_intervals[i][0]
def gen_random(alphabet,length,probabilities=None):
from random import random
from itertools import repeat
if probabilities is None:
probabilities = dict(zip(alphabet,repeat(1.0)))
elif len(probabilities) > 0 and isinstance(probabilities[0],(int,long,float)):
probabilities = dict(zip(alphabet,probabilities)) #ordered
usable_probabilities = accumulate_normalize_values(probabilities)
gen = []
while len(gen) < length:
gen.append(select(usable_probabilities,random()))
return gen
Utilisation :
>>> gen_random (['a','b','c','d'],10,[100,300,400,200])
['d', 'b', 'b', 'a', 'c', 'c', 'b', 'c', 'c', 'c'] #<--- some of the time