C# 用Cardano方法求解一个三次方程
我正试图用Cardano方法找到一个由四个系数组成的三次方程的实根。问题是,我的实现找到的根实际上不起作用-通过在等式中插入它们进行测试会产生显著的错误(超过所需的10^-6)。算法是否执行错误,或者错误是由其他原因造成的,比如舍入精度C# 用Cardano方法求解一个三次方程,c#,algorithm,math,C#,Algorithm,Math,我正试图用Cardano方法找到一个由四个系数组成的三次方程的实根。问题是,我的实现找到的根实际上不起作用-通过在等式中插入它们进行测试会产生显著的错误(超过所需的10^-6)。算法是否执行错误,或者错误是由其他原因造成的,比如舍入精度 static double CubicRoot(double n) { return Math.Pow(Math.Abs(n), 1d / 3d) * Math.Sign(n); } public static List<
static double CubicRoot(double n)
{
return Math.Pow(Math.Abs(n), 1d / 3d) * Math.Sign(n);
}
public static List<double> SolveCubic(double A, double B = 0, double C = 0, double D = 0)
{
List<double> output = new List<double>();
if (A != 0)
{
double A1 = B / A;
double A2 = C / A;
double A3 = D / A;
double P = -((A1 * A1) / 3) + A2;
double Q = ((2.0 * A1 * A1 * A1) / 27.0) - ((A1 * A2) / 3.0) + A3;
double cubeDiscr = Q * Q / 4.0 + P * P * P / 27.0;
if (cubeDiscr > 0)
{
double u = CubicRoot(-Q / 2.0 + Math.Sqrt(cubeDiscr));
double v = CubicRoot(-Q / 2.0 - Math.Sqrt(cubeDiscr));
output.Add(u + v - (A1 / 3.0));
return output;
}
else if (cubeDiscr == 0)
{
double u = CubicRoot(-Q / 2.0);
output.Add(2u - (A1 / 3.0));
output.Add(-u - (A1 / 3.0));
}
else if (cubeDiscr < 0)
{
double r = CubicRoot(Math.Sqrt(-(P * P * P / 27.0)));
double alpha = Math.Atan(Math.Sqrt(-cubeDiscr) / (-Q / 2.0));
output.Add(r * (Math.Cos(alpha / 3.0) + Math.Cos((6 * Math.PI - alpha) / 3.0)) - A1 / 3.0);
output.Add(r * (Math.Cos((2 * Math.PI + alpha) / 3.0) + Math.Cos((4 * Math.PI - alpha) / 3.0)) - A1 / 3.0);
output.Add(r * (Math.Cos((4 * Math.PI + alpha) / 3.0) + Math.Cos((2 * Math.PI - alpha) / 3.0)) - A1 / 3.0);
}
}
return output;
}
静态双立方体(双n)
{
返回数学功率(数学Abs(n),1d/3d)*数学符号(n);
}
公共静态列表(双A,双B=0,双C=0,双D=0)
{
列表输出=新列表();
如果(A!=0)
{
双A1=B/A;
双A2=C/A;
双A3=D/A;
双P=-((A1*A1)/3)+A2;
双Q=((2.0*A1*A1*A1)/27.0)-(A1*A2)/3.0)+A3;
双立方结构=Q*Q/4.0+P*P*P/27.0;
如果(cubeDiscr>0)
{
双u=CubicRoot(-Q/2.0+Math.Sqrt(cubeDiscr));
双v=CubicRoot(-Q/2.0-Math.Sqrt(cubeDiscr));
输出。添加(u+v-(A1/3.0));
返回输出;
}
else if(cubeDiscr==0)
{
双u=立方英尺(-Q/2.0);
输出。添加(2u-(A1/3.0));
输出。添加(-u-(A1/3.0));
}
否则如果(cubeDiscr<0)
{
双r=CubicRoot(数学Sqrt(-(P*P*P/27.0));
double alpha=Math.Atan(Math.Sqrt(-cubeDiscr)/(-Q/2.0));
Add(r*(Math.Cos(alpha/3.0)+Math.Cos((6*Math.PI-alpha)/3.0))-A1/3.0);
Add(r*(Math.Cos((2*Math.PI+alpha)/3.0)+Math.Cos((4*Math.PI-alpha)/3.0))-A1/3.0;
Add(r*(Math.Cos((4*Math.PI+alpha)/3.0)+Math.Cos((2*Math.PI-alpha)/3.0))-A1/3.0;
}
}
返回输出;
}
一些事情
将返回零对零,这恰好是您在本例中想要的结果,但可能您在代码或算法更改方面没有那么幸运李>Math.Sign
- 您将遇到舍入问题,并且在应该执行时不会执行
分支。您可能存在舍入问题,并出于同样的原因执行错误的cubeDiscr==0
和>0
分支。改为在0的增量范围内进行测试(见下文)李><0
- 但是
分支是错误的,因为1)您没有计算cubeDiscr==0
,2)v
是一个值为2的2u
,而不是UInt32
李>2*u
- 计算alpha是错误的(见下文)
- (可能还有更多,但我一眼就看到了这些)
double alpha = Math.Atan(Math.Sqrt(-cubeDiscr) / (-Q / 2.0));
不一样
double alpha = Math.Atan(Math.Sqrt(-d) / q * 2.0);
if (q > 0) // if q > 0 the angle becomes PI + alpha
alpha = Math.PI + alpha;
使用该页面中包含的代码有什么问题
public double Xroot(double a, double x)
{
double i = 1;
if (a < 0)
i = -1;
return (i * Math.Exp( Math.Log(a*i)/x));
}
public int Calc_Cardano() // solve cubic equation according to cardano
{
double p, q, u, v;
double r, alpha;
int res;
res = 0;
if (a1 != 0)
{
a = b / a1;
b = c / a1;
c = d / a1;
p = -(a * a / 3.0) + b;
q = (2.0 / 27.0 * a * a * a) - (a * b / 3.0) + c;
d = q * q / 4.0 + p * p * p / 27.0;
if (Math.Abs(d) < Math.Pow(10.0, -11.0))
d = 0;
// 3 cases D > 0, D == 0 and D < 0
if (d > 1e-20)
{
u = Xroot(-q / 2.0 + Math.Sqrt(d), 3.0);
v = Xroot(-q / 2.0 - Math.Sqrt(d), 3.0);
x1.real = u + v - a / 3.0;
x2.real = -(u + v) / 2.0 - a / 3.0;
x2.imag = Math.Sqrt(3.0) / 2.0 * (u - v);
x3.real = x2.real;
x3.imag = -x2.imag;
res = 1;
}
if (Math.Abs(d) <= 1e-20)
{
u = Xroot(-q / 2.0, 3.0);
v = Xroot(-q / 2.0, 3.0);
x1.real = u + v - a / 3.0;
x2.real = -(u + v) / 2.0 - a / 3.0;
res = 2;
}
if (d < -1e-20)
{
r = Math.Sqrt(-p * p * p / 27.0);
alpha = Math.Atan(Math.Sqrt(-d) / q * 2.0);
if (q > 0) // if q > 0 the angle becomes PI + alpha
alpha = Math.PI + alpha;
x1.real = Xroot(r, 3.0) * (Math.Cos((6.0 * Math.PI - alpha) / 3.0) + Math.Cos(alpha / 3.0)) - a / 3.0;
x2.real = Xroot(r, 3.0) * (Math.Cos((2.0 * Math.PI + alpha) / 3.0) + Math.Cos((4.0 * Math.PI - alpha) / 3.0)) - a / 3.0;
x3.real = Xroot(r, 3.0) * (Math.Cos((4.0 * Math.PI + alpha) / 3.0) + Math.Cos((2.0 * Math.PI - alpha) / 3.0)) - a / 3.0;
res = 3;
}
}
else
res = 0;
return res;
}
公共双x根(双a,双x)
{
双i=1;
if(a<0)
i=-1;
返回(i*Math.Exp(Math.Log(a*i)/x));
}
public int Calc_Cardano()//根据Cardano解三次方程
{
双p,q,u,v;
双r,α;
国际关系;
res=0;
如果(a1!=0)
{
a=b/a1;
b=c/a1;
c=d/a1;
p=-(a*a/3.0)+b;
q=(2.0/27.0*a*a*a)-(a*b/3.0)+c;
d=q*q/4.0+p*p*p/27.0;
如果(数学Abs(d)0,D==0,D<0
如果(d>1e-20)
{
u=Xroot(-q/2.0+数学Sqrt(d),3.0);
v=Xroot(-q/2.0-数学Sqrt(d),3.0);
x1.real=u+v-a/3.0;
x2.real=-(u+v)/2.0-a/3.0;
x2.imag=数学Sqrt(3.0)/2.0*(u-v);
x3.real=x2.real;
x3.imag=-x2.imag;
res=1;
}
if(Math.Abs(d)0)//如果q>0,角度变成PI+alpha
alpha=Math.PI+alpha;
x1.real=Xroot(r,3.0)*(Math.Cos((6.0*Math.PI-alpha)/3.0)+Math.Cos(alpha/3.0))-a/3.0;
x2.real=Xroot(r,3.0)*(Math.Cos((2.0*Math.PI+alpha)/3.0)+Math.Cos((4.0*Math.PI-alpha)/3.0))-a/3.0;
x3.real=Xroot(r,3.0)*(Math.Cos((4.0*Math.PI+alpha)/3.0)+Math.Cos((2.0*Math.PI-alpha)/3.0))-a/3.0;
res=3;
}
}
其他的
res=0;
返回res;
}
一些事情
将返回零对零,这恰好是您在本例中想要的结果,但可能您在代码或算法更改方面没有那么幸运李>Math.Sign
- 您将遇到舍入问题,并且在应该执行时不会执行
分支。您可能存在舍入问题,并出于同样的原因执行错误的cubeDiscr==0
和>0
分支。改为在0的增量范围内进行测试(见下文)李><0
- 但是
分支是错误的,因为1)您没有计算cubeDiscr==0
,2)v
是一个值为2的2u
,而不是UInt32
李>2*u
- 计算alpha是错误的(见下文)
- (可能还有更多,但我一眼就看到了这些)
double alpha = Math.Atan(Math.Sqrt(-cubeDiscr) / (-Q / 2.0));
不一样
double alpha = Math.Atan(Math.Sqrt(-d) / q * 2.0);
if (q > 0) // if q > 0 the angle becomes PI + alpha
alpha = Math.PI + alpha;
使用该页面中包含的代码有什么问题
public double Xroot(double a, double x)
{
double i = 1;
if (a < 0)
i = -1;
return (i * Math.Exp( Math.Log(a*i)/x));
}
public int Calc_Cardano() // solve cubic equation according to cardano
{
double p, q, u, v;
double r, alpha;
int res;
res = 0;
if (a1 != 0)
{
a = b / a1;
b = c / a1;
c = d / a1;
p = -(a * a / 3.0) + b;
q = (2.0 / 27.0 * a * a * a) - (a * b / 3.0) + c;
d = q * q / 4.0 + p * p * p / 27.0;
if (Math.Abs(d) < Math.Pow(10.0, -11.0))
d = 0;
// 3 cases D > 0, D == 0 and D < 0
if (d > 1e-20)
{
u = Xroot(-q / 2.0 + Math.Sqrt(d), 3.0);
v = Xroot(-q / 2.0 - Math.Sqrt(d), 3.0);
x1.real = u + v - a / 3.0;
x2.real = -(u + v) / 2.0 - a / 3.0;
x2.imag = Math.Sqrt(3.0) / 2.0 * (u - v);
x3.real = x2.real;
x3.imag = -x2.imag;
res = 1;
}
if (Math.Abs(d) <= 1e-20)
{
u = Xroot(-q / 2.0, 3.0);
v = Xroot(-q / 2.0, 3.0);
x1.real = u + v - a / 3.0;
x2.real = -(u + v) / 2.0 - a / 3.0;
res = 2;
}
if (d < -1e-20)
{
r = Math.Sqrt(-p * p * p / 27.0);
alpha = Math.Atan(Math.Sqrt(-d) / q * 2.0);
if (q > 0) // if q > 0 the angle becomes PI + alpha
alpha = Math.PI + alpha;
x1.real = Xroot(r, 3.0) * (Math.Cos((6.0 * Math.PI - alpha) / 3.0) + Math.Cos(alpha / 3.0)) - a / 3.0;
x2.real = Xroot(r, 3.0) * (Math.Cos((2.0 * Math.PI + alpha) / 3.0) + Math.Cos((4.0 * Math.PI - alpha) / 3.0)) - a / 3.0;
x3.real = Xroot(r, 3.0) * (Math.Cos((4.0 * Math.PI + alpha) / 3.0) + Math.Cos((2.0 * Math.PI - alpha) / 3.0)) - a / 3.0;
res = 3;
}
}
else
res = 0;
return res;
}
公共双x根(双a,双x)
{
双i=1;
if(a<0)
i=-1;
返回(i*Math.Exp(Math.Log(a*i)/x));
}
public int Calc_Cardano()//根据Cardano解三次方程
{
双p,q,u,v;
双r,α;
国际关系;
res=0;
如果(a1!=0)
{
a=b/a1;
b=c