Как сохранить случайное подмножество потока данных?
У меня поток событий, протекающих через мои серверы. Для меня нецелесообразно хранить их все, но я хотел бы периодически обрабатывать некоторые из них в совокупности. Итак, я хочу сохранить подмножество потока, которое является случайным выборкой всего, что я видел, но имеет максимальный размер.
Итак, для каждого нового элемента мне нужен алгоритм, чтобы решить, добавить ли я его к сохраненному набору, или если я его отброшу. Если я добавлю его, и я уже на пределе, мне нужен алгоритм для выселения одного из старых предметов.
Очевидно, это легко, пока я ниже своего предела (просто сохраните все). Но как я могу поддерживать хороший случайный выбор, не будучи предвзятым к старым предметам или новым предметам, как только я пройду этот предел?
Спасибо,
Ответы
Ответ 1
Для каждого элемента e i в вашем потоке ввода создайте случайное число r i. Добавьте пару (r i, e i) в набор. Когда набор превышает ваш размер выборки n, выкиньте пару с наименьшим r. (A heap - удобная структура данных для этого.)
В конце процедуры у вас теперь есть образец элементов, соединенных с наибольшими n случайными числами (поэтому выбирается равномерно из всех выборок этой длины).
Например, в Python вы должны записать его следующим образом:
import heapq, random
def sample(s, n):
"""
Generate a random sample of n elements from the sequence s.
"""
pairs = ((random.random(), e) for e in s)
for r, e in heapq.nlargest(n, pairs):
yield e
или, скорее, вы бы, если бы не тот факт, что random.sample
уже является функцией в стандартной библиотеке!
Ответ 2
Это общий вопрос для интервью.
Один простой способ сделать это - сохранить n-й элемент с вероятностью k/n (или 1, в зависимости от того, что меньше). Если вам нужно удалить элемент для сохранения нового образца, выведите случайный элемент.
Это дает вам равномерно случайное подмножество n элементов. Если вы не знаете n, вы можете оценить его и получить приблизительно равномерное подмножество.
Ответ 3
Это называется случайной выборкой. Источник: http://en.wikipedia.org/wiki/Reservoir_sampling
array R[k]; // result
integer i, j;
// fill the reservoir array
for each i in 1 to k do
R[i] := S[i]
done;
// replace elements with gradually decreasing probability
for each i in k+1 to length(S) do
j := random(1, i); // important: inclusive range
if j <= k then
R[j] := S[i]
fi
done
Порядочное объяснение/доказательство: http://propersubset.com/2010/04/choosing-random-elements.html
Ответ 4
Пока этот документ не является тем, что вы ищете, это может быть хорошей отправной точкой в вашем поиске.
Ответ 5
хранить образцы в первой очереди в первом (FIFO).
установить частоту дискретизации так много событий между выборками или рандомизировать это немного - в зависимости от ваших шаблонов событий.
сохранить каждое n-ое событие или когда ваша скорость подскажет вам, затем вставьте его в конец очереди.
поп один сверху, если размер слишком большой.
Ответ 6
Назначьте вероятность записи каждого события и сохраните событие в индексируемой структуре данных. Когда размер структуры достигнет порога, удалите случайный элемент и добавьте новые элементы. В Ruby вы можете сделать это:
@storage = []
prob = 0.002
while ( message = getnextMessage) do
@storage.delete((rand() * @storage.length).floor) if @storage.length > MAX_LEN
@storage << message if (rand() < prob)
end
Это касается вашего максимального размера и вашего смещения в сторону, когда произошло событие. Вы также можете выбрать, какой элемент будет удален, разбив ваши сохраненные элементы на ведра и затем удалив элемент из любого ведра, содержащего более одного элемента. Например, метод bucket позволяет хранить один из каждого часа.
Вы также должны знать, что теория выборки - это Большая математика. Если вам нужно больше, чем идея непрофессионала, вы должны проконсультироваться с квалифицированным математиком в вашем районе.
Ответ 7
Предполагается, что вы не знаете общее количество событий, которые будут получены, и что вам не нужно минимальное количество элементов в подмножестве.
arr = arr[MAX_SIZE] //Create a new array that will store the events. Assuming first index 1.
counter = 1 //Initialize a counter.
while(receiving event){
random = //Generate a random number between 1 and counter
if( counter == random ){
if( counter <= MAX_SIZE ){
arr[counter] = event
}
else{
tmpRandom = //Generate a random number between 1 and MAX_SIZE
arr[tmpRandom] = event
}
}
counter =+ 1
}