《計算方法》 李曉紅等 第五章 插值 例題 代碼

#include "stdafx.h"

#include <math.h>

// 插值

 

double PI()

{

    return(atan(1.)*4);

}

 

// x | pi/6     pi/4        pi/3

//------------------------------------

// y | 1/2    sqrt(2)/2     sqrt(3)/2

void LagrangeInterpolation_Order1()

{

    printf("Lagrange 插值(1階): \n");

    int n = 1;

    double x[2],y[2];

    double phi,x0,y0,l;

    int k,i;

    double err;

 

    x[0] = PI()/6;  y[0] = 0.5;

    x[1] = PI()/4;  y[1] = sqrt(2.)/2.;

     

    x0 = PI()*5/18;

    y0 = sin(x0);   // 真值

    PS(&y0,1,"sin(5*pi/18)");

    phi = 0.;

    for (k=0;k<=n;k++)

    {

        l = 1.;

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

        {

            if(i!=k)

                l *= ( (x0-x[i])/(x[k]-x[i]) );

        }

        phi += l*y[k];

    }

    PS(&phi,n,"phi");

    err = sin(x0)*(x0-x[0])*(x0-x[1])/2.;

    err = abs(err);

    printf("誤差 R1(x0) = %f\n",err);

}

 

// x | pi/6     pi/4        pi/3

//------------------------------------

// y | 1/2    sqrt(2)/2     sqrt(3)/2

void LagrangeInterpolation_Order2()

{

    printf("Lagrange 插值(2階): \n");

    int n = 2;

    double x[3],y[3];

    double phi,x0,y0,l;

    int k,i;

    double err;

 

    x[0] = PI()/6;  y[0] = 0.5;

    x[1] = PI()/4;  y[1] = sqrt(2.)/2.;

    x[2] = PI()/3;  y[2] = sqrt(3.)/2.;

 

    x0 = PI()*5/18;

    y0 = sin(x0);   // 真值

    PS(&y0,1,"sin(5*pi/18)");

    phi = 0.;

    for (k=0;k<=n;k++)

    {

        l = 1.;

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

        {

            if(i!=k)

                l *= ( (x0-x[i])/(x[k]-x[i]) );

        }

        phi += l*y[k];

    }

    PS(&phi,1,"phi");

    err = 1;

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

        err *= ((x0-x[i])/(i+1));

    }

    err *= cos(PI()/6);

    err = abs(err);

    printf("誤差 R2(x0) = %f\n",err);

}

 

// x | 0    pi/6        pi/4        pi/3        pi/2

//-----------------------------------------------------

// y | 0    1/2   sqrt(2)/2     sqrt(3)/2         1

void NewtonInterpolation()

{

    printf("四次牛頓插值多項式(Page77):\n");

    double f[5][5];

    int n = 4;

    int i,j;

    double pi = PI();

    double x[5] = {0,pi/6,pi/4,pi/3,pi/2};

    double N;

    double tmp;

    double x0 = pi*5/18;

 

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

    {

        for(j=0;j<=n;j++) f[i][j]=0.;

    }

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

        f[i][0] = sin(x[i]);

//      x[i] /= pi;

//      x[i] *= 180;

    }

    // 計算差商

    for (j=1;j<=n;j++)

    {  // 列,階數

        for (i=0;i<=n-j;i++)

        {

            f[i][j] = f[i][j-1] - f[i+1][j-1];

            f[i][j] /= (x[i]-x[i+j]);

        }

    }  

    printf("%d階差商:\n",n);

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

    {

        for(j=0;j<=n-i;j++)

            printf("%.9f\t",f[i][j]);

        printf("\n");

    }

    // 求值

    N = 0;

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

    {

        tmp = 1.;

        for (j=0;j<i;j++)

            tmp = tmp * (x0-x[j]);

        N += tmp*f[0][i];

    }

    PS(&N,1,"N4(5*pi/18)");

}

 

void NewtonInterpolation_example4()

