Алгоритм случайного заполнения массива без коллизий

Скажем, у меня есть массив из N целых чисел со значением «0», и я хочу выбрать случайный элемент этого массива со значением «0» и присвоить ему значение «1».

Как мне сделать это эффективно?

Я придумал 2 решения, но они выглядят довольно неэффективно

Первое решение

int array[N] //init to 0s
int n //number of 1s we want to add to the array
int i = 0
while i < n
    int a = random(0, N)
    if array[a] == 0
        array[a] = 1
        i++
    end if
end while

Это было бы крайне неэффективно для больших массивов из-за вероятности столкновения

Второй включает в себя список, содержащий все 0 оставшихся позиций, и мы выбираем случайное число между 0 и числом оставшихся 0 для поиска значения в списке, которое соответствует индексу в массиве. Это намного надежнее, чем первое решение, поскольку количество операций ограничено, но все же имеет сложность сценария в худшем случае N², если мы хотим полностью заполнить массив.


person user3548298    schedule 15.08.2016    source источник


Ответы (2)


Ваше второе решение на самом деле является хорошим началом. Я предполагаю, что это связано с перестроением списка позиций после каждого изменения, что делает его O (N²), если вы хотите заполнить весь массив. Однако вам не нужно каждый раз перестраивать список. Поскольку вы все равно хотите заполнить массив, вы можете просто использовать случайный порядок и соответствующим образом выбрать оставшиеся позиции.

В качестве примера возьмем следующий массив (размер 7 и изначально не полный нулей): [0, 0, 1, 0, 1, 1, 0]

После того, как вы создали список нулевых позиций, здесь [0, 1, 3, 6], просто перетасуйте его, чтобы получить случайный порядок. Затем заполните массив в порядке, заданном позициями.

Например, если перемешивание дает [3, 1, 6, 0], вы можете заполнить массив следующим образом:

[0, 0, 1, 0, 1, 1, 0]  <- initial configuration
[0, 0, 1, 1, 1, 1, 0]  <- First, position 3
[0, 1, 1, 1, 1, 1, 0]  <- Second, position 1
[0, 1, 1, 1, 1, 1, 1]  <- etc.
[1, 1, 1, 1, 1, 1, 1]

Если массив изначально заполнен нулями, то еще проще. Ваш исходный список — это список целых чисел от 0 до N (размер массива). Перемешайте его и примените тот же процесс.

Если вы не хотите заполнять весь массив, вам все равно нужно построить весь список, но вы можете обрезать его после его перетасовки (что просто означает прекращение заполнения массива после некоторого момента).

Конечно, это решение требует, чтобы массив не менялся между каждым шагом.

person Nelfeal    schedule 15.08.2016
comment
Я думаю, что это самый эффективный способ сделать это, так как я не выполняю операции над результирующим массивом, который его изменяет. Спасибо. - person user3548298; 15.08.2016

Вы можете заполнить массив n единицами и N-n нулями и произвести случайное перемешивание.

тасовка Фишера-Йейтса имеет линейную сложность:

for i from N−1 downto 1 do
     j ← random integer such that 0 ≤ j ≤ i
     exchange a[j] and a[i]
person MBo    schedule 15.08.2016
comment
Дело в том, что мне нужны состояния промежуточного массива в другом процессе - person user3548298; 15.08.2016
comment
Вам нужен массив с 1 единицей, затем с 2 единицами и так далее? Должны ли они быть в случайных местах или подходит монотонно возрастающая случайная последовательность? - person MBo; 15.08.2016
comment
Вам нужен массив с 1 единицей, затем с 2 единицами и так далее? Да. Новые единицы, которые добавляются на каждом шаге, должны выбираться случайным образом среди оставшихся нулей. - person user3548298; 15.08.2016
comment
Первый вариант - 5,12, 6, 2. Второй - 2, 5, 6, 12. Подходит ли второй? - person MBo; 15.08.2016
comment
Первый вариант - 5,12, 6, 2. Второй - 2, 5, 6, 12. Подходит ли второй? Я понятия не имею, что ты пытаешься сказать - person user3548298; 15.08.2016
comment
Второй подход - заполнить случайные места, но в порядке возрастания - person MBo; 15.08.2016
comment
UV'd - это правильный способ сделать это со статистической точки зрения. - person Bathsheba; 15.08.2016