Рассчитать скользящее/скользящее среднее значение в С++
Я знаю, что это достигается с помощью 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
Все это работает в цикле, где последнее значение является динамическим.