{

    printf("牛頓向前向後插值(節點等間隔): Page81");

    double x[4] = {0.4,0.5,0.6,0.7};

    double f[4][4];

    int i,j;

    int n=3;

    double x0 = 0.65;

    double t,h=0.1;

    double N,tmp;

     

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

    {

        for(j=0;j<=n;j++) f[i][j]=0.;

    }

 

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

        f[i][0] = sin(x[i]);

    }

    // 計算差分

    for (j=1;j<=n;j++)

    {  // 列,階數

        for (i=0;i<=n-j;i++)

        {

            f[i][j] = f[i+1][j-1]-f[i][j-1];

//          f[i][j] /= (x[i]-x[i+j]);

        }

    }  

    printf("%d階差商:\n",n);

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

    {

        for(j=0;j<=n-i;j++)

            printf("%.5f\t",f[i][j]);

        printf("\n");

    }

 

    // 前向差分

    t = (x0-x[0])/h;

    N = 0.;

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

        tmp = 1.;

        for (j=0;j<i;j++) {

            tmp = tmp*(t-j)/(j+1);

        }

        N+=tmp*f[0][i];

    }

    printf("向前插值近似:\n");

    PS(&N,1,"sin(0.65)");

    // 三次向前插值誤差

    double R3 = 1.;

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

        R3 *= h*(t-i)/(i+1);

    }

    R3 = abs(R3);

    printf("R3=%.3ef\n",R3);

 

    // 後向差分近似

    t = (x0-x[n])/h;

    N = 0.;

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

        tmp = 1.;

        for (j=0;j<i;j++) {

            tmp = tmp*(t+j)/(j+1);

        }

        N+=tmp*f[n-i][i];

    }

    printf("向後插值近似:\n");

    PS(&N,1,"sin(0.65)");

    printf("真值sin(0.65)=%f\n",sin(x0));

 

    // 向後插值近似(2次)

    n--;

    t = (x0-x[n])/h;

    N = 0.;

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

        tmp = 1.;

        for (j=0;j<i;j++) {

            tmp = tmp*(t+j)/(j+1);

        }

        N+=tmp*f[n-i][i];

    }

    printf("向後插值近似(2次):\n");

    PS(&N,1,"N2");

    // 二次向後插值誤差

    double R2 = 1.;

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

        R2 *= h*(t+i)/(i+1);

    }

    R2 = abs(R2);

    printf("R3=%.3ef\n",R2);

}

 

// x | 0.4      0.5     0.7     0.8

//---------------------------------

// y | lnx

//---------------------------------

// y'| 1/x

void HermiteInterpolation()

{

    printf("埃爾米特插值:\n");

    double x[4] = {0.4,0.5,0.7,0.8};

    int n = 3;

    int orderN = 2*n+1;

    double xp = 0.6;

    double tmp;

 

    double y[4],dy[4];

    int i,j,k;

    double H[4],h[4]; // 插值 H(0.6)

 

    // 賦值

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

        y[i] = log(x[i]);

        dy[i]= 1./x[i];

    }

     

    for (i=0;i<=n;i++) // hi,Hi, i=0,1,...,n,多項式個數

    {

        h[i] = H[i] = 1.;

        for (j=0;j<=n;j++)  // j=0,1,...,n,因式個數

        {

            if (j==i) // i==j時,Hi,hi子式不同,其餘情況子式相同

            {

                tmp = 1.;

                for (k=0;k<=n;k++)

                {

                    if(k!=j)

                        tmp += 2*(xp-x[i])/(x[k]-x[j]);

                }

                h[i] *= tmp;

                H[i] *= (xp-x[i]);

            }

            else

            {

                tmp = (xp-x[j])/(x[i]-x[j]);

                tmp = tmp*tmp;

                h[i] *= tmp;

                H[i] *= tmp;

            }

        }

    }

    PS(h,n+1,"h");

    PS(H,n+1,"H");

    // 插值近似

    tmp = 0.;

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

        tmp += ((h[i]*y[i])+(H[i]*dy[i]));

    printf("插值ln(0.6)=%f\n",tmp);

    printf("真值ln(0.6)=%f\n",log(0.6));

}

 

// x | xi = -1 + i/5

//---------------------------------

// y | 1/(1+25x^2)

//---------------------------------

void LagrangeInterpolation_Order10()

{

    printf("龍格現象: Page86\n");

    int n =10;

    double x[11],y[11];

    double phi[11],x0[11],y0[11],l;

    int k,i,j;

    double err[11];

 

    // 插值節點

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

        x[i] = -1 + 2.*i/10;

        y[i] = 1+25*x[i]*x[i];

        y[i] = 1./y[i];

    }

 

    // 估計節點,真實值

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

        x0[i] = 0.1*i;

        y0[i] = 1+25*x0[i]*x0[i];

        y0[i] = 1./y0[i];

    }

    PS(x0,n+1,"xi");

    PS(y0,n+1,"f(xi)");

 

    // 計算插值點

    for (j=0;j<=n;j++)  // 插值節點

    {

        phi[j] = 0.;

        for (k=0;k<=n;k++) // 子多項式個數

        {

            l = 1.;

            for (i=0;i<=n;i++) // 因式個數

            {

                if(i!=k)

                    l *= ( (x0[j]-x[i])/(x[k]-x[i]) );

            }

            phi[j] += l*y[k];

        }  

    }

    PS(phi,n+1,"phi10(xi)");

 

    // 誤差

    for (i=0;i<=n;i++) err[i] = abs(y0[i]-phi[i]);

    PS(err,n+1,"error");

}

 

// 插值多項式次數通常<=7

// 節點比較多時,採用分段插值算法

// 分段:線性,拋物線,連續且經過插值點

// 分段:埃爾米特,同時滿足導數相等

void FenDuanLinearInterpolation()

