数学计算中的计算精度

时间:2018-02-21 15:57:09

标签: c++ math interpolation spline

我需要建立三次样条插值曲线,给出空间中的点。

我有下一个代码。这很好,但没有必要的精度(这很可能是逻辑错误)。

#include <iostream>
#include <vector>
#include <cmath>

using namespace std;

struct INPUT {
    int N;
    std::vector<double> x;
    int M;
    std::vector<std::vector<double> > lattice;
    int K;
    std::vector<double> res_lattice;
} input_data;

class CubicSpline{
    double a = 0;
    double b = 0;
    double c = 0;
    double d = 0;
    double x = 0;
public:
    CubicSpline() {}

    double get(double input_x){
        double f_x = input_x-x;
        return a + b*f_x + c*f_x*f_x/2 + d*f_x*f_x*f_x/6;
    }

    void setA(double new_a) {
        a = new_a;
    }

    void setX(double new_x) {
        x = new_x;
    }

    void setB(double new_b) {
        b = new_b;
    }

    void setC(double new_c) {
        c = new_c;
    }

    void setD(double new_d) {
        d = new_d;
    }
};

class CubicSplineCurve{
    std::vector<double> x_values;
    std::vector<double> y_values;
    int N;
    std::vector<CubicSpline> splines;
public:
    CubicSplineCurve(std::vector<double> x_values, std::vector<double> y_values, int N) : x_values(x_values), y_values(y_values), N(N) {
        splines = std::vector<CubicSpline>(N);
        int i;
        for (i = 1; i < N; i++){
            splines[i].setA(y_values[i]);
            splines[i].setX(x_values[i]);
        }

//        std::vector<double> c_values(*get_c_values());
//        std::vector<double> c_values(N);
//        get_c_values(c_values);
        std::vector<double> c_values = get_c_values();
        for (i = 1; i < N; i++){
            splines[i].setC(c_values[i]);
        }
        std::vector<double> d_values(N);
        d_values[1] = c_values[1]/h(1);
        for (i = 2; i < N; i++){
            d_values[i] = (c_values[i] - c_values[i-1])/h(i);
            splines[i].setD(d_values[i]);
        }

        for (i = 1; i < N; i++){
//            splines[i].setB(u(i) + c_values[i]*h(i)/2 - d_values[i]*pow(h(i), 2)/6);
            splines[i].setB(h(i)*(2*c_values[i] + c_values[i-1])/6 + u(i));

        }

    }

    double get(double x){
        int i;
        for (i = 1; i < N; i++){
            if (x <= x_values[i]){
                return splines[i].get(x);
            }
        }
    }


private:
    std::vector<double> get_c_values(){
        std::vector<double> c_values(N);
        c_values[0] = 0;
        c_values[N-1] = 0;
        int k, i;

        std::vector<std::vector<double> > c_matrix = std::vector<std::vector<double> >(N-2);

        for (i = 0; i < N-2; i++){
            c_matrix[i] = std::vector<double>(N-2);
        }
        int n = N-3;
        c_matrix[0][0] = 2;
        c_matrix[0][1] = h(2)/(h(1) + h(2));
        c_matrix[n][n] = 2;
        c_matrix[n][n-1] = h(N-2)/(h(N-2) + h(N-1));

        for (i = 1; i < n; i++){
            c_matrix[i][i-1] = h(i)/(h(i) + h(i+1));
            c_matrix[i][i] = 2;
            c_matrix[i][i+1] = h(i+1)/(h(i) + h(i+1));
        }

        std::vector<double> res_vector(N-2);

        for (i = 0;i < n; i++){
            res_vector[i] = 6 * triple_u(i+2);
        }

//        std::vector<double> valuable_c_values_gauss = gauss_for_tridiagonal_matrix(c_matrix, res_vector, N-2);
        std::vector<double> valuable_c_values = shuttle(c_matrix, res_vector, N-2);


        for (i = 1; i < N-1; i++){
            c_values[i] = valuable_c_values[i-1];
        }
//        return new std::vector<double>(c_values);
        return c_values;


    }

    // Here i try to round near 0 values, and get rid of computational error.
    double h(int i){
        return round((x_values[i] - x_values[i-1])*10000000000)/10000000000;
    }

    double u(int i){
        return round((y_values[i]-y_values[i-1])*1000000000/h(i))/1000000000;
    }

    double triple_u(int i){
        return round((u(i) - u(i-1))*1000000000/(x_values[i]-x_values[i-2]))/1000000000;
    }

