用C++计算滚动/移动平均值

goj*_*oji 39 c++ boost moving-average

我知道这是可以通过提升实现的:

使用boost :: accumulators,如何重置滚动窗口大小,是否保留了额外的历史记录?

但我真的想避免使用提升.我用谷歌搜索,没有找到任何合适或可读的例子.

基本上我想使用最新的1000个数字作为数据样本来跟踪正在进行的浮点数流的移动平均值.

实现这一目标的最简单方法是什么?


我尝试使用圆形阵列,指数移动平均线和更简单的移动平均线,发现圆形阵列的结果最适合我的需要.

ste*_*eha 79

如果您的需求很简单,您可以尝试使用指数移动平均线.

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

简而言之,您创建了一个累加器变量,并且当您的代码查看每个示例时,代码会使用新值更新累加器.你选择一个介于0和1之间的常量"alpha",并计算:

accumulator = (alpha * new_value) + (1.0 - alpha) * accumulator
Run Code Online (Sandbox Code Playgroud)

您只需要找到"alpha"值,其中给定样本的效果仅持续约1000个样本.

嗯,我现在还不确定它是否适合你,现在我把它放在这里.问题是,对于指数移动平均线而言,1000是一个相当长的窗口; 我不确定是否有一个alpha会在过去的1000个数字上传播平均值,而在浮点计算中没有下溢.但是如果你想要一个较小的平均值,比如30个数左右,这是一个非常简单快捷的方法.

  • 不,它只需要两个乘法和每个新数字的加法.如果你没有预先计算`(1.0 - alpha)`,加上一个减法.越接近`(1.0 - alpha)`到1.0,前一个数字的影响越久,每个新数字的影响越小.alpha越接近1.0,移动平均值响应新值的速度越快. (13认同)
  • @ user315052表示,如果你将alpha设置为"1.0/1000",它将接近平均1000个样本.它不能与1000个样本的实际平均值相同,但我认为它的效果与许多目的相似.我建议你尝试一下:使用指数移动平均线,alpha设置为"1.0/1000",看看你是否喜欢你得到的平均值. (2认同)

Kar*_*han 19

你只需要一个1000个元素的圆形数组,在这里你可以将元素添加到前一个元素并存储它......它变成了一个增加的总和,你可以随时得到任意两对元素之和,并除以数字它们之间的元素,以产生平均值.

  • 我希望避免将所有数字存储在数组中并保持"长期".似乎这可能是唯一合适的方式. (3认同)

Ton*_*roy 14

基本上我想使用最新的1000个数字作为数据样本来跟踪正在进行的浮点数流的移动平均值.

请注意,下面更新total_ as元素作为添加/替换,避免代价高昂的O(N)遍历来计算平均值所需的总和 - 按需.

template <typename T, typename Total, size_t N>
class Moving_Average
{
  public:
    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];
    size_t num_samples_{0};
    Total total_{0};
};
Run Code Online (Sandbox Code Playgroud)

total_由不同的参数从Total支持例如使用T共计1000个时long longS,一个long用于intS,或一个char到总double秒.

这有点有缺陷,float可能会过去num_samples_- 如果你关心你可以使用a num_samples_,或者使用额外的bool数据成员来记录容器第一次填充时在数组周围循环num_samples_(最好重命名为无关紧要的东西,如" pos") .


jxh*_*jxh 14

您可以通过在输入流上应用加权平均值来估算滚动平均值.

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

这样,您就不需要维护1000个桶.但是,这是一个近似值,因此它的值与真正的滚动平均值不完全匹配.

编辑:刚注意到@ steveha的帖子.这相当于指数移动平均线,alpha为1/N(在这种情况下,我将N取为1000来模拟1000个桶).

  • 但是,使用此方法可以快速累积误差,尤其是对于具有高度空间的数据集.考虑具有相对不频繁的高振幅尖峰的信号.当他们进入窗户时,他们会将平均值提高,但是当他们离开后门时,平均值只会减少平均值/ N而不是秒杀/ N. (4认同)