C++ 求函数的根

标签 c++ numerical-methods bisection

我被要求找到下面函数的根

sin((a*x / (1 + pow(x, 2))) + 1) * atan(b*x - 1 / 2) + exp(-c*x) * atan(x)

对于两组值

  • a=10b=2c=0
  • a=4.5b=2.8c=1

但我没有得到我需要在其中找到根的开始结束 值。我该如何进行?

注:atan()表示tan的反函数。

代码片段:

double f(double x, double a, double b, double c)
{
    return sin((a*x / (1 + pow(x, 2))) + 1) * atan(b*x - 1 / 2) + exp(-c*x) * atan(x);
}

double RootFinder(double f(double, double, double, double), double a, double b, double c, double left, double right, double precision)
{
    double f_left = f(left, a, b, c), now = left + precision, f_right = f(now, a, b, c);
    while (f_left * f_right > 0 && now < right)
    {
        f_left = f_right;
        now += precision;
        f_right = now;
    }
    return now - precision / 2;
}

最佳答案

您的函数实现中存在错误。

atan(b*x - 1 / 2)

术语 1/2 进行整数除法并求值为 0,这可能不是您想要的。通常,在对 double 变量进行算术运算时使用 double 字面值。 pow() 函数确实将 (double, int) 作为它的重载之一,所以你在那里很好。它也有一个 (double, double) 重载,但如果你的指数实际上是一个整数,那么你不需要那个。

这是最简单的求根方法——二分法的简单实现(后来我注意到OP使用了二分法标签,完美)。

#include <iostream>
#include <cmath>
#include <random>

double f(const double x, const double a, const double b, const double c)
{
    return sin((a*x / (1.0 + pow(x, 2))) + 1.0) * atan(b*x - 1.0 / 2.0) + exp(-c*x) * atan(x);
}

double BisectionMethod(
    double f(double, double, double, double),
    const double a, const double b, const double c,
    const std::random_device::result_type entropy)
{
    std::mt19937 gen(entropy);
    static const auto lower_bound = -1.0;
    static const auto upper_bound =  1.0;
    std::uniform_real_distribution<> dis(lower_bound, upper_bound);

    auto pos_pt = dis(gen);
    auto neg_pt = dis(gen);

    while (f(pos_pt, a, b, c) < 0.0)
        pos_pt = dis(gen);

    while (f(neg_pt, a, b, c) > 0.0)
        neg_pt = dis(gen);

    static const auto about_zero_mag = 1E-8;
    for (;;)
    {
        const auto mid_pt = (pos_pt + neg_pt)/2.0;
        const auto f_mid_pt = f(mid_pt, a, b, c);
        if (fabs(f_mid_pt)  < about_zero_mag)
            return mid_pt;

        if (f_mid_pt >= 0.0)
            pos_pt = mid_pt;
        else
            neg_pt = mid_pt;
    }
}

int main()
{
    double a, b, c;
    std::random_device rd;
    static const auto entropy = rd();

    a =10, b = 2.0, c = 0.0;
    const auto root1 = BisectionMethod(f, a, b, c, entropy);
    std::cout << "a = " << a << ", b = " << b << ", c = " << c << std::endl;
    std::cout << "Found root: (" << root1 << ", " << f(root1, a, b, c) << ")" << std::endl;

    a =4.5, b = 2.8, c = 1.0;
    const auto root2 = BisectionMethod(f, a, b, c, entropy);
    std::cout << "a = " << a << ", b = " << b << ", c = " << c << std::endl;
    std::cout << "Found root: (" << root2 << ", " << f(root2, a, b, c) << ")" << std::endl;
}

Output :

g++ -O3 -std=c++11 -Wall -Wextra -pedantic main.cpp -o root && ./root
a = 10, b = 2, c = 0
Found root: (0.143042, -2.12425e-09)
a = 4.5, b = 2.8, c = 1
Found root: (0.136172, 5.81247e-09)

输出会随着每次运行而改变,因为这使用了 RNG。从视觉上看,输出看起来是正确的。

代码假定根以 -1.0 和 1.0 为界,这在您的情况下是正确的。如果您希望它更通用,那么您需要添加处理溢出和检查 nans 的逻辑。如果根不在 -1.0 和 1.0 之间,这将永远循环。尽管如此,它解决了这个问题中的具体问题,并且是更普遍的事情的开始。

另请注意,您的函数有多个根,而给定的代码只找到一个根。

编辑:清理代码。添加了 entropy 作为 BisectionMethod() 的参数,以便它是可重现的,如果我们谈论数值方法,这似乎是可取的。

关于C++ 求函数的根,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28379801/

相关文章:

c++ - 派生类的this指针是如何找到成员函数的?

python-3.x - 在没有软件包的情况下使用 Python 中的 Runge Kutta 4 阶求解洛伦兹模型

python - 二分索引搜索 - 为什么与 len(a) 比较?

algorithm - Python二分搜索逻辑错误-返回不准确的结果

c++ - LINK1104 的 Opencv 构建错误

c++ - boost::asio::io_service 在 win_mutex 锁中崩溃

c++ - boost ASIO 串行写入十六进制值

c - 为什么这样实现 log_sum 效率更高?

python - 梯度下降陷入局部最小值?

python - 使用二分法查找列表中 f(x) 变化的位置(在 Python 中)