kisara11235
@kisara11235

Are you sure you want to delete the question?

Leaving a resolved question undeleted may help others!

LM法を用いて非線形フィティングを行いたい

Q&A

Closed

解決したいこと

sqrt(pow((2 * J * (1 - cos(q)) + ganma / 2), 2) - pow( ganma / 2, 2));
のJ,ganmaのパラメータをフィティングしたい。
LM法を用いている理由は非線形フィティングを行う方法として良い方法と調べたから。もともと最小二乗法を行っていたが、全くうまく行かなかった。

発生している問題・エラー

値が収束しない、または結果として出た数値を上の関数に入れて計算したところもとのグラフと全く合わない。

該当するソースコード

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

#define P 501
int N = (P + 1) / 2;

// 関数の定義
double f(double q, double J, double ganma) {
    double A = 2 * J * (1 - cos(q));
    double C = ganma / 2;
    double E = pow((A + C), 2) - pow(C, 2);
    double F = sqrt(E);

    return F;
}

// ヤコビアン行列を計算する関数
void calculateJacobian(double x[], double J[], double ganma[], int n, double J_val, double ganma_val) {
    for (int i = 0; i < n; i++) {
        double q = x[i];

        double A = (1-cos(q));
        double B = 4*J_val*A*A;
        double C = ganma_val*A;
        double G = +J_val*A;

        double E =4*J_val*J_val*A*A+2*J_val*ganma_val*A;
        double F = pow(E,0.5);

        J[i] = (A+B)/F;
        ganma[i] = G/F;
    }
}

// Levenberg-Marquardt法
void levenbergMarquardt(double x[], double y[], int n, double* J, double* ganma) {
    double lambda = 0.0000001; // 初期のパラメータ更新係数
    double epsilon = 1e-6;   // 収束判定の閾値
    int maxIterations = 100000; // 最大反復回数

    double J_val = *J;
    double ganma_val = *ganma;

    double residuals[P];    // 残差ベクトル
    double Jacobian[P];     // ヤコビアン行列
    double ganma_Jacobian[P];

    int iterations = 0;
    double prevCost = 0.0;

    while (iterations < maxIterations) {
        // 残差ベクトルの計算
        for (int i = 0; i < n; i++) {
            double q = x[i];
            residuals[i] = y[i] - f(q, J_val, ganma_val);
        }

        // ヤコビアン行列の計算
        calculateJacobian(x, Jacobian, ganma_Jacobian, n, J_val, ganma_val);

        // システム行列の計算
        double H[2][2] = {{0}};
        double b[2] = {0};

        for (int i = 0; i < n; i++) {
            H[0][0] += Jacobian[i] * Jacobian[i];
            H[0][1] += Jacobian[i] * ganma_Jacobian[i];
            H[1][0] += ganma_Jacobian[i] * Jacobian[i];
            H[1][1] += ganma_Jacobian[i] * ganma_Jacobian[i];

            b[0] += Jacobian[i] * residuals[i];
            b[1] += ganma_Jacobian[i] * residuals[i];
        }

        // パラメータの更新
        double H_sum = H[0][0] + H[1][1];
        double det = H[0][0] * H[1][1] - H[0][1] * H[1][0];

        double delta[2];
        delta[0] = (H_sum * b[0] - (H[0][1] * b[1])) / det;
        delta[1] = ((-H[1][0] * b[0]) + (H[0][0] * b[1])) / det;

        J_val += delta[0];
        ganma_val += delta[1];

        // 収束判定
        double currentCost = 0.0;
        for (int i = 0; i < n; i++) {
            currentCost += pow(residuals[i], 2);
        }

        if (fabs(currentCost - prevCost) < epsilon) {
            break;
        }

        if (currentCost < prevCost) {
            lambda /= 10.0;
        } else {
            lambda *= 10.0;
            J_val -= delta[0];
            ganma_val -= delta[1];
        }

        prevCost = currentCost;
        iterations++;
    }

    *J = J_val;
    *ganma = ganma_val;
}

int main() {
    FILE* fp;
    FILE* fp_1;
    FILE* fp_2;

    double x[(P + 1) / 2], y[(P + 1) / 2];

    // データポイント
    if ((fp = fopen("J.txt", "w")) == NULL) {
        printf("Cannot open the file\n");
        exit(1);
    }
    fp_1 = fopen("q.txt", "r");
    if (fp_1 == NULL) {
        printf("ファイルを開くことができませんでした.\n");
    }
    for (int l = 0; l < N; l++) {
        fscanf(fp_1, "%lf", &(x[l]));
    }
    fp_2 = fopen("omega.txt", "r");
    if (fp_2 == NULL) {
        printf("ファイルを開くことができませんでした.\n");
    }
    for (int i = 0; i < N; i++) {
        fscanf(fp_2, "%lf", &(y[i]));
    }

    // パラメータの初期値
    double J = 0.30;
    double ganma = 0.090;

    // Levenberg-Marquardt法でパラメータを最適化
    levenbergMarquardt(x, y, N, &J, &ganma);

    // 結果表示
    printf("J: %.6f\n", J);
    printf("ganma: %.6f\n", ganma);

    for (int i = 0; i < (P + 1); i++) {
        double p = M_PI / (P + 1) * i;
        double fc = f(p, J, ganma);
        double fc_1 = f(p, J, 0);
        fprintf(fp, "%lf %lf %lf\n", p, fc, fc_1);
    }

    fclose(fp);
    fclose(fp_1);
    fclose(fp_2);

    return 0;
}

自分で試したこと

最小二乗法やテイラー展開での近似などを行ったがほとんどうまく行かなかった。
初期値や刻み方、収束しきい値などを変えても全くうまく行かなかった。

0

2Answer

赤の他人はエスパーではないので、「うまく行かない」「合わない」と言うだけでは、あなたがどういう結果を期待しているか誰も判りません。
正しい結果が分かっているなら、デバッグして計算過程の変数が入力値に対して出力値が期待通りになっているか地道に検証していくしかないんじゃないですか?

0Like

お好きな関数をチョイスして下さい。

標本の特性は目で診て感じ取って下さい。

0Like

Your answer might help someone💌