2

我写了一个牛顿拉夫森求根算法的简单实现,它以初始猜测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 个参数,而不仅仅是一个或两个(请忽略我在该示例是简单的线性函数,这只是一个示例)。有什么方法可以为不同数量的参数实现函数,还是我必须为每种情况编写一个实现?

谢谢,

纳克斯

笔记

正如您现在所知道的那样,这个问题实际上并不是关于 newton-raphson,但是如果我将它用作实际问题的示例,它会变得更容易,这是针对不同数量参数的函数的单一实现。

更新

下面的一些答案使用std::bindstd::function解决问题,实际上比选择的答案更好地解决了我的问题;但是,它们是 c++11 库类/函数(请不要误会,这是我强烈敦促每个 c++ 程序员继续学习的东西),在撰写本文时,我正面临一些问题使用它们; 使用 g++ 4.7(符合 c++11)的 Eclipse Juno 仍然无法识别std::function,因此我决定继续使用下面的检查答案,这也很好用。

4

4 回答 4

2

我认为您要的是可变参数函数:

可变参数函数 - 使用以省略号 (...) 结尾的参数列表声明的函数 - 可以接受不同数量的不同类型的参数。可变参数函数很灵活,但它们也很危险。编译器无法验证对可变参数函数的给定调用是否传递了适当数量的参数,或者这些参数是否具有适当的类型。因此,对传递不适当参数的可变参数函数的运行时调用会产生未定义的行为。这种未定义的行为可以被利用来运行任意代码。从这里:

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++ 中的“反射”不是“真正的”反射,而是一种糟糕且不完整的实现。

于 2013-03-18T04:49:11.123 回答
2

对于您在这里尝试做的事情,您正在寻找的是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更加灵活,因此您几乎可以肯定要使用它(如果可能的话)。

于 2013-03-18T04:50:03.400 回答
0

您可以使用std::bindstd::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);
...
于 2013-03-18T05:02:15.143 回答
0

我用你的问题来强迫自己学习 C++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 );
于 2013-03-18T07:42:11.763 回答