Пройдите все числа m от 0 до N, решая, включать ли m в набор при обнаружении.Вам необходимо обновить вероятность включения следующего числа на основе уже обработанных номеров.
Давайте применим эту идею к приведенному примеру с n = 3 и N = 5.Сначала рассмотрим m = 0.Осталось 3 числа и 5 вариантов, поэтому 0 находится в наборе с вероятностью 3/5.Используйте генератор случайных чисел, чтобы решить, включать число или нет.Теперь рассмотрим m = 1.Если вы включили 0 в набор, то у вас осталось 2 числа и 4 возможности, поэтому его следует включить с вероятностью 2/4, но если 0 не включено, у вас осталось 3 числа и 4 возможности и, следовательно, 1 должна быть включенас вероятностью 3/4.Это продолжается до тех пор, пока необходимые 3 числа не будут включены в набор.
Вот реализация на Python:
from __future__ import division
import random
def rand_set(n, N):
nums_included=set()
for m in range(N):
prob = (n-len(nums_included)) / (N-m)
if random.random() < prob:
nums_included.add(m)
return nums_included
Вы можете (и, вероятно, должны) добавить в тест, чтобы увидеть, когда вы 'В вашем наборе достаточно номеров и вырваться из цикла рано.
Числа хранятся в наборе, размер которого варьируется от 0 до n, поэтому используется хранилище O(n)
.Все остальное использует постоянное пространство, поэтому оно в целом O(n)
.
EDIT На самом деле, вы можете пойти немного дальше с этим подходом, чтобы он занимал постоянное пространство.В Python просто создайте генератор на основе вышеизложенного:
def rand_set_iter(n, N):
num_remaining = n
m = 0
while num_remaining > 0:
prob = num_remaining / (N-m)
if random.random() < prob:
num_remaining -= 1
yield m
m += 1
Здесь я пошел дальше и использовал цикл while вместо цикла for.Чтобы сохранить результаты, вам, конечно, нужно использовать O(n)
пробел.Но если все, что вам нужно сделать, это перебрать числа, версия генератора сделает это в O(1)
.
Для языка без генераторов вы можете запустить свой собственный генератор, многократно вызывая функцию и обновляя статическую или глобальную переменную.