Как сохранить случайное подмножество потока данных?

У меня поток событий, протекающих через мои серверы. Для меня нецелесообразно хранить их все, но я хотел бы периодически обрабатывать некоторые из них в совокупности. Итак, я хочу сохранить подмножество потока, которое является случайным выборкой всего, что я видел, но имеет максимальный размер.

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

Очевидно, это легко, пока я ниже своего предела (просто сохраните все). Но как я могу поддерживать хороший случайный выбор, не будучи предвзятым к старым предметам или новым предметам, как только я пройду этот предел?

Спасибо,

Ответ 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

}