Jerkwin分享 http://blog.sciencenet.cn/u/Jerkwin

博文

样条函数插值拟合

已有 8048 次阅读 2014-3-13 03:43 |个人分类:数学轮子|系统分类:科研笔记| 积分, 拟合, 插值, 样条

样条函数插值拟合
2014–02–11 09:26:49

在拟合势能函数的时候, 除解析式外, 也可以利用样条函数进行拟合. 样条拟合与其插值正好相反: 已知函数在节点上的值求任意位置的值, 做插值; 已知函数的某些组合值求函数节点上的值, 属于拟合. 由于样条函数可以化为节点函数值的线性表达式, 这样就可以将待求参数线性化, 得到最优情况下函数的形状, 为寻找合适的解析式提供依据, 当然也可以直接利用得到的离散数据拟合解析式.

样条函数可以是零阶, 一阶, 二阶, 三阶或更高阶. 实际使用中, 三阶使用最为普遍. 由于三次样条的构造需要求解一个三对角线性方程组, 其显式解很难得到, 所以线性化结果很繁琐.

  • 零阶

    在每一区间上样条函数为常量, 函数整体呈台阶状. 对等距情况, 计算时最好使用就近原则, 取最近点的值作为拟合点, 可用i=nint(x/dx)实现.

  • 一阶

    在每一区间上样条函数为线性函数, 函数整体呈折线状$.$

    fi(x)=yi+yi+1yiΔx(xxi)

    此式自动满足函数值连续条件, 即零阶连续.

  • 二阶

    在每一区间上, 样条函数为二次函数, 整体一阶连续, 即有连续的导数. 但仅有节点的函数值不能唯一确定整个函数, 还须提供某一节点上的导数值, 一般可令端点的导数值为零. 二次样条函数在偶数点的曲率不连续. 由二阶开始, 插值函数不再具有局域性, 改变某一节点, 函数整体都会改变. 使得线性系数分离很困难.

    fi(x)ki+1=yi+ki(xxi)+ki+1ki2Δx(xxi)2=ki+2yi+1yiΔx,ki=2yi+1yiΔxki+1

    可化简得

    fi(x)α=yi+ki(xxi)+(yi+1yikiΔx)(xxiΔx)2=α2yi+1+(1α2)yi+(1α)ωki=ω/Δx,ω=xxi

    对势能函数, 一般满足远距离处导数为零, 故可使用自然条件 kn=0 , 由此, 可推知所有系数 ki .

    ki=2ΔxTi,Δi=yi+1yi, 则 Ti 满足递推式

    Ti=ΔiTi+1

    可求得

    Ti=j=in1(1)jiΔj

    样条函数可写为

    fi(x)=α2yi+1+(1α2)yi+2α(1α)Ti,α=(xxi)/Δx

    对不等距划分, 令 Δi=yi+1yixi+1xi, ki 满足如下递推式

    ki=2Δiki+1

    求得

    ki=2j=in1(1)jiΔj

  • 三阶

    对等距划分的均匀样条, 设节点为 1,2,....n, 若 x[xi,xi+1],a=xxi,b=xi+1x,a+b=h=Δx, 则

    6hfi(x)=6(ayi+1+byi)+a(a2h2)Mi+1+b(b2h2)Mi

    Mi 为节点的二阶导数, 对应于力学上的弯矩, 满足下面的方程

    Mi+4Mi+1+Mi+2=di+1=6h2(yi+22yi+1+yi),i=1,2...n2

    要求的 Mi 个数为 n, 而对应的方程数目为 n2, 故还需两个边界条件才能唯一确定, 边界条件可取为两端点的导数值或是二阶导数值. 常用的自然边界条件指 M1=Mn=0. 加上边界条件后便可求得 M2,M3,...Mn1

    Mi 满足的方程为

    0M2M3Mn3Mn2+++++4M24M34M44Mn24Mn1+++++M3M4M5Mn10=====d2d3d4dn2dn1

    写为矩阵形式

    411410014114M2M3Mn2Mn1=d2d3dn2dn1

    可见, 此方程为三对角方程组, 对角占优, 存在唯一解, 可利用所谓的追赶法求解. 中文常称的追赶法, 是Thomas方法的形象翻译. 大致求解过程分为两步:

    1. 追: 利用消元法将原方程化为二对角方程, 向前递推, 使系数矩阵主对角线变为1. 由第一个方程, 得到 M2M3 的关系, 将其带入第二个方程, 消去主对角线下方系数. 以此进行, 最终追到 Mn1, 得到其解. 变换后, 其方程为

      10A21000A310An21M2M3Mn2Mn1=D2D3Dn2Dn1

    2. 赶: Mn1 已经追得, 然后由此倒推, 得到其他 Mi 值.

    对上面的方程, 由于系数是固定的, Thomas方法的递推式为

    A2D2Mn1=14,=A2d2,=Dn1,AiDiMi=14Ai1=Ai(diDi1)=DiAiMi+1

    Ai, 可求得其通式

    Ai=23ti+1ti1,t=(2+3)2

    Di, 向后递推至 D2, 一般项可写为

    Dj=k=2j(1)jkPjkdk

    Mi, 向前递推至 Mn1, 一般项可写为

    Mi=j=in1(1)jiPj1iDj

    综合上面两个结果, 得到

    MiPnm=j=in1k=2j(1)2jikPj1iPjkdk=j=in1k=2j(1)i+kPj1iPjkdk,i=2,3,...n1=l=mnAl=AmAm+1An1An,n>m

    根据插值公式

    dk6hfi(x)=6h2(yk+12yk+yk1)=6(αyi+1+βyi)+α(α2h2)Mi+1+β(β2h2)Mi

    以划分间距 h 为单位, 约化上述公式

    fi(x)α=αyi+1+βyi+α(α21)μi+1+β(β21)μi=ah,β=bh=1α,μi=Mi6/h2

    由此可见, 虽然三次样条函数仍可写为节点值的线性形式, 但其系数十分复杂.

    上面公式看起来清楚, 但是实际计算时需要计算 MiMi+1, 两个三重循环, 整体计算量为 O(N3). 利用 Ai 的近似关系和 Mi 的递推关系可以将公式的计算量减少一些.

    fi(x)=αyi+1+βyi+α(α21)μi+1+β(β21)μi=αyi+1+βyi+β(β21)Di+[α(α21)β(β21)Ai]μi+1

    对不等距划分, 上面的递推公式太过复杂, 很难写出一般项了. 样条函数可写为

    fi(x)hi=ayi+1+byihi+a(a2h2)Mi+1+b(b2h2)Mi6hi=xi+1xi,a=xxi,b=hia

    Mi 满足方程

    hiMi+2(hi+hi+1)Mi+1+hi+1Mi+2=6(yi+2yi+1hi+1yi+1yihi),i=1,2,,n2

