C++: Ньютон Рафсон и функции перегрузки

Я написал простую реализацию алгоритма поиска корня Ньютона-Рафсона, который принимает начальное предположение init, унарную функцию f и допуск tol в качестве аргументов, как показано ниже:

bool newton_raphson(double& init,
                    double(*f)(double),
                    double tol){
    const int max_iter = 10000;
    double next_x, soln = init;
    int i = 0;

    while(++i < max_iter){
        next_x = soln - f(soln)/fp_x(f, soln);
        if(fabs(next_x - soln) < tol){
            init = next_x;
            return true;
        }
        soln = next_x;
    }
    return false;
}

double fp_x(double(*f)(double),
            double x){
    const double h = 0.000001;
    return (f(x + h) - f(x - h))/2.0/h;
}

Мой вопрос: хотя это прекрасно работает для унарных функций, я хотел бы изменить реализацию, чтобы она работала для функций f, которые имеют более одного параметра, но все параметры, кроме одного, имеют постоянные значения. Чтобы уточнить: если у меня есть функция f(x) = 3x + 2, как показано ниже

double f(double x){
    return (3*x + 2);
}

Тогда моя реализация работает. Однако я также хотел бы, чтобы это работало для любых функций с любым заданным количеством аргументов, но только первый аргумент является переменным. Итак, если у меня есть функция f(x,y) = 3x + 2y

double f(double x, double y){
    return (3*x + 2*y);
}

Я хотел бы найти корень f(x,2) или f(x,3), используя ту же функцию, и так далее для n аргументов, а не только один или два (пожалуйста, не обращайте внимания на то, что функции, которые я показал в пример — простые линейные функции, это просто пример). Есть ли способ реализовать функцию для различного количества аргументов или мне нужно написать реализацию для каждого случая?

Спасибо,

НАКС

ПРИМЕЧАНИЕ

Как вы уже поняли, этот вопрос на самом деле не о ньютоне-рафсоне, но будет проще, если я использую его в качестве примера для фактического вопроса, который представляет собой единую реализацию для функций с разным количеством аргументов.

ОБНОВЛЕНИЕ

В нескольких ответах ниже для решения проблемы используются std::bind и std::function, что на самом деле лучше отвечает на мой вопрос, чем выбранный ответ; тем не менее, они представляют собой библиотечные классы/функции С++ 11 (что, не поймите меня неправильно, я настоятельно призываю каждого программиста С++ идти вперед и учиться), и на момент написания этой статьи я столкнулся с некоторыми проблемами. используя их; Eclipse Juno, использующий g++ 4.7 (совместимый с C++11), все еще каким-то образом не смог распознать std::function, и поэтому я решил пойти и придерживаться проверенного ответа ниже, который также прекрасно работает.


person naxchange    schedule 18.03.2013    source источник
comment
если вы найдете корни до n, то разве это не справедливо, что вы должны возвращать все эти корни через контейнер? Или вы хотите вычислить 3-й корень, например, при каждом вызове?   -  person Koushik Shetty    schedule 18.03.2013
comment
Я думаю, что вы приняли ответ слишком быстро.   -  person Apprentice Queue    schedule 18.03.2013


Ответы (4)


Я думаю, вы запрашиваете вариативные функции:

Вариативная функция — функция, объявленная со списком параметров, заканчивающимся многоточием (...) — может принимать различное количество аргументов разных типов. Функции Variadic гибки, но они также опасны. Компилятор не может проверить, что данный вызов функции с переменным числом аргументов передает соответствующее количество аргументов или что эти аргументы имеют соответствующие типы. Следовательно, вызов во время выполнения функции с переменным числом аргументов, которая передает недопустимые аргументы, приводит к неопределенному поведению. Такое неопределенное поведение может быть использовано для запуска произвольного кода. Отсюда:

https://www.securecoding.cert.org/confluence/display/cplusplus/DCL31-CPP.+Do+not+define+variadic+functions

Однако, как было сказано выше, с ними возникает ряд проблем.

В частности, это работает только во время времени компиляции!

Однако, если вы заинтересованы в его реализации, вот статья с хорошим примером:

http://www.informit.com/guides/content.aspx?g=cplusplus&seqNum=138

ОБНОВИТЬ:

