Равномерная генерация перестановки с повторением не более k раз? - PullRequest
1 голос
/ 01 июня 2019

Мы установили {1, 2, 3, ...,n} чисел. Мы хотим сгенерировать перестановку длины m, созданную из этих чисел, с повторением каждого числа не более k раз.

Если мы предположим n=5, k=2, m=3, то мы могли бы получить: {3,3,1}, но не {3, 3, 3}, поскольку 3 во втором примере получается трижды в выходе, что больше, чем k.

Есть ли способ равномерной генерации такой перестановки быстрым способом?

Я пробовал два разных решения.

Во-первых:

1) генерация случайной перестановки с повторением, существует n^m разных перестановок.

2) проверить, является ли это правильной перестановкой (если она не содержит более k раз того же числа

3) если да, вернитесь, иначе перейдите к 1)

Фрагмент Python:

import numba
import numpy as np


@numba.jit(nopython=True)
def gen_sequence1(n, k, m):
    result = np.random.randint(0, n, (1, m))[0]
    while not is_correct(result, k):
        result = np.random.randint(0, n, (1, m))[0]
    return result


@numba.jit(nopython=True)
def most_frequent(iter):
    return np.bincount(iter).max()


@numba.jit(nopython=True)
def is_correct(pruf, k):
    return most_frequent(pruf) <= k

Второй метод:

Генерировать случайное целое число, добавлять его в последовательность, только если оно не появлялось до k раз. Оптимизированная версия этих слов представлена ​​ниже (написана на Python). Фрагмент Python:

def gen_seq(n, d, m):
    choices = list(range(n))
    degrees = [0] * n
    result = []
    k = n - 1
    for i in range(m):
        rand = np.random.randint(0, k)
        result.append(choices[rand])
        degrees[choices[rand]] += 1
        if degrees[choices[rand]] == d:
            choices[rand], choices[k] = choices[k], choices[rand]
            k -= 1
    return result

Проблема в том, что первый метод очень медленный для n=30, m=28, d=1, ему нужно 10^9 раз для генерации последовательности, что довольно очевидно.

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

Есть ли у вас идеи, как можно было бы генерировать такую ​​последовательность быстро и равномерно?

Ответы [ 2 ]

0 голосов
/ 02 июня 2019

Если я правильно помню, у np.choice есть опции для определения вероятностей, тогда вы можете сделать что-то вроде этого:

  1. Установить массив [1..n].

  2. Дублируйте массив k раз: [1..n, 1..n, 1..n, ... 1..n] в большой массив.точно так же, как предложенный @rossum.

  3. Генерация вероятностей для этой униформы большого массива (1 / (k * n)).

Повторите m раз:

Получить одно число в массиве результатов Установить вероятности того, что для вероятности нарисованного элемента будет 0, а для остальных с одинаковым значением, распределенным между ними равномерно 1 / (k * n), которое мы просто установили в 0

Пример:

Пусть S = [1,1,1,2,2,2,3,3,3,4,4,4] большой массив с kкаждого элемента внутри, k = 3 и m = 4.

  1. генерирует P = [1/12] * len (S)

  2. результат = случайный (S, P) Предположим, что результат = [1]

  3. Вероятности будут такими: P = [0,1 / 12 + 1 / 36,1 / 12 + 1/ 36,1 / 12 + 1/36, остальные остаются неизменными]

Повторите шаги 2 и 3 м

, если больше нет значений, которые являютсятак же, как показано на рисунке, установите его равным 0 и сделайте вероятности покоя, чтобы сохранить этот коэффициент и суммировать с 1.я думаю, что самой сложной частью будет манипулирование вероятностями.

0 голосов
/ 01 июня 2019

Предполагается, что у вас достаточно памяти для хранения чисел [1..n] k раз.

  1. Настройка массива [1..n].

  2. Дублируйте массив k раз: [1..n, 1..n, 1..n, ... 1..n] в большой массив.

  3. Выполните первые m шагов шага Фишера-Йейтса в большом дублированном массиве, чтобы получить требуемую перестановку. Нет необходимости перетасовывать весь массив, так как вам нужно только m чисел.

...