학교수업, CS/수치해석

보간법(선형, 랑그라제, 네빌레, 뉴튼 다항식, 전/후향 차분법, 스플라인)

빨대도둑 2022. 11. 30. 14:35

선형 보간법

선형 보간법은 여러 기본적인 수치해석 방법의 기초가 된다. 선형 보간법을 이용하여 사다리꼴 법이라는 한 적분 방법도 유도해 낼 수 있으며 미분 방정식을 푸는데도 사용된다. 선형 보간법은 단순히 주어진 두 점을 이은 직선으로 구하는 방법이다.

<수식>

#include <stdio.h>
#include <math.h>
#include <conio.h>
#include <windows.h>

void clrscr(void)
{
    COORD Cur = { 0, 0 };
    unsigned long dwLen;
    FillConsoleOutputCharacter(GetStdHandle(STD_OUTPUT_HANDLE), ' ', 80 * 25, Cur, &dwLen);
}

double linear_interpol(double* x1, double* x2, double* y1, double* y2, double* x) {
    double result;
    result = (*y2 - *y1) / (*x2 - *x1) * (*x - *x1) + *y1;
    return result;
}

int main(void) {
    double x, x1, x2, y1, y2, output;

    x1 = 1.0;
    x2 = 2.0;
    y1 = exp(x1);
    y2 = exp(x2);

    clrscr();
    printf("\n------\t Interpolated value ------ Real value -------------\n");

    for (x = 1.0; x <= 2.0; x += 0.1) {
        output = linear_interpol(&x1, &x2, &y1, &y2, &x);
        printf("\n\t f(%lf)=%lf\t exp(%lf)=%lf", x, output, x, exp(x));
    }
    printf("\n");

    _getch();
}

 


랑그라제 다항식을 이용한 보간법

점과 점을 직선이 아닌 어떤 곡선으로 연결하여 구하는 방법

<수식>

#include <stdio.h>
#include <math.h>
#include <conio.h>
#include <windows.h>

#define DATA 4

void clrscr(void)
{
    COORD Cur = { 0, 0 };
    unsigned long dwLen;
    FillConsoleOutputCharacter(GetStdHandle(STD_OUTPUT_HANDLE), ' ', 80 * 25, Cur, &dwLen);
}

double linear_interpol(double* x1, double* x2, double* y1, double* y2, double* x) {
    double result;
    result = (*y2 - *y1) / (*x2 - *x1) * (*x - *x1) + *y1;
    return result;
}

int main(void) {
    int i, j, k;
    double result, numerator, denominator;

    double x[] = { 1.5,2.7,3.4 };
    double in[] = { 1.,2.,3.,4. };
    double out[] = { 0.0,0.30103,0.47712,0.60206 };

    clrscr();
    printf("\n-------\t interpolated value ----- real value --------------\n");

    for (i = 0; i < 3; i++) {
        result = 0.0;
        for (j = 0; j < DATA; j++) {
            numerator = 1.0;
            denominator = 1.0;

            for (k = 0; k < DATA; k++) {
                if (k == j)continue;

                numerator *= (x[i] - in[k]);
                denominator *= (in[j] - in[k]);
            }
            result += numerator / denominator * out[j];
        }
        printf("\n\tf(%lf)=%lf\t log(%lf)=%lf", x[i], result, x[i], log10(x[i]));
    }
    printf("\n");

    _getch();
}

 


네빌레의 반복 보간법

랑그라제 보간 다항식의 단줌중 하나가 데이터 중 새로운 점이 하나만 더 추가되어도 그 앞에서 구했던 계산 결과를 이용할 수 없다는 점이다. 새로운 점이 추가되면 새로운 식을 처음부터 다시 구해야 하는 단점이 있는데, 네빌레의 반복보간법은 앞에서 구했던 계산이나 결과를 다음 단계에서 활용함으로써 보다 편하게 보간 다항식을 구할 수 있다.

<수식>

#include <stdio.h>
#include <math.h>
#include <conio.h>
#include <windows.h>

#define DATA 5

