[Tutor] Weighted Random Choice - Anyone have an efficient algorithm?

Albert-Jan Roskam fomcl at yahoo.com
Fri Dec 24 10:40:06 CET 2010


Hi Steven,

Doesn't this qualify as 'monkeying with the loop index'? [*]

>>> import random
>>> weights = [5, 20, 75]
>>> counts = {0:0, 1:0, 2:0}
>>> for i in xrange(1000000):
...     i = weighted_choice(weights) # <--- monkeying right here (?)
...     counts[i] += 1

[*] http://stackoverflow.com/questions/457036/dont-monkey-with-the-loop-index


 Cheers!!
Albert-Jan


~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
All right, but apart from the sanitation, the medicine, education, wine, public 
order, irrigation, roads, a fresh water system, and public health, what have the 
Romans ever done for us?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~




________________________________
From: Steven D'Aprano <steve at pearwood.info>
To: tutor at python.org
Sent: Thu, December 23, 2010 1:30:50 PM
Subject: Re: [Tutor] Weighted Random Choice - Anyone have an efficient 
algorithm?

Modulok wrote:
> Does anyone know of an efficient way of doing a weighted random
> choice? (I don't even know what algorithms like this would be called.)

If you google for "python weighted random choice" you will find a number of 
hits.


> Preferably, something that doesn't grow exponentially with the number
> of elements in the list, or the size of their respective values.


Here's one method that is linear on the number of elements:

def weighted_choice(weights):
    total = sum(weights)
    p = random.uniform(0, total)  # random float between 0 and total.
    assert 0.0 <= p <= total
    # Do a linear search for the right index value. If you have a
    # huge number of weights, a binary search may be faster.
    running_total = 0
    for i, weight in enumerate(weights):
        running_total += weight
        if p <= running_total:
            return i

And tested:

>>> import random
>>> weights = [5, 20, 75]
>>> counts = {0:0, 1:0, 2:0}
>>> for i in xrange(1000000):
...     i = weighted_choice(weights)
...     counts[i] += 1
...
>>> counts
{0: 50252, 1: 199997, 2: 749751}
>>> [n*1e6/100 for n in weights]  # expected values
[50000.0, 200000.0, 750000.0]

As you can see, the results are very close to what should be expected, and of 
course the difference can be chalked up to random chance.



-- Steven

_______________________________________________
Tutor maillist  -  Tutor at python.org
To unsubscribe or change subscription options:
http://mail.python.org/mailman/listinfo/tutor



      
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/tutor/attachments/20101224/4c87868d/attachment-0001.html>


More information about the Tutor mailing list