Weighted choices
Peter Otten
__peter__ at web.de
Sun Sep 8 02:13:16 EDT 2013
Jason Friedman wrote:
> choices = dict()
> choices["apple"] = 10
> choices["pear"] = 20
> choices["banana"] = 15
> choices["orange"] = 25
> choices["kiwi"] = 30
>
> I want to pick sets of fruit, three in a set, where the chance of
> selecting a given fruit is proportional to its weight. In the example
> above, pears should appear twice as often as apples and kiwis should
> appear twice as often as bananas.
>
> I see this solution
> (http://stackoverflow.com/questions/3679694/a-weighted-version-of-random-
choice)
> but it is for single choices at a time. random.sample() does not
> provide a "weight" argument.
>
> I can picture how to write this with many, many lines of code but I
> have a feeling there is an elegant solution.
Let's assume for a moment that you always have low integral weights. Given
data = []
for weight, value in choices.items():
data.extend([value]*weight)
picking values with
N = 3
sample = [random.choice(data) for i in range(N)]
does not give te same probabilities as
sample = random.sample(data, N)
Which one doe you want? If it's the former, i. e. picking an item does not
affect the item's probability, just adapt Raymond Hettinger's
def weighted_choice(choices):
values, weights = zip(*choices)
total = 0
cum_weights = []
for w in weights:
total += w
cum_weights.append(total)
x = random() * total
i = bisect(cum_weights, x)
return values[i]
to (untested)
def weighted_choice_iter(choices):
values, weights = zip(*choices)
total = 0
cum_weights = []
for w in weights:
total += w
cum_weights.append(total)
while True:
x = random() * total
i = bisect(cum_weights, x)
yield values[i]
def weighted_choices(choices, N):
return list(itertools.islice(weighted_choice_iter(choices), N))
print weighted_choices(choices, 3)
More information about the Python-list
mailing list