一元三次方程求解(equation)——二分+精度处理
题目描述:
有形如:ax^3+bx^2+cx+d=0 这样的一个一元三次方程。给出该方程中各项的系数(a,b,c,d 均为实数),并约定该方程存在三个不同实根(根的范围在-10000至10000之间),且根与根之差的绝对值>=1。要求由小到大依次在同一行输出这三个实根(根与根之间留有空格),并精确到小数点后4位。
输入格式
一行4个实数,分别表示三次项系数,二次项系数,一次项系数,常数项(每个的系数均保证绝对值在10^9以内)
输出格式
一行三个实数,为方程的3个实根,精确到小数点后4位
输入样例
1 0 -1 0
输出样例
-1.0000 0.0000 1.0000
分析:
本题的题眼是那个根与根之差的绝对值>=1,
所以开始以一为区间长度枚举根所处的区间,
若区间两端点乘积小于零,则说明,该区间内有一个根,
对该区间进行二分查找。
1 program equation;
2 var
3 i,j,n,m,k,l:longint;
4 kk,ll,r,rr:real;
5 a,b,c,d:real;
6
7 function cal(x:real):real;
8 var
9 i,j:longint;
10 begin
11 exit(trunc((a*x*x*x+b*x*x+c*x+d)*100000)/100000);
12 end;
13 {
14 这里的*100000/100000是为了处理精度问题,
15 由于精度问题,
16 最终有的数据会产生爆栈错误,原因就在这:
17 由于cal函数返回值趋近于0而不等于0,(可能为0.00000001,在real精度范围内)
18 那么,下面的find函数会递归调用进去,
19 对一个小区间不断的二分查找,
20 直到某个值可以使cal函数返还0值,
21 一般说来(解不是较整的数),这时是因为精度的略微偏差导致0的产生
22 (假如你用更精确的extended。。。可能爆栈更多。。。)
23 而,到达这个精度偏差的程度,
24 需要二分相当多次,
25 导致压栈压爆。。。。
26
27 这里的处理相当于一个小数部分的约等于操作,
28 截去小数5位后的部分,防止精度问题作怪。
29 }
30
31 function find(le,ri:real):real;
32 var
33 i:longint;
34 z:real;
35 begin
36 z:=(le+ri)/2;
37 if cal(z)=0 then exit(z)
38 else if cal(le)*cal(z)<0 then exit(find(le,z))
39 else if cal(z)*cal(ri)<0 then exit(find(z,ri));
40 end;//这里的二分和普通二分没有什么太大的不同之处
41
42 begin
43 assign(input,'equation.in');
44 reset(input);
45 assign(output,'equation.out');
46 rewrite(output);
47 readln(a,b,c,d);
48 l:=0;
49 for i:=-10001 to 10000 do//注意这里的枚举起点,要小心不要把-10000的可能漏掉
50 begin//l的作用是记录i+1点前面的点的带入值的正负(当然,你也可以在比较时重新求一遍,然后直接相乘
51 if cal(i+1)=0 then begin rr:=i+1;write(rr:0:4,' ');l:=0;end
52 //判断若i+1的整数为式子的解,直接输出
53 else if (l=0)and(cal(i+1)<0) then l:=-1
54 else if (l=0)and(cal(i+1)>0) then l:=1//若l=0,重新标记l
55 else if (l<0)and(cal(i+1)>0) then begin
56 r:=find(i,i+1);
57 write(r:0:4,' ');
58 l:=1;
59 end
60 else if (l>0)and(cal(i+1)<0) then begin
61 r:=find(i,i+1);
62 write(r:0:4,' ');
63 l:=-1;
64 end;//上面两部分,若符合条件,则区间内存在解,二分查找即可
65 end;
66 close(input);
67 close(output);
68 end.