最小二乘法的c语言实现

99ANYc3cd6
预计阅读时长 16 分钟
位置: 首页 C语言 正文

最小二乘法原理回顾

最小二乘法是一种数学优化技术,它通过最小化误差的平方和寻找数据的最佳函数匹配,对于线性回归问题,我们目标是找到一条直线 y = ax + b,使得所有数据点到这条直线的垂直距离的平方和最小。

给定一组数据点 (x₁, y₁), (x₂, y₂), ..., (xₙ, yₙ),我们需要求解斜率 a 和截距 b

最小二乘法的正规方程如下:

  1. 求解斜率 a: a = (n * Σ(xy) - Σx * Σy) / (n * Σ(x²) - (Σx)²)

  2. 求解截距 b: b = (Σy - a * Σx) / n

  • n 是数据点的数量。
  • Σx 是所有 x 值的和。
  • Σy 是所有 y 值的和。
  • Σ(xy) 是所有 x * y 乘积的和。
  • Σ(x²) 是所有 x 平方的和。

我们的C语言实现将围绕计算这些求和项,然后代入公式求解 ab


C语言实现步骤

我们将分步实现这个过程:

  1. 定义数据结构:使用结构体来存储计算出的斜率和截距。
  2. 核心计算函数:编写一个函数,接收数据点数组、数据点数量作为输入,返回计算出的 ab
  3. 处理边界情况:检查数据点是否足够(至少2个点)以及分母是否为零(所有x值相同,此时直线为垂直,无解)。
  4. 主函数:创建一个示例数据集,调用核心函数,并打印结果。

完整C语言代码

下面是一个完整的、可运行的C语言实现。

#include <stdio.h>
#include <math.h> // 用于 fabs 函数
// 定义一个结构体来存储线性回归的结果
typedef struct {
    double slope;      // 斜率 a
    double intercept;  // 截距 b
    int success;       // 标记计算是否成功
} LinearRegressionResult;
/**
 * @brief 使用最小二乘法计算线性回归的斜率和截距
 * 
 * @param points 数据点数组,每个点是一个包含x和y的结构体
 * @param numPoints 数据点的数量
 * @return LinearRegressionResult 包含斜率、截距和成功状态的结构体
 */
LinearRegressionResult leastSquares(double points[][2], int numPoints) {
    LinearRegressionResult result;
    result.success = 0; // 默认设为失败
    // 1. 检查数据点数量是否足够
    if (numPoints < 2) {
        printf("错误: 至少需要2个数据点才能进行线性回归,\n");
        return result;
    }
    // 2. 初始化求和变量
    double sumX = 0.0;
    double sumY = 0.0;
    double sumXY = 0.0;
    double sumXSquare = 0.0;
    // 3. 遍历数据点,计算各项求和
    for (int i = 0; i < numPoints; i++) {
        double x = points[i][0];
        double y = points[i][1];
        sumX += x;
        sumY += y;
        sumXY += x * y;
        sumXSquare += x * x;
    }
    // 4. 计算斜率 a 和截距 b
    // 分母: n * Σ(x²) - (Σx)²
    double denominator = numPoints * sumXSquare - sumX * sumX;
    // 5. 检查分母是否为零(所有x值都相同)
    if (fabs(denominator) < 1e-9) { // 使用一个很小的数来避免浮点数精度问题
        printf("错误: 所有x值都相同,无法计算唯一的斜率(分母为零),\n");
        return result;
    }
    // 计算斜率 a
    double numerator_a = numPoints * sumXY - sumX * sumY;
    result.slope = numerator_a / denominator;
    // 计算截距 b
    result.intercept = (sumY - result.slope * sumX) / numPoints;
    // 标记计算成功
    result.success = 1;
    return result;
}
int main() {
    // 示例数据集
    // 假设我们有以下 (x, y) 点:
    // (1, 1), (2, 2), (3, 3), (4, 4.5), (5, 5)
    // 这条线应该大致接近 y = 1.1x - 0.2
    double dataPoints[][2] = {
        {1.0, 1.0},
        {2.0, 2.0},
        {3.0, 3.0},
        {4.0, 4.5},
        {5.0, 5.0}
    };
    int numDataPoints = sizeof(dataPoints) / sizeof(dataPoints[0]);
    // 调用最小二乘法函数
    LinearRegressionResult lr = leastSquares(dataPoints, numDataPoints);
    // 打印结果
    if (lr.success) {
        printf("最小二乘法计算成功!\n");
        printf("拟合的直线方程为: y = %.4fx + %.4f\n", lr.slope, lr.intercept);
        // 验证一下结果
        double x_test = 6.0;
        double y_predicted = lr.slope * x_test + lr.intercept;
        printf("当 x = %.1f 时,预测的 y 值为: %.4f\n", x_test, y_predicted);
    } else {
        printf("最小二乘法计算失败,请检查输入数据,\n");
    }
    return 0;
}

代码讲解

  1. LinearRegressionResult 结构体:

    • 我们定义了这个结构体来让函数返回多个值(斜率、截距和状态),这比使用指针参数传递更清晰、更安全。
    • slope: 存储计算得到的 a
    • intercept: 存储计算得到的 b
    • success: 一个 int 类型的标志。1 表示成功,0 表示失败,这是一种简单的错误处理机制。
  2. leastSquares 函数:

    • 参数: double points[][2] 是一个二维数组,表示数据点。int numPoints 是点的数量。
    • 初始化: sumX, sumY, sumXY, sumXSquare 被初始化为 0,用于累加。
    • 求和循环: for 循环遍历所有数据点,根据公式计算四个核心求和项。
    • 分母检查: 这是至关重要的一步,如果所有 x 值都相同,分母 n * Σ(x²) - (Σx)² 会变为零,导致除零错误,我们使用 fabs(denominator) < 1e-9 来判断,因为浮点数计算可能存在微小的精度误差,直接判断 == 0 不可靠。
    • 计算 ab: 如果分母不为零,就按照正规方程公式计算斜率和截距。
    • 返回结果: 将计算结果和成功状态填充到 LinearRegressionResult 结构体中并返回。
  3. main 函数:

    • 定义数据: 我们创建了一个名为 dataPoints 的二维数组来存储示例数据。
    • 计算点数: sizeof(dataPoints) / sizeof(dataPoints[0]) 是一个计算C数组元素个数的常用技巧。
    • 调用函数: 调用 leastSquares 函数并接收返回结果。
    • 打印结果: 检查 result.success,如果成功,则格式化打印出最终的直线方程 y = ax + b,我们还增加了一个简单的预测来展示结果的应用。

如何编译和运行

  1. 将上述代码保存为一个文件,linear_regression.c
  2. 打开终端或命令提示符。
  3. 使用GCC编译器进行编译,由于代码中包含了 math.h,我们需要链接数学库 (-lm)。
    gcc linear_regression.c -o linear_regression -lm
    • gcc: 编译器
    • linear_regression.c: 源文件
    • -o linear_regression: 指定输出的可执行文件名
    • -lm: 链接数学库
  4. 运行生成的可执行文件:
    ./linear_regression

预期输出:

最小二乘法计算成功!
拟合的直线方程为: y = 1.1000x + -0.2000
当 x = 6.0 时,预测的 y 值为: 6.4000

这个输出与我们预期的 y ≈ 1.1x - 0.2 非常吻合,证明代码是正确的。

-- 展开阅读全文 --
头像
织梦系统参数为何无法打开?
« 上一篇 今天
计算机二级C语言考试书该怎么选?
下一篇 » 45分钟前

相关文章

取消
微信二维码
支付宝二维码

目录[+]