void clrscr(void)
{
    COORD Cur = { 0, 0 };
    unsigned long dwLen;
    FillConsoleOutputCharacter(GetStdHandle(STD_OUTPUT_HANDLE), ' ', 80 * 25, Cur, &dwLen);
}

int main(void) {
    int i, j;
    float in[5], out[5][5], x;

    /*initialization*/

    for (i = 0; i < DATA; i++) {
        in[i] = (float)(i + 1);
        out[0][i] = sqrt((float)(i + 1));
    }
        clrscr();
        printf("\n Enter the x-axis value:");
        scanf_s("%f", &x);

        /*nevile alogorithm*/

        for (j = 0; j < DATA-1; j++) 
            for (i = 0; i < DATA - j - 1; i++)
                out[j + 1][i];
            ((x - in[i]) * out[j][i + 1] - (x - in[i + j + 1]) * out[j][i]) / 
            (in[i + j + 1] - in[i]);

        /*print out the result*/

            printf("\n the interpolated out put of input %f is %f\n", x, out[0][1]);
            printf("the real value of input %f is %f\n", x, sqrt((float)x));
    
}

 


뉴튼 다항식에 의한 보간법

랑그라제 보간법의 단점

  1. 하나의 보간에 필요로 하는 계산량이 많다
  2. 데이터의 수가 증가할 때 그 전의 사용 결과가 사용되지 않는다
  3. 에러 계산이 용이하지 않다

 

뉴튼 보간법은 이 단점을 극복한다. 

  1. 뉴튼 보간법은 주어진 데이터를 기초로 하여 차분표를 적성한다
  2. 차분표가 작성되면 보간공식을 쉽게 구할 수 있다. 따라서 보간 다항식의 차수는 데이터가 추가되어도 쉽게 그 차수를 늘리는 장점이 있다.

<수식>

#include <stdio.h>
#include <math.h>
#include <conio.h>
#include <windows.h>

#define DATA 5

void clrscr(void)
{
    COORD Cur = { 0, 0 };
    unsigned long dwLen;
    FillConsoleOutputCharacter(GetStdHandle(STD_OUTPUT_HANDLE), ' ', 80 * 25, Cur, &dwLen);
}

int main(void) {
    int i, j;
    double temp, result, x;
    double in[] = { 3.,3.3,3.5,3.7,4. }, out[DATA][DATA] = 
    { {1.09861,1.19392,1.25276,1.30833,1.335}, };
    
    clrscr();
    printf("\n enter the x-axis value: ");
    scanf_s("%lf", &x);

    for (j = 1; j < DATA; j++)
        for (i = 0; i < DATA - j; i++)
            out[j][i];
            (out[j - 1][i + 1] - out[j - 1][i]) / (in[i + j] - in[i]);

            result = 0.;

            for (j = 0; j < DATA; j++) {
                temp = 1.;
                for (i = 0; i < j; i++)
                    temp *= (x - in[i]);
                result += temp * out[j][0];
            }

            printf("\n the interpolated output of input %lf is %lf\n", x, result);
   

}

 


전향 차분법

<수식>

#include <stdio.h>
#include <math.h>
#include <conio.h>
#include <windows.h>

#define DATA 5

void clrscr(void)
{
    COORD Cur = { 0, 0 };
    unsigned long dwLen;
    FillConsoleOutputCharacter(GetStdHandle(STD_OUTPUT_HANDLE), ' ', 80 * 25, Cur, &dwLen);
}

double binomial(double s, int j) {
    int m, n;
    double temp, bino;

    if (j == 0)bino = 1.;
    else if (j == 1)bino = s;
    else {
        m = j;
        temp = s;

        for (n = 1; n < m; n++) {
            temp *= (s - (double)n);
            j *= n;
        }
        bino = temp / (double)j;
    }
    return bino;
}

