Мне нужно было написать взвешенную версию random.choice (каждый элемент в списке имеет различную вероятность выбора). Вот что я придумал:
def weightedChoice(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 in sorted(space.keys() + [current]):
if rand < key:
return choice
choice = space[key]
return None
Эта функция кажется мне слишком сложной и безобразной. Я надеюсь, что все здесь могут предложить некоторые предложения по улучшению или альтернативные способы сделать это. Эффективность не так важна для меня, как чистота кода и удобочитаемость.
python
optimization
Colin
источник
источник
random.choices
для отдельных вызовов. Если вам нужно много случайных результатов, очень важно выбрать их все сразу, настроивnumber_of_items_to_pick
. Если вы это сделаете, это будет на порядок быстрее.len(list_of_candidates)
list_of_candidates[draw]
Начиная с Python 3.6 есть метод
choices
изrandom
модуля.Обратите внимание, что
random.choices
будет образец с заменой , в соответствии с документами :Если вам нужно выполнить выборку без замены, то, как гласит блестящий ответ @ ronan-paixão , вы можете использовать
numpy.choice
, чейreplace
аргумент контролирует такое поведение.источник
random.choices
нет, поэтому, конечно, он медленнее в минимальном списке из 8 элементов, и если вы выбираете 10k раз из такого списка, вы правы. Но для случаев, когда список больше (в зависимости от того, как вы тестируете, я вижу точки разрыва между 100-300 элементами),np.random.choice
начинает выигрыватьrandom.choices
от довольно большого разрыва. Например, включая шаг нормализации вместе с вызовом numpy, я получаю ускорение почти в 4 разаrandom.choices
для списка из 10 тыс. Элементов.источник
upto +=w; if upto > r
if r < 0
r <= 0
. Рассмотрим входной набор из 1 предметов и бросок 1,0. Утверждение потерпит неудачу тогда. Я исправил эту ошибку в ответе.# pragma: no branch
0.0 <= x < total
.Если вам нужно сделать более одного выбора, разделите его на две функции: одну для построения совокупных весов, а другую для деления пополам на случайную точку.
источник
O(n)
из-за совокупного расчета распределения.random()
не может вернуть 1.0. Согласно документам, он возвращает результат в полуоткрытом интервале[0.0, 1.0)
, то есть он может вернуть ровно 0,0, но не может вернуть ровно 1,0. Наибольшее значение, которое он может вернуть, составляет 0,999999999999999988897769753748434595763683319091796875 (которое Python печатает как 0,99999999999999999 и является самым большим 64-разрядным числом с плавающей запятой меньше 1).Если вы не возражаете против использования numpy, вы можете использовать numpy.random.choice .
Например:
Если вы знаете, сколько выборов нужно сделать заранее, вы можете сделать это без цикла, подобного следующему:
источник
Грубо, но может быть достаточно
Это работает?
Печать:
Предполагается, что все веса являются целыми числами. Они не должны добавлять до 100, я просто сделал это, чтобы результаты теста было легче интерпретировать. (Если веса являются числами с плавающей запятой, умножьте их все на 10 несколько раз, пока все веса>> 1.)
источник
[[]]*10
- все элементы внешнего списка указывают на один и тот же список.int
вы по-прежнему получаете много ссылок на один и тот же объект, выполняя что-то подобное, вы[id(x) for x in ([99**99] * 100)]
наблюдаете, чтоid
при каждом вызове возвращается один и тот же адрес памяти.Если у вас есть взвешенный словарь вместо списка, вы можете написать это
Обратите внимание, что
[k for k in items for dummy in range(items[k])]
производит этот список['a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'c', 'b', 'b', 'b', 'b', 'b']
источник
Начиная с Python
v3.6
,random.choices
может использоваться для возвратаlist
элементов заданного размера из заданной совокупности с необязательными весами.население :
list
содержит уникальные наблюдения. (Если пусто, поднимаетIndexError
)веса : точнее относительные веса, необходимые для выбора.
cum_weights : совокупные веса, необходимые для выбора.
k : размер (
len
) объектаlist
для вывода. (По умолчаниюlen()=1
)Несколько предостережений:
1) Используется взвешенная выборка с заменой, чтобы вытянутые элементы впоследствии были заменены. Значения в последовательности весов сами по себе не имеют значения, но их относительное соотношение имеет значение.
В отличие от того,
np.random.choice
который может принимать только вероятности в качестве весов, а также который должен обеспечивать суммирование индивидуальных вероятностей до 1 критерия, здесь нет таких правил. Пока они принадлежат числовым типам (int/float/fraction
кромеDecimal
типа), они все равно будут работать.2) Если ни веса, ни cum_weights не указаны, выборы делаются с равной вероятностью. Если указана последовательность весов , она должна быть той же длины, что и последовательность совокупности .
Задание весов и cum_weights повышает a
TypeError
.3) cum_weights обычно являются результатом
itertools.accumulate
функции, которая действительно удобна в таких ситуациях.Таким образом, либо поставка,
weights=[12, 12, 4]
либоcum_weights=[12, 24, 28]
для нашего надуманного дела дает тот же результат, и последний кажется более быстрым / эффективным.источник
Вот версия, которая включена в стандартную библиотеку для Python 3.6:
Источник: https://hg.python.org/cpython/file/tip/Lib/random.py#l340
источник
источник
Я, вероятно, слишком поздно, чтобы внести что-то полезное, но вот простой, короткий и очень эффективный фрагмент:
Нет необходимости сортировать ваши вероятности или создавать вектор с помощью cmf, и он завершается, когда находит свой выбор. Память: O (1), время: O (N), со средним временем работы ~ N / 2.
Если у вас есть вес, просто добавьте одну строку:
источник
np.random.choice
. Но что еще более интересно, есть режим отказа, где это вызывает исключение. Выполнениеprobabilities = weights / sum(weights)
не гарантирует, чтоprobabilities
составит 1; например, еслиweights
is,[1,1,1,1,1,1,1]
тоprobabilities
сумма будет только 0,99999999999999998, что меньше максимально возможного возвращаемого значенияrandom.random
(которое составляет 0,99999999999999999). Тогдаchoice <= cmf
никогда не будешь доволен.Если ваш список взвешенных вариантов относительно статичен и вам требуется частая выборка, вы можете выполнить один O (N) -процесс предварительной обработки, а затем выполнить выбор в O (1), используя функции из этого связанного ответа .
источник
Я посмотрел указанную другую нить и нашел этот вариант в моем стиле кодирования, он возвращает индекс выбора для подсчета, но просто вернуть строку (закомментированная альтернатива возврата):
источник
Это зависит от того, сколько раз вы хотите попробовать дистрибутив.
Предположим, вы хотите попробовать распределение K раз. Тогда сложность времени, используемая
np.random.choice()
каждый раз, - этоO(K(n + log(n)))
когдаn
количество элементов в распределении.В моем случае мне нужно было выбрать одно и то же распределение несколько раз порядка 10 ^ 3, где n порядка 10 ^ 6. Я использовал приведенный ниже код, который предварительно вычисляет накопительное распределение и пробует его в
O(log(n))
. Общая сложность времени естьO(n+K*log(n))
.источник
Если у вас есть Python 3, и вы боитесь устанавливать
numpy
или писать свои собственные циклы, вы можете сделать:Потому что вы можете собрать все что угодно из пакета адаптеров! Хотя ... Я должен признать, что ответ Неда, хотя и немного длиннее, легче понять.
источник
Общее решение:
источник
Вот еще одна версия weighted_choice, которая использует numpy. Передайте вектор весов, и он вернет массив из 0, содержащий 1, указывающий, какой лот был выбран. По умолчанию в коде используется только одна раздача, но вы можете указать количество разыгранных розыгрышей, и будет возвращено количество разыгранных бинов.
Если вектор весовых коэффициентов не равен 1, он будет нормализован.
источник
Другой способ сделать это, предполагая, что у нас есть веса с тем же индексом, что и у элементов в массиве элементов.
Теперь давайте предположим, что мы должны отобрать 3 элемента в 1 пробной версии. Вы можете предположить, что есть три шара R, G, B, присутствующие в большом количестве в соотношении их весов, заданных массивом весов, следующие результаты могут быть возможными:
Вы также можете думать о количестве элементов, которые будут выбраны в качестве количества биномиальных / полиномиальных испытаний в наборе. Итак, вышеприведенный пример можно еще поработать как
источник
Об этом есть лекция Себастьяна Турна в бесплатном курсе AI для робототехники Udacity. По сути, он создает циклический массив индексированных весов с помощью оператора mod
%
, устанавливает переменную beta в 0, случайным образом выбирает индекс для циклов по N, где N - число индексов, а в цикле for сначала увеличивается бета по формуле:бета = бета + единообразная выборка из {0 ... 2 * Weight_max}
и затем вложенный в цикл for, цикл while согласно ниже:
Затем перейдите к следующему индексу для повторной выборки на основе вероятностей (или нормированной вероятности в случае, представленном в курсе).
Ссылка на лекцию: https://classroom.udacity.com/courses/cs373/lessons/48704330/concepts/487480820923
Я вошел в Udacity со своей школьной учетной записью, поэтому, если ссылка не работает, это Урок 8, видео № 21 «Искусственного интеллекта для робототехники», где он читает лекции по фильтрам частиц.
источник
Одним из способов является рандомизация по сумме всех весов, а затем использование значений в качестве предельных точек для каждой переменной. Вот грубая реализация в качестве генератора.
источник
Используя NumPy
источник
np.random.choice
, как уже упоминалось в принятом ответе, который был здесь с 2014 года, уже есть. Какой смысл кататься самостоятельно?Мне нужно было сделать что-то вроде этого очень быстро, очень просто, от поиска идей я наконец-то создал этот шаблон. Идея состоит в том, чтобы получить взвешенные значения в форме JSON от API, который здесь моделируется диктом.
Затем переведите его в список, в котором каждое значение повторяется пропорционально его весу, и просто используйте random.choice, чтобы выбрать значение из списка.
Я попробовал запустить его с 10, 100 и 1000 итерациями. Распределение кажется довольно солидным.
источник
Мне не понравился синтаксис любого из них. Я действительно хотел просто указать, что это были за вещи и какой вес у каждого из них. Я понимаю, что мог бы использовать,
random.choices
но вместо этого я быстро написал класс ниже.источник
Укажите random.choice () с предварительно взвешенным списком:
Решение и тест:
Вывод:
источник