如果给出一系列非线性方程系数和一些范围,我们怎样才能在给定的范围内找到方程的根?
例如:等式
因此系数数组将是 a 的数组。让我们说等式是
然后系数数组为{ 1, -5, -9, 16 }
。
正如Google所说,首先我们需要将给定的函数(实际上是等式)转换为其他函数。例如。如果给定的等式是y = f(x)
,我们应该定义其他函数x = g(x)
,然后执行算法:
while (fabs(f(x)) > etha)
x = g(x);
找出根。
问题是:如何使用系数数组定义g(x)
并仅给出范围?
问题是:当我像这样定义g(x)
时
或
对于给定的等式,x
的任何起始值都将引导我进入第二个等式的根。并且没有人会给我另外两个(根是{ -2.5, 1.18, 6.05 }
而我的代码只给1.18
。)
我的代码是这样的:
float a[] = { 1.f, -5.f, -9.f, 16.f }, etha = 0.001f;
float f(float x)
{
return (a[0] * x * x * x) + (a[1] * x * x) + (a[2] * x) + a[3];
}
float phi(float x)
{
return (a[3] * -1.f) / ((a[0] * x * x) + (a[1] * x) + a[2]);
}
float iterationMethod(float a, float b)
{
float x = (a + b) / 2.f;
while (fabs(f(x)) > etha)
{
x = phi(x);
}
return x;
}
因此,调用iterationMethod()
传递范围{ -3, 0 }
,{ 0, 3 }
和{ 3, 10 }
将提供三次1.18
个数字。
我错在哪里,我应该采取什么行动让它正常运作?
UPD1 :我不需要任何第三方库。
UPD2 :我完全需要“简单迭代”算法。
答案 0 :(得分:4)
更传统的根查找算法之一是Newton's method。迭代步骤涉及找到函数的一阶近似的根
因此,如果我们有一个函数'f'并且位于x0
点,则线性fisrt阶近似将是
f_(x) = f'(x0)*(x - x0) + f(x0)
和相应的近似根x'
是
x' = phi(x0) = x0 - f(x0)/f'(x0)
(请注意,你需要使用派生函数,但是对于多项式来说它应该很容易获得)
牛顿方法的好处很容易实现,而且通常非常快。糟糕的是,有时它表现不佳:该方法在f'(x) = 0
点上失败,某些函数中的某些输入可能会发散(因此您需要检查并在需要时重新启动)。
答案 1 :(得分:3)
您在评论中发布的link解释了为什么您无法使用此算法找到所有根 - 它只会在根周围|phi'(x)| < 1
收敛到根。多项式的任何根都不是这种情况;对于大多数起点,迭代将最终在中间根部反弹,最终偶然接近它;无论它在哪里开始,它几乎肯定永远不会与其他根源接近。
要查找所有三个根,您需要一个更稳定的算法,例如Newton's method(在您链接的tutorial中也会对此进行描述)。这也是一种迭代方法;您可以使用迭代f(x)
找到x -> x - f(x)/f'(x)
的根。这仍然不能保证收敛,但收敛条件要宽松得多。对于多项式,它可能看起来像这样:
#include <iostream>
#include <cmath>
float a[] = { 1.f, -5.f, -9.f, 16.f }, etha = 0.001f;
float f(float x)
{
return (a[0] * x * x * x) + (a[1] * x * x) + (a[2] * x) + a[3];
}
float df(float x)
{
return (3 * a[0] * x * x) + (2 * a[1] * x) + a[2];
}
float newtonMethod(float a, float b)
{
float x = (a + b) / 2.f;
while (fabs(f(x)) > etha)
{
x -= f(x)/df(x);
}
return x;
}
int main()
{
std::cout << newtonMethod(-5,0) << '\n'; // prints -2.2341
std::cout << newtonMethod(0,5) << '\n'; // prints 1.18367
std::cout << newtonMethod(5,10) << '\n'; // prints 6.05043
}
还有许多其他算法可以找到根; here是开始学习的好地方。