【问题标题】:How can I define a function using an object created in the main function (c++)?如何使用在主函数 (c++) 中创建的对象定义函数?
【发布时间】:2019-11-08 03:58:16
【问题描述】:

我正在尝试编写一个程序,该程序将为我提供 c++ 中内核估计器的交叉验证带宽(大约有 20,000 个数据点,因此 Matlab 太慢了)。我通过在留一法估计器的目标的导数上使用割线算法来做到这一点。我的问题是这个函数将数据作为参数,这些数据是从 csv 文件中拉到主函数中的。

割线算法的一个参数是一个函数,它接受一个双精度并返回一个双精度,但我为目标导数编写的函数必须采用大量其他在数学上我们会考虑参数的废话(例如数据、核函数的选择等)。

我需要能够编写一个函数,该函数使用定义目标的函数,放入主函数从文件中提取的数据,并将双变量作为其唯一输入。有没有办法做到这一点?

double cvobjective(double data[], int n, double (*k)(double), double (*kd)(double), double h)
{
    double cvob = 0;
    double xi;
    double xj;
    for(int i = 0; i < n; ++i)
    {
        xi = data[i];
        double sumki = 0;
        double sumkdi = 0;
        for(int j = 0; j < n; ++j) //find sum of k((xj-xi)/h) and k'((xj-xi)/h)*(xj-xi)
        {
            xj = data[j];
            sumki = sumki + k((xj-xi)/h);
            sumkdi = sumkdi + kd((xj-xi)/h)*(xj-xi);
        }
        sumki = sumki-k(0);//gets rid of the terms where i=j
        sumkdi = sumkdi-kd(0);
        cvob = cvob - reciprocal(sumki)*(reciprocal(h)*sumki+reciprocal(pow(h,2))*sumkdi);
    }
    return cvob;
}

double secantmethod(double (*obj)(double), double init, double tolerance, int giveup)
{
    double x = init;
    double old = init+1;
    double newp;
    double fold = obj(old);
    double fnew;

    for(int i=0;i<giveup;++i)
    {
        fnew = obj(x);
        if(abs(fnew-fold)<tolerance)
        {
            cout << "Objective values get too close after " << i << " iterations." << endl;
            break;
        }
        newp = newp - (x-old)*reciprocal(fnew-fold)*fnew;
        old = x;
        x = newp;
        cout << "Estimate is currently: " << x << endl;
        fold = fnew;

        if(abs(fnew)<tolerance)
            break;
        if(i == giveup - 1)
            cout << "Secant algorithm did not converge." << endl;
    }
    return newp;
}

int main()
{
    const int N = 19107;
    double incomes[N];
    std::ifstream ifile("incomes.csv", std::ios::in);
    std::vector<double> scores;

    //check to see that the file was opened correctly:
    if (!ifile.is_open()) {
        std::cerr << "There was a problem opening the input file.\n";
        exit(1);//exit or do additional error checking
    }

    double num = 0.0;
    //keep storing values from the text file so long as data exists:
    while (ifile >> num) {
        scores.push_back(num);
    }

    //verify that the scores were stored correctly:
    for (int i = 0; i < scores.size(); ++i) {
        incomes[i]=scores[i];
    }

    double sv = silverman(incomes,N);
    double cvbandwidth = secantmethod(cvobj,sv,0.000001,100);
    cout << setprecision(10) << cvbandwidth << endl;
    return 0;
}

显然,我省略了一些不重要的外围功能的代码。我考虑过是否可以更改 secantmethod 算法,以便它期望采用一个函数,该函数将 cvobjective 的所有内容作为其输入,但我并不清楚我将如何做到这一点.

理想情况下,我可以在 main 中创建一个函数,以便收入数组在该函数的范围内,但要么我不正确理解 lambda,要么它们并不特别适合此目的。如果做不到这一点,如果有办法以上述方式更改 secantmethod,那也可以。

编辑:在上面的 cvobj 中没有定义,目前用作占位符。我希望它是这样的

double cvobj(double h)
return cvobjective(incomes,N,normpdf,normpdfdiff,h);

但很明显,当我尝试这样做时,它会抱怨收入和 N 不在函数的范围内。

