三次样条插值

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了三次样条插值相关的知识,希望对你有一定的参考价值。

条件
(1)输入$x_{i},y_{i}=f(x_{i}),0\leq i\leq n$
(2)要求拟合的曲线$S(x)$满足:对于任意的$1\leq i\leq n-1$,在$x_{i}$处一阶二阶导数连续,$S(x)$ 也连续,且$S^{‘}(x_{0})=f^{‘}(x_{0})$,$S^{‘}(x_{n})=f^{‘}(x_{n})$

求解过程
设$S_{x_{j}}^{‘‘}=M_{j}$。对于区间$[x_{j},x_{j+1}]$,$S(x)$是$[x_{j},x_{j+1}]$上的线性函数,所以设$S^{‘‘}=M_{j}\frac{x_{j+1}-x}{h_{j}}+M_{j+1}\frac{x-x_{j}}{h_{j}}$,$h_{j}=x_{j+1}-x_{j}$。对$S^{‘‘}$两次积分,并利用$S(x_{j})=y_{j},S(x_{y+1})=y_{j+1}$,得到:
$S(x)=M_{j}\frac{(x_{j+1}-x)^{3}}{6h_{j}}+M_{j+1}\frac{(x-x_{j})^{3}}{6h_{j}}+(y_{j}-\frac{M_{j}h_{j}^{2}}{6})\frac{x_{j+1}-x}{h_{j}}+(y_{j+1}-\frac{M_{j+1}h_{j}^{2}}{6})\frac{x-x_{j}}{h_{j}}$
对上式求导得到:
$S^{‘}(x)=-M_{j}\frac{(x_{j+1}-x)^{2}}{2h_{j}}+M_{j+1}\frac{(x-x_{j})^{2}}{2h_{j}}+\frac{y_{j+1}-y_{j}}{h_{j}}-\frac{M_{j+1}-M_{j}}{6}h_{j}$
根据在$x_{j},1\leq j\leq n-1$处一阶导数相等可得:
$\mu_{j}M_{j-1}+2M_{j}+\lambda _{j}M_{j+1}=d_{j},1\leq j\leq n-1$
其中$\mu_{j}=\frac{h_{j-1}}{h_{j-1}+h_{j}}$,$\lambda _{j}=\frac{h_{j}}{h_{j-1}+h_{j}}$,$d_{j}=6\frac{f[x_{j},x_{j+1}]-f[x_{j-1},x_{j}]}{h_{j-1}+h_{j}}$,$f[x_{j},x_{j+1}]=\frac{y_{j+1}-y_{j}}{x_{j+1}-x{j}}$

同时对于边界条件$S^{‘}(x_{0})=f^{‘}(x_{0})$,$S^{‘}(x_{n})=f^{‘}(x_{n})$可得:$2M_{0}+M_{1}=\frac{6}{h_{0}}(f[x_{0},x_{1}]-f^{‘}(x_{0}))$,$M_{n-1}+2M_{n}=\frac{6}{h_{n-1}}(f^{‘}(x_{n})-f[x_{n-1},x_{n}])$.

#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cmath>
#include <cassert>
#include <ctime>

class MclVector
{
public:
    int n;
    double *Mat;
    /**
      type=0: 列向量
      type=1: 行向量
    **/
    int type;

    MclVector() { Mat=NULL; n=0; }
    MclVector(int len,double initVal=0.0)
    {
        n=len;
        Mat=new double[n+1];
        for(int i=0;i<=n;i++) Mat[i]=initVal;
        type=0;
    }


    double operator[](int id) const
    {
        return Mat[id];
    }


    double& operator[](int id)
    {
        return Mat[id];
    }

    double length() const
    {
        double sum=0;
        for(int i=1;i<=n;i++) sum+=Mat[i]*Mat[i];
        return sqrt(sum);
    }

    MclVector operator*(double val) const
    {
        MclVector ans=MclVector(n);
        for(int i=1;i<=n;i++) ans[i]=Mat[i]*val;
        return ans;
    }

    MclVector operator/(double val) const
    {
        MclVector ans=MclVector(n);
        for(int i=1;i<=n;i++) ans[i]=Mat[i]/val;
        return ans;
    }

    MclVector operator+(const MclVector &newVector) const
    {
        MclVector ans=MclVector(n);
        for(int i=1;i<=n;i++) ans[i]=Mat[i]+newVector[i];
        return ans;
    }


    MclVector operator-(const MclVector &newVector) const
    {
        MclVector ans=MclVector(n);
        for(int i=1;i<=n;i++) ans[i]=Mat[i]-newVector[i];
        return ans;
    }