{

    printf("分段線性插值:\n");

    int n = 2;

    int i;

    double l[3];

    double x[3] = {0,1,2};

    double y[3] = {0,6,4};

    double x1,f;

 

    x1 = 1.56789;

    for (i=1;i<=n;i++)   //x1在區間x[i-1]~x[i]

    {

        if (x1>=x[i-1]&&x1<x[i])

        {

            l[i-1] = (x1-x[i])/(x[i-1]-x[i]);

            l[i] = (x1-x[i-1])/(x[i]-x[i-1]);

        }

        else

            l[i] = 0.;

    }

    f = 0.;

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

        f+=l[i]*y[i];

    PS(&f,1,"f");

}

 

// cubic spline interpolation

// 三次樣條插值

//-----------------------------------------

// i | 0    1       2       3       4     |

//-----------------------------------------

// x | -3   -1      0       3       4     |

//-----------------------------------------

// y | 7    11      26      56      29    |

//-----------------------------------------

// 第二邊值條件: x0, xn處S(x)二階導數爲0.

void CubicSplineInterpolation()

{

    printf("三次樣條插值:\n");

    double x[5] = {-3,-1,0,3,4};

    double y[5] = {7,11,26,56,29};

    int n = 4;

    double M[5];  // 四個區間

    int i,j,k;

    double a[3][3];

    double d[3];

    double xx[3],yy[3];

    double T0,T1,T2;  // T0=x(i)-x(i-1),T1=x(i+1)-x(i),T2=x(i+1)-x(i-1);

    double dy0,dy1;

 

    for (i=0;i<n-1;i++)

        d[i] = 0.;

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

        M[i] = 0.;

    for (i=0;i<n-1;i++) {

        for (j=0;j<n-1;j++) {

            a[i][j] = 0.;

        }

    }

 

    // 係數

    for (i=1;i<=n-1;i++) // i = 1,2,...,n-1

    {

        for (j=0;j<n-1;j++)

        {

            k = i-1;

            T0= x[i]-x[i-1];

            T1=x[i+1]-x[i];

            T2=x[i+1]-x[i-1]; // T0+T1

            if (k==j)

                a[k][j] = 2.;

            else if (j==k+1)  // lamda = 1-mu

                a[k][j] = T1/T2;                   

            else if (j==k-1) // mu

                a[k][j] = T0/T2;

            else

                a[k][j] = 0.;

        }

    }

    printf("a=\n");

    for (i = 0; i < n-1; i++)

    {

        for (j = 0; j < n-1; j++)

            printf("%f\t",a[i][j]);

        printf("\n");

    }

    // 常數

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

        k = i-1;

        T0= x[i]-x[i-1];

        T1=x[i+1]-x[i];

        T2=x[i+1]-x[i-1]; // T0+T1

        dy0 = (y[i]-y[i-1])/T0;

        dy1 = (y[i+1]-y[i])/T1;

//      d[k] = 6.*((y[i+1]-y[i])/T1-(y[i]-y[i-1])/T0)/T2;  

        d[k] = 6.*(dy1-dy0)/T2; // 二階導數

    }

    PS(d,n-1,"d");

 

    // 求解,追趕法II

    // 追

    double p[3];

    double q[2];

    int nn = n-1;

 

    p[0] = a[0][0];

    q[0] = a[0][1]/p[0];

    for (i = 1; i < nn; i++)

    {

        p[i] = a[i][i] - a[i][i-1]*q[i-1];

        if (i<nn-1) q[i] = a[i][i+1]/p[i];

    }

    PS(p,nn,"p");PS(q,nn-1,"q");

    // 趕

    for (i=0;i<nn;i++)

    {

        yy[i] = d[i];

        if(i>0) yy[i] -= a[i][i-1]*yy[i-1];

        yy[i] /= p[i];

    }

    for (i=nn-1;i>=0;i--) {

        xx[i] = yy[i];

        if(i<nn-1) xx[i]-=q[i]*xx[i+1];

    }

    PS(yy,nn,"y");PS(xx,nn,"x");

 

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

        M[i] = xx[i-1];

    }

    PS(M,n+1,"M");

     

    // 求S(1)

    double x0 = 1;

    double S = 0.;

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

        if (x0>=x[i-1]&&x0<x[i])

        {

            T0= x[i]-x[i-1];

//          T1=x[i+1]-x[i];

//          T2=x[i+1]-x[i-1]; // T0+T1

            S = (x[i]-x0)*(x[i]-x0)*(x[i]-x0)*M[i-1]/6/T0

                + (x0-x[i-1])*(x0-x[i-1])*(x0-x[i-1])*M[i]/6/T0

                + (x[i]-x0)/T0*(y[i-1]-M[i-1]/6*T0*T0)

                + (x0-x[i-1])/T0*(y[i]-M[i]/6*T0*T0);

        }

    }

    PS(&S,1,"S");

}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章