C++如何实现带滑动窗口的统计指标?(平均值/最大值等)

3次阅读

滑动窗口平均值用deque维护实时和,sum动态增减,未满窗时按实际大小求均值;最大值用单调递减索引队列,需校验队首索引是否在窗内。

C++如何实现带滑动窗口的统计指标?(平均值/最大值等)

滑动窗口平均值:用 std::deque 维护实时求和

直接用循环每次重算 O(n) 太慢,尤其窗口大、数据流快时。核心是复用上一窗口的和:sum -= old_value; sum += new_value;。但得记住窗口里每个值,std::dequestd::vector 更合适——头删尾插都是 O(1)。

常见错误是没处理窗口未填满的情况(比如刚启动时只有 2 个数,但窗口大小是 5),此时不该返回 NaN 或崩溃,而应按实际数量算平均值。

  • 初始化一个空 std::deque<double></double>double sum = 0.0
  • 每来一个新值 xsum += x;deque.push_back(x);
  • deque.size() > window_size,则 sum -= deque.front(); deque.pop_front();
  • 平均值就是 sum / deque.size()(不是固定除 window_size

滑动窗口最大值:单调队列必须存索引

std::deque<int></int> 存的是数组下标,不是值本身——否则无法判断某个最大值是否已滑出窗口。队列保持“从头到尾值递减”,同时确保队首索引始终在当前窗口内。

容易踩的坑是只比较值、忽略索引有效性。比如窗口 [i, i+k-1],队首索引 idx,就得 <code>pop_front;插入新值前,从队尾开始弹出所有 ≤ 当前值的元素,保证单调性。

立即学习C++免费学习笔记(深入)”;

  • 队列里存的是原始数据的下标,对应值通过 arr[idx]
  • 每次滑动后,先清理过期索引:while (!dq.empty() && dq.front()
  • 再清理小值:while (!dq.empty() && arr[dq.back()]
  • 队首就是当前窗口最大值的下标

多指标复用同一窗口:别为每个指标单独存一份数据

平均值要和,最大值要单调队列,最小值是另一个单调队列,标准差还得维护平方和……全存下来内存翻倍、逻辑耦合。更合理的方式是共享输入流 + 独立状态,用结构体封装:

struct SlidingWindowStats {     std::deque<double> values;     double sum = 0.0;     std::deque<size_t> max_deque; // 存索引     std::deque<size_t> min_deque;     double sum_sq = 0.0; };

关键点:所有操作基于同一份 values 的增删,但各指标更新时机一致(比如都在 add() 里触发),避免因调用顺序不同导致状态错位。别把 max_dequemin_deque 更新写在不同函数里。

性能陷阱:频繁拷贝、浮点精度、整数溢出

窗口很大时,std::deque 内部是分段连续内存,迭代器失效规则比 std::vector 复杂,但只要不用随机访问中间元素,影响不大。真正拖慢的是无意识的拷贝——比如把整个 deque 传给函数时用了值传递。

另外,累加和用 double 能扛住大部分场景,但如果数据是 int64_t 且窗口超 1e6,sum 可能丢失低比特精度;而用 int64_t 累加又容易溢出。折中方案是:整数输入 → 用 long double 累加和与平方和,最后转成所需类型输出。

  • 所有接口参数优先用 const T& 或指针,避免 std::deque<t></t> 值传
  • 不要用 std::accumulate 在运行时反复遍历窗口,它破坏 O(1) 更新前提
  • 如果业务允许误差,可对超大窗口启用采样或指数衰减近似,但这是另一回事了

窗口越长,状态同步的边界条件越多;最常漏掉的是“窗口清空后重新填充”那段过渡期逻辑——这时候 sum 是 0,但 deque 已空,size() 是 0,除零风险就藏在这儿。

text=ZqhQzanResources