Pregunta

Tengo un manojo de llaves que cada uno tiene una variable de improbabilidad. Quiero elegir al azar una de estas teclas, sin embargo, yo quiero que sea más improbable para improbables, valores (clave) para ser elegido de un (una mayor probabilidad) de objetos menos improbable. Me pregunto si usted tiene alguna sugerencia, preferiblemente un módulo de Python existente que podría utilizar, de lo que tendrá que hacer por mí mismo.

He descargado el módulo al azar; no parece proporcionar esto.

Tengo que tomar esas decisiones millones de veces para 1000 juegos diferentes de objetos que contienen cada una 2.455 objetos. Cada juego será intercambiar objetos entre sí por lo que el selector de azar debe ser dinámico. Con 1000 conjuntos de 2.433 objetos, es decir 2.433 millones de objetos; bajo consumo de memoria es crucial. Y ya que estas opciones no son el grueso del algoritmo, necesito que este proceso es bastante rápido; CPU-tiempo es limitado.

Thx

Actualización:

Ok, he intentado tener en cuenta sus sugerencias con prudencia, pero el tiempo es tan limitado ...

Miré el enfoque del árbol de búsqueda binaria y parece demasiado arriesgado (complejo y complicado). Las otras sugerencias todos se parecen a la receta ActiveState. Lo tomé y lo modifiqué un poco con la esperanza de hacer más eficaz:

def windex(dict, sum, max):
    '''an attempt to make a random.choose() function that makes
    weighted choices accepts a dictionary with the item_key and
    certainty_value as a pair like:
    >>> x = [('one', 20), ('two', 2), ('three', 50)], the
    maximum certainty value (max) and the sum of all certainties.'''
    n = random.uniform(0, 1)
    sum = max*len(list)-sum 
    for key, certainty in dict.iteritems():
        weight = float(max-certainty)/sum
        if n < weight:
            break
        n = n - weight
    return key

Tengo la esperanza de obtener una ganancia de eficiencia de mantener dinámicamente la suma de las certezas y la máxima seguridad. Cualquier otra sugerencia son bienvenidos. Ustedes me ahorra mucho tiempo y esfuerzo, al tiempo que aumenta la eficacia de mi, es una locura. ¡Gracias! ¡Gracias! Thx!

Update2:

decidí hacerlo más eficiente al permitir que se elija más opciones a la vez. Esto dará como resultado una pérdida aceptable de precisión en mi algo porque es de naturaleza dinámica. De todos modos, esto es lo que tengo ahora:

def weightedChoices(dict, sum, max, choices=10):
    '''an attempt to make a random.choose() function that makes
    weighted choices accepts a dictionary with the item_key and
    certainty_value as a pair like:
    >>> x = [('one', 20), ('two', 2), ('three', 50)], the
    maximum certainty value (max) and the sum of all certainties.'''
    list = [random.uniform(0, 1) for i in range(choices)]
    (n, list) = relavate(list.sort())
    keys = []
    sum = max*len(list)-sum 
    for key, certainty in dict.iteritems():
        weight = float(max-certainty)/sum
        if n < weight:
            keys.append(key)
            if list: (n, list) = relavate(list)
            else: break
        n = n - weight
    return keys
def relavate(list):
    min = list[0]
    new = [l - min for l in list[1:]]
    return (min, new)

Yo no lo he probado todavía. Si tiene cualquier comentario y / o sugerencias, por favor no dude. Thx!

Update3:

He estado trabajando todo el día en una versión adaptada a tareas de respuesta de Rex Logan. En lugar de un 2 matrices de objetos y pesos, en realidad es una clase especial de diccionario; que hace las cosas bastante compleja ya que el código de Rex genera un índice al azar ... También codifiqué un caso de prueba que se asemeja a la clase de lo que sucederá en mi algo (pero no puedo saber realmente hasta que lo intente!). El principio básico es: la clave se genera aleatoriamente más a menudo, el más improbable que se genera de nuevo:

import random, time
import psyco
psyco.full()

