找到四次方程的正实解的专门算法?

时间:2011-07-03 12:29:17

标签: algorithm math microcontroller

我正在寻找一种专门的算法来找到具有实系数的四次方程的正实解(也称为4阶的二次方程或多项式方程)。他们有以下形式:

a 4 x 4 + a 3 x 3 + a 2 x 2 + a 1 x + a 0 = 0

1 2 ,...是实数。

它应该在微控制器上运行,这需要进行大量的计算。因此,性能是一个问题。这就是为什么我正在寻找一种专门用于正解的算法。如果可能的话,我希望它能够计算出确切的解决方案。

我知道有一种计算四次方程解的一般方法,但它涉及计算方面。

有人能指出我正确的方向吗?

编辑:

从答案来看:有些人似乎误解了我(虽然我很清楚)。 我知道解决四次方程的标准方法。他们没有为我做这件事 - 他们既不适合记忆也不足够快。我需要的是一种高精度的高效算法,只能找到具有实系数的四次方程的真实解(如果有帮助)。我不确定是否有这样的算法,但我想你们可能都知道。 P.S。:downvotes不是来自我。

7 个答案:

答案 0 :(得分:5)

这是使用复杂算法更容易找到所有根的情况之一,而不是仅仅找到正实根。因为听起来你需要一次找到多个根,我建议使用Durand-Kerner方法,它基本上是对Weierstrass方法的改进:

http://en.wikipedia.org/wiki/Durand%E2%80%93Kerner_method

Weierstrass'方法又是牛顿方法的一种改进,它可以并行地解决多项式的所有根(并且它具有很大的优点,即它很容易编码)。它通常以大约二次速率收敛,但对于多个根只是线性收敛。对于大多数四次多项式,只需几次迭代就可以确定根源。如果您需要更通用的解决方案,那么您应该使用Jenkins-Traub:

http://en.wikipedia.org/wiki/Jenkins%E2%80%93Traub_method

这对于更高次多项式更快,并且基本上通过将问题转换为找到伴随矩阵的特征值来工作:

http://en.wikipedia.org/wiki/Companion_matrix

编辑:作为第二个建议,您也可以尝试在伴随矩阵上使用幂方法。由于你的方程只有非负系数,你可能会发现将Perron-Frobenius定理应用于伴随矩阵是有用的。最小化时,这证明至少存在一个非负根:

http://en.wikipedia.org/wiki/Perron%E2%80%93Frobenius_theorem

答案 1 :(得分:2)

是的,有一般方法。你需要一个根查找算法,比如包围和分割,割线,假位置,Ridder,Newton-Raphson,通货紧缩,Muller,Laguerre或Jenkins-Traub - 我是否让任何人离开?

查看“数字食谱”了解详情。

答案 2 :(得分:1)

您能提供良好的起始值,以确保始终找到所有解决方案。 牛顿方法会快速收敛。

我检查了千里马:

solve(a*x^4+b*x^3+c*x^2+d*x+c=0,x);

解决方案看起来确实很糟糕。 您很容易遇到稳定性问题。每当您减去两个具有接近值的浮点数时,就会发生这种情况。

但是,如果系数是常数,则可以实现直接公式。 您可以通过安装maxima获得解决方案,也可以在wolframalpha.com上输入等式

答案 3 :(得分:1)

看看Ferrari's method。然而,它涉及相当多的计算,但可能满足您的需求。

答案 4 :(得分:1)

没有。没有神奇的方法可以找到四阶多项式方程的根,至少在没有完成工作的情况下也是如此。是的,有一个四阶多项式的公式涉及复杂的算术,但它将返回所有的根,复数,实数正,负。要么使用迭代方案,要么使用代数。

你很幸运,有一个分析解决方案。如果你是一个五阶多项式,那就不存在了。

答案 5 :(得分:1)

我意识到这个答案已经很晚了,但我认为已经提到的方法的一个很好的替代方法是TOMS Algorithm 326,它基于Terence RFNonweiler CACM的论文“低阶多项式的根”(4月) 1968年)。