    // Next 2 methods solving SLE, they gives the same results, for now i use shutle method.
    std::vector<double> gauss_for_tridiagonal_matrix(std::vector<std::vector<double> > tridiagonalMatrix, std::vector<double> res_vector, int N){
        int i;
        int k;
        for (i = 0; i < N-1; i++){
            double factor = tridiagonalMatrix[i+1][i]/tridiagonalMatrix[i][i];
            tridiagonalMatrix[i+1][i] = 0;
            for (k = i + 1; k < N; k++){
                tridiagonalMatrix[i+1][k] -= tridiagonalMatrix[i][k]*factor;
            }
            res_vector[i+1] -= res_vector[i]*factor;
        }
        for (i = N-1; i > 0; i--){
            double factor = tridiagonalMatrix[i][i];
            tridiagonalMatrix[i][i] = 1;
            res_vector[i] = res_vector[i]/factor;
            factor = tridiagonalMatrix[i-1][i]/tridiagonalMatrix[i][i];
            for (k = i; k > 0; k--){
                tridiagonalMatrix[i-1][k] -= tridiagonalMatrix[i][k]*factor;
            }
            res_vector[i-1] -= res_vector[i]*factor;

        }
        double factor = tridiagonalMatrix[0][0];
        res_vector[0] = res_vector[0]/factor;
        return res_vector;

    }
// I get it in internet, because have no clear understanding of this method yet.
    std::vector<double> shuttle(std::vector<std::vector<double> > tridiagonalMatrix, std::vector<double> res_vector, int N){
        double y;
        std::vector<double> a(N);
        std::vector<double> B(N);
        int N1 = N - 1;
        y = tridiagonalMatrix[0][0];
        a[0] = -tridiagonalMatrix[0][1] / y;
        B[0] = res_vector[0] / y  ;
        for (int i = 1; i < N1; i++) {
            y = tridiagonalMatrix[i][i] + tridiagonalMatrix[i][i - 1] * a[i - 1];
            a[i] = -tridiagonalMatrix[i][i + 1] / y;
            B[i] = (res_vector[i] - tridiagonalMatrix[i][i - 1] * B[i - 1]) / y;
        }


        res_vector[N1] = (res_vector[N1] - tridiagonalMatrix[N1][N1 - 1] * B[N1 - 1]) / (tridiagonalMatrix[N1][N1] + tridiagonalMatrix[N1][N1 - 1] * a[N1 - 1]);
        for (int i = N1 - 1; i >= 0; i--) {
            res_vector[i] = a[i] * res_vector[i + 1] + B[i];
        }

        return res_vector;
    }
};

void proceed_input(INPUT input){
    int i,k;
    std::vector<CubicSplineCurve> curves;
    for (i = 0; i < input.M; i++){
        curves.push_back(CubicSplineCurve(input.x, input.lattice[i], input.N));
    }
    double result;
    cout.precision(8);
//    for (i = 0; i < input.N; i++){
//        double first = input.lattice[0][i];
//        double second = curves[0].get(input.x[i]);
//        cout << first << " " << second << " " << abs(first - second) << endl;
//    }

    for (i = 0; i < input.M; i++){
        for (k = 0; k < input.K; k++){
            result = curves[i].get(input.res_lattice[k]);
            cout << result << " ";
        }
        cout << endl;
    }
}

int main() {
    cin >> input_data.N;
//    input_data.x = std::vector<double>(input_data.N);
    input_data.x = std::vector<double>(input_data.N);
    int i;
    for (i = 0; i < input_data.N; i++){
        cin >> input_data.x[i];
    }
    cin >> input_data.M;
    input_data.lattice = std::vector<std::vector<double> >(input_data.M);
    int k;
    for (k = 0; k < input_data.M; k++){
        input_data.lattice[k] = std::vector<double>(input_data.N);
        for (i = 0; i < input_data.N; i++){
            cin >> input_data.lattice[k][i];
        }
    }
    cin >> input_data.K;
    input_data.res_lattice = std::vector<double>(input_data.K);
    for (i = 0; i < input_data.K; i++){
        cin >> input_data.res_lattice[i];
    }
    proceed_input(input_data);
    return 0;
}

例如,当我尝试插入sin函数时。我得到了下一件神器。plotting artifact

在这里我们可以看到,即使函数是连续的,它们的1阶和2阶导数也不等于它。但我无法理解为什么会这样。

哪里可能有错误?

0 个答案:

没有答案