найти медиану в окне перемещения фиксированного размера вдоль длинной последовательности данных

учитывая последовательность данных (она может иметь дубликаты), перемещение фиксированного размера окна, переместить окно на каждой итерации от начала данных последовательность, такая, что (1) самый старый элемент данных удаляется из окна и новые данные элемент выталкивается в окно (2) Найдите медиану данных внутри окна при каждом перемещении.

следующие сообщения не полезны.

эффективно найти медианное значение случайного последовательность

объединение данных на основе движущегося окна времени в R

мои мысли:

используйте 2 кучи для удержания медианы. В боковой части окна отсортируйте данные в окне на первой итерации минимальная куча содержит большую часть, а максимальная куча занимает меньшую часть. Если в окне нечетное количество данных, то максимальная куча возвращает медиану в противном случае среднее арифметическое верхних элементов две кучи-это медиана.

когда новые данные выталкиваются в окно, удаляются самые старые данные из одного из кучи и сравните новые данные с верхней частью кучи max и min так чтобы решить, какую кучу данных поставить. Затем найдите медиану просто как в первой итерации.

но, как найти элемент данных в кучу-это проблема. Кучи-это двоичный дерево не двоичное дерево поиска.

можно ли решить его с помощью O (n) или O( n * lg m), где m-размер окна и пространство: O (1) ?

любая помощь будет действительно оценили.

спасибо

5 ответов


O (n*lg m) легко:

просто поддерживайте свое окно как два std::sets, один для нижней половины, один для верхней половины. Вставка нового элемента стоит O (lg m), поиск и удаление старого элемента стоит столько же. Определение медианы с помощью метода, описанного в вашем вопросе, стоит O (1).

при скольжении окна над вашей последовательностью на каждой итерации вы удаляете элемент, выпадающий из окна (O (lg m)), вставляете новый элемент (O (lg m)) и вычисляете медиана (O (1)), в результате чего в общей сложности O(n lg m).

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


я реализовал почти точно алгоритм, который вы описываете здесь:http://ideone.com/8VVEa, и описал его здесь:скользящая медиана в реализации C-Turlach

способ обойти проблему "найти самый старый" - сохранить значения в круговом буфере, поэтому у вас всегда есть указатель на самый старый. Что вы храните в куче буфера индексов. Таким образом, требование к пространству составляет 2M, а каждое обновление-O(lg M).


тот же ответ, что и hc_, но вместо использования запаса BST используйте версию, где каждый узел имеет количество элементов в этом под-дереве. Таким образом, медиана поиска равна O(log (m)).


Я дал этот ответ на вопрос "скользящая медиана в C"

Я не мог найти современную реализацию структуры данных c++ со статистикой порядка, поэтому в конечном итоге реализовал обе идеи в top coders link (Редакция Матча: прокрутите вниз до FloatingMedian).

два мультимножества

первая идея разбивает данные на две структуры данных (кучи, мультисети и т. д.) С O (ln N) на вставку / удаление не позволяет изменять квантиль динамически без больших затрат. Т. е. у нас может быть скользящая медиана или скользящие 75%, но не оба одновременно.

дерево отрезков

вторая идея использует дерево сегментов, которое O (ln N) для вставки/удаления/запросов, но является более гибким. Лучше всего "N" - это размер вашего диапазона данных. Поэтому, если ваша скользящая медиана имеет окно в миллион элементов, но ваши данные варьируются от 1..65536, после этого только 16 деятельностей необходимы в движение Окна завальцовки 1 миллион!! (И вам нужно только 65536 * sizeof (counting_type) байты, например 65536*4).

деревья статистики порядка GNU

перед тем, как сдаться, я обнаружил, что stdlibc++ содержит деревья статистики порядка!!!

Они имеют две критические операции:

iter = tree.find_by_order(value)
order = tree.order_of_key(value)

посмотреть libstdc++ руководство policy_based_data_structures_test (поиск "split and join").

я обернул дерево для использования в удобном заголовке для компиляторов поддержка C++0x/C++11 стиль частичные typedefs:

