Как я могу определить функцию, используя объект, созданный в главной функции (c ++)? - PullRequest
0 голосов
/ 08 ноября 2019

Я пытаюсь написать программу, которая даст мне пропускную способность для перекрестной проверки оценщика ядра в 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, чтобы массив доходности входил в сферу действия этой функции, но либо я не правильно понимаю лямбды, либо они не особенно подходят для этогоцель. В противном случае, если есть способ изменить secantmethod описанным выше способом, это также сработает.

EDIT: В приведенном выше cvobj не определен и в настоящее время служит в качестве заполнителя. Мне бы хотелось, чтобы это было что-то вроде

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

, но, очевидно, когда я пытаюсь это сделать, он жалуется, что доходы и N не входят в сферу действия функции.

1 Ответ

0 голосов
/ 08 ноября 2019

Так что я думаю, что нашел обходной путь, который должен был определить класс

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;
}

, и я сейчас спорю с выводом, который он мне дает (уверен, что где-то математическая задача), а не с тем, что я могу скомпилировать еще.

...