C++ 以C+计算移动平均线+;

C++ 以C+计算移动平均线+;,c++,algorithm,time,moving-average,C++,Algorithm,Time,Moving Average,我试图计算信号的移动平均值。信号值(双精度)在随机时间更新。 我正在寻找一种有效的方法来实时计算它在一个时间窗口内的时间加权平均值。我可以自己做,但比我想象的更具挑战性 我在互联网上找到的大部分资源都是计算周期信号的移动平均值,但我的资源是随机更新的 有人知道这方面的好资源吗 谢谢注意:显然这不是解决问题的方法。将其留在这里,以供参考这种方法的错误之处。检查评论 更新-根据奥利的评论。。。但我不确定他所说的不稳定 根据值使用“到达时间”的排序图。值到达后,将到达时间与其值一起添加到已排序的地图中

我试图计算信号的移动平均值。信号值(双精度)在随机时间更新。 我正在寻找一种有效的方法来实时计算它在一个时间窗口内的时间加权平均值。我可以自己做,但比我想象的更具挑战性

我在互联网上找到的大部分资源都是计算周期信号的移动平均值,但我的资源是随机更新的

有人知道这方面的好资源吗


谢谢

注意:显然这不是解决问题的方法。将其留在这里,以供参考这种方法的错误之处。检查评论

更新-根据奥利的评论。。。但我不确定他所说的不稳定

根据值使用“到达时间”的排序图。值到达后,将到达时间与其值一起添加到已排序的地图中,并更新移动平均值

警告:这是伪代码:

SortedMapType< int, double > timeValueMap;

void onArrival(double value)
{
    timeValueMap.insert( (int)time(NULL), value);
}