#if !defined(GNU_ORDER_STATISTIC_SET_H)
#define GNU_ORDER_STATISTIC_SET_H
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>

// A red-black tree table storing ints and their order
// statistics. Note that since the tree uses
// tree_order_statistics_node_update as its update policy, then it
// includes its methods by_order and order_of_key.
template <typename T>
using t_order_statistic_set = __gnu_pbds::tree<
                                  T,
                                  __gnu_pbds::null_type,
                                  std::less<T>,
                                  __gnu_pbds::rb_tree_tag,
                                  // This policy updates nodes'  metadata for order statistics.
                                  __gnu_pbds::tree_order_statistics_node_update>;

#endif //GNU_ORDER_STATISTIC_SET_H

я прикрепляю дерево сегментов (см. Мой другой пост), которое позволяет очень эффективно запрашивать частотное распределение отсчетов.

это реализует следующую структуру данных:

|-------------------------------|
|---------------|---------------|
|-------|-------|-------|-------|
|---|---|---|---|---|---|---|---|
  0   1   2   3   4   5   6   7  

каждый сегмент сохраняет количество элементов подсчета в диапазоне, который он охватывает. Я использую 2n сегментов для диапазона значений от 1..Н. Они размещаются в одном развернутом векторе, а не в формате дерева, показанном образно выше.

Так если вы высчитываете завальцовку медианы набора чисел, которые варьируются от 1..65536, тогда вам нужно только 128kb для их хранения и может вставлять/удалять/запрашивать с помощью O(ln N), где N = размер диапазона, т. е. 2**16 операций.

Это большой выигрыш, если диапазон данных намного меньше, чем ваше скользящее окно.

#if !defined(SEGMENT_TREE_H)
#define SEGMENT_TREE_H
#include <cassert>
#include <array>
#include <algorithm>
#include <set>

#ifndef NDEBUG
#include <set>
#endif

template<typename COUNTS, unsigned BITS>
class t_segment_tree
{
    static const unsigned                       cnt_elements    = (1 << BITS);
    static const unsigned                       cnt_storage     = cnt_elements << 1;
    std::array<COUNTS, cnt_elements * 2 - 1>    counts;
    unsigned                                    count;

#ifndef NDEBUG
    std::multiset<unsigned>                     elements;
#endif
    public:

    //____________________________________________________________________________________

    //  constructor

    //____________________________________________________________________________________
    t_segment_tree(): count(0)
    {
        std::fill_n(counts.begin(), counts.size(),  0);
    }
    //~t_segment_tree();

    //____________________________________________________________________________________

    //  size

    //____________________________________________________________________________________
    unsigned size() const  { return count; }

    //____________________________________________________________________________________

    //  constructor

    //____________________________________________________________________________________
    void insert(unsigned x)
    {
#ifndef NDEBUG
        elements.insert(x);
        assert("...............This element is too large for the number of BITs!!..............." && cnt_elements > x);
#endif
        unsigned ii = x + cnt_elements;
        while (ii)
        {
            ++counts[ii - 1];
            ii >>= 1;
        }
        ++count;
    }

    //____________________________________________________________________________________

    //  erase 

    //      assumes erase is in the set
    //____________________________________________________________________________________
    void erase(unsigned x)
    {
#ifndef NDEBUG
        // if the assertion failed here, it means that x was never "insert"-ed in the first place
        assert("...............This element was not 'insert'-ed before it is being 'erase'-ed!!..............." && elements.count(x));
        elements.erase(elements.find(x));
#endif
        unsigned ii = x + cnt_elements;
        while (ii)
        {
            --counts[ii - 1];
            ii >>= 1;
        }
        --count;
    }

    // 
    //____________________________________________________________________________________

    //  kth element

    //____________________________________________________________________________________
    unsigned operator[](unsigned k)
    {
        assert("...............The kth element: k needs to be smaller than the number of elements!!..............." && k < size());
        unsigned ii = 1;
        while (ii < cnt_storage)
        {
            if (counts[ii - 1] <= k)
               k -= counts[ii++ - 1];
            ii <<= 1;
        }
        return (ii >> 1) - cnt_elements;
    }

};
#endif