样条曲线插值应用挺多的,其实这方面的代码不少。
不过都有一些缺憾,大家使用起来不便利,因而特意改编一个供初学者使用。
本文的程序改编自:
三次样条曲线插值的基本原理及其C#实现_仿真专家的博客-CSDN博客_三次样条曲线声明:本人空间的所有文章,若无特别声明,皆为本人原创,可自由转载,但要注明原作者和原始出处,不可作为商业用途。 下面的内容是直接从Word文档复制粘贴出来的,有很多内容丢失,完整的PDF版本可到百度网盘下载:链接:https://pan.baidu.com/s/1aEc0htnERV3I75hLq4fFsA 密码:wf6t三次样条曲线插值的基本原理及其C#实现作...https://blog.csdn.net/simxpert/article/details/80330256
上面的算法与程序有很大的局限性:
(1)仅仅适用于相对简单的、只能从左到右、不能折弯的曲线插值,比如 ECharts 内的曲线插值。
(2)仅限于二维;
(3)计算效率比较低;
(4)数据点分布比较复杂的时候无法计算;
工业化的插值应该选用更好的、更高效的算法与代码,比如双三次B样条,T样条及 NURBS 等等。
using System;
using System.Text;
using System.Collections;
using System.Collections.Generic;
namespace Legalsoft.Truffer
{
///
/// 三次样条曲线插值(2D)
///
public class Spline_Interpolation
{
private double[] Xi { get; set; }
private double[] Yi { get; set; }
private double[] A { get; set; }
private double[] B { get; set; }
private double[] C { get; set; }
private double[] H { get; set; }
private double[] Lamda { get; set; }
private double[] Mu { get; set; }
private double[] G { get; set; }
private int N { get; set; } = 0;
private int n { get; set; } = 0;
private double[] M;
public Spline_Interpolation()
{
}
///
/// 应用演示代码
///
public static void Drive()
{
#region 构建实验数据
Random rnd = new Random();
int num = 10;
double[] xx = new double[num];
double[] yy = new double[num];
for (int i = 0; i < num; i++)
{
xx[i] = i + rnd.NextDouble() * 0.1;
yy[i] = rnd.NextDouble();
}
#endregion
#region 曲线插值与绘制
Spline_Interpolation spline = new Spline_Interpolation();
spline.Initialize(xx, yy);
double xv = xx[0];
double last_xv = xv;
double last_yv = yy[0];
while (xv < xx[num - 1])
{
double yv = spline.Interpolate(xv);
if (last_xv > xv)
{
// 绘制线段
//g.DrawLine(pen, last_xv, last_yv, xv, yv);
}
last_xv = xv;
last_yv = yv;
xv += 0.01;
}
#endregion
}
public bool Initialize(double[] Xi, double[] Yi)
{
if (Xi.Length != Yi.Length)
{
return false;
}
if (Xi.Length == 0)
{
return false;
}
// 根据给定的Xi,Yi元素的数目来确定N的大小。
N = Xi.Length;
n = N - 1;
// 根据实际大小给各个成员变量分配内存
A = new double[N - 1];
B = new double[N];
C = new double[N - 1];
this.Xi = new double[N];
this.Yi = new double[N];
H = new double[N - 1];
Lamda = new double[N - 1];
Mu = new double[N - 1];
G = new double[N];
// 把输入数据点的数值赋给成员变量。
for (int i = 0; i <= n; i++)
{
this.Xi[i] = Xi[i];
this.Yi[i] = Yi[i];
}
// 求出hi,Lamda(i),Mu(i),gi
GetH();
GetLamda_Mu_G();
GetABC();
// 调用追赶法求出系数矩阵M
Chasing_Solver chase = new Chasing_Solver();
if (chase.Initialize(A, B, C, G))
{
if (chase.Solve(out M))
{
return true;
}
}
return false;
}
private void GetH()
{
for (int i = 0; i < n; i++)
{
H[i] = Xi[i + 1] - Xi[i];
}
}
private void GetLamda_Mu_G()
{
for (int i = 1; i < n; i++)
{
Lamda[i] = H[i] / (H[i] + H[i - 1]);
Mu[i] = 1.0 - Lamda[i];
double t1 = (Yi[i] - Yi[i - 1]) / H[i - 1];
double t2 = (Yi[i + 1] - Yi[i]) / H[i];
G[i] = 3.0 * (Lamda[i] * t1 + Mu[i] * t2);
}
G[0] = 3.0 * (Yi[1] - Yi[0]) / H[0];
G[n] = 3.0 * (Yi[n] - Yi[n - 1]) / H[n - 1];
Mu[0] = 1.0;
Lamda[0] = 0.0;
}
private void GetABC()
{
for (int i = 1; i < n; i++)
{
A[i - 1] = Lamda[i];
C[i] = Mu[i];
}
A[n - 1] = 1.0;
C[0] = 1.0;
for (int i = 0; i <= n; i++)
{
B[i] = 2.0;
}
}
private double fai0(double x)
{
double t1 = 2.0 * x + 1.0;
double t2 = (x - 1.0) * (x - 1.0);
return t1 * t2;
}
private double fai1(double x)
{
return x * (x - 1.0) * (x - 1.0);
}
private int GetSection(double x)
{
for (int i = 0; i < n; i++)
{
if (x <= Xi[i])
{
return i - 1;
}
}
return -999999;// iNum;
}
///
/// 按x值计算y值(插值计算)
///
///
///
public double Interpolate(double x)
{
double t = x;
int iNum = GetSection(x);
if (iNum == -1)
{
return Yi[0];
}
else if (iNum == -999999)
{
return Yi[n];
}
double P1 = (t - Xi[iNum]) / H[iNum];
double P2 = (Xi[iNum + 1] - t) / H[iNum];
return Yi[iNum] * fai0(P1) +
Yi[iNum + 1] * fai0(P2) +
M[iNum] * H[iNum] * fai1(P1) -
M[iNum + 1] * H[iNum] * fai1(P2);
}
}
}
using System;
using System.Text;
using System.Collections;
using System.Collections.Generic;
namespace Legalsoft.Truffer
{
///
/// 求解严格对角占优的对角方程组的追赶法
///
public class Chasing_Solver
{
///
/// Dimension of Martrix Ax=d
///
private int N { get; set; }
///
/// Ax=d
///
private double[] d { get; set; }
///
/// a in A
///
private double[] Aa { get; set; }
///
/// b in A
///
private double[] Ab { get; set; }
///
/// c in A
///
private double[] Ac { get; set; }
///
/// LU-->L
///
private double[] L { get; set; }
///
/// LU-->U
///
private double[] U { get; set; }
///
/// store the result
///
private double[] S { get; set; }
public Chasing_Solver()
{
}
///
/// 数据初始化
///
///
///
///
///
///
public bool Initialize(double[] a, double[] b, double[] c, double[] d)
{
int na = a.Length;
int nb = b.Length;
int nc = c.Length;
int nd = d.Length;
if (nb < 3)
{
return false;
}
N = nb;
if (na != N - 1 || nc != N - 1 || nd != N)
{
return false;
}
S = new double[N];
L = new double[N - 1];
U = new double[N];
Aa = new double[N - 1];
Ab = new double[N];
Ac = new double[N - 1];
this.d = new double[N];
for (int i = 0; i <= N - 2; i++)
{
Ab[i] = b[i];
this.d[i] = d[i];
Aa[i] = a[i];
Ac[i] = c[i];
}
Ab[N - 1] = b[N - 1];
this.d[N - 1] = d[N - 1];
return true;
}
///
/// 追赶法求解器
///
///
///
public bool Solve(out double[] R)
{
#region A=LU
R = new double[Ab.Length];
U[0] = Ab[0];
for (int i = 2; i <= N; i++)
{
// L[i] = Aa[i] / U[i - 1];
L[i - 2] = Aa[i - 2] / U[i - 2];
//U[i]=Ab[i]-Ac[i-1]*L[i];
U[i - 1] = Ab[i - 1] - Ac[i - 2] * L[i - 2];
}
#endregion
#region Ly=d
double[] Y = new double[d.Length];
Y[0] = d[0];
for (int i = 2; i <= N; i++)
{
//Y[k]=d[k]-L[k]*Y[k-1];
Y[i - 1] = d[i - 1] - (L[i - 2]) * (Y[i - 2]);
}
#endregion
#region Ux=Y
//X[n]=Y[n]/U[n];
R[N - 1] = Y[N - 1] / U[N - 1];
//X[k]=(Y[k]-C[k]*X[k+1])/U[k];(n-1,,.....1)
for (int i = N - 1; i >= 1; i--)
{
R[i - 1] = (Y[i - 1] - Ac[i - 1] * R[i]) / U[i - 1];
}
#endregion
for (int i = 0; i < R.Length; i++)
{
if (double.IsInfinity(R[i]) || double.IsNaN(R[i]))
{
return false;
}
}
return true;
}
}
}