Создание списка случайных чисел с весами и ограничениями

#python

Вопрос:

Я хотел бы создать список из 3 чисел со статистическими весами. Я нашел функцию random.choices, которая делает это дружелюбно.

 random.choices(population=[0,1,2], weights = [0.4,0.3,0.3], k=100)
 

Но в дополнение к этому я хотел бы применить некоторые ограничения к этой генерации случайных чисел, где я хотел бы

  • запретить последовательное появление 0
  • запретите 2-му повторяться более двух раз подряд

похоже, функция random.choices() не обладает такой функциональностью. У кого-нибудь есть предложения о том, как подойти к этому?

С наилучшими пожеланиями и заранее спасибо за вашу помощь!

Ответ №1:

Поскольку выборки, выводимые random.choices (помните: с заменой), статистически независимы, просто выбросьте тех участников, которые нарушают ваши ограничения. Вот пример генератора:

 def restricted_choices(population, weights=None, k=1, restrictions={}):
    """Yield k values from `population` with restrictions.
    Works almost like `random.choices()`.

    For every item (k, v) in `restrictions` holds:
      the yielded sequence does not include a sub-sequence `v * [k]`.
    """
    N = 0 # count how many values we have yielded so far
    last_value = None # last value that was yielded
    repeat_count = 0 # how often it has been yielded in a row
    while N < k:
        while True:
            x = random.choices(population, weights)[0]
            if x == last_value and 1   repeat_count == restrictions.get(x, 0):
                continue
            break
        yield x
        N  = 1
        if x == last_value:
            repeat_count  = 1
        else:
            repeat_count = 1
            last_value = x
 

Используйте его вот так:

 list(restricted_choices(population=[0,1,2], weights = [0.4,0.3,0.3],
                        k=100, restrictions={0:2, 2:3}))
 

Однако обратите внимание, что результирующая последовательность больше не будет [.4, .3, .3] распределяться.

Комментарии:

1. Привет! Большое спасибо за ваш ответ Х. Доблер! Работает как заклинание. Я надеялся сам выяснить, как переписать эту функцию динамическим способом, чтобы она могла обрабатывать различные объемы и ограничения типов без необходимости переписывать. В настоящее время я нахожусь на этом этапе:

2. def nchoices_with_restrictions(probability_splits,restrictions): choice_list = [] i = 0 while i < 100: while True: x = rd.choices(range(len(probability_splits)), weights = probability_splits)[0] for restriction in restrictions: if x == restriction: continue choice_list.append(x) i = 1 break return choice_list

3. Который не только ужасно отформатирован, но и просто не работает.

4. Я не знаю, о каких ограничениях вы думаете. В любом случае, чтобы реализовать ограничения, отображающие произвольное количество элементов в обратном направлении , я бы предложил использовать a collections.deque в качестве гибкой замены last1 , last2 а для «кодирования» ваших ограничений вы могли бы использовать что-то вроде forbidden={0: [[0]], 2: [[2,2]]} примера в вопросе.

5. Привет! Спасибо за ваш ответ1 Ограничения будут одного и того же типа, так как все они будут ограничивать количество встречающихся в определенное время. Таким образом, вместо того, чтобы 0 не разрешалось повторяться последовательно, 1 не разрешалось бы повторяться 8 раз подряд. Также может быть введено еще несколько ограничений. В моем примере действовало только два правила, но желаемая функция должна была обрабатывать теоретически бесконечное количество правил. (На практике более 5, вероятно, не потребуется)