class ProbDict():
    """
    Modified version of Rex Logans RandomObject class. The more a key is randomly
    chosen, the more unlikely it will further be randomly chosen. 
    """
    def __init__(self,keys_weights_values={}):
        self._kw=keys_weights_values
        self._keys=self._kw.keys()
        self._len=len(self._keys)
        self._findSeniors()
        self._effort = 0.15
        self._fails = 0
    def __iter__(self):
        return self.next()
    def __getitem__(self, key):
        return self._kw[key]
    def __setitem__(self, key, value):
        self.append(key, value)
    def __len__(self):
        return self._len
    def next(self):
        key=self._key()
        while key:
            yield key
            key = self._key()
    def __contains__(self, key):
        return key in self._kw
    def items(self):
        return self._kw.items()
    def pop(self, key):  
        try:
            (w, value) = self._kw.pop(key)
            self._len -=1
            if w == self._seniorW:
                self._seniors -= 1
                if not self._seniors:
                    #costly but unlikely:
                    self._findSeniors()
            return [w, value]
        except KeyError:
            return None
    def popitem(self):
        return self.pop(self._key())
    def values(self):
        values = []
        for key in self._keys:
            try:
                values.append(self._kw[key][1])
            except KeyError:
                pass
        return values
    def weights(self):
        weights = []
        for key in self._keys:
            try:
                weights.append(self._kw[key][0])
            except KeyError:
                pass
        return weights
    def keys(self, imperfect=False):
        if imperfect: return self._keys
        return self._kw.keys()
    def append(self, key, value=None):
        if key not in self._kw:
            self._len +=1
            self._kw[key] = [0, value]
            self._keys.append(key)
        else:
            self._kw[key][1]=value
    def _key(self):
        for i in range(int(self._effort*self._len)):
            ri=random.randint(0,self._len-1) #choose a random object
            rx=random.uniform(0,self._seniorW)
            rkey = self._keys[ri]
            try:
                w = self._kw[rkey][0]
                if rx >= w: # test to see if that is the value we want
                    w += 1
                    self._warnSeniors(w)
                    self._kw[rkey][0] = w
                    return rkey
            except KeyError:
                self._keys.pop(ri)
        # if you do not find one after 100 tries then just get a random one
        self._fails += 1 #for confirming effectiveness only
        for key in self._keys:
            if key in self._kw:
                w = self._kw[key][0] + 1
                self._warnSeniors(w)
                self._kw[key][0] = w
                return key
        return None
    def _findSeniors(self):
        '''this function finds the seniors, counts them and assess their age. It
        is costly but unlikely.'''
        seniorW = 0
        seniors = 0
        for w in self._kw.itervalues():
            if w >= seniorW:
                if w == seniorW:
                    seniors += 1
                else:
                    seniorsW = w
                    seniors = 1
        self._seniors = seniors
        self._seniorW = seniorW
    def _warnSeniors(self, w):
        #a weight can only be incremented...good
        if w >= self._seniorW:
            if w == self._seniorW:
                self._seniors+=1
            else:
                self._seniors = 1
                self._seniorW = w
def test():
    #test code
    iterations = 200000
    size = 2500
    nextkey = size 


    pd = ProbDict(dict([(i,[0,i]) for i in xrange(size)]))
    start = time.clock()
    for i in xrange(iterations):
        key=pd._key()
        w=pd[key][0]
        if random.randint(0,1+pd._seniorW-w):
            #the heavier the object, the more unlikely it will be removed
            pd.pop(key)
        probAppend = float(500+(size-len(pd)))/1000
        if random.uniform(0,1) < probAppend:
            nextkey+=1
            pd.append(nextkey)
    print (time.clock()-start)*1000/iterations, "msecs / iteration with", pd._fails, "failures /", iterations, "iterations"
    weights = pd.weights()
    weights.sort()
    print "avg weight:", float(sum(weights))/pd._len, max(weights), pd._seniorW, pd._seniors, len(pd), len(weights)
    print weights
test()

Cualquier comentario todavía son bienvenidos. @Darius: sus árboles binarios son demasiado complejo y complicado para mí; y no creo que sus hojas se pueden eliminar de manera eficiente ... Thx toda

¿Fue útil?

Solución

Esta receta ActiveState da un enfoque fácil de seguir, en concreto la versión en el los comentarios que no requiere que comprobar la validez de normalizar sus pesos:

import random

def weighted_choice(items):
    """items is a list of tuples in the form (item, weight)"""
    weight_total = sum((item[1] for item in items))
    n = random.uniform(0, weight_total)
    for item, weight in items:
        if n < weight:
            return item
        n = n - weight
    return item

Este será lenta si usted tiene una gran lista de elementos. Una búsqueda binaria, probablemente sería mejor en ese caso ... pero también sería más complicado de escribir, por poca ganancia si usted tiene un pequeño tamaño de la muestra. He aquí un ejemplo del enfoque de búsqueda binaria en Python si quiere seguir esa ruta.

