Если это реальный поток, вам нужно O(n)
время для его сканирования.
Ваш существующий алгоритм хорош. (Я ошибался раньше.) Вы можете по индукции доказать, что вероятность того, что вы не выбрали первый элемент в i
попытках, равна 1 - i/n = (n-i)/n
. Первое, что верно для i=0
осмотром. Теперь, если вы не выбрали его в i
-х попытках, вероятность того, что следующий выберет его, равна 1/(n-i)
. И тогда шансы выбрать его на i+1
-ой попытке - ((n-i)/n) * (1/(n-i)) = 1/n
. Это означает, что вероятность не выбрать его в первый раз i+1
равна 1 - i/n - 1/n = 1 - (i+i)/n
. Это завершает индукцию. И поэтому вероятность выбора первого элемента в первых k
попытках - это вероятность того, что он не был выбран, или 1 - (n - k/n) = k/n
.
Но что, если у вас есть O(1)
доступ к любому элементу? Обратите внимание, что выбор k
- это то же самое, что и выбор n-k
. Таким образом, без ограничения общности мы можем предположить, что k <= n/2
. Это означает, что мы можем использовать рандомизированный алгоритм, подобный этому:
chosen = set()
count_chosen = 0
while count_chosen < k:
choice = random_element(stream)
if choice not in chosen:
chosen.add(choice)
count_chosen = count_chosen + 1
Набор будет O(k)
пробел, и, поскольку вероятность того, что каждый случайный выбор будет для вас новым, составляет не менее 0.5
, ожидаемое время выполнения не хуже, чем 2k
вариантов.