Поиск это последовательность времени? - PullRequest
0 голосов
/ 21 декабря 2018

У нас есть поток входных данных в формате - {val, timestamp}.Отметка времени строго увеличивается.Значения являются целыми числами (32-разрядными).Нас интересуют только последние n отметок времени.Например.Если n равно 100 и вызывается add (400, 12), нас интересуют только временные ряды [300, 400].Мы также хотим иметь возможность искать в последних n событиях временной метки.

Для поиска, если нет значения с определенной временной меткой, мы хотим вернуть значение предыдущей временной метки (учитывая, что предыдущая временная меткав диапазоне [latestTimestamp-n, latestTimestamp]).

Один из способов решения этой проблемы - использовать двоичное дерево поиска (отображение в C ++).При добавлении мы добавим элемент в BST.Таким образом, сложение будет иметь сложность O (log n).Для поиска мы просто выполнили бы поиск по нижнему пределу (в C ++) и проверили, попадает ли временная метка в допустимый диапазон ([latestTimestamp-n, latestTimestamp]).Поиск также будет иметь сложность O (log n).

Я хочу знать, существует ли алгоритм с лучшей временной сложностью даже за счет увеличения сложности пространства?Меня больше интересует улучшение временной сложности операции поиска (отметка времени).

Ответы [ 3 ]

0 голосов
/ 25 декабря 2018

Вы можете использовать хеш-таблицу и иметь среднее значение O (1) на операцию и худший случай O (n).Но если вас волнует наихудший случай, используйте ответ @Matt Timmermans.

0 голосов
/ 26 декабря 2018

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

Сначала (так же, как @MattОтвет Тиммерманс), вы можете использовать очень простую структуру данных для хранения пар (timestamp, value): взять массив pairs размера N и смещение cur_pair.Чтобы добавить новый элемент, в псевдокоде:

pairs[cur_pair] = (timestamp, value)
cur_pair = cur_pair + 1 % N

Это O (1) .

Для поиска заданной отметки времени в O (n) , просто переберите pairs[cur_pair], ..., pairs[N-1], pairs[0], ..., pairs[cur_pair-1], пока вы не превысите отметку времени, которую вы ищете.Результатом является последнее значение, которое вы прочитали перед выходом из цикла.(Вам решать, что вы делаете, когда отметка времени находится до первой сохраненной отметки времени или после последней сохраненной отметки времени).

Как вы уже видели, можно ускорить поиск с помощью двоичного поиска:

# look for timestamp t
s = cur_pair, e = (cur_pair-1) % N
loop:
    if (e-s) % N <= 1:
        return pairs[s].value

    middle = (s+e) // 2 % N # // is integer division
    if pairs[middle].timestamp > t:
        s = middle
    else:
        e = middle

Это O (log N) .

Теперь вы можете, при некоторых предположениях, искать в O (1) амортизируется .Прежде чем дать предположения, давайте рассмотрим идею.

Сохраните предыдущий массив pairs, но добавьте новый массив timestamps длины M.Этот массив разрезает время на равные промежутки времени длительностью slice.У вас есть cur_ts для хранения текущего индекса.Добавить сейчас:

new_ts = timestamp // slice
for ts in [cur_ts + 1, ..., new_ts]: # every time slice bewteen cur+1 and new ts...
    timestamps[ts % M] = cur_pair # ...gets the value of the index in pair array.
cur_ts = new_ts % M

# and the previous add:
pairs[cur_pair] = (timestamp, value)
cur_pair = cur_pair + 1 % N

Это все еще O (1).Обратите внимание, что сохраняется только первая временная метка во временном интервале.

Теперь ищите отметку времени t (я не пишу крайние случаи):

index = t // slice
s = timestamps[index] # lower bound
e = timestamps[index + 1 % M] # upper bound

# do the full lookup or the binary search here.

Теперь, почему я говорю, что это O (1) амортизированная сложность времени?

  1. вычислить индекс метки времени в timestamp (деление по модулю M: O (1) )
  2. найти индекс пары(доступ к элементу в массиве: O (1) )
  3. найти правильную метку времени, начиная с преобладающего индекса.

Шаг 3. is O (1) только если вы можете выбрать значение slice, которое достаточно мало, чтобы быть уверенным, что у вас почти никогда не будет двух временных меток в срезе.То, что «почти никогда» означает, является частью известного компромисса пространства-времени.

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

0 голосов
/ 22 декабря 2018

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

Если вы используете динамический массив -очередь с резервным копированием (например, ArrayDeque в Java), затем добавление новой записи в конец и удаление любых записей с начала, которые она устарела, можно выполнить за амортизированное постоянное время.Поиск будет простым бинарным поиском и займет O (log N).

...