【问题讨论】:

  • 所以缺少的部分是secantmethod(cvobj,sv,0.000001,100) 调用中的cvobjcvobj(x) 的值究竟应该是什么?
  • @aschepler 哦,对不起,我应该说清楚的。 cvobj 将(在理想世界中)c double cvobj(double h) return cvobjective(incomes,N,normpdf,normpdfdiff,h); 但很明显,当我尝试这样做时,IDE 抱怨收入和 N 不在函数的范围内
  • 是否可以通过创建一个采用收入数组的类并按照编辑建议的方式定义一个公共函数来做到这一点?

标签: c++ parameter-passing


【解决方案1】:

所以我想我找到了一个解决方法,就是定义类

class kernelwdata
{
public:
    double observations[N];
    double cvobj(double h)
    {
        return cvobjective(observations,N,normpdf,normpdfdiff,h);
    }
};

并更改 secantmethod 使其将 kernelwdata 作为输入。现在之前显示的代码看起来像

double cvobjective(double data[], int n, double (*k)(double), double (*kd)(double), double h)
{
    double cvob = 0;
    double xi;
    double xj;
    for(int i = 0; i < n; ++i)
    {
        xi = data[i];
        double sumki = 0;
        double sumkdi = 0;
        for(int j = 0; j < n; ++j) //find sum of k((xj-xi)/h) and k'((xj-xi)/h)*(xj-xi)
        {
            xj = data[j];
            if(j==i)
                xj=xi+1;
            sumki = sumki + k((xj-xi)/h);
            sumkdi = sumkdi + kd((xj-xi)/h)*(xj-xi);
        }
        sumki = sumki-k(1/h);//gets rid of the terms where i=j
        sumkdi = sumkdi-kd(1/h);
        cvob = cvob - reciprocal(sumki)*(reciprocal(h)*sumki+reciprocal(pow(h,2))*sumkdi);
    }
    return cvob;
}

class kernelwdata
{
public:
    double observations[N];
    double cvobj(double h)
    {
        return cvobjective(observations,N,normpdf,normpdfdiff,h);
    }
};

double secantmethod(kernelwdata obj, double init, double tolerance, int giveup)
{
    double x = init;
    double old = init+1;
    double newp;
    double fold = obj.cvobj(old);
    double fnew;

    for(int i=0;i<giveup;++i)
    {
        fnew = obj.cvobj(x);
        cout << "fold is " << fold << " and fnew is " << fnew << endl;
        if(abs(fnew-fold)<tolerance)
        {
            cout << "Objective values get too close after " << i << " iterations." << endl;
            break;
        }
        newp = newp - (x-old)*reciprocal(fnew-fold)*fnew;
        old = x;
        x = newp;
        cout << "Estimate is currently: " << x << endl;
        fold = fnew;

        if(abs(fnew)<tolerance)
            break;
        if(i == giveup - 1)
            cout << "Secant algorithm did not converge." << endl;
    }
    return newp;
}

double silverman(double data[], int n)
{
    double mean = 0;
    for(int counter = 0; counter < n; ++counter)
        mean = mean+data[counter]/n;
    double sigmahat = 0;
    for(int counter = 0; counter < n; ++counter)
    {
        sigmahat = sigmahat + pow(data[counter]-mean,2);
    }
    sigmahat = sigmahat/(n-1);
    sigmahat = sqrt(sigmahat);
    double m = (double) n;
    return sigmahat*pow(m,-0.2);
}

int main()
{
    kernelwdata incomes;
    std::ifstream ifile("incomestest.csv", std::ios::in);
    std::vector<double> scores;

    //check to see that the file was opened correctly:
    if (!ifile.is_open()) {
        std::cerr << "There was a problem opening the input file.\n";
        exit(1);//exit or do additional error checking
    }

    double num = 0.0;
    //keep storing values from the text file so long as data exists:
    while (ifile >> num) {
        scores.push_back(num);
    }

    //verify that the scores were stored correctly:
    for (int i = 0; i < scores.size(); ++i) {
        incomes.observations[i]=scores[i];
    }

    //double sv = silverman(incomes.observations,N);
    double cvbandwidth = secantmethod(incomes,10,0.000001,100);
    cout << "The cross-validation bandwidth is: " << setprecision(10) << cvbandwidth << endl;
    return 0;
}

我现在正在争论它给我的输出(很肯定是某个地方的数学问题),而不是我还不能编译的东西。

【讨论】:

    猜你喜欢
    • 2013-09-19
    • 1970-01-01
    • 1970-01-01
    • 2017-08-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-09-16
    相关资源
    最近更新 更多