Алгоритм скользящей медианы в C
В настоящее время я работаю над алгоритмом реализации скользящего медианного фильтра (аналог скользящего среднего фильтра) на языке C. Из моего поиска в литературе следует, что есть два достаточно эффективных способа сделать это. Первый заключается в сортировке начального окна значений, а затем выполнении двоичного поиска для вставки нового значения и удаления существующего на каждой итерации.
Второй (из Hardle and Steiger, 1995, JRSS-C, Алгоритм 296) строит двухстороннюю структуру кучи, с максимальной кучей на одном конце, минимальной кучей на другом, и медианой в середине. Это дает алгоритм линейного времени вместо алгоритма O(n log n).
Вот в чем моя проблема: реализация первого варианта вполне выполнима, но мне нужно прогнать его на миллионах временных рядов, поэтому эффективность имеет большое значение. Второе оказалось очень сложно реализовать. Я нашел код в файле Trunmed.c из кода для пакета stats в R, но он довольно неразборчив.
Знает ли кто-нибудь хорошо написанную реализацию на C для алгоритма скользящей медианы с линейным временем?
Edit: Ссылка на код 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's
src/library/stats/src/Trunmed.c
, так как хотел получить нечто подобное в отдельном классе C++ / подпрограмме C. Обратите внимание, что на самом деле это две реализации в одной, см.src/library/stats/man/runmed.Rd
(источник файла справки), в котором написаноЯ не мог'т найти современная реализация на C++ структуры данных для статистики, так что в итоге реализации идей в топ-кодеров ссылке, предложенной МАК (игре в редакции: прокрутите вниз, чтобы FloatingMedian).
Двух мультимножеств
Первая мысль разделяет данные на две структуры данных (кучи, мультимножеств и т. д) С О(В Н) на вставку/удаление не позволяет квантиль, чтобы быть динамически изменены без больших затрат. Т. е. мы можем иметь скользящий средний, или переходящий 75%, но не оба одновременно.
Дерево отрезков,
Вторая идея использует дерево отрезков, которое составляет o(ЛН N) для вставки/делеции/запросы, но является более гибким. Лучший из всех "Н" это размер круга сведения. Так что если ваш прокатный медиана имеет окно из миллионов элементов, но ваших данных варьируется от 1..65536, то только 16 операций, необходимых в движение завальцовки окна на 1 млн!!
Код на C++ похож на то, что Денис выложил выше (на"Здесь'ы простой алгоритм для квантованных данных и")
ГНУ порядке статистические деревьев
Просто прежде чем сдаваться, я обнаружил, что stdlibc++ содержит порядковую статистику деревья!!!
Они имеют две важные операции:
См. libstdc++ в ручном policy_based_data_structures_test (поиск и"и на").
Я обернула дерево используемые в заголовке удобство для компиляторов поддерживает C++0x или в C++11 стиль частичные определения типов:
Я'ве сделали [к реализации][1][ здесь][2]. Еще несколько подробностей в данный вопрос: https://stackoverflow.com/questions/5527437/rolling-median-in-c-turlach-implementation.
Пример использования:
Я использую этот добавочный средний сметчик:
который имеет такую же форму, как более универсальное средство оценки:
Вот эта - это небольшой параметра скорости обучения (например,
0.001
), ифункция SGN () - это функция Сигнум, который возвращает одно из
{-1, 0, 1}. (Использовать константу
ета, как это, если данные нестационарные и вы хотите отслеживать изменения с течением времени; в противном случае, для стационарных источников, использовать что-то вроде
ета = 1 / N, чтобы сходятся, где
n` - количество отсчетов видел до сих пор.)Кроме того, я изменил средний оценщик, чтобы заставить его работать для произвольного квантилей. В общем, функция квантиля представляет собой значение, которое делит данные на две фракции:
P
и1 - п
. Следующие оценки постепенно это значение:Значение " Р " должно быть в пределах[0, 1]
. Это существенно сдвигает
функция SGN()функция'ы симметричный выход
{-1, 0, 1}склоняться к одной стороне, секционирование данных образцов на две неодинаково размер закрома (фракции
Pи
1 - пданных меньше/больше, чем квантильные оценки, соответственно). Обратите внимание, что для п = 0.5
, это снижает средней оценки.Здесь'ы простой алгоритм для квантованных данных (месяцев):
Прокат медиана может быть найден путем поддержания двух разделов цифр.
Для поддержания перегородки использовать кучу мин и Макс кучи.
Максимальный размер кучи будет содержать числа меньше равна медиана.
Мин кучи будет содержать числа больше или равно среднему.
Ограничение Балансировки: если общее количество элементов, даже тогда обе кучи должны иметь равные элементы.
если количество элементов нечетное, то максимум кучи есть еще один элемент, чем min кучи.
Медианный элемент: если обе секции имеет одинаковое число элементов, то медианой будет половина суммы максимального элемента из первого раздела и min элемент из второго раздела.
В противном случае медиана будет максимальный элемент из первого раздела. в <предварительно> Алгоритм- 1 - Взять две кучи(1 мин 1 Макс кучи и кучи) Максимальный размер кучи будет содержать первую половину количество элементов Мин кучи будет содержать второй половине числа элементов
2 - сравниваем новый номер из потока с верхней части Макс кучи, если она меньше или равна добавить, что число в макс кучи. В противном случае добавить число в Мин кучи.
3 - Если мин кучи имеет больше элементов, чем максимальный размер кучи затем удалить верхний элемент из кучи мин и добавить в Максим Кучко. если максимальный размер кучи имеет более чем один элемент, чем в Мин кучи затем удалить верхний элемент максимальный размер кучи и добавьте в Мин кучи.
4 - Если обе кучи имеет одинаковое количество элементов, то медиана будет половина суммы максимальный элемент из кучи Макс и мин элемент из мин кучи. В противном случае медиана будет максимальный элемент из первого раздела. </пред>
Это может быть отметить, что существует особый случай, который имеет простое точное решение: когда все значения в поток целых чисел в (относительно) небольших определенный диапазон. Например, предположим, что у них все должно лежать между 0 и 1023. В этом случае просто определить массив из 1024 элементов и количество, и снимите все эти значения. Для каждого значения в потоке приращение соответствующей bin и граф. После завершения потока находим bin, который содержит количество/2 высшая ценность - легко достигается путем добавления последовательные ячейки, начиная с 0. Используя тот же метод, значение произвольного порядка и ранга Могут быть найдены. (Есть небольшие осложнения в случае обнаружения ОГРН насыщенность и "обновление" в размер бункеры большего типа во время выполнения, потребуется.)
Этот особый случай может показаться искусственной, но на практике это очень часто. Он также может быть применен в качестве приближения для действительных чисел, если они находятся в пределах диапазона и усилитель;"достаточно хорошо" в точности известно. Это позволит проводить практически любые измерения на группы и"и quot реальном мире&; объекты. Например, высоты или веса группы людей. Не достаточно большой набор? Он будет работать так же хорошо для длины или веса всех (отдельных) бактерий на планете - если кто-то может предоставить данные!
Похоже, я неправильно понял оригинал - который выглядит, как он хочет, скользящий средний, а не просто средний, а очень длинный поток. Этот подход по-прежнему работает для этого. Нагрузка в N-й поток значений для начального окна, тогда для N+1-го значения потока приращение соответствующей Бен постоянно уменьшающимся ОГРН, соответствующий значению 0-й поток. Надо в таком случае, чтобы сохранить последние N значений, чтобы уменьшить, что может быть сделано эффективно, циклично обращаясь массив размера N. После установки Медианы можно только изменить на -2,-1,0,1,2 на каждом шагу раздвижные окна, это'т необходимо просуммировать все ячейки до медиану на каждом шагу, просто поменяйте на "средний указатель" в зависимости от того, какая сторона(ы) ячейки были изменены. Например, если новое значение и удаляется опуститься ниже нынешнего среднего уровня, то она не'т изменение (смещение = 0). Метод ломается, когда n становится слишком большим, чтобы удобно держать в памяти.
Для тех, кому нужен бег медиана в Java...PriorityQueue-твой друг. O(зарегистрируйте N) вставка O(1) по текущей медианы, и о(n) удалить. Если вы знаете распределение ваших данных, вы можете сделать намного лучше, чем этот.
Если у вас есть возможность ссылаться на значения как на функцию моментов времени, вы можете сделать выборку значений с заменой, применяя bootstrapping для генерации бутстраппированного медианного значения в пределах доверительных интервалов. Это может позволить вам вычислить приближенную медиану с большей эффективностью, чем постоянная сортировка входящих значений в структуре данных.
Вот один, который может быть использован для точного выхода не важна (для целей отображения и т. д.) Вам нужно totalcount и lastmedian, плюс значениев.
Производит довольно точные результаты для таких вещей, как page_display_time.
Правила: входной поток должен быть гладким на порядок время отображения страницы, большого количества (>30 и т. д.), и есть ненулевой средний.
Пример: время загрузки страницы, 800 деталей, 10 мс...3000ms, средняя 90МС, реальный средний показатель:11мс
После 30 входов, ошибка медианы как правило, <=20% (9ms..12 мс), и становится все меньше и меньше. После 800 входов, погрешность составляет +-2%.
Еще один мыслитель с подобным решением-это здесь: https://stackoverflow.com/questions/11482529/median-filter-super-efficient-implementation/15150968#15150968
Вот это реализация Java
Если вам нужно только сглаженное среднее значение, быстрый и простой способ - умножить последнее значение на x и среднее значение на (1-x), затем сложить их. Это и будет новым средним значением.
Редактировать: Не то, что просил пользователь, и не так статистически достоверно, но достаточно хорошо для многих случаев.
Я'оставлю это здесь (несмотря на даунвоты) для поиска!