(recomiendo hacer algunas pruebas de rendimiento rápido de ambos métodos en el conjunto de datos. El rendimiento de los diferentes enfoques de este tipo de algoritmo es a menudo un poco intuitivo.)


Editar:. Tomé mi propio consejo, desde que era curioso, e hice algunas pruebas

He comparado cuatro enfoques:

La función weighted_choice anteriormente.

Una función de elección binaria de búsqueda de este modo:

def weighted_choice_bisect(items):
    added_weights = []
    last_sum = 0

    for item, weight in items:
        last_sum += weight
        added_weights.append(last_sum)

    return items[bisect.bisect(added_weights, random.random() * last_sum)][0]

Una versión de compilación de 1:

def weighted_choice_compile(items):
    """returns a function that fetches a random item from items

    items is a list of tuples in the form (item, weight)"""
    weight_total = sum((item[1] for item in items))
    def choice(uniform = random.uniform):
        n = uniform(0, weight_total)
        for item, weight in items:
            if n < weight:
                return item
            n = n - weight
        return item
    return choice

Una versión de compilación de 2:

def weighted_choice_bisect_compile(items):
    """Returns a function that makes a weighted random choice from items."""
    added_weights = []
    last_sum = 0

    for item, weight in items:
        last_sum += weight
        added_weights.append(last_sum)

    def choice(rnd=random.random, bis=bisect.bisect):
        return items[bis(added_weights, rnd() * last_sum)][0]
    return choice

Entonces, construyó una gran lista de opciones, así:

choices = [(random.choice("abcdefg"), random.uniform(0,50)) for i in xrange(2500)]

Y una función de perfiles excesivamente simple:

def profiler(f, n, *args, **kwargs):
    start = time.time()
    for i in xrange(n):
        f(*args, **kwargs)
    return time.time() - start

Los resultados:

(segundos tomadas para 1.000 llamadas a la función.)

  • simple sin compilar: ,918624162674
  • binario sin compilar: 1.01497793198
  • simple compilado: ,287325024605
  • binario compilado: 0.00327413797379

Los resultados "compilados" incluyen el tiempo medio necesario para compilar la función de la elección de una vez. (I cronometré 1.000 compila, luego se divide entonces por 1000, y añadió el resultado a la vez la función de elección.)

Así que:. Si usted tiene una lista de elementos + pesos que cambian muy raramente, el método binario compilado es , con mucho, el más rápido

Otros consejos

En los comentarios en el post original, Nicolás Leonard sugiere que tanto el intercambio y la toma de muestras que tenga que ser rápido. He aquí una idea para ese caso; Yo no lo he probado.

Si solamente el muestreo tuvo que ser rápido, podríamos utilizar una matriz de los valores junto con la suma acumulada de sus probabilidades, y hacer una búsqueda binaria en la suma continua (con ser clave un número aleatorio uniforme) - una junta (log (n)) operación. Sin embargo, un cambio requeriría la actualización de todos los valores de rodaje suma que aparecen después de las entradas intercambió - un O (n) la operación. (Podría optar por cambiar sólo los elementos cerca del final de sus listas? No voy a asumir.)

Así que vamos a apuntar para O (log (n)) en ambas operaciones. En lugar de una matriz, mantener un árbol binario para cada conjunto de muestras de. Una hoja mantiene el valor de la muestra y su probabilidad (no normalizada). Un nodo de rama tiene la probabilidad total de sus hijos.

Para muestra, generar una x número aleatorio uniforme entre 0 y la probabilidad total de la raíz, y descender el árbol. En cada rama, elija el niño izquierda si el niño tiene la izquierda <= x probabilidad total. Otra cosa restar la probabilidad de que el niño dejó de x y vaya a la derecha. Devolver el valor de la hoja que llegue.

Para intercambiar, retire la hoja de su árbol y ajustar las ramas que conducen a él (disminuyendo su probabilidad total, y de cortar cualquier nodos rama de un solo hijo). Inserte la hoja en el árbol de destino: usted tiene la opción de dónde ponerlo, a fin de mantenerlo equilibrado. Recogiendo un niño al azar en cada nivel es probablemente lo suficientemente bueno - que es donde me gustaría empezar. Aumentar la probabilidad de cada nodo padre, de vuelta hasta la raíz.

