微分求解器上的 C++ 段错误

标签 c++ math memory memory-management

我编写了一个程序,使用亚当方法逼近常微分方程的解。

用 gdb 运行程序给我:

Program received signal EXC_BAD_ACCESS, Could not access memory.
Reason: KERN_PROTECTION_FAILURE at address: 0x00007fff5f3ffff8
0x0000000100003977 in std::vector<double, std::allocator<double> >::push_back (this=0x100005420, __x=@0x100005310) at stl_vector.h:604
604         this->_M_impl.construct(this->_M_impl._M_finish, __x);

显然我对 vector.push_back 的处理有问题,但我不知道从哪里开始寻找。我想不出修改 vector 是非法的情况。

调用 differentiate() 开始。数学是在 step() 中完成的。使用 advance() 跨间隔推进的自适应时间。在再次运行 step() 之前,使用 checkdt() 检查所选的时间步长。

对于巨大的代码转储,我们深表歉意。我确信可以纯粹从 C++ 的角度进行许多改进,而无需数学知识:

//============================================================================
// Description : An Adam's Method Ordinary Differential Equation Approximation
// Disclaimer  : Posted to StackOverflow for help on a segmentation fault
//============================================================================

#include <iostream> //IO
#include <vector> //std::vector
#include <cmath> //abs, pow, sqrt
#include <numeric> //accumulate
using namespace std;

/* Terminology:
 * f(t, y) = the ordinary differential equation that will be solved
 * y(t) = solution of f at point t.
 *  told = the previous point at which f was evaluated and solved
 *  tnow = the current point at which f is evaluated and solved
 *  tnew = the new (interesting) point at which f will be evaluated and solved
 *  
 *  Yold = the corrected solution of the differential equation at told
 *  Ynow = the corrected solution of the differential equation at tnow
 *  Ynew = the corrected solution of the differential equation at tnew
 *  
 *  fold = the value of the given differential equation at told
         = f(told, Yold)
 *  fnow = the value of the given differential equation at tnow
         = f(tnow, Ynow)
 *  fnew = the value of the given differential equation at tnew
         = f(tnew, Ynew)
 *
 *  Pnew = prediction for the value of Ynew
 *  dt = abs(tnew - tnow)
 *  dtold = abs(tnow - told)
 */

//Information storage
struct simTime {
    double told;
    double tnow;
    double tnew;
    double dt;
    double dtold;
    double tol;
    double agrow;
    double ashrink;
    double dtmin;
    double dtmax;
    double endTime;
    double fold;
    double fnow;
    double fnew;
    double Yold;
    double Ynow;
    double Ynew;
    double Pnew;
    int stepsSinceRejection;
    int stepsRejected;
    int stepsAccepted;
} test;

//Define global variables
std::vector<double> errorIndicators(0);
std::vector<double> solutions(0);
std::vector<double> differencesDDY(0);
std::vector<double> differencesDDYSquared(0);
std::vector<double> timesTNew(0);

//Function declarations
void step(double fnow, double fold, double Ynow, double tnew, double tnow,
        double dtold, double dt, double(*f)(double t, double y), double told);
void advance();
void shiftvariables();
void printvector(std::vector<double>& vec);
void differentiate(double(*f)(double t, double y), double y0, double a,
        double b);
double f(double t, double y);
void checkdt();

int main() {
    differentiate(f, 0, 1, 5);
    cout << "Time values:" << endl;
    printvector(timesTNew);
    cout << "Solutions:" << endl;
    printvector(solutions);
    cout << "Differences between Prediction and Solution:" << endl;
    printvector(differencesDDY);
    return 0;
}

//Shift back all the variables to make way for the new values
void shiftvariables() {
    test.tnow = test.tnew;
    test.dtold = test.dt;
    test.Yold = test.Ynow;
    test.Ynow = test.Ynew;
    test.fold = test.fnow;
    test.fnow = test.fnew;
    advance();
}

//Ordinary differential equation to be solved
double f(double t, double y) {
    return pow(t, 2);
}

//Calculate the predicted and corrected solution at a chosen tnew
void step(double fnow, double fold, double Ynow, double tnew, double tnow,
        double dtold, double dt, double(*f)(double t, double y), double told) {
    //The calculation for Ynew requires integration. I first thought I would need to
    //  use my project 1 code to calculate the integration, but now I see in class we
    //  solved it analytically such that integration is not required:

    //Linear prediction of Ynew using Ynow and fnow
    double Pnew = Ynow + (dt * fnow) + (dt * dt / (2 * dtold)) * (fnow - fold);
    test.Pnew = Pnew;

    //Predict the value of f at tnew using Pnew
    double fnew = f(tnew, Pnew);
    test.fnew = fnew;

    //Calculate the corrected solution at tnew
    double interpolationFactor = fnew - (fnow + dt * (fnow - fold) / dtold);
    double integration = (dt / 6) * (2 * dt + 3 * dtold) / (dt + dtold);
    double Ynew = Pnew + interpolationFactor * integration;
    test.Ynew = Ynew;

    //Update the variables for the next round
    shiftvariables();
}

