【C/C++学习】分段三次Hermit插值pchip


【C/C++学习】加速线性插值
加速方法见头顶。

1. 功能概述

1.1 功能效果

分段三次Hermit插值(pchip)的C/C++实现,知道你对原理不感兴趣,直接上代码。

1.2 使用方法

  1. 输入要求:输入插值自变量、应变量、数据个数和插值点;
//测试pchip
	double X[6] = { 2, 3, 5, 6, 9, 10};
	double Y[6] = { 5, 4, 3, 8, 10, 11};
	int nX = sizeof(X) / sizeof(double);
	double dx = 0.2;
	int num = floor((11 - 0) / dx);
	double *x = new double[num] {};
	double *y = new double[num] {};
	for (int i = 0; i < num; i++) {
		x[i] = dx*i;
	}
	for (int i = 0; i < num; i++) {
		CPchip pchip(X, Y, nX, x[i]);
		y[i] = pchip.getY();
	}
	ofstream outFile;
	outFile.open(".\\pchip_result.txt", ios::out);
	outFile << "x\t" << "y\n";
	for (int i = 0; i <num; i++) {
			outFile << x[i] << "\t";
			outFile << y[i] << "\n";
	}
	outFile.close();
	delete[] x;
	x = nullptr;
	delete[] y;
	y = nullptr;
  1. 输出结果:

在这里插入图片描述

2. 注意事项

  1. 要求至少有三个插值点,只有两个选择线性插值吧;
  2. x只能是一个数;
  3. 外插可能会出现意想不到的情况,但是你非要用也拦不住。

3. 详细代码

CPchip.h

/*
* Auther:	 sanfan66
*
* Date        | Change
*--------------------------------------------
* 231117      | <record>: Ver 1.1a
*             | (describe): pchip
*             | !warning!: none
*/
#pragma once
#include <iostream>//cout
using namespace std;//cout

class CPchip
{
public:
	CPchip(const double *X, const double *Y, const int n, const double x);
	~CPchip();
	int FindIndex(const double *X, const int n, const double x);
	double ComputeDiff(const double *h, const double *delta, const int n, const int k);
	double getY() { return y; };
private:
	double y;
	double *h;
	double *delta;
};

CPchip.cpp

#include "CPchip.h"
CPchip::CPchip(const double *X, const double *Y, const int n, const double x)
{
	int k = FindIndex(X, n, x);
	h = new double[n] {};
	delta = new double[n] {};
	for (int i = 0; i <= n - 2; i++) {
		h[i] = X[i + 1] - X[i]; //节点上增量
		if (h[i] < 1e-12) {
			cout << "X[" << i + 1 << "]=" << X[i + 1] << "不递增" << endl;
			abort();
		}
		delta[i] = (Y[i + 1] - Y[i]) / h[i]; //节点上差商,向后差分法, 注意点除
	}
	double diff1 = ComputeDiff(h, delta, n, k);
	double diff2 = ComputeDiff(h, delta, n, k + 1);

	double a = (diff2 + diff1 - 2 * delta[k]) / h[k] / h[k];
	double b = (-diff2 - 2 * diff1 + 3 * delta[k]) / h[k];
	double c = diff1;
	double d = Y[k];
	y = a * pow(x - X[k], 3) + b * pow(x - X[k], 2) + c * (x - X[k]) + d;

	delete[] h;
	h = nullptr;
	delete[] delta;
	delta = nullptr;
}
CPchip::~CPchip()
{
	delete[] h;
	h = nullptr;
	delete[] delta;
	delta = nullptr;
}

int CPchip::FindIndex(const double *X, const int n, const double x)
{
	/* 找到插值位置 */
	if (n < 3) {
		cout << "pchip要求三个及以上节点!" << endl;
		abort();
	}
	int index = 0;
	int iBeg = 0;
	if (x <= X[1]) {
		index = 0;
		if (x < X[0]) {
			cout << "x=" << x << ",pchip外插,继续但不保证正确性" << endl;
		}
	}
	else if (x >= X[n - 2]) {
		index = n - 2;
		if (x > X[n + 1]) {
			cout << "x=" << x << ",pchip外插,继续但不保证正确性" << endl;
		}
	}
	else {
		iBeg = floor((n - 1) / (X[n - 1] - X[0])*(x - X[0]));
		if (x >= X[iBeg]) {
			for (int i = iBeg; i <= n - 3; i++) {
				if (x >= X[i] && x <= X[i + 1]) {
					index = i;
					break;
				}
			}
		}
		else {
			for (int i = iBeg - 1; i >= 1; i--) {
				if (x >= X[i] && x <= X[i + 1]) {
					index = i;
					break;
				}
			}
		}
	}
	return index;
}