Ahora, tanto de muestreo y el intercambio son O (log (n)) en promedio. (Si necesita equilibrio garantizado, de una manera sencilla es añadir otro campo para los nodos rama que se mantiene el recuento de hojas en todo el sub-árbol. Al añadir una hoja, en cada nivel recoger al niño con un menor número de hojas. Esto deja la posibilidad de una árbol recibiendo desequilibrada únicamente por supresiones;. esto no puede ser un problema si no es razonable incluso, el tráfico entre los conjuntos, pero si lo es, entonces elegir rotaciones durante el borrado utilizando la información de la hoja de recuento en cada nodo en su recorrido)

Actualización: A petición, he aquí una implementación básica. no han sintonizado en absoluto. Uso:

>>> t1 = build_tree([('one', 20), ('two', 2), ('three', 50)])
>>> t1
Branch(Leaf(20, 'one'), Branch(Leaf(2, 'two'), Leaf(50, 'three')))
>>> t1.sample()
Leaf(50, 'three')
>>> t1.sample()
Leaf(20, 'one')
>>> t2 = build_tree([('four', 10), ('five', 30)])
>>> t1a, t2a = transfer(t1, t2)
>>> t1a
Branch(Leaf(20, 'one'), Leaf(2, 'two'))
>>> t2a
Branch(Leaf(10, 'four'), Branch(Leaf(30, 'five'), Leaf(50, 'three')))

Código:

import random

def build_tree(pairs):
    tree = Empty()
    for value, weight in pairs:
        tree = tree.add(Leaf(weight, value))
    return tree

def transfer(from_tree, to_tree):
    """Given a nonempty tree and a target, move a leaf from the former to
    the latter. Return the two updated trees."""
    leaf, from_tree1 = from_tree.extract()
    return from_tree1, to_tree.add(leaf)

class Tree:
    def add(self, leaf):
        "Return a new tree holding my leaves plus the given leaf."
        abstract
    def sample(self):
        "Pick one of my leaves at random in proportion to its weight."
        return self.sampling(random.uniform(0, self.weight))
    def extract(self):
        """Pick one of my leaves and return it along with a new tree
        holding my leaves minus that one leaf."""
        return self.extracting(random.uniform(0, self.weight))        

class Empty(Tree):
    weight = 0
    def __repr__(self):
        return 'Empty()'
    def add(self, leaf):
        return leaf
    def sampling(self, weight):
        raise Exception("You can't sample an empty tree")
    def extracting(self, weight):
        raise Exception("You can't extract from an empty tree")

class Leaf(Tree):
    def __init__(self, weight, value):
        self.weight = weight
        self.value = value
    def __repr__(self):
        return 'Leaf(%r, %r)' % (self.weight, self.value)
    def add(self, leaf):
        return Branch(self, leaf)
    def sampling(self, weight):
        return self
    def extracting(self, weight):
        return self, Empty()

def combine(left, right):
    if isinstance(left, Empty): return right
    if isinstance(right, Empty): return left
    return Branch(left, right)

class Branch(Tree):
    def __init__(self, left, right):
        self.weight = left.weight + right.weight
        self.left = left
        self.right = right
    def __repr__(self):
        return 'Branch(%r, %r)' % (self.left, self.right)
    def add(self, leaf):
        # Adding to a random branch as a clumsy way to keep an
        # approximately balanced tree.
        if random.random() < 0.5:
            return combine(self.left.add(leaf), self.right)
        return combine(self.left, self.right.add(leaf))
    def sampling(self, weight):
        if weight < self.left.weight:
            return self.left.sampling(weight)
        return self.right.sampling(weight - self.left.weight)
    def extracting(self, weight):
        if weight < self.left.weight:
            leaf, left1 = self.left.extracting(weight)
            return leaf, combine(left1, self.right)
        leaf, right1 = self.right.extracting(weight - self.left.weight)
        return leaf, combine(self.left, right1)

Actualización 2: En contestar otro problema , Jason Orendorff señala que los árboles binarios se pueden mantener perfectamente equilibrado representándolos en una matriz como la estructura clásica del montón. (Esto ahorra el espacio dedicado a los punteros, también.) Ver mis comentarios a la respuesta de cómo adaptar su código para este problema.

Te sugiero puerto esta aplicación PHP de aleatoria ponderada a Python. En particular, el segundo algoritmo de búsqueda binaria basada ayudan a solucionar problemas de velocidad.

