Всегда ли вы знаете общее количество значений в словаре? В этом случае это может быть легко сделать с помощью следующего алгоритма, который можно использовать всякий раз, когда вы хотите сделать вероятностный выбор некоторых элементов из упорядоченного списка:
- Переберите свой список ключей.
- Генерирует равномерно распределенное случайное значение между 0 и 1 (он же «бросает кубик»).
- Предполагая, что этот ключ имеет значения N_VALS, связанные с ним, и есть общие значения TOTAL_VALS во всем словаре, примите этот ключ с вероятностью N_VALS / N_REMAINING, где N_REMAINING - количество элементов, оставшихся в списке.
Преимущество этого алгоритма заключается в том, что нет необходимости создавать новые списки, что важно, если ваш словарь большой. Ваша программа платит только за цикл по ключам K для вычисления итогового значения, еще один цикл по ключам, который в среднем закончится на полпути, и сколько угодно будет сгенерировать случайное число от 0 до 1. Генерация такого случайного числа является очень распространенное приложение в программировании, поэтому большинство языков имеют быструю реализацию такой функции. В Python генератор случайных чисел реализация C алгоритма Mersenne Twister , которая должна быть очень быстрой. Кроме того, в документации утверждается, что эта реализация является поточно-ориентированной.
Вот код. Я уверен, что вы можете очистить его, если хотите использовать больше возможностей Pythonic:
#!/usr/bin/python
import random
def select_weighted( d ):
# calculate total
total = 0
for key in d:
total = total + len(d[key])
accept_prob = float( 1.0 / total )
# pick a weighted value from d
n_seen = 0
for key in d:
current_key = key
for val in d[key]:
dice_roll = random.random()
accept_prob = float( 1.0 / ( total - n_seen ) )
n_seen = n_seen + 1
if dice_roll <= accept_prob:
return current_key
dict = {
'a': [1, 3, 2],
'b': [6],
'c': [0, 0]
}
counts = {}
for key in dict:
counts[key] = 0
for s in range(1,100000):
k = select_weighted(dict)
counts[k] = counts[k] + 1
print counts
После выполнения этого 100 раз, я получаю ключи выбора это количество раз:
{'a': 49801, 'c': 33548, 'b': 16650}
Это довольно близко к вашим ожидаемым значениям:
{'a': 0.5, 'c': 0.33333333333333331, 'b': 0.16666666666666666}
Редактировать: Майлз указал на серьезную ошибку в моей первоначальной реализации, которая с тех пор была исправлена. Извините за это!