int main(void) {
    int i, j;
    double result, x, s=0;
    double in[] = { 10.,11.,12.,13.,14. }, out[DATA][DATA] = 
    { {3.162278,3.316626,3.464102,3.605551,3.741657}, };

    printf("\n enter the x-axis value:");
    scanf_s("%lf", &x);

    /*algorithm implementation*/

    /*finding h and s
    h=in[1]-in[0]
    s=(x-in[0]/h */

    /*finding result*/

    result = 0.;

    for (j = 0; j <= 4; j++) {
        for (i = j + 1; i <= 4; i++)
            out[j + 1][i] = out[j][i] - out[j][i - 1];
        result += binomial(s, j) * out[j][j];
    }

    printf("\n the interpolated output of input %lf is %lf\n", x, result);
}

 

후향 차분법

<수식>

#include <stdio.h>
#include <math.h>
#include <conio.h>
#include <windows.h>

#define DATA 5

void clrscr(void)
{
    COORD Cur = { 0, 0 };
    unsigned long dwLen;
    FillConsoleOutputCharacter(GetStdHandle(STD_OUTPUT_HANDLE), ' ', 80 * 25, Cur, &dwLen);
}

double binomial(double s, int j) {
    int m, n;
    double temp, bino;

    if (j == 0)bino = 1.;
    else if (j == 1)bino = s;
    else {
        m = j;
        temp = s;

        for (n = 1; n < m; n++) {
            temp *= (s - (double)n);
            j *= n;
        }
        bino = temp / (double)j;
    }
    return bino;
}

int main(void) {
    int i, j;
    double result, x, s=0;
    double in[] = { 1.0,1.3,1.6,1.9,2.2 }, out[DATA][DATA] = 
    { {0.7651977,0.6200860,0.4554022,0.2818186,0.1103623}, };

    clrscr();
    printf("\n enter the value of x-domain :");
    scanf_s("%lf", &x);

    /*algorithm implementation*/

    /*finding h and s
    h=in[1]-in[0]
    s=(x-in[DATA-1])/h */

    /*finding result*/

    result = 0.;

    for (j = 0; j <DATA; j++) {
        for (i = j + 1; i <DATA; i++)
            out[j + 1][i] = out[j][i] - out[j][i - 1];
        result += binomial(-s, j) * out[j][DATA-1]*pow(-1.0,j);
    }

    printf("\n the interpolated output of input %lf is %lf\n", x, result);
}

 


3차원 스플라인 보간법

종종 많은 데이터들이 하나의 매끄러운 단일 곡선의 점들일 수가 있다. 만일 이러한 경우에 주어진 데이터들을 라그랑제 보간법이나 뉴튼의 방법으로 보간한다면 차수가 늘어남에 따라 더욱 큰 오차가 발생하게 될 것이다. 이러한 경우에는 3차원 스플라인 보간법이 최적의 방법이다

3차원 스플라인 보간법에는 두 개의 이웃하는 데이터 사이에 3차 다항식이 사용된다. 이 다항식은 4개의 미정 계수가 있으므로 4가지 조건을 필요로 한다. 4개의 미정 계수들 중 2개는 이 다항식이 두 점을 반드시 통과해야 한다는 조건에서 구할 수 있고, 나머지 2개는 1차 미분, 2차 미분한 값이 주어진 두 점에서 연속해야 한다는 사실로부터 구할 수 있다. 

<수식>

#include <stdio.h>
#include <math.h>
#include <conio.h>
#include <windows.h>

#define DATA 9

void clrscr(void)
{
    COORD Cur = { 0, 0 };
    unsigned long dwLen;
    FillConsoleOutputCharacter(GetStdHandle(STD_OUTPUT_HANDLE), ' ', 80 * 25, Cur, &dwLen);
}

void tridia(double element_1[], double element_2[], double element_3[], 
double element_4[], double *s) {
    int i;
    double temp;
    for (i = 1; i < DATA - 2; i++) {
        temp = element_1[i] / element_2[i - 1];
        element_2[i] -= temp * element_3[i - 1];
        element_4[i] -= temp * element_4[i - 1];
    }
    s[DATA - 3] = element_4[DATA - 3] / element_2[DATA - 3];

    for (i = DATA - 4; i >= 0; i--)
        s[i] = (element_4[i] - element_3[i] * s[i + 1] / element_2[i]);
}