Me gustaría utilizar esta receta . Usted necesita agregar un peso de sus objetos, pero eso es sólo una relación simple y los pone en una lista de tuplas (Object, convicción / (suma de las convicciones)). Esto debería ser fácil de hacer uso de una lista por comprensión.

Esta es una forma clásica de hacerlo, en pseudocódigo, donde random.random () le da un flotador aleatorio de 0 a 1.

let z = sum of all the convictions
let choice = random.random() * z 
iterate through your objects:
    choice = choice - the current object's conviction
    if choice <= 0, return this object
return the last object

Por ejemplo: imagine que tiene dos objetos, uno con un peso de 2, otra con el peso 4. Se genera un número de 0 a 6. Si choice está entre 0 y 2, que va a pasar con 2/6 = 1 / 3 probabilidad, entonces se conseguirá restado por 2 y se elige el primer objeto. Si la elección es entre 2 y 6, que va a pasar con 4/6 = 2/3 probabilidad, a continuación, la primera resta todavía tendrá elección ser> 0, y la segunda resta hará que el segundo objeto conseguir elegido.

Usted quiere dar a cada objeto un peso. Cuanto más grande sea el peso es más probable que va a pasar. Más precisamente probx = peso / sum_all_weights.

A continuación, generar un número aleatorio en el rango 0 a sum_all_weights y asignarla a cada objeto.

Este código le permite generar un índice de azar y se asigna cuando se crea el objeto de la velocidad. Si todos los conjuntos de objetos tienen la misma distribución, entonces puede llegar a funcionar con sólo un objeto RandomIndex.

import random

class RandomIndex:
    def __init__(self, wlist):
        self._wi=[]
        self._rsize=sum(wlist)-1
        self._m={}
        i=0
        s=wlist[i]
        for n in range(self._rsize+1):
            if n == s:
                i+=1
                s+=wlist[i]
            self._m[n]=i    

    def i(self):
        rn=random.randint(0,self._rsize)
        return self._m[rn]


sx=[1,2,3,4]


wx=[1,10,100,1000] #weight list
ri=RandomIndex(wx)

cnt=[0,0,0,0]

for i in range(1000):
    cnt[ri.i()] +=1  #keep track of number of times each index was generated

print(cnt)  

Alrededor de 3 años después ...

Si utiliza numpy, quizás la opción más sencilla es utilizar np.random.choice, que tiene una lista de valores posibles, y una secuencia opcional de probabilidades asociadas con cada valor:

import numpy as np

values = ('A', 'B', 'C', 'D')
weights = (0.5, 0.1, 0.2, 0.2)

print ''.join(np.random.choice(values, size=60, replace=True, p=weights))
# ACCADAACCDACDBACCADCAAAAAAADACCDCAADDDADAAACCAAACBAAADCADABA

Lo más sencillo es utilizar random.choice (que utiliza una distribución uniforme) y variar la frecuencia de ocurrencia en el objeto en la colección de origen.

>>> random.choice([1, 2, 3, 4])
4

... vs:

>>> random.choice([1, 1, 1, 1, 2, 2, 2, 3, 3, 4])
2

Así que sus objetos podrían tener una tasa de ocurrencia base (n) y entre 1 y n objetos se añaden a la colección de origen como una función de la tasa de condena. Este método es muy simple; Sin embargo, puede tener una sobrecarga significativa si el número de objetos distintos es grande o la tasa de condena tiene que ser de grano muy fino.

Por otra parte, si se generan más de un número aleatorio con una distribución uniforme y sumarlos, los números que se producen cerca de la media son más probable que los que se producen cerca de los extremos (piensa en lanzar dos dados y la probabilidad de obtener 7 frente a 12 o 2). A continuación, puede ordenar los objetos por su tasa de condenas y generar un número usando múltiples tiradas de dados que se utiliza para el cálculo y el índice en los objetos. Utilizar números cerca de la media de bajo índice de condena objetos y números cerca de los extremos a los artículos de alto índice de condena. Se puede variar la probabilidad exacta de que un objeto dado se seleccionará cambiando el "número de lados" y el número de su "dados" (puede ser más sencillo de poner los objetos en los cubos y utilizar los dados con un pequeño número de lados en lugar de tratando de asociar cada objeto con un resultado específico):

>>> die = lambda sides : random.randint(1, sides)
>>> die(6)
3
>>> die(6) + die(6) + die(6)
10

Una manera muy fácil y sencilla de hacer esto es establecer los pesos para cada uno de los valores, y no requeriría mucha memoria.