IMO, я думаю, вам лучше определить функции, которые принимают аргументы структуры или объекта (т. е. общий объект функции), и написать функции, которые работают с этими аргументами явно.

Другой вариант — выполнить некоторое отражение во время компиляции, что было бы полезно, но в таком примере, как этот, слишком много хлопот. Кроме того, "отражение" в C++ не является "истинным" отражением, а скорее плохой и неполной его реализацией.

person jrd1    schedule 18.03.2013

Для того, что вы пытаетесь сделать здесь, вы ищете std::bind (или, если вы имеете дело с компилятором C++03, std::bind1st и std::bnd2nd).

Это позволит вам «привязать» значения к другим параметрам, оставив вам функцию (технически, функциональный объект), для которой требуется только один параметр.

В идеале вы хотели бы что-то вроде этого:

double f(double x, double y) { 
     return 3*x + 2*y;
}

double init = 1.0;

newton_raphson(init, std::bind2nd(f, 3), 1e-4);

К сожалению, в реальном использовании все не так просто — для работы с std::bind2nd вы не можете использовать настоящую функцию; вместо этого вам нужно использовать функциональный объект, и он должен быть производным от std::binary_function.

std::bind немного более гибкий, так что вы почти наверняка захотите использовать его (если это вообще возможно).

person Jerry Coffin    schedule 18.03.2013

Я использовал ваш вопрос как способ заставить себя изучить вариативный шаблон С++ 11, вот рабочий пример.

template< typename... Ts >
double f( Ts... Vs ) {
    double array[] = { Vs... };
    int numArg = sizeof...( Vs );
    switch (numArg) {
    case 1:
        return 3 * array[0] + 2;
    case 2:
        return 3 * array[0] + 2 * array[1];
    case 3:
        return 3 * array[0] + 2 * array[1] + 1 * array[3];
    ....
    default:
        return 0.0;
    }
}

template< typename... Ts >
double newton_raphson( double &init, double tol,
                       double (*func) ( Ts... Vs ), Ts... Vs ) {
    return func( Vs... );
}

вы можете назвать это как

newton_raphson( &init, 1.0, f, 1.0, 2.0, 3.0, 4.0, 5.0 );
person yngccc    schedule 18.03.2013

Вы можете использовать std::bind и std::function. Тип std::function<double(double)> представляет собой функционал, который принимает значение типа double и возвращает значение типа double. Точно так же std::function<double(int,int)> для функционала, принимающего 2 целых числа и возвращающего двойное число.

#include <functional>

bool newton_raphson(double& init,
                    std::function<double(double)>& f,
                    double tol){
    const int max_iter = 10000;
    double next_x, soln = init;
    int i = 0;

    while(++i < max_iter){
        next_x = soln - f(soln)/fp_x(f, soln);
        if(fabs(next_x - soln) < tol){
            init = next_x;
            return true;
        }
        soln = next_x;
    }
    return false;
}

double myfunction(double x, double y){
    return (3*x + 2*y);
}
double fp_x(std::function<double(double)> f, double x) {
    ...
}
...
double d = 1.0;
// Here we set y=2.5 and we tell bind that 1st parameter is unbounded
// If we wanted to switch and set x=2.5 and let y be unbounded, then
// we would use  (&myfunction, 2.5, std::placeholders::_1)
newton_raphson(d, std::bind(&myfunction, std::placeholders::_1, 2.5) , 1e-6);
...
person Apprentice Queue    schedule 18.03.2013
comment
Спасибо за этот ответ, на самом деле я предпочитаю этот, потому что он, кажется, напрямую касается моего вопроса, но у меня есть пара вопросов: во-первых, должна ли шаблонная переменная function иметь идентификатор (например, в ее объявлении внутри fp_x )? А если нет, то каким будет его синтаксис? Кроме того, я пытаюсь это сделать с помощью g++, и использование function<double(double)> дает мне ошибку: Symbol 'function' could not be resolved. Ты знаешь почему? - person naxchange; 18.03.2013
comment
Я также пробовал это, используя g++ 4.7 в Ubuntu 12.10, используя -std=c++11, и он все еще не решен. Я использую eclipse juno - person naxchange; 18.03.2013
comment
@naxchange, да, к сожалению, это C++11. Я полагаю, вы включили «функциональный»? - person Apprentice Queue; 18.03.2013