这是对3阶和4阶多项式的代数解,它是相当紧凑和快速的。它肯定比Jenkins Traub更简单,更快,并且不需要迭代。

不要使用TOMS代码,因为它做得很差。

对此算法进行了重写here,对其进行了严格的准确性测试。

答案 6 :(得分:1)

这是我写的C / C ++代码。但它只给出了方程式的真正根源

#include <stdio.h>
#include <iostream>
#include <math.h>
/*--------------------------------------------

 --------------------------------------------*/
double cubic(double b,double c,double d)
{
    double p=c-b*b/3.0;
    double q=2.0*b*b*b/27.0-b*c/3.0+d;

    if(p==0.0) return pow(q,1.0/3.0);
    if(q==0.0) return 0.0;

    double t=sqrt(fabs(p)/3.0);
    double g=1.5*q/(p*t);
    if(p>0.0)
    return -2.0*t*sinh(asinh(g)/3.0)-b/3.0;


    if(4.0*p*p*p+27.0*q*q<0.0)
    return 2.0*t*cos(acos(g)/3.0)-b/3.0;

    if(q>0.0)
    return -2.0*t*cosh(acosh(-g)/3.0)-b/3.0;

    return 2.0*t*cosh(acosh(g)/3.0)-b/3.0;
}
/*--------------------------------------------

 --------------------------------------------*/
int quartic(double b,double c,double d,double e,double* ans)
{

    double p=c-0.375*b*b;
    double q=0.125*b*b*b-0.5*b*c+d;
    if(q==0)
    {
        double m=0.25*p*p+0.01171875*b*b*b*b-e+0.25*b*d-0.0625*b*b*c;
        if(m<0.0) return 0;
        int nroots=0;

        if(m-0.5*p>=0.0)
        {
            double absy=sqrt(m-0.5*p);
            ans[nroots++]=absy-0.25*b;
            ans[nroots++]=-absy-0.25*b;
        }
        if(m+0.5*p>=0.0)
        {
            double absy=sqrt(m+0.5*p);
            ans[nroots++]=absy-0.25*b;
            ans[nroots++]=-absy-0.25*b;
        }

        return nroots;
    }
    double m=cubic(p,0.25*p*p+0.01171875*b*b*b*b-e+0.25*b*d-0.0625*b*b*c,-0.125*q*q);

    if(m<0.0) return 0;
    double sqrt_2m=sqrt(2.0*m);
    int nroots=0;
    if(-m-p+q/sqrt_2m>=0.0)
    {
        double delta=sqrt(2.0*(-m-p+q/sqrt_2m));
        ans[nroots++]=0.5*(-sqrt_2m+delta)-0.25*b;
        ans[nroots++]=0.5*(-sqrt_2m-delta)-0.25*b;
    }

    if(-m-p-q/sqrt_2m>=0.0)
    {
        double delta=sqrt(2.0*(-m-p-q/sqrt_2m));
        ans[nroots++]=0.5*(sqrt_2m+delta)-0.25*b;
        ans[nroots++]=0.5*(sqrt_2m-delta)-0.25*b;
    }

    return nroots;
}
/*--------------------------------------------

 --------------------------------------------*/
int main(int nargs,char* args[])
{
    if(nargs!=6)
    {
        printf("5 arguments are needed\n");
        return EXIT_FAILURE;
    }
    double a=atof(args[1]);
    double b=atof(args[2]);
    double c=atof(args[3]);
    double d=atof(args[4]);
    double e=atof(args[5]);
    if(a==0.0)
    {
        printf("1st argument should be nonzero\n");
        return EXIT_FAILURE;
    }

    int nroots;
    double ans[4];
    nroots=quartic(b/a,c/a,d/a,e/a,ans);
    if(nroots==0)
        printf("Equation has no real roots!\n");
    else
    {
        printf("Equation has %d real roots: ",nroots);
        for(int i=0;i<nroots-1;i++) printf("%.16lf, ",ans[i]);
        printf("%.16lf\n",ans[nroots-1]);
    }

    return EXIT_SUCCESS;
}