在Matlab中求解系数非常小的二次方程

在Matlab中求解系数非常小的二次方程,matlab,numerical-methods,Matlab,Numerical Methods,我正在matlab中实现一个代码,使用预解公式求解二次方程: 以下是代码: clear all format short a=1; b=30000000.001; c=1/4; rdelta=sqrt(b^2-4*a*c); x1=(-b+rdelta)/(2*a); x2=(-b-rdelta)/(2*a); fprintf(' Roots of the polynomial %5.3f x^2 + %5.3f x+%5.3f \n',a,b,c) fprintf ('x1= %

我正在matlab中实现一个代码,使用预解公式求解二次方程:

以下是代码:

clear all 
format short 
a=1; b=30000000.001; c=1/4; 
rdelta=sqrt(b^2-4*a*c); 
x1=(-b+rdelta)/(2*a);
x2=(-b-rdelta)/(2*a);
fprintf(' Roots of the polynomial %5.3f x^2 + %5.3f x+%5.3f \n',a,b,c)  
fprintf ('x1= %e\n',x1)
fprintf ('x2= %e\n\n',x2)
valor_real_x1= -8.3333e-009;
valor_real_x2= -2.6844e+007;

error_abs_x1 = abs (valor_real_x1-x1);
error_abs_x2 = abs (valor_real_x2-x2);

error_rel_x1 = abs (error_abs_x1/valor_real_x1);
error_rel_x2 = abs (error_abs_x2/valor_real_x2);

fprintf(' absolute_errorx1 = |real value - obtained value| = |%e - %e| = %e \n',valor_real_x1,x1,error_abs_x1)  
fprintf(' absolute_errorx2 = |real value - obtained value| = |%e - %e| = %e \n\n',valor_real_x2,x2,error_abs_x2) 

fprintf(' relative error_x1 = |absolut error / real value| = |%e / %e| = %e \n',error_abs_x1,valor_real_x1,error_rel_x1 ) 
 fprintf(' relative_error_x2 = |absolut error / real value|  = |%e / %e| = %e \n',error_abs_x2,valor_real_x2,error_rel_x2) 
我的问题是,它给了我一个精确的解决方案​​a=1,b=300000000001 c=1/4,数值​​其根源包括:

Roots of the polynomial 1.000 x^2 + 30000000.001 x+0.250 
 x1= -9.313226e-009
 x2= -3.000000e+007
知道多项式根的精确值为:

x1= -8.3333e-009
x2= -2.6844e+007
这给了我计算的绝对和相对精度的以下误差:

 absolute_errorx1 = |real value - obtained value| = |-8.333300e-009 - -9.313226e-009| = 9.799257e-010 
 absolute_errorx2 = |real value - obtained value| = |-2.684400e+007 - -3.000000e+007| = 3.156000e+006 

 relative error_x1 = |absolut error / real value| = |9.799257e-010 / -8.333300e-009| = 1.175916e-001 
 relative_error_x2 = |absolut error / real value|  = |3.156000e+006 / -2.684400e+007| = 1.175682e-001
我的问题是:有没有一种获得二次方程根的最佳方法?也就是说,我可以修改我的代码,以减少预期解和结果解之间的相对误差?

我认为您的“真实”值可能是错误的(或者可能是精度问题……我不知道)


为什么这听起来像是数值分析第一堂课上的家庭作业

我已经有一段时间没有那么年轻了,但我记得有一个窍门。无论如何,你错了。多项式的真根是

solve('x^2 + 30000000.001*x + 0.25')
ans =
          -30000000.000999991666666666944442
 -0.0000000083333333330555578703796293981491
根在这里有多好

p = [1 30000000.001 1/4];
format long g
roots(p)
ans =
             -30000000.001
     -8.33333333305556e-09
这看起来很不错。HPF是怎么做的

DefaultNumberOfDigits 64
a = hpf(1);
b = hpf('30000000.001');
c = hpf('0.25');

r1 = (-b + sqrt(b*b - 4*a*c))/(2*a)
r1 =
-0.000000008333333333055557870379629398149125529835186899898569329967

r2 = (-b - sqrt(b*b - 4*a*c))/(2*a)
r2 =
-30000000.000999991666666666944442129620370601850874470164813100101
是的,HPF也能很好地工作

那么当你使用双精度数字和标准公式时会发生什么呢?是的,克拉波拉来了

a = 1;
b = 30000000.001;
c = 0.25;

>> r1 = (-b + sqrt(b*b - 4*a*c))/(2*a)
r1 =
     -7.45058059692383e-09

>> r2 = (-b - sqrt(b*b - 4*a*c))/(2*a)
r2 =
             -30000000.001
同样,大量的减法相消会蚕食结果。(我似乎记得这就是你在上一个问题中遇到的问题。)


有一个技巧你可以使用。请注意,大的解是经过很好的估计的,而不是接近于零的解。那么,如果使用二次公式求解fliplr(p)的根,会发生什么?这如何解决你的问题?当您这样做时,隐式地完成了什么转换?(对不起,我不会做你的家庭作业。无论如何,我认为上面的提示已经足够了。)

在这种情况下直接使用二次公式会导致减去两个非常相似的数值,从而大大降低数值精度。这是因为

sqrt(b*b - 4*a*c)

与b几乎相同。所以你应该只使用这两个根中的一个,一个不需要减去两个非常接近的值的根,而对于另一个根,你可以使用(例如)二次曲线的根的乘积是c/a的事实。我让你来填补空白。

解决这个问题的好办法:

var q = sqrt(c*a)/b;
var f = .5 + .5 *sqrt(1-4*q*q);
var x1=-b*f/a;
var x2=-c/(f*b);

不完全是家庭作业,是科学计算实验室的一部分,我想详细了解正在发生的事情。顺便说一句,我想知道如何访问您的工具HPF。非常感谢。HPF尚未发布在FEX上。我正在修改它以使用migits,但这意味着几乎从零开始部分重写它。注意,您还可以使用Java BigDecimal工具。我看了看,但它本身也有一些我不喜欢的缺点。在此之前,考虑一下我在回答结束时提出的诀窍。这是一个多项式的隐式变换,所以你必须变换结果的根。@ MelkHIHA66——嗯,我终于把HPF变成了一个我认为beta版本的形式。只有一件事:当你认为你有“真正的”解决方案时,用吉拉德关系检查它们(我们在拉丁语国家这样称呼它们,我认为在盎格鲁-撒克逊国家,它们被称为牛顿恒等式或类似的东西)。woodchips似乎很好地回答了你的问题,但还记得,在双精度世界中,你的实际尾数精度是2^53,如果差值大于2^54,或者小于指数,那么你最终会得到垃圾2+2^54==2+2^54+1在Matlab中试试这个,或者这个2^54-(2^54-1)==0,看看结果如何
var q = sqrt(c*a)/b;
var f = .5 + .5 *sqrt(1-4*q*q);
var x1=-b*f/a;
var x2=-c/(f*b);