    MclVector operator*=(double val)
    {
        for(int i=1;i<=n;i++) Mat[i]=Mat[i]*val;
        return *this;
    }

    MclVector operator/=(double val)
    {
        for(int i=1;i<=n;i++) Mat[i]=Mat[i]/val;
        return *this;
    }


    MclVector operator+=(const MclVector &newVector)
    {
        for(int i=1;i<=n;i++) Mat[i]+=newVector[i];
        return *this;
    }

    MclVector operator-=(const MclVector &newVector)
    {
        for(int i=1;i<=n;i++) Mat[i]-=newVector[i];
        return *this;
    }

    MclVector GetTranspose() const
    {
        MclVector ans=*this;
        ans.type=1;
        return ans;
    }


    void print() const
    {
        for(int i=1;i<=n;i++) printf("%8.3lf ",Mat[i]);
        puts("");
    }


};

class MclMatrix
{
public:
    int row,col;
    MclVector *Mat;

    MclMatrix() {Mat=NULL;}
    MclMatrix(int _row,int _col,double initVal=0.0)
    {
        row=_row;
        col=_col;
        Mat=new MclVector[row+1];
        for(int i=0;i<=row;i++) Mat[i]=MclVector(col,initVal);
    }

    void setIdentityMatrix()
    {
        for(int i=1;i<=row;i++)
        {
            for(int j=1;j<=col;j++)
            {
                if(i==j) Mat[i][j]=1;
                else Mat[i][j]=0;
            }
        }
    }

    MclMatrix GetTranspose() const
    {
        MclMatrix ans=MclMatrix(col,row);
        for(int i=1;i<=ans.row;i++)
        {
            for(int j=1;j<=ans.col;j++)
            {
                ans[i][j]=Mat[j][i];
            }
        }
        return ans;
    }

    void print() const
    {
        for(int i=1;i<=row;i++) Mat[i].print();
        puts("");
    }

    MclVector& operator[](int id) const
    {
        return Mat[id];
    }


    MclVector& operator[](int id)
    {
        return Mat[id];
    }

    MclMatrix operator*(const MclMatrix &Matrix) const
    {
        MclMatrix ans=MclMatrix(row,Matrix.col);
        for(int i=1;i<=row;i++)
        {
            for(int j=1;j<=Matrix.col;j++)
            {
                for(int k=1;k<=col;k++)
                {
                    ans[i][j]+=Mat[i][k]*Matrix[k][j];
                }
            }
        }
        return ans;
    }

    MclMatrix operator+(const MclMatrix &Matrix) const
    {
        MclMatrix ans=MclMatrix(row,Matrix.col);
        for(int i=1;i<=row;i++)
        {
            for(int j=1;j<=Matrix.col;j++)
            {
                ans[i][j]=Mat[i][j]+Matrix[i][j];
            }
        }
        return ans;
    }

    MclMatrix operator-(const MclMatrix &Matrix) const
    {
        MclMatrix ans=MclMatrix(row,Matrix.col);
        for(int i=1;i<=row;i++)
        {
            for(int j=1;j<=Matrix.col;j++)
            {
                ans[i][j]=Mat[i][j]-Matrix[i][j];
            }
        }
        return ans;
    }

    MclVector GetCol(int colId) const
    {
        MclVector ans=MclVector(row);
        for(int i=1;i<=row;i++) ans[i]=Mat[i][colId];
        return ans;
    }
    MclVector GetRow(int rowId) const
    {
        MclVector ans=MclVector(row);
        for(int i=1;i<=col;i++) ans[i]=Mat[rowId][i];
        return ans;
    }


    MclMatrix operator*=(const MclMatrix &Matrix)
    {
        return *this=*this*Matrix;
    }
    MclMatrix operator+=(const MclMatrix &Matrix)
    {
        return *this=*this+Matrix;
    }
    MclMatrix operator-=(const MclMatrix &Matrix)
    {
        return *this=*this-Matrix;
    }

    MclMatrix operator*(double x) const
    {
        MclMatrix ans=*this;
        for(int i=1;i<=row;i++)
        {
            for(int j=1;j<=col;j++)
            {
                ans[i][j]*=x;
            }
        }
        return ans;
    }

};

MclMatrix vectorMulVector(const MclVector &A,const MclVector& B)
{
    if(A.type==0)
    {
        MclMatrix ans=MclMatrix(A.n,B.n);
        for(int i=1;i<=A.n;i++)
        {
            for(int j=1;j<=B.n;j++)
            {
                ans[i][j]+=A[i]*B[j];
            }
        }
        return ans;
    }
    else
    {
        assert(A.n==B.n);
        MclMatrix ans=MclMatrix(1,1);
        for(int i=1;i<=A.n;i++)
        {
            ans[1][1]+=A[i]*B[i];
        }
        return ans;
    }
}

