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

timestamps = [1467267654, 1467267657, 1467267660, ... 146726821]

Я должен искать интервал в 30 секунд, где количество повторов превышает 5.

Итак, если между i и j и timestamps [j] -timestamps [i] <= 30 секунд есть как минимум 5 элементов, затем верните истину.

Какой алгоритм лучше использовать здесь? Имейте в виду, что я использую python, поэтому, возможно, он уже реализован под numpy. Любые предложения полезны.

-2
ctotolin 30 Июн 2016 в 09:24
Ваши данные отсортированы?
 – 
01axel01christian
30 Июн 2016 в 09:28
Да, массив отсортирован
 – 
ctotolin
30 Июн 2016 в 09:30
Пожалуйста, уточните однозначно, что вы имеете в виду под «как минимум 5 элементами между». Сколько должно быть j-i?
 – 
Yves Daoust
30 Июн 2016 в 10:23
J-i должно быть 4, что означает, что 5 элементов находятся между i и j
 – 
ctotolin
30 Июн 2016 в 11:01
Проблема с «между» в том, что вы не знаете, является ли оно включающим или нет; а с индексированием по основанию 0 принято говорить "включающее влево / исключающее право", поэтому существует как минимум три интерпретации.
 – 
Yves Daoust
30 Июн 2016 в 11:18

2 ответа

Поскольку данные отсортированы, достаточно одного прохода. Выполните итерации по массиву следующим образом:

  • запомните текущий индекс i, давайте назовем этот индекс start
  • увеличивайте i до тех пор, пока не будет i = start + 4 или a[i] > a[start] + 30: в первом случае верните true, во втором случае обновите start = start + 1 и продолжите

Общая сложность: O(n).

Бонус

  • если вы хотите вернуть все такие интервалы, то в случае, если вы нашли подходящий интервал, запомните его границы и продолжайте
  • если вы хотите вернуть самый длинный такой интервал, не останавливайтесь, когда вы найдете i = start + 4 такое, что a[i] < a[start] + 30, а увеличьте интервал дальше. Когда вы наконец встретите индекс j, чтобы a[j] > a[start] + 30 запомнил границы этого самого длинного на данный момент интервала, обновите start = start + 1 и продолжите
0
Miljen Mikic 30 Июн 2016 в 11:02
Пробовать промежуточные элементы между start и start + 5 - это излишне, достаточно сравнить две крайности, чтобы сделать вывод. В худшем случае вы выполняете в 5 раз больше работы.
 – 
Yves Daoust
30 Июн 2016 в 10:30
Согласен. Я знал об этом при написании ответа, но для небольшого размера интервала (в данном случае 5) асимптотически это не имеет большого значения (и оно было лучше, чем другое предложенное решение, которое было O (nlogn)). Ваше решение определенно более эффективно.
 – 
Miljen Mikic
30 Июн 2016 в 11:02

Попробуйте все пары меток времени с 4 индексами друг от друга, пока не найдете ту, которая отличается менее чем на 30 секунд.

for i in range(len(timestamps) - 4):
    if timestamp[i + 4] - timestamp[i] <= 30:
        return true
return false

После постановки задачи нет необходимости сообщать положение интервала. Цикл принимает ровно I+1 сравнений, где I - это индекс первого совпадающего интервала (N - 4, если его нет).

Сравнение наилучшего случая 1, наихудшего случая N-4, ожидаемого случая p(E(I)+1)+(1-p)(N-4), где E(I) - это ожидание I, а p - вероятность того, что существует подходящий интервал.


В более современном и заметно неэффективном стиле (timestamp сокращено до t),

reduce(lambda a, b: a or b, [t[i + 4] - t[i] <= 30 for i in range(len(t) - 4)])
0
Yves Daoust 30 Июн 2016 в 11:53