Рассчитать скользящее/скользящее среднее значение в С++

Я знаю, что это достигается с помощью boost:

Используя boost:: аккумуляторы, как я могу reset увеличить размер окна, сохранить дополнительную историю?

Но я действительно хотел бы избежать использования boost. Я googled и не нашел подходящих или читаемых примеров.

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

Каков самый простой способ достичь этого?


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

Ответ 1

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

Ответ 2

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

http://en.wikipedia.org/wiki/Moving_average#Exponential_moving_average

Проще говоря, вы создаете переменную аккумулятора, и по мере того, как ваш код смотрит на каждый образец, код обновляет аккумулятор с новым значением. Вы выбираете константу "альфа", которая находится между 0 и 1, и вычисляйте это:

accumulator = (alpha * new_value) + (1.0 - alpha) * accumulator

Вам просто нужно найти значение "alpha", где эффект данного образца длится всего около 1000 образцов.

Хм, я на самом деле не уверен, что это подходит для вас, теперь, когда я положил его сюда. Проблема в том, что 1000 - довольно длинное окно для экспоненциального скользящего среднего; Я не уверен, что есть альфа, которая будет распространять среднее значение по последним тысячам чисел, без недочета в вычислении с плавающей точкой. Но если вы хотите получить меньший средний показатель, например 30 номеров или около того, это очень простой и быстрый способ сделать это.

Ответ 3

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

template <unsigned N>
double approxRollingAverage (double avg, double input) {
    avg -= avg/N;
    avg += input/N;
    return avg;
}

Таким образом, вам не нужно поддерживать 1000 ведер. Однако это приближение, поэтому значение не будет точно соответствовать истинному скользящему среднему.

Изменить: только что заметил пост @steveha. Это эквивалентно экспоненциальному скользящему среднему, причем альфа составляет 1/N (в этом случае я принимал N в 1000 для моделирования 1000 ковшей).

Ответ 4

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

Обратите внимание, что ниже обновляет total_ как элементы как добавленные/замененные, избегая дорогостоящего обхода O (N) для вычисления суммы, необходимой для среднего - по требованию.

template <typename T, typename Total, int N>
class Moving_Average
{
  public:
    Moving_Average()
      : num_samples_(0), total_(0)
    { }

    void operator()(T sample)
    {
        if (num_samples_ < N)
        {
            samples_[num_samples_++] = sample;
            total_ += sample;
        }
        else
        {
            T& oldest = samples_[num_samples_++ % N];
            total_ += sample - oldest;
            oldest = sample;
        }
    }

    operator double() const { return total_ / std::min(num_samples_, N); }

  private:
    T samples_[N];
    int num_samples_;
    Total total_;
};

Total выполняется другой параметр из T для поддержки, например. используя long long при сумме 1000 long s, a int для char s или double к сумме float s.

Это немного испорчено тем, что num_samples_ может пройти мимо INT_MAX - если вам интересно, вы можете использовать unsigned long long или использовать дополнительный элемент данных bool для записи, когда контейнер сначала заполняется во время циклического num_samples_ вокруг массив (лучше всего переименовать что-то безобидное, например "pos" ).

Ответ 5

Простой класс для расчета среднего скользящего среднего, а также стандартного отклонения прокатки:

#define _stdev(cnt, sum, ssq) sqrt((((double)(cnt))*ssq-pow((double)(sum),2)) / ((double)(cnt)*((double)(cnt)-1)))

class moving_average {
private:
    boost::circular_buffer<int> *q;
    double sum;
    double ssq;
public:
    moving_average(int n)  {
        sum=0;
        ssq=0;
        q = new boost::circular_buffer<int>(n);
    }
    ~moving_average() {
        delete q;
    }
    void push(double v) {
        if (q->size() == q->capacity()) {
            double t=q->front();
            sum-=t;
            ssq-=t*t;
            q->pop_front();
        }
        q->push_back(v);
        sum+=v;
        ssq+=v*v;
    }
    double size() {
        return q->size();
    }
    double mean() {
        return sum/size();
    }
    double stdev() {
        return _stdev(size(), sum, ssq);
    }

};

Ответ 6

Вы можете реализовать кольцевой буфер . Создайте массив из 1000 элементов и несколько полей для хранения начальных и конечных индексов и общего размера. Затем просто сохраните последние 1000 элементов в кольцевом буфере и пересчитайте среднее значение по мере необходимости.

Ответ 7

простая скользящая средняя для 10 элементов, используя список:

#include <list>

std::list<float> listDeltaMA;

float getDeltaMovingAverage(float delta)
{
    listDeltaMA.push_back(delta);
    if (listDeltaMA.size() > 10) listDeltaMA.pop_front();
    float sum = 0;
    for (std::list<float>::iterator p = listDeltaMA.begin(); p != listDeltaMA.end(); ++p)
        sum += (float)*p;
    return sum / listDeltaMA.size();
}

Ответ 8

Одним из способов может быть циклическое сохранение значений в массиве буфера. и вычислить средний этот путь.

int j = (int) (counter % size);
buffer[j] = mostrecentvalue;
avg = (avg * size - buffer[j - 1 == -1 ? size - 1 : j - 1] + buffer[j]) / size;

counter++;

// buffer[j - 1 == -1 ? size - 1 : j - 1] is the oldest value stored

Все это работает в цикле, где последнее значение является динамическим.