Я написал простую реализацию алгоритма поиска корня Ньютона-Рафсона, которая принимает первоначальное предположение 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 аргументов, а не только для одного или двух (пожалуйста, игнорируйте идею, что функции, которые я показывал в Примером являются простые линейные функции, это всего лишь пример). Есть ли способ реализовать функцию для различного числа аргументов, или мне нужно написать реализацию для каждого случая?
Спасибо,
NAX
НОТА
Как вы уже могли сказать, этот вопрос на самом деле не относится к ньютону-Рафсону, но облегчает его использование в качестве примера для реального вопроса, который представляет собой единую реализацию для функций с различным числом аргументов.
ОБНОВИТЬ
Несколько ответов ниже использовать std::bind
а также std::function
решить проблему, которая на самом деле лучше отвечает на мой вопрос, чем на выбранный ответ; тем не менее, они являются библиотечными классами / функциями c ++ 11 (что, не поймите меня неправильно, я настоятельно призываю каждого программиста на c ++ идти дальше и учиться), и во время написания этой статьи я столкнулся с некоторыми проблемами используя их; Eclipse Juno, использующий g ++ 4.7 (который совместим с c ++ 11), все еще почему-то не смог распознать std::function
, и поэтому я решил пойти и придерживаться проверенного ответа ниже, который также хорошо работает.
Я думаю, что вы просите о вариативных функциях:
Variadic функция — функция, объявленная с окончанием списка параметров
с многоточием (…) — может принимать различное количество аргументов
разные типы. Вариативные функции являются гибкими, но они также
опасные. Компилятор не может проверить, является ли данный вызов переменной
Функция передает соответствующее количество аргументов или что те
аргументы имеют соответствующие типы. Следовательно, вызов времени выполнения
Функция variadic, которая передает неуместные аргументы, дает неопределенный
поведение. Такое неопределенное поведение может быть использовано для запуска произвольно
код.
Отсюда:
Однако, как указано выше, с ними есть ряд проблем.
В частности, это работает только для время компиляции!
Однако, если вы заинтересованы в его реализации, вот статья с хорошим примером:
http://www.informit.com/guides/content.aspx?g=cplusplus&SEQNUM = 138
ОБНОВИТЬ:
ИМО, я думаю, что вам лучше определить функции, которые принимают аргументы структуры или объекта (то есть объект общего назначения), и писать функции, которые работают с этими аргументами эксплицитно.
Другой вариант — сделать некоторое время компиляции. отражение — что было бы полезно, но в таком примере это слишком сложно. Кроме того, «отражение» в C ++ — это не «настоящее» отражение, а скорее плохое и неполный реализация этого.
То, что вы пытаетесь сделать здесь, то, что вы ищете, 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
немного более гибким, так что это то, что вы почти наверняка хотите использовать вместо этого (если это вообще возможно).
Я использовал ваш вопрос как способ заставить себя изучить шаблон C ++ 11 variadic, вот рабочий пример.
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 );
Ты можешь использовать std::bind
а также std::function
, Тип std::function<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);
...