2010-11-25 16 views
5

duplicados posibles:
Random weighted choice
Generate random numbers with a given (numerical) distributionPython: Selección de números con probabilidades asociadas

tengo una lista de la lista que contiene una serie de números y hay probabilidades asociadas.

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

por ejemplo en prob_list[0] el número 1 tiene una probabilidad de 0,5 asociada a ella. Entonces, esperaría que 1 apareciera el 50% del tiempo.

¿Cómo agrego peso a los números cuando los selecciono?

NOTA: la cantidad de números de la lista puede variar de 6 - 100


EDITAR

En la lista tengo 6 números con sus probabilidades asociadas. Quiero seleccionar dos números según su probabilidad.

No se puede seleccionar ningún número dos veces. Si se selecciona "2", no se puede volver a seleccionar.

+0

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

+0

¿Estás tratando de generar números aleatorios? Calcular el valor esperado – robert

+0

Hola, no entiendo la pregunta ... ¿qué te gustaría hacer con los números? – SubniC

Respuesta

1

Esto podría ser lo que estás buscando. Extensión a una solución en Generate random numbers with a given (numerical) distribution. Elimina el elemento seleccionado de la distribución, actualiza las probabilidades y devuelve selected item, updated distribution. No se ha demostrado que funcione, pero debería dar una buena impresión de la idea.

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

Quizás el problema esté relacionado con la estructura de datos. Sería más fácil si tuviera un diccionario en lugar de una lista de listas:

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

De esta manera, se puede obtener el peso correspondiente al número:

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

Voy a asumir la todas las probabilidades suman 1. Si no lo hacen, vas a tener que escalarlas en consecuencia para que así sea.

Primero genere una variable aleatoria uniforme [0, 1] usando random.random(). Luego pasa a través de la lista, sumando las probabilidades. La primera vez que la suma exceda el número aleatorio, devuelva el número asociado. De esta manera, si la variable aleatoria uniforme generado está dentro del intervalo (0,5, 0,75] en su ejemplo, 2 será devuelto, lo que le otorga la requerida 0,25 probabilidad de ser devuelto.

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" 

Aquí hay una prueba que muestra que obras:

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 

que da salida:

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

eDIT: acabo de ver la edición en la pregunta Si desea seleccionar dos números distintos, puede repetir lo anterior hasta que se. El número de cond elegido es distinto. Pero esto será terriblemente lento si un número tiene un valor muy alto (por ejemplo, 0.99999999) probabilidad asociada con él. En este caso, puede eliminar el primer número de la lista y volver a escalar las probabilidades para que sumen a 1 antes de seleccionar el segundo número.

3

Aquí hay algo que parece funcionar y cumple con todas sus especificaciones (y subjetivamente parece bastante rápido). Tenga en cuenta que su restricción de que el segundo número no sea el mismo que el primero arroja las probabilidades para seleccionarlo. Ese problema es efectivamente ignorado por el código a continuación y solo refuerza la restricción (en otras palabras, la probabilidad de que el segundo número sea no será que la dada para cada número en el 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] 
Cuestiones relacionadas