2010-11-25 31 views
5

Doublons possibles:
Random weighted choice
Generate random numbers with a given (numerical) distributionPython: La sélection de nombres avec des probabilités associées

J'ai une liste de la liste qui contient une série de chiffres et il y a des probabilités associées.

prob_list = [[1, 0.5], [2, 0.25], [3, 0.05], [4, 0.01], [5, 0.09], [6, 0.1]] 

par exemple dans prob_list[0] le numéro 1 a une probabilité de 0,5 qui lui est associé. Donc vous vous attendez à ce que 1 apparaisse 50% du temps.

Comment ajouter du poids aux chiffres lorsque je les sélectionne?

REMARQUE: la quantité de numéros dans la liste peut varier de 6 - 100


EDIT

Dans la liste que j'ai 6 numéros avec leurs probabilités associées. Je veux sélectionner deux nombres en fonction de leur probabilité.

Aucun numéro ne peut être sélectionné deux fois. Si "2" est sélectionné, il ne peut plus être sélectionné.

+0

double possible de http://stackoverflow.com/questions/4265988/generate-random-numbers-with-a-given-numerical-distribution/ – khachik

+0

Vous essayez de générer des nombres aléatoires? Calculer la valeur attendue? – robert

+0

Salut, je ne comprends pas la question ... que voudriez-vous faire avec les chiffres? – SubniC

Répondre

1

Cela pourrait être ce que vous cherchez. Extension à une solution dans Generate random numbers with a given (numerical) distribution. Supprime l'élément sélectionné de la distribution, met à jour les probabilités et renvoie selected item, updated distribution. Non prouvé au travail, mais devrait donner une bonne impression de l'idée.

def random_distr(l): 
    assert l # don't accept empty lists 
    r = random.uniform(0, 1) 
    s = 0 
    for i in xrange(len(l)): 
     item, prob = l[i] 
     s += prob 
     if s >= r: 
      l.pop(i) # remove the item from the distribution 
      break 
    else: # Might occur because of floating point inaccuracies 
     l.pop() 
    # update probabilities based on new domain 
    d = 1 - prob 
    for i in xrange(len(l)): 
     l[i][1] /= d 
    return item, l 

dist = [[1, 0.5], [2, 0.25], [3, 0.05], [4, 0.01], [5, 0.09], [6, 0.1]] 
while dist: 
    val, dist = random_distr(dist) 
    print val 
1

Le problème est peut-être lié à la structure de données. Il serait plus facile si vous aviez un dictionnaire au lieu d'une liste de listes:

prob_list = { 1:0.5, 2:0.25, 3:0.05, 4:0.01, 5:0.09, 6:0.1} 

De cette façon, vous pouvez obtenir le poids correspondant au nombre:

import random 
number = weight = -1 
while not(number in prob_list): 
    number = random.randint(0, length(prob_list)) 
    weight = prob_list[ number ] 

print(number, weight) 
4

Je vais assumer la les probabilités totalisent jusqu'à 1. Si elles ne le font pas, vous devrez les mettre à l'échelle en conséquence pour qu'elles le fassent.

D'abord générer une variable aléatoire uniforme [0, 1] en utilisant random.random(). Passez ensuite dans la liste, en additionnant les probabilités. La première fois que la somme dépasse le nombre aléatoire, renvoyez le nombre associé. De cette façon, si la variable aléatoire uniforme généré tombe dans la plage (0,5, 0,75] dans votre exemple, 2 sera retourné, lui donnant ainsi la nécessaire 0,25 probabilité d'être retourné.

import random 
import sys 
def pick_random(prob_list): 
    r, s = random.random(), 0 
    for num in prob_list: 
    s += num[1] 
    if s >= r: 
     return num[0] 
    print >> sys.stderr, "Error: shouldn't get here" 

Voici un test montrant fonctionne:

import collections 
count = collections.defaultdict(int) 
for i in xrange(10000): 
    count[pick_random(prob_list)] += 1 
for n in count: 
    print n, count[n]/10000.0 

qui sort:

1 0.498 
2 0.25 
3 0.0515 
4 0.0099 
5 0.0899 
6 0.1007 

EDIT: Si vous voulez juste vu la modifier dans la question de sélectionner deux numéros distincts, vous pouvez répéter ce qui précède jusqu'à ce que votre soi. Le nombre de cond choisi est distinct. Mais ce sera terriblement lent si un nombre a un très haut (par exemple 0.99999999) probabilité associée à elle. Dans ce cas, vous pouvez supprimer le premier nombre de la liste et redimensionner les probabilités de sorte qu'elles soient égales à 1 avant de sélectionner le second nombre.

3

Voici quelque chose qui semble fonctionner et répondre à toutes vos spécifications (et subjectivement, il semble assez rapide). Notez que votre contrainte que le second nombre ne soit pas le même que le premier jette les probabilités pour le sélectionner. Ce problème est effectivement ignoré par le code ci-dessous et il impose simplement la restriction (en d'autres termes, la probabilité de ce que le deuxième nombre est ne sera pas être celle donnée pour chaque nombre dans le prob_list).

import random 

prob_list = [[1, 0.5], [2, 0.25], [3, 0.05], [4, 0.01], [5, 0.09], [6, 0.1]] 

# create a list with the running total of the probabilities 
acc = 0.0 
acc_list = [acc] 
for t in prob_list: 
    acc += t[1] 
    acc_list.append(acc) 

TOLERANCE = .000001 
def approx_eq(v1, v2): 
    return abs(v1-v2) <= TOLERANCE 

def within(low, value, high): 
    """ Determine if low >= value <= high (approximately) """ 
    return (value > low or approx_eq(low, value)) and \ 
      (value < high or approx_eq(high, value)) 

def get_selection(): 
    """ Find which weighted interval a random selection falls in """ 
    interval = -1 
    rand = random.random() 
    for i in range(len(acc_list)-1): 
     if within(acc_list[i], rand, acc_list[i+1]): 
      interval = i 
      break 
    if interval == -1: 
     raise AssertionError('no interval for {:.6}'.format(rand)) 
    return interval 

def get_two_different_nums(): 
    sel1 = get_selection() 
    sel2 = sel1 
    while sel2 == sel1: 
     sel2 = get_selection() 
    return prob_list[sel1][0], prob_list[sel2][0]