void interpol(double in[], double out[], double s[], double h[], double inv) {
    int i;
    double temp, a[DATA], b[DATA], c[DATA], d[DATA], result;

    for(i = 0; i < DATA - 1; i++) {
        a[i] = (s[i + 1] - s[i]) / (6.0 * h[i]);
        b[i] = s[i] / 2.0;
        c[i] = (out[i + 1] - out[i]) / h[i] - (2.0 * h[i] * s[i] + h[i] * s[i + 1]) / 
        6.0;
        d[i] = out[i];
    }
    temp = 0.0;

    do {
        for (i = 0; i < DATA - 1; i++) {
            if ((temp >= in[i]) && (temp < in[i + 1])) {
                result = d[i] + (temp - in[i] * (c[i] + (temp - in[i]) * (b[i] + a[i] * 
                (temp - in[i]))));
                printf("\n F[%lf]=%lf", temp, result);
                break;
            }
        }
        temp += inv;
    } while (temp < in[DATA - 1]);
}

int main(void) {
    int i, cond;
    double h[DATA - 1], element_1[DATA], element_2[DATA], element_3[DATA], element_4[DATA], 
    s[DATA], inv;

    double in[] = { 0.0, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 1.0 }, out[] = 
    { 0.302,0.185,0.106,0.093,0.240,0.579,0.561,0.468,0.302 };

    clrscr();
    printf("\n type the output interval of x-domain that you wanna find: ");
    scanf_s("%lf", &inv);

    for (i = 0; i < DATA - 1; i++)
        h[i] = in[i + 1] - in[i];

    for (i = 1; i < DATA - 3; i++) {
        element_1[i] = h[i];
        element_2[i] = 2. * (h[i] + h[i + 1]);
        element_3[i] = h[i + 1];
    }
    for (i = 0; i < DATA - 2; i++)
        element_4[i] = 6.0 * ((out[i + 2] - out[i + 1]) / h[i + 1] - 
        (out[i + 1] - out[i]) / h[i]);

    for (cond = 0; cond < 3; cond++) {
        if (cond == 0) {
            s[0] = s[DATA - 1] = 0.0;
            element_1[DATA - 3] = h[DATA - 3];
            element_2[0] = 2.0 * (h[0] + h[1]);
            element_2[DATA - 3] = 2.0 * (h[DATA - 3] + h[DATA - 2]);
            element_3[0] = h[1];

            tridia(element_1, element_2, element_3, element_4, s);
            printf("\n for boundary condition 1 -----------------------");
            interpol(in, out, s, h, inv);
        }
        else if (cond == 1) {
            element_1[DATA - 3] = h[DATA - 3];
            element_2[0] = 3.0 * (h[0] + h[1]);
            element_2[DATA - 3] = 3.0 * (h[DATA - 2] + 2.0 * h[DATA - 3]);
            element_3[0] = h[1];

            tridia(element_1, element_2, element_3, element_4, s);

            s[0] = s[1], s[DATA - 1] = s[DATA - 2];
            printf("\n for boundary condition 2 -----------------------");
            interpol(in, out, s, h, inv);

        }
        else {
            element_2[0] = (h[0] + h[1]) * (h[0] + 2. * h[1]) / 
            h[1] * (h[1] * h[1] - h[0] * h[0]) / h[1];
            element_2[DATA - 3] = (h[DATA - 2] + h[DATA - 3]) * 
            (h[DATA - 2] + 2. * h[DATA - 3] )/ h[DATA - 3];
            element_1[DATA - 3] = (h[DATA - 3] * h[DATA - 3] - h[DATA - 2]  * h[DATA - 2]) /
            h[DATA - 3];
            element_3[0]= ( h[1] * h[1] - h[0] * h[0]) / h[1];

            tridia(element_1, element_2, element_3, element_4, s);

            s[0]= ((h[0] + h[1]) * s[1] - h[0]*s[2]) / h[1];
            s[DATA-1]= ((h[DATA - 3] + h[DATA - 2]) * s[DATA - 2] - h[DATA - 2]*s[DATA-3]) / 
            h[DATA - 3];

            printf("\n for boundary condition 2 -----------------------");

            interpol(in, out, s, h, inv);
        }
    }
}