Мне нужно было написать взвешенную версию random.choice (каждый элемент в списке имеет разную вероятность быть выбранным). Вот что я придумал.:
defweightedChoice(choices): """Like random.choice, but each element can have a different chance of being selected.
choices can be any iterable containing iterables with two items each. Technically, they can have more than two items, the rest will just be ignored. The first item is the thing being chosen, the second item is its weight. The weights can be any numeric values, what matters is the relative differences between them. """ space = {} current = 0 for choice, weight in choices: if weight > 0: space[current] = choice current += weight rand = random.uniform(0, current) for key insorted(space.keys() + [current]): if rand < key: return choice choice = space[key] returnNone
Эта функция кажется мне чрезмерно сложной и уродливой. Я надеюсь, что все присутствующие могут предложить несколько предложений по ее улучшению или альтернативные способы сделать это. Эффективность для меня не так важна, как чистота и удобочитаемость кода.
Переведено автоматически
Ответ 1
Начиная с версии 1.7.0, в NumPy есть choice функция, поддерживающая распределения вероятностей.
from numpy.random import choice draw = choice(list_of_candidates, number_of_items_to_pick, p=probability_distribution)
Обратите внимание, что probability_distribution это последовательность в том же порядке, что и list_of_candidates. Вы также можете использовать ключевое слово replace=False, чтобы изменить поведение, чтобы нарисованные элементы не заменялись.
Ответ 2
Начиная с Python 3.6, существует метод choices из random модуля.
Обратите внимание, что random.choices будет выполнена выборка с заменой в соответствии с документами:
Возвращает k список элементов, выбранных из совокупности, с заменой.
Обратите внимание на полноту ответа:
Когда единица выборки берется из конечной совокупности и возвращается в эту совокупность после того, как были записаны ее характеристики, перед тем, как будет выбрана следующая единица, выборка называется "с заменой". По сути, это означает, что каждый элемент может быть выбран более одного раза.
Если вам нужно выполнить выборку без замены, то, как говорится в блестящем ответе@ronan-paixão, вы можете использовать numpy.choice, аргумент которого replace управляет таким поведением.
Ответ 3
defweighted_choice(choices): total = sum(w for c, w in choices) r = random.uniform(0, total) upto = 0 for c, w in choices: if upto + w >= r: return c upto += w assertFalse, "Shouldn't get here"
Ответ 4
Распределите веса в кумулятивное распределение.
Используйте random.random() для выбора случайного значения с плавающей точкой 0.0 <= x < total.
from random import random from bisect import bisect
defweighted_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]
Если вам нужно сделать более одного выбора, разделите это на две функции: одну для вычисления совокупных весов, а другую для деления пополам до случайной точки.