2

For example I have these three options:

 Option 1
 Option 2
 Option 3

I want to randomly choose one of them, but with a certain probability bias. For example:

 Option 1: 30% chance
 Option 2: 50% chance
 Option 3: 20% chance

So what is the best approach to randomly picking one while preserving those probabilities of them happening in python?

user1487000
  • 1,161
  • 3
  • 12
  • 17

2 Answers2

8

Not very efficient, but very easy:

random.choice([1]*30 + [2]*50 + [3]*20)
Keith Randall
  • 22,985
  • 2
  • 35
  • 54
  • That's a very simple and effective answer; +1. Also, it won't be too slow, as the max amount of elements you will have (assuming the weighting is on a integer-percent-based system), is 100. However, if the weighting is not a percent or if it uses percents that are fractions (especially irrational) it may become inefficient. – Rushy Panchal Feb 23 '13 at 01:43
  • 7
    You could make it more "efficient" by multiplying by 3, 5 and 2 respectively :) – Lipis Feb 23 '13 at 01:44
  • Beautifully simple. Thank you. – user1487000 Feb 23 '13 at 02:36
5

This works:

import random

def weighted_choice(weights):
    choice = random.random() * sum(weights)
    for i, w in enumerate(weights):
        choice -= w
        if choice < 0:
            return i

weighted_choice([.3, .5, .2])   # returns 0,1,2 in proportion to the weight

To test it:

import collections
c = collections.Counter()
n = 1000000
for i in range(n):
    c[weighted_choice([.3, .5, .2])] += 1

for k, v in c.items():
    print '{}: {:.2%}'.format(k,float(v)/n)

Prints:

0: 30.11%
1: 50.08%
2: 19.81%

The advantage, besides being fairly fast, is that 1) the list elements do not need to add up to 1 or 100, 2) for more choices, just add more elements to the list:

for i in range(n):
   c[weighted_choice([.3,.35,.1,.1,.15,.4])]+=1

Prints:

0: 21.61%
1: 25.18%
2: 7.22%
3: 7.03%
4: 10.57%
5: 28.38%

(Timed against the accepted answer, it is about 2x faster...)

dawg
  • 98,345
  • 23
  • 131
  • 206