int sgn(double x)
{
    if(x<-0.000001) return -1;
    if(x>0.000001) return 1;
    return 0;
}

/**
  高斯消去 A为n*n B为n*1
**/
MclVector Gauss(MclMatrix A,MclVector B)
{
    const int n=A.row;
    const int m=A.col;
    assert(m==B.n&&m==n);
    MclVector ans=MclVector(m);

    for(int i=1;i<=n;i++)
    {
        int row=i;
        for(int j=i;j<=n;j++)
        {
            if(sgn(A[j][i])) 
            {
                row=i; break;
            }
        }
        if(row!=i) 
        {
            for(int j=1;j<=n;j++) std::swap(A[i][j],A[row][j]);
            std::swap(B[i],B[row]);
        }
        for(int j=1;j<=n;j++) if(i!=j)
        {
            const double det=A[j][i]/A[i][i];
            for(int k=i;k<=n;k++) A[j][k]-=det*A[i][k];
            B[j]-=det*B[i];
        }
    }
    for(int i=1;i<=n;i++) ans[i]=B[i]/A[i][i];
    return ans;
}

/**
  n: 点个数[0,n]
  RangeSplitPos: 分界点 大小(n+1)
  SplitPosValue: 函数值 大小(n+1)
  LValue,RValue: 两侧的一阶导数

  返回值: 每个区间的多项式 大小n
**/
std::vector<Polynomial> CubicSplineInterpolation(
    const int n,
    const std::vector<double> RangeSplitPos,
    const std::vector<double> SplitPosValue,
    const double LValue,
    const double RValue)
{
#define x(i) RangeSplitPos[i]
#define y(i) SplitPosValue[i]
#define h(i) (RangeSplitPos[i+1]-RangeSplitPos[i])
#define f(i,j) ((y(j)-y(i))/h(i))

    double *mou=new double[n+1];
    double *lamd=new double[n+1];
    double *d=new double[n+1];

    memset(mou,0,sizeof(double)*(n+1));
    memset(lamd,0,sizeof(double)*(n+1));
    memset(d,0,sizeof(double)*(n+1));


    lamd[0]=1;
    d[0]=6.0/h(0)*(f(0,1)-LValue);

    mou[n]=1;
    d[n]=6.0/h(n-1)*(RValue-f(n-1,n));

    for(int j=1;j<=n-1;j++)
    {
        mou[j]=h(j-1)/(h(j-1)+h(j));
        lamd[j]=h(j)/(h(j-1)+h(j));
        d[j]=6.0/(h(j-1)+h(j))*(f(j,j+1)-f(j-1,j));
    }

    MclMatrix A=MclMatrix(n+1,n+1);
    for(int i=1;i<=n+1;i++)
    {
        if(i>1) A[i][i-1]=mou[i-1];
        if(i<n+1) A[i][i+1]=lamd[i-1];
        A[i][i]=2;
    }
    MclVector B=MclVector(n+1);
    for(int i=1;i<=n+1;i++) B[i]=d[i-1];

    delete[] mou;
    delete[] lamd;
    delete[] d;

    MclVector MArr=Gauss(A,B);

    std::vector<Polynomial> LastAns;
    for(int j=0;j<n;j++)
    {
        const double Mj=MArr[j+1];
        const double Mj1=MArr[j+2];
        Polynomial poly;

        double tmp=-Mj/(6.0*h(j));
        poly.add(3,tmp);
        poly.add(2,-3*x(j+1)*tmp);
        poly.add(1,3*x(j+1)*x(j+1)*tmp);
        poly.add(0,-x(j+1)*x(j+1)*x(j+1)*tmp);

        tmp=Mj1/(6*h(j));
        poly.add(3,tmp);
        poly.add(2,-3*x(j)*tmp);
        poly.add(1,3*x(j)*x(j)*tmp);
        poly.add(0,-x(j)*x(j)*x(j)*tmp);

        tmp=(y(j)-Mj*h(j)*h(j)/6.0)/h(j);
        poly.add(0,tmp*x(j+1));
        poly.add(1,-tmp);


        tmp=(y(j+1)-Mj1*h(j)*h(j)/6.0)/h(j);
        poly.add(0,-tmp*x(j));
        poly.add(1,tmp);

        LastAns.push_back(poly);
    }

    return LastAns;
}

 

以上是关于三次样条插值的主要内容,如果未能解决你的问题,请参考以下文章

三次样条插值介绍

三次样条插值

如何使用 CuPy 在 python 上进行三次样条插值?

三次样条插值

如何通过python实现三次样条插值

Spline(三次样条插值)