//Check the previous solution and choose a new dt to continue evaluation
void advance() {
    //The error indicator is the l2-norm of the prediction minus the correction
    double err_ind = sqrt(
            std::accumulate(differencesDDYSquared.begin(),
                    differencesDDYSquared.end(), 0));
    errorIndicators.push_back(err_ind);
    // Case where I reject the step and retry
    if (err_ind > test.tol && test.dt > test.dtmin) {
        ++test.stepsRejected;
        test.stepsSinceRejection = 0;
        test.dt = test.dt * 0.5;
        test.tnew = test.tnow + test.dt;
        checkdt();
    }
    // Cases where I accept the step and move forward
    else {
        ++test.stepsAccepted;
        ++test.stepsSinceRejection;
        solutions.push_back(test.Ynew);
        differencesDDY.push_back(abs(test.Pnew - test.Ynew));
        differencesDDYSquared.push_back(pow((test.Pnew - test.Ynew), 2));
        //Decrease dt
        if (err_ind >= 0.75 * test.tol) {
            test.dtold = test.dt;
            test.dt = (test.dt * test.ashrink);
            test.tnew = test.tnow + test.dt;
            checkdt();
        }
        //Increase dt
        else if (err_ind <= test.tol / 4) {
            if ((test.stepsRejected != 0) && (test.stepsSinceRejection >= 2)) {
                test.dt = (test.dt * test.agrow);
                test.tnew = test.tnow + test.dt;
                checkdt();
            } else if (test.stepsRejected == 0) {
                test.dt = (test.dt * test.agrow);
                test.tnew = test.tnow + test.dt;
                checkdt();
            }
        }
    }
}

//Check that the dt chosen by advance is acceptable
void checkdt() {
    if ((test.tnew < test.endTime) && (test.endTime - test.tnew < test.dtmin)) {
        cout << "Reached endTime." << endl;
    } else if (test.dt < test.dtmin) {
        test.dt = test.dtmin;
        test.tnew = test.tnow + test.dt;
        timesTNew.push_back(test.tnew);
        step(test.fnow, test.fold, test.Ynow, test.tnew, test.tnow, test.dtold,
                test.dt, f, test.told);
    } else if (test.dt > test.dtmax) {
        test.dt = test.dtmax;
        test.tnew = test.tnow + test.dt;
        timesTNew.push_back(test.tnew);
        step(test.fnow, test.fold, test.Ynow, test.tnew, test.tnow, test.dtold,
                test.dt, f, test.told);
    } else if ((test.tnew + test.dt) > test.endTime) {
        test.dt = test.endTime - test.tnew;
        test.tnew = test.tnow + test.dt;
        checkdt();
    } else if (((test.tnew + test.dt) < test.endTime)
            && ((test.tnew + 2 * test.dt) > test.endTime)) {
        test.dt = (test.endTime - test.tnew) / 2;
        test.tnew = test.tnow + test.dt;
        checkdt();
    }
    //If none of the above are satisfied, then the chosen dt
    //  is ok and proceed with it
    else {
        timesTNew.push_back(test.tnew);
        step(test.fnow, test.fold, test.Ynow, test.tnew, test.tnow, test.dtold,
                test.dt, f, test.told);
    }
}

//meta function to solve a differential equation, called only once
void differentiate(double(*f)(double t, double y), double y0, double a,
        double b) {
    //Set the starting conditions for the solving of the differential equation
    test.fnow = f(a, y0);
    test.endTime = b;
    test.Ynow = y0;
    //solutions.push_back(y0);
    timesTNew.push_back(a);

    //Set the constants
    test.ashrink = 0.8;
    test.agrow = 1.25;
    test.dtmin = 0.05;
    test.dtmax = 0.5;
    test.tol = 0.1;

    //Set fold = fnow for the first step
    test.fold = test.fnow;
    test.tnow = a;
    test.told = a - test.dtmin;
    test.dtold = abs(test.tnow - test.told);

    //Create the first prediction, which will then lead to correcting it with step
    advance();
}

// Takes a vector as its only parameters and prints it to stdout
void printvector(std::vector<double>& vec) {
    for (vector<double>::iterator it = vec.begin(); it != vec.end(); ++it) {
        cout << *it << ", ";
    }
    cout << "\n";
}

谢谢。

最佳答案

由于您正在使用递归,是否有可能您用完了堆栈内存,从而导致了段错误?如果您的应用递归次数过多,或者某些错误导致它无限递归,则可能会发生这种情况。

请注意,正如评论中所暗示的那样,调试器可能会帮助您确定是否属于这种情况。

关于微分求解器上的 C++ 段错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7799288/

相关文章:

c++ - 欧拉相机,在相机本地系统中绕 x 轴旋转

java - Java 中整数不带前导零的十进制表示形式

c++ - 如何使用新的 SQL Server Native Client 将 datetime 迁移到 datetime2

c++ - C++中进行良好性能优化的想法

c++ - 在 C++ 中创建数组

algorithm - 要插入 [a,b] 中的最小数字数,使得 2 个连续数字的 GCD 为 1

c# - 使用 P/Invoke 导致系统 AccessViolationException

c++ - 何时删除 try-catch block 中的指针

c++ - 当 memcpy() 比 memmove() 快时,什么是真正重要的情况?

asp.net - 64位机器上系统内存不足异常?