C ++:newton raphson和重载函数

时间:2013-03-18 04:31:44

标签: c++ function iteration newtons-method

我写了一个牛顿raphson根寻找算法的简单实现,它采用初始猜测init,一元函数f和容差tol作为参数,如下所示:< / p>

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

注意

正如你现在所知道的那样,这个问题实际上并不是关于newton-raphson的,但是如果我把它作为实际问题的一个例子,它会更容易,这是对不同数量的参数的函数的单个实现

更新

下面的几个答案使用std::bindstd::function来解决问题,这实际上比选择的答案更能解决我的问题;然而,它们是c ++ 11库类/函数,(我不会误解,是我强烈要求每个c ++程序员继续学习)并且在撰写本文时,我遇到了一些问题使用它们; Eclipse Juno使用g ++ 4.7(符合c ++ 11标准)仍然无法识别std::function,因此我决定坚持使用下面的检查答案,这也很有效。

4 个答案:

答案 0 :(得分:2)

我认为你要求的是可变函数:

  

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

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,我认为你最好定义带有结构或对象参数的函数(即一般函数对象),并编写对这些参数明确的函数。

另一种选择是做一些编译时reflection - 这将是有用的,但在这样的例子中做太麻烦了。另外,C ++中的“反射”并非“真实”的反映,而是一种糟糕且不完整的实现。

答案 1 :(得分:2)

对于你在这里尝试做的事情,你正在寻找的是std::bind(或者,如果你正在处理一个C ++ 03编译器,std::bind1st和{{1 }})。

这些将允许您将值“绑定”到其他参数,为您提供一个只需要一个参数的函数(技术上,一个函数对象)。

你理想的是这样的事情:

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更加灵活,所以你几乎肯定想要使用它(如果可能的话)。

答案 2 :(得分:0)

您可以使用std::bindstd::function。类型std::function<double(double)>表示一个函数,它接受一个double并返回一个double。类似地,std::function<double(int,int)>用于获取2个int的函数并返回double。

#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);
...

答案 3 :(得分: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 );