В настоящее время я работаю над алгоритмом для реализации скользящего медианного фильтра (аналогичного фильтру скользящего среднего) в C. Из моего поиска в литературе, похоже, есть два достаточно эффективных способа сделать это. Первый - отсортировать начальное окно значений, затем выполнить двоичный поиск, чтобы вставить новое значение и удалить существующее на каждой итерации.
Второй (из Hardle and Steiger, 1995, JRSS-C, Algorithm 296) строит двустороннюю структуру кучи с maxheap на одном конце, minheap на другом и медианной средой в середине. Это дает алгоритм линейного времени вместо алгоритма O (n log n).
Вот моя проблема: реализовать первое можно, но мне нужно запустить это на миллионах временных рядов, поэтому эффективность имеет большое значение. Последнее оказывается очень сложно реализовать. Я нашел код в файле Trunmed.c кода для пакета статистики R, но он не поддается расшифровке.
Кто-нибудь знает хорошо написанную реализацию C для алгоритма скользящей медианы с линейным временем?
Изменить: ссылка на код Trunmed.c http://google.com/codesearch/p?hl=en&sa=N&cd=1&ct=rc#mYw3h_Lb_e0/R-2.2.0/src/library/stats/src/Trunmed.c
Ответы:
Я смотрел на R
src/library/stats/src/Trunmed.c
несколько раз, так как мне тоже хотелось чего-то подобного в автономной подпрограмме C ++ class / C. Обратите внимание, что на самом деле это две реализации в одной, см.src/library/stats/man/runmed.Rd
(Источник файла справки), в котором говоритсяБыло бы хорошо, если бы это использовалось повторно в более автономном режиме. Вы работаете волонтером? Я могу помочь с некоторыми R-битами.
Редактировать 1 : Помимо ссылки на старую версию Trunmed.c выше, вот текущие копии SVN
Srunmed.c
(для версии Stuetzle)Trunmed.c
(для версии Turlach)runmed.R
для функции R, вызывающей этиИзменить 2 : у Райана Тибширани есть код C и Fortran для быстрого медианного биннинга, который может быть подходящей отправной точкой для оконного подхода.
источник
Мне не удалось найти современную реализацию структуры данных C ++ со статистикой порядка, поэтому в итоге я реализовал обе идеи в ссылке на топ-кодеры, предложенной MAK ( Match Editor : прокрутите вниз до FloatingMedian).
Два мультимножества
Первая идея разделяет данные на две структуры данных (кучи, мультимножества и т.д.) с O (ln N) на каждую вставку / удаление, что не позволяет динамически изменять квантиль без больших затрат. То есть у нас может быть скользящая медиана или скользящие 75%, но не то и другое одновременно.
Сегментное дерево
Вторая идея использует сегментное дерево O (ln N) для вставки / удаления / запросов, но более гибкое. Лучше всего "N" - это размер вашего диапазона данных. Так что, если ваша скользящая медиана имеет окно из миллиона элементов, но ваши данные варьируются от 1..65536, то для перемещения скользящего окна в 1 миллион требуется всего 16 операций !!
Код на C ++ аналогичен тому, что написал Денис выше («Вот простой алгоритм для квантованных данных»).
Деревья статистики заказов GNU
Незадолго до того, как сдаться, я обнаружил, что stdlibc ++ содержит деревья статистики порядка !!!
У них есть две важные операции:
См. Руководство по libstdc ++ policy_based_data_structures_test (поиск по запросу "split and join").
Я обернул дерево для использования в удобном заголовке для компиляторов, поддерживающих частичные определения типов в стиле c ++ 0x / c ++ 11:
источник
Я сделал реализацию C здесь . В этом вопросе есть еще несколько деталей: скользящая медиана в реализации C - Turlach .
Пример использования:
источник
Я использую эту инкрементальную оценку медианы:
который имеет ту же форму, что и более распространенная оценка среднего:
Здесь eta - это небольшой параметр скорости обучения (например,
0.001
) иsgn()
сигнум-функция, возвращающая одно из значений{-1, 0, 1}
. (Используйте такую константу,eta
если данные нестационарны и вы хотите отслеживать изменения с течением времени; в противном случае для стационарных источников используйте что-то вродеeta = 1 / n
сходимости, гдеn
- количество отсчетов, просмотренных на данный момент.)Кроме того, я модифицировал среднюю оценку, чтобы она работала для произвольных квантилей. Как правило, функция квантиля сообщает вам значение, которое делит данные на две части:
p
и1 - p
. Следующее оценивает это значение постепенно:Значение
p
должно быть в пределах[0, 1]
. Это существенно смещаетsgn()
симметричный вывод функции{-1, 0, 1}
в сторону одной стороны, разделяя выборки данных на две ячейки разного размера (долиp
и1 - p
данные меньше / больше квантильной оценки, соответственно). Обратите внимание, что дляp = 0.5
, это сводится к средней оценке.источник
Вот простой алгоритм для квантованных данных (через несколько месяцев):
источник
Скользящую медиану можно найти, поддерживая два разбиения чисел.
Для поддержки разделов используйте Min Heap и Max Heap.
Максимальная куча будет содержать числа, меньшие медианы.
Мин. Куча будет содержать числа, превышающие медианное значение.
Ограничение балансировки: если общее количество элементов четное, то обе кучи должны иметь равные элементы.
если общее количество элементов нечетное, то максимальная куча будет иметь на один элемент больше, чем минимальная куча.
Медианный элемент: если оба раздела имеют одинаковое количество элементов, то медиана будет равна половине суммы максимального элемента из первого раздела и минимального элемента из второго раздела.
В противном случае медиана будет максимальным элементом из первого раздела.
источник
Возможно, стоит указать на особый случай, который имеет простое точное решение: когда все значения в потоке являются целыми числами в пределах (относительно) небольшого определенного диапазона. Например, предположим, что все они должны находиться в диапазоне от 0 до 1023. В этом случае просто определите массив из 1024 элементов и счетчик и очистите все эти значения. Для каждого значения в потоке увеличивают соответствующий интервал и счетчик. После завершения потока найдите ячейку, которая содержит максимальное значение count / 2 - это легко сделать, добавив последовательные ячейки, начиная с 0. Используя тот же метод, можно найти значение произвольного порядка ранжирования. (Есть небольшая сложность, если потребуется обнаружение насыщения бункеров и «обновление» размера бункеров до большего типа во время прогона.)
Этот частный случай может показаться искусственным, но на практике он очень распространен. Его также можно применять в качестве приближения для действительных чисел, если они лежат в пределах диапазона и известен «достаточно хороший» уровень точности. Это справедливо практически для любого набора измерений группы объектов «реального мира». Например, рост или вес группы людей. Недостаточно большой набор? Он будет работать так же хорошо для длины или веса всех (отдельных) бактерий на планете - если кто-то может предоставить данные!
Похоже, я неправильно истолковал оригинал - похоже, что ему нужна медиана скользящего окна, а не просто медиана очень длинного потока. Этот подход по-прежнему работает. Загрузите первые N значений потока для начального окна, затем для значения N + 1-го потока увеличьте соответствующий интервал, уменьшая при этом интервал, соответствующий 0-му значению потока. В этом случае необходимо сохранить последние N значений, чтобы разрешить декремент, что может быть эффективно выполнено путем циклической адресации массива размера N. Поскольку положение медианы может изменяться только на -2, -1,0,1 , 2 на каждом шаге скользящего окна, нет необходимости суммировать все ячейки до медианы на каждом шаге, просто настройте «средний указатель» в зависимости от того, какие стороны (-и) ячейки были изменены. Например, если и новое, и удаляемое значение ниже текущей медианы, оно не меняется (смещение = 0). Метод перестает работать, когда N становится слишком большим, чтобы его можно было удобно хранить в памяти.
источник
Если у вас есть возможность ссылаться на значения в зависимости от моментов времени, вы можете выбрать значения с заменой, применив самозагрузку для генерации начального медианного значения в пределах доверительных интервалов. Это может позволить вам вычислить приблизительную медиану с большей эффективностью, чем постоянная сортировка входящих значений в структуру данных.
источник
Для тех, кому нужна работающая медиана на Java ... PriorityQueue - ваш друг. O (log N) вставить, O (1) текущая медиана и O (N) удалить. Если вы знаете распределение ваших данных, вы можете сделать это намного лучше.
источник
}), higher = new PriorityQueue<Integer>();
илиnew PriorityQueue<Integer>(10,
. Я не мог запустить код.Вот тот, который можно использовать, когда точный вывод не важен (для целей отображения и т. Д.). Вам нужны totalcount и lastmedian, а также новое значение.
Дает довольно точные результаты для таких вещей, как page_display_time.
Правила: входной поток должен быть плавным в соответствии с порядком времени отображения страницы, большим по счетчику (> 30 и т. Д.) И иметь ненулевую медиану.
Пример: время загрузки страницы, 800 элементов, 10 мс ... 3000 мс, в среднем 90 мс, реальная медиана: 11 мс
После 30 вводов ошибка медианы обычно составляет <= 20% (9 мс..12 мс) и становится все меньше и меньше. После 800 входов погрешность составляет + -2%.
Другой мыслитель с похожим решением находится здесь: Медианный фильтр Суперэффективная реализация
источник
Вот реализация java
источник
Если вам просто требуется сглаженное среднее, быстрый / простой способ - умножить последнее значение на x, а среднее значение на (1-x), а затем сложить их. Затем это становится новым средним значением.
edit: Не то, о чем просил пользователь, и не так статистически достоверно, но достаточно хорошо для множества применений.
Я оставлю его здесь (несмотря на отрицательные голоса) для поиска!
источник