代码及测试测试结果

awk实现的代码如下

  1. awk ' BEGIN{ Ndat=0 }

  2. NF==3 { Ndat++; X[Ndat]=$2; Y[Ndat]=$3 }


  3. END {

  4.    for(i=1; i<Ndat; i++) dX[i]=X[i+1]-X[i]

  5.    for(i=2; i<Ndat; i++) d[i]=6*( (Y[i+1]-Y[i])/dX[i] - (Y[i]-Y[i-1])/dX[i-1] )


  6.    A[1]=0; A[Ndat]=0

  7.    D[1]=0; D[Ndat]=0

  8.    for(i=2; i<=Ndat-1; i++) {

  9.        ai=dX[i-1]

  10.        bi=2*(dX[i-1]+dX[i])

  11.        ci=dX[i]

  12.        A[i]=ci/(bi-ai*A[i-1])

  13.        D[i]=(d[i]-ai*D[i-1])*A[i]/ci

  14.    }

  15.    M[Ndat]=0

  16.    for(i=Ndat-1; i>1; i--) M[i]=D[i]-A[i]*M[i+1]


  17.    # for(i=1; i<=Ndat; i++) print i, dX[i], d[i], A[i], D[i], M[i]

  18.    h=X[2]-X[1]

  19.    for(x=X[1]; x<X[Ndat]; x+=.1*h) print x, SP2(x, 0, Ndat, X, Y, dX), SP3(x, 0, Ndat, X, Y, dX, M)

  20. }


  21. function SP2(x, i, Ndat, X, Y, dX,      j,a,ki) {

  22.    if(i==0) {

  23.        for(i=1; i<Ndat; i++) if(X[i+1]>x) break

  24.    }

  25.    ki=0

  26.    for(j=i; j<=Ndat-1; j++) ki += 2*(-1)^(j-i)*(Y[j+1]-Y[j])/dX[j]


  27.    a=(x-X[i])/dX[i]

  28.    return a^2*Y[i+1]+(1-a^2)*Y[i]+(1-a)*ki*(x-X[i])

  29. }

  30. #

  31. function SP3(x, i, Ndat, X, Y, dX, M,       a,b,h) {

  32.    if(i==0) {

  33.        for(i=1; i<Ndat; i++) if(X[i+1]>x) break

  34.    }

  35.    h=dX[i]

  36.    a=x-X[i]; b=h-a

  37.    return (a*Y[i+1]+b*Y[i])/h + ( a*(a^2-h^2)*M[i+1]+b*(b^2-h^2)*M[i] )/(6*h)

  38. }

  39. #

  40. function Psp3(Ndat,     i,j,k,a) {

  41.    a=(2+sqrt(3))^2

  42.    for(i=2; i<Ndat; i++) {

  43.        for(j=i; j<Ndat; j++) {

  44.            P[i,j]=1

  45.            for(k=i; k<=j; k++) P[i,j] *= 2-sqrt(3)*(a^k+1)/(a^k-1)

  46.        }

  47.    }

  48. }

  49. #

  50. function uniSP3(x, i, Ndat, X, Y,       j,k,a,b,h) {

  51.    if(i==0) {

  52.        for(i=1; i<Ndat; i++) if(X[i+1]>x) break

  53.    }


  54.    h=X[2]-X[1]

  55.    a=(x-X[i])/h; b=1-a


  56.    for(j=1; j<=Ndat; j++) Coef[j]=0


  57.    Coef[i]   += b

  58.    Coef[i+1] += a


  59.    Rsec=b*(b^2-1)

  60.    for(k=2; k<=i; k++) {

  61.        Rtmp=Rsec * (-1)^(i-k) * P[k,i]

  62.        Coef[k+1] +=    Rtmp

  63.        Coef[k]   += -2*Rtmp

  64.        Coef[k-1] +=    Rtmp

  65.    }


  66.    Rsec=a*(a^2-1)-b*(b^2-1)*P[i,i]

  67.    for(j=i+1; j<=Ndat-1; j++) {

  68.        Rij=Rsec * (-1)^(i+1)

  69.        if(j!=i+1) Rij *= P[i+1,j-1]

  70.        for(k=2; k<=j; k++) {

  71.            Rtmp=Rij * (-1)^k * P[k,j]

  72.            Coef[k+1] +=    Rtmp

  73.            Coef[k]   += -2*Rtmp

  74.            Coef[k-1] +=    Rtmp

  75.        }

  76.    }


  77.    F=0

  78.    for(j=1; j<=Ndat; j++) F += Coef[j]*Y[j]


  79.    return F #( a*Y[i+1]+b*Y[i] + a*(a^2-1)*Mi(i+1, Ndat) + b*(b^2-1)*Mi(i, Ndat) )

  80. }

  81. #

  82. function Mi(i, Ndat,        j,k,Rtmp,ret) {

  83.    ret=0

  84.    for(j=i; j<=Ndat-1; j++) {

  85.        Rtmp=(-1)^i

  86.        if(j!=i) Rtmp *= P[i,j-1]

  87.        for(k=2; k<=j; k++)

  88.            ret += Rtmp*(-1)^k*P[k,j] *d[k]

  89.    }

  90.    return ret

  91. }


  92. ' ABS >ABS.sp

  93. # ' CUB  > CUB.sp

  94. #' LJ  >LJ.sp

