[问题描述]
有形如:ax3+bx2+cx+d=0这样的一个一元三次方程。给出该方程中各项的系数(a,
b,c,d 均为实数),并约定该方程存在三个不同实根(根的范围在-100至100之间),
且根与根之差的绝对值≥1。
要求由小到大依次在同一行输出这三个实根(根与根之间留有空格),并精确到小数点
后2位。
提示;记方程f(x)=0,若存在2个数x1和x2,且x1 < x2,f(x1)*f(x2) < 0,
则在(x1,x2)之间一定有一个根。
[样例]
输入: 1 -5 -4 20
输出:-2.00 2.00 5.00
问题分析
根据题意,三次方程 ax^3+bx^2+cx+d=0在区间[-100,100]有3个不等实根,不妨设这3个实根分别为x1,x2,x3,且x1<x2<x3,则方程可表示为a(x-x1)(x-x2)(x-x3)=0;于是,求解这3个实根,可转化为求函数f(x)= a(x-x1)(x-x2)(x-x3)图像与x轴交点的横坐标。如图:
二分法:
1. 确定根的区间
考虑在什么样的区间内会有根,由于题目给出了所有的根都在-100到100之间,且根与根之间的差不小于1的限制条件,可知,在[-100,100],[-99,-98],…[99,100],[100,100]这201个区间内,每个区间内至多只能存在一个根,这样除去区间[100,100]外,其他区间[a,a+1],要么f(a)=0,要么f(a)×f(a+1)<0时这个方程在此区间内才有解。
2. 求方程的根
确定了方程f(x)=0在区间(a,b)内如果有且只有一个根,那么我们可以有逐步缩小根可能存在的范围的方法确定出某精度下根的数值。本题规定根的精度是0.01,下面采用二分法求区间(a,b)内方程的根,过程如下:
(1) 取当前可能存在解的区间(a,b);
(2) 若a+0.001>b或 f((a+b)/2)=0,则可确定根为(a+b)/2并退出过程;
(3) 若f(a)×f((a+b)/2)<0,则根在区间(a, (a+b)/2)中,故对区间(a, (a+b)/2)重复该过程;
(4) 若f(a)× f((a+b)/2)>0,则必然有 f((a+b)/2)×f(b)<0,也就是说根在((a+b)/2,b)中,应该对此区间重复该过程。
二分法求根的算法如下:
#include<iostream>
#include<cmath>
using namespace std;
double x[3];
double a,b,c,d,u,v;
int i,t;
double f(double x)
{
double temp;
temp=((a*x+b)*x+c)*x+d;
return temp;
}
int main()
{
while(cin>>a>>b>>c>>d)
{
t=-1;
for(i=-100;i<=100;i++)
{
u=double(i);
v=u+0.99999;
if(fabs(f(u))<0.00001||f(u)*f(v)<=0)
{
t++;
if(fabs(f(u))<0.00001)
x[t]=u;
else
{
while((u+0.001<v)&&fabs(f((u+v)/2))>=0.00001)
{
if(f(u)*f((u+v)/2)<0)
v=(u+v)/2;
else
u=(u+v)/2;
}
x[t]=(u+v)/2;
}
}
}
cout.setf(ios::fixed);
cout.precision(2);
cout<<x[0]<<" "<<x[1]<<" "<<x[2]<<endl;
}
return 0;
}
注意:由于实数运算的误差,判断x是否满足方程f(x)=0应用条件表达式 fabs(f(x))<0.00001来判断,否则将失根。