21xrx.com
2024-11-22 07:58:06 Friday
登录
文章检索 我的文章 写文章
C++三次样条插值实现
2023-06-30 08:38:25 深夜i     --     --
C++ 三次样条 插值 实现

C++三次样条插值是一种常用的数据插值方法,主要用于在给定的节点上通过插值函数,得到数据的平滑曲线,从而减小数据的波动性,适用于各种数据场景下的插值。

具体实现过程如下:

1. 根据插值节点计算出节点间距离和节点之间的相邻斜率

2. 构建三对角矩阵,并解出矩阵方程组

3. 根据解得的系数,计算出每个插值节点上的插值函数和拟合曲线

下面是一个简单的C++实现示例:


#include <iostream>

#include <vector>

#include <cmath>

using namespace std;

//定义节点结构体,包括x值和y值

struct Point y;

;

//计算相邻两个节点间的斜率

vector<double> calcSlope(vector<Point>& points) {

  int n = points.size();

  vector<double> slope(n + 1, 0);

  for (int i = 1; i < n; ++i) {

    slope[i] = (points[i].y - points[i - 1].y) / (points[i].x - points[i - 1].x);

  }

  slope[n] = slope[n - 1];

  return slope;

}

//构建三对角矩阵

void buildMatrix(vector<Point>& points, vector<double>& slope, vector<double>& A, vector<double>& B, vector<double>& C, vector<double>& F) {

  int n = points.size() - 1;

  double h1, h2, a, b, c, f;

  for (int i = 1; i < n; ++i) {

    h1 = points[i].x - points[i - 1].x;

    h2 = points[i + 1].x - points[i].x;

    a = h1 / (h1 + h2);

    b = 2;

    c = h2 / (h1 + h2);

    f = 6 * (slope[i + 1] - slope[i]) / (points[i + 1].x - points[i - 1].x);

    A.push_back(a);

    B.push_back(b);

    C.push_back(c);

    F.push_back(f);

  }

}

//解三对角方程组

void tridiagonalSolve(vector<double>& A, vector<double>& B, vector<double>& C, vector<double>& F, vector<double>& result) {

  int n = A.size();

  vector<double> alpha(n), beta(n);

  alpha[1] = B[0];

  for (int i = 1; i < n; ++i) {

    beta[i] = C[i - 1] / alpha[i];

    alpha[i + 1] = B[i] - A[i] * beta[i];

  }

  vector<double> gamma(n);

  gamma[1] = F[0] / alpha[1];

  for (int i = 1; i < n; ++i) {

    gamma[i + 1] = (F[i] - A[i] * gamma[i]) / alpha[i + 1];

  }

  result[n] = gamma[n];

  for (int i = n - 1; i >= 1; --i) {

    result[i] = gamma[i] - beta[i] * result[i + 1];

  }

}

//计算每个节点上的拟合函数

void calcFunc(vector<Point>& points, vector<double>& slope, vector<double>& coeffs, vector<double>& func) {

  int n = points.size() - 1;

  double h, a, b, c, d;

  for (int i = 0; i < n; ++i) {

    h = points[i + 1].x - points[i].x;

    a = (coeffs[i + 1] - coeffs[i]) / (6 * h);

    b = coeffs[i] / 2;

    c = (slope[i + 1] - slope[i]) / h - h * (2 * coeffs[i] + coeffs[i + 1]) / 6;

    d = points[i].y;

    for (double x = points[i].x; x <= points[i + 1].x; x += 0.1) {

      double y = a * pow(x - points[i].x, 3) + b * pow(x - points[i].x, 2) + c * (x - points[i].x) + d;

      func.push_back(y);

    }

  }

}

int main() {

  vector<Point> points = { 1, 1, 1, 3, 2}; //插值节点

  vector<double> slope = calcSlope(points);

  vector<double> A, B, C, F, coeffs(points.size());

  buildMatrix(points, slope, A, B, C, F);

  tridiagonalSolve(A, B, C, F, coeffs);

  vector<double> func;

  calcFunc(points, slope, coeffs, func);

  for (double y : func)

    cout << y << " ";

  

  return 0;

}

通过上述代码,可以得到给定插值节点的拟合曲线,波动性较小,可以更加直观地表示数据特点,提高数据的可读性和可靠性。

  
  

评论区

{{item['qq_nickname']}}
()
回复
回复