利用Matlab的csape函数可以进行三次样条函数的插值, 示例代码如下

  1. format long


  2. x=1:0.02:15;

  3. y=-1E3*(-12./x.^13+6./x.^7);


  4. pp=csape(x,y,'second',[0,0]);

  5. pp=csape(x,y);

  6. xsp=1:0.01:1.5;

  7. ysp=ppval(pp,xsp);

  8. yst=-1E3*(-12./xsp.^13+6./xsp.^7);


  9. plot(x,y,'o',xsp,ysp,'-',xsp,yst,'-')

  10. axis([1 1.5 -600 600])


  11. FID=fopen('LJ.mat', 'w');

  12. Ndat=length(xsp);

  13. for i=1:Ndat

  14.    fprintf(FID, '%f %fn', xsp(i), ysp(i));

  15. end

几个测试函数的结果


样条函数的积分

对已经拟合的样条函数进行数值积分时可利用可利用Simpson方法. Simpson方法具有三阶精度, 对不超过三次的多项式精确成立, 很适合于二次和三次样条函数.




https://blog.sciencenet.cn/blog-548663-775279.html

上一篇:GAMESS编译使用简记
下一篇:Bash脚本中使用颜色
收藏 IP: 130.184.197.*| 热度|

0

该博文允许注册用户评论 请点击登录 评论 (0 个评论)

数据加载中...
扫一扫,分享此博文

Archiver|手机版|科学网 ( 京ICP备07017567号-12 )

GMT+8, 2024-12-23 22:52

Powered by ScienceNet.cn

Copyright © 2007- 中国科学报社

返回顶部