//for example this runs every 10 seconds and the moving window is 120 seconds long
void recalcRunningAverage()
{
    // you know that the oldest thing in the list is 
    // going to be 129.9999 seconds old
    int expireTime = (int)time(NULL) - 120;
    int removeFromTotal = 0;
    MapIterType i;
    for( i = timeValueMap.begin();
    (i->first < expireTime || i != end) ; ++i )
    {
    }

    // NOW REMOVE PAIRS TO LEFT OF i

    // Below needs to apply your time-weighting to the remaining values
    runningTotal = calculateRunningTotal(timeValueMap); 
    average = runningTotal/timeValueMap.size();
}
SortedMapTypetimeValueMap;
到达时无效(双值)
{
timeValueMap.insert((int)time(NULL),value);
}
//例如,每10秒运行一次,移动窗口的长度为120秒
void recalcRunningAverage()
{
//你知道列表中最古老的是
//将是129.9999秒
int expireTime=(int)time(NULL)-120;
int removeFromTotal=0;
地图类型i;
for(i=timeValueMap.begin();
(i->first
那里。。。没有完全充实,但你明白了

注意事项: 正如我所说,上面的代码是伪代码。你需要选择一个合适的地图。 在迭代过程中不要删除这些对,因为这样会使迭代器无效,并且必须重新开始

另请参见下面的奥利评论。

如果近似值可以,并且两次采样之间的间隔时间最短,您可以尝试超级采样。具有一个数组,该数组表示小于最小值的等距时间间隔,并在每个时间段存储收到的最新样本。间隔越短,平均值越接近真实值。周期不得超过最小值的一半,否则可能会丢失样本。

诀窍如下:通过
无效更新(int-time,float-value)
在随机时间获得更新。但是,您还需要跟踪更新何时脱离时间窗口,因此您设置了一个“报警”,该报警在
time+N
时调用,从而在计算中不再考虑以前的更新

如果这是实时发生的,您可以请求操作系统调用方法
void drop\u off\u oldest\u update(int time)
time+N调用

如果这是一个模拟,您无法从操作系统获得帮助,您需要手动执行。在模拟中,您将使用作为参数提供的时间(与实时无关)调用方法。然而,一个合理的假设是,调用被保证是这样的,即时间参数在增加。在这种情况下,您需要维护报警时间值的排序列表,并且对于每次
更新
读取
调用,您都需要检查时间参数是否大于报警列表的开头。当您执行与报警相关的处理(删除最旧的更新)时,移除头部并再次检查,直到处理给定时间之前的所有报警。然后执行更新调用

到目前为止,我假设您将为实际计算做什么是显而易见的,但我将详细说明以防万一。我假设您有一个方法
float read(int-time)
,用于读取值。我们的目标是让这个电话尽可能有效。因此,您不会每次调用
read
方法时都计算移动平均值。而是预计算上次更新或上次报警时的值,并通过两个浮点操作“调整”该值,以说明自上次更新以来的时间推移。(也就是说,除了处理一系列堆积的警报之外,操作的数量是恒定的)

希望这是清楚的——这应该是一个非常简单的算法,并且非常有效

进一步优化:剩下的问题之一是,如果在时间窗口内发生大量更新,那么很长一段时间内既没有读取也没有更新,然后出现读取或更新。在这种情况下,上述算法在增量更新每个正在下降的更新的值方面效率低下。这是没有必要的,因为我们只关心时间窗口之外的最后一次更新,所以如果有一种方法可以有效地删除所有旧的更新,这将有所帮助

为此,我们可以修改算法,对更新进行二进制搜索,以查找时间窗口之前的最新更新。如果需要“删除”的更新相对较少,则可以增量更新每个删除的更新的值。但是,如果有许多更新需要删除,那么可以在删除旧更新后重新计算值

增量计算附录:我应该在上面的句子“调整”中阐明增量计算的含义,通过两个浮点运算来解释自上次更新以来的时间推移。初始非增量计算:

sum = 0; 
updates_in_window = /* set of all updates within window */; 
prior_update' = /* most recent update prior to window with timestamp tweaked to window beginning */; 
relevant_updates = /* union of prior_update' and updates_in_window */,  
然后按时间增加的顺序迭代相关的\u更新

for each update EXCEPT last { 
    sum += update.value * time_to_next_update; 
},  
最后

移动平均=(总和+上次更新
sum -= prior_update'.value * time_to_next_update + first_update_in_last_window.value * time_from_first_update_to_new_window_beginning;
sum += previously_most_recent_update.value * corresponding_time_to_next_update. 
#include <map>
#include <iostream>

// Sample - the type of a single sample
// Date - the type of a time notation
// DateDiff - the type of difference of two Dates    
template <class Sample, class Date, class DateDiff = Date>
class TWMA {
private:
  typedef std::map<Date, Sample> qType;
  const DateDiff windowSize; // The time width of the sampling window
  qType samples; // A set of sample/date pairs
  Sample average; // The answer

public:

  // windowSize - The time width of the sampling window
  TWMA(const DateDiff& windowSize) : windowSize(windowSize), average(0) {}

  // Call this each time you receive a sample
  void
  Update(const Sample& sample, const Date& now) {
    // First throw away all old data
    Date then(now - windowSize);
    samples.erase(samples.begin(), samples.upper_bound(then));

    // Next add new data
    samples[now] = sample;

    // Compute average: note: this could move to Average(), depending upon
    // precise user requirements.
    Sample sum = Sample();
    for(typename qType::iterator it = samples.begin();
        it != samples.end();
        ++it) {
      DateDiff duration(it->first - then);
      sum += duration * it->second;
      then = it->first;
    }
    average = sum / windowSize;
  }

  // Call this when you need the answer.
  const Sample& Average() { return average; }

};

int main () {
  TWMA<double, int> samples(10);

  samples.Update(1, 1);
  std::cout << samples.Average() << "\n"; // 1
  samples.Update(1, 2);
  std::cout << samples.Average() << "\n"; // 1
  samples.Update(1, 3);
  std::cout << samples.Average() << "\n"; // 1
  samples.Update(10, 20);
  std::cout << samples.Average() << "\n"; // 10
  samples.Update(0, 25);
  std::cout << samples.Average() << "\n"; // 5
  samples.Update(0, 30);
  std::cout << samples.Average() << "\n"; // 0
}