Probablemente se podría utilizar un hash / diccionario para hacer esto.

Lo que usted querrá hacer es tener el número aleatorio, x , multiplicado y sumada sobre todo el conjunto de cosas que desea seleccionado, y dividir ese resultado por el número de objetos en su set.

Pseudo-código:

objectSet = [(object1, weight1), ..., (objectN, weightN)]
sum = 0
rand = random()
for obj, weight in objectSet
    sum = sum+weight*rand
choice = objectSet[floor(sum/objectSet.size())]

editar : me acaba de ocurrir lo lento que mi código sería con conjuntos muy grandes (que es O (n)). El siguiente pseudo-código es O (log (n)), y es básicamente utilizando una búsqueda binaria.

objectSet = [(object1, weight1), ..., (objectN, weightN)]
sort objectSet from less to greater according to weights
choice = random() * N # where N is the number of objects in objectSet
do a binary search until you have just one answer

Existen implementaciones de búsqueda binaria en Python por toda la 'red, por lo que no es necesario repetir aquí.

Aquí hay una mejor respuesta para una distribución especial de probabilidad, la respuesta la de Rex Logan parece estar orientado a. La distribución es la siguiente: cada objeto tiene un peso entero entre 0 y 100, y su probabilidad es en proporción a su peso. Desde esa es la respuesta aceptada actualmente, creo que esto vale la pena pensar.

Por lo tanto mantener una matriz de 101 contenedores. Cada bandeja puede contener una lista de todos los objetos con su peso específico. Cada contenedor también conoce el Total peso de todos sus objetos.

Para la muestra: seleccione un bin al azar en proporción a su peso total. (Use una de las recetas estándar para esto -. Búsqueda lineal o binario). A continuación, recoger un objeto de la papelera de manera uniforme al azar

Para transferir un objeto: sacarlo de su compartimiento, ponerlo en su bin en el objetivo, y actualizar los pesos de los dos cubos de basura. (Si está usando la búsqueda binaria para el muestreo, también debe actualizar las sumas ejecución que utiliza. Esto sigue siendo bastante rápido ya que no hay muchos contenedores.)

Me necesitaban en las funciones más rápidas, para un número muy grande no. Así que aquí está, en Visual C ++:

#undef _DEBUG // disable linking with python25_d.dll
#include <Python.h>
#include <malloc.h>
#include <stdlib.h>

static PyObject* dieroll(PyObject *, PyObject *args)
{
    PyObject *list;
    if (!PyArg_ParseTuple(args, "O:decompress", &list))
        return NULL;

    if (!PyList_Check(list)) 
        return PyErr_Format(PyExc_TypeError, "list of numbers expected ('%s' given)", list->ob_type->tp_name), NULL;

    int size = PyList_Size(list);

    if (size < 1)
        return PyErr_Format(PyExc_TypeError, "got empty list"), NULL;

    long *array = (long*)alloca(size*sizeof(long));

    long sum = 0;
    for (int i = 0; i < size; i++) {
        PyObject *o = PyList_GetItem(list, i);

        if (!PyInt_Check(o))
            return PyErr_Format(PyExc_TypeError, "list of ints expected ('%s' found)", o->ob_type->tp_name), NULL;
        long n = PyInt_AsLong(o);
        if (n == -1 && PyErr_Occurred())
            return NULL;
        if (n < 0)
            return PyErr_Format(PyExc_TypeError, "list of positive ints expected (negative found)"), NULL;

        sum += n; //NOTE: integer overflow
        array[i] = sum;
    }

    if (sum <= 0)
        return PyErr_Format(PyExc_TypeError, "sum of numbers is not positive"), NULL;

    int r = rand() * (sum-1) / RAND_MAX; //NOTE: rand() may be too small (0x7fff).    rand() * sum may result in integer overlow.

    assert(array[size-1] == sum);
    assert(r < sum && r < array[size-1]);
    for (int i = 0; i < size; ++i)
    {
        if (r < array[i])
            return PyInt_FromLong(i);
    }
    return PyErr_Format(PyExc_TypeError, "internal error."), NULL;
}

static PyMethodDef module_methods[] = 
{
    {"dieroll", (PyCFunction)dieroll, METH_VARARGS, "random index, beased on weights" },
    {NULL}  /* Sentinel */
};

PyMODINIT_FUNC initdieroll(void) 
{
    PyObject *module = Py_InitModule3("dieroll", module_methods, "dieroll");
    if (module == NULL)
        return;
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top