double CPchip::ComputeDiff(const double *h, const double *delta, const int n, const int k)
{
	double diff = 0;
	if (k == 0) {
		double t = ((2 * h[0] + h[1])*delta[0] - h[0] * delta[1]) / (h[0] + h[1]);
		if (t*delta[0] <= 0)
			diff = 0;
		else if (delta[0] * delta[1] < 0 && abs(t) > abs(3 * delta[0]))
			diff = 3 * delta[0];
		else
			diff = t;
	}
	else if (k == n - 1) {
		double t = ((2 * h[n - 2] + h[n - 3])*delta[n - 2] - h[n - 2] * delta[n - 3]) / (h[n - 2] + h[n - 3]);
		if (t*delta[n - 2] <= 0)
			diff = 0;
		else if (delta[n - 2] * delta[n - 3] < 0 && abs(t) > abs(3 * delta[n - 2]))
			diff = 3 * delta[n - 2];
		else
			diff = t;
	}
	else {
		if (delta[k] * delta[k - 1] <= 0)
			diff = 0;
		else if (delta[k] * delta[k - 1] > 0 && abs(h[k] - h[k - 1]) < 1e-12)
			diff = 2 * delta[k] * delta[k - 1] / (delta[k] + delta[k - 1]);
		else {
			double w1 = 2 * h[k] + h[k - 1];
			double w2 = h[k] + 2 * h[k - 1];
			diff = delta[k] * delta[k - 1] / (w1*delta[k] + w2 * delta[k - 1])*(w1 + w2);
		}
	}
	return diff;
}
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Hermit插值法是一种用于数据拟合的方法,可以用于生成曲线或曲面,具有高精度和快速计算的优点。下面是一个基于C语言的Hermit插值法示例: ```c #include <stdio.h> #include <stdlib.h> #include <math.h> #define MAX 100 struct point { double x; double y; }; double hermit_interpolation(double x, struct point *p, int n) { double h[MAX], b[MAX], c[MAX], d[MAX], f[MAX]; int i, j, k; for (i = 0; i < n; i++) { h[i] = p[i+1].x - p[i].x; b[i] = (p[i+1].y - p[i].y) / h[i]; } c[0] = 0.0; c[n-1] = 0.0; for (i = 1; i < n-1; i++) { c[i] = 2.0 * (h[i-1] + h[i]) - h[i-1] * c[i-1] / 3.0; d[i] = (b[i] - b[i-1]) / h[i-1] - (b[i+1] - b[i]) / h[i]; c[i] = (d[i] - h[i-1] * c[i-1] / 3.0) / c[i]; } f[n-1] = b[n-1]; for (i = n-2; i >= 0; i--) f[i] = b[i] - c[i] * f[i+1] - (2.0 * h[i] * f[i+1] + h[i] * f[i]) / 3.0; for (i = 0; i < n; i++) { if (x >= p[i].x && x <= p[i+1].x) { k = i; break; } } double A, B, C, D; A = f[k] * pow(p[k+1].x - x, 3) / (6.0 * h[k]); B = f[k+1] * pow(x - p[k].x, 3) / (6.0 * h[k]); C = (p[k+1].y - f[k] * pow(h[k], 2) / 6.0) * (x - p[k].x) / h[k]; D = (p[k].y - f[k+1] * pow(h[k], 2) / 6.0) * (p[k+1].x - x) / h[k]; return A + B + C + D; } int main() { int n, i; double x, y; struct point p[MAX]; printf("Enter the number of data points: "); scanf("%d", &n); printf("Enter the data points:\n"); for (i = 0; i < n; i++) { printf("x[%d] = ", i); scanf("%lf", &p[i].x); printf("y[%d] = ", i); scanf("%lf", &p[i].y); } printf("Enter the value of x for which y is to be found: "); scanf("%lf", &x); y = hermit_interpolation(x, p, n-1); printf("The interpolated value of y at x = %lf is %lf\n", x, y); return 0; } ``` 在该示例中,我们首先定义了一个结构体来存储数据点的坐标。然后我们定义了一个函数 `hermit_interpolation` 来进行Hermit插值。该函数接受一个待插值的x值,数据点集合以及数据点的数量作为输入参数。该函数的主要任务是计算插值多项式,并使用它来计算给定x值的插值y值。在计算插值多项式时,我们使用了Hermit插值法的公式,该公式需要计算一些常数和系数,并使用它们来计算插值多项式。最后,我们在主函数中读取数据点,输入待插值的x值,并使用 `hermit_interpolation` 函数计算插值y值。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值