

新闻资讯
技术学院最稳妥方式是用std::accumulate两遍遍历:先求均值,再算平方偏差均值;样本方差除以n-1,总体方差除以n;需预检查NaN/inf、空容器及分母为零。
std::accumulate 手动计算方差和标准差最稳妥标准库没有内置的方差或标准差函数,std::valarray 虽有 sum() 但不支持直接求均值平方差;依赖第三方库(如 Boost.Math)会增加构建复杂度。实际项目中,用 std::accumulate 两遍遍历是最可控的方式:第一遍算均值,第二遍算平方偏差均值。
mean,再遍历计算 (x - mean) * (x - mean),不能合并成单次 accumulate —— 否则会因浮点精度丢失导致方差为负(尤其数据量大、数值集中时)n-1 作分母(贝塞尔校正),总体方差用 n;C++ 里需显式判断并传入 ddof = 0 或 1
std::vector 或类似可迭代浮点序列,避免整数除法截断#include#include #include double variance(const std::vector
& data, int ddof = 0) { if (data.empty()) return 0.0; double mean = std::accumulate(data.begin(), data.end(), 0.0) / data.size(); double sum_sq_diff = std::accumulate(data.begin(), data.end(), 0.0, [mean](double acc, double x) { return acc + (x - mean) * (x - mean); }); return sum_sq_diff / (data.size() - ddof); } double stddev(const std::vector
& data, int ddof = 0) { return std::sqrt(variance(data, ddof)); }
std::valarray 快速原型但慎用于生产std::valarray 支持向量化运算,写起来简洁,但存在隐式拷贝开销、不支持迭代器、且部分老编译器(如 MSVC 2015 前)实现不全。仅建议在小规模数据、快速验证公式时使用。
valarray 的 sum() 返回 double,但中间运算可能触发 promotion 规则,若原始类型是 float,结果仍可能是 float,导致精度不足valarray 做“减去标量均值”操作而不生成临时对象,内存效率不如手写循环- mean 都构造新 valarray,不适用于大数据集#include#include double variance_valarray(const std::valarray
& v) { if (v.size() == 0) return 0.0; double mean = v.sum() / v.size(); std::valarray diff = v - mean; return (diff * diff).sum() / v.size(); }
nan 或负方差?检查输入和溢出路径调用后得到 nan 或 variance 返回负值,几乎一定是以下原因:
NaN 或 inf:用 std::isnan(x) 和 std::isinf(x) 预过滤,否则 (x - mean) 可能传播 nan
(x - mean) * (x - mean) 溢出 double(如 x ≈ 1e155),此时应改用 Welford 在线算法避免大数相减ddof = 1 → 分母为 0 → 返回 inf;需在函数开头加 if (data.size()
当数据来自流式输入(如传感器、文件逐行读取)、不能存全量或内存受限时,Welford 方法可在一次遍历中累积计算方差,且数值稳定性优于两遍法。
Mk(当前均值)和 Sk(平方和修正项),递推更新,无须存储全部数据S / (n - ddof),其中 S 是递推得到的 Sk
M = 0.0, S = 0.0, n = 0,每来一个 x 更新一次,n 从 1 开始计数
struct Welford {
double M = 0.0, S = 0.0;
size_t n = 0;
void add(double x) {
n++;
double delta = x - M;
M += delta / n;
S += delta * (x - M);
}
double variance(int ddof = 0) const {
return n <= static_castzuojiankuohaophpcnsize_tyoujiankuohaophpcn(ddof) ? 0.0 : S / (n - ddof);
}
double stddev(int ddof = 0) const {
return std::sqrt(variance(ddof));
}};
Welford 算法的数值稳定性常被低估——它真正难处理的是极端情况:比如所有数都接近 1e308,此时 delta 计算仍可能失真
。这种时候,要么换更高精度类型(long double),要么做预平移(减去估计均值再算)。