最小平方法是資料擬合最基礎的演算法。本文提供兩個 MFC 就緒函式:linearReg_Y_a0_a1X 解算截距 a0 與斜率 a1,linearReg_Y_aX 解算通過原點的斜率 a,皆使用 CArray<double,double> 作為輸入容器。
對資料點 (xi, yi),最小化誤差平方和 Σ(yi - (a0 + a1 xi))^2,對 a0, a1 求偏導數為零,得正規方程:
a1 = (Σxi yi - n x̄ ȳ) / (Σxi^2 - n x̄^2) a0 = ȳ - a1 x̄
通過原點模型 y = a x 僅需最小化 Σ(yi - a xi)^2,解為:
a = Σxi yi / Σxi^2
void linearReg_Y_a0_a1X(double& a0, double& a1,
const CArray<double,double>& X,
const CArray<double,double>& Y)
{
double sumXiYi = 0, sumXi = 0, sumYi = 0, sumXi2 = 0;
INT_PTR n = X.GetSize();
for (INT_PTR i = 0; i < n; ++i) {
sumXiYi += X[i] * Y[i];
sumXi += X[i];
sumYi += Y[i];
sumXi2 += X[i] * X[i];
}
double xAvg = sumXi / n;
double yAvg = sumYi / n;
a1 = (sumXiYi - n * xAvg * yAvg) / (sumXi2 - n * xAvg * xAvg);
a0 = yAvg - a1 * xAvg;
}
void linearReg_Y_aX(double& a,
const CArray<double,double>& X,
const CArray<double,double>& Y)
{
double sumXiYi = 0, sumXi2 = 0;
INT_PTR n = X.GetSize();
for (INT_PTR i = 0; i < n; ++i) {
sumXiYi += X[i] * Y[i];
sumXi2 += X[i] * X[i];
}
a = sumXiYi / sumXi2;
}
CArray<double,double> X, Y;
X.Add(1.0); Y.Add(2.1);
X.Add(2.0); Y.Add(3.9);
X.Add(3.0); Y.Add(6.2);
X.Add(4.0); Y.Add(7.8);
double a0, a1;
linearReg_Y_a0_a1X(a0, a1, X, Y);
// a0 ≈ 0.15, a1 ≈ 1.95
double a;
linearReg_Y_aX(a, X, Y);
// a ≈ 2.05
R^2 計算,評估擬合優度。std::vector<double> 取代 CArray 以跨平台。