一元四次方程的求解

一元四次方程的求解

0.引言

在学习过程中需要求解一个一元四次方程。Alt
由于带有未知参数,本想使用MATLAB求解出它的解析解然后写入C++进行计算。后来发现求解出来的解太长。
Alt

要是直接放入C++中,通过解析解来实时求它的解甚至会比求解一元四次方程花费的时间更多。于是另谋出路,查阅资料如何求解一元四次方程。

1.方法一

Ferrari方法,这也是最容易Google到的方法。

时间紧张原理就不看了,直接找实现。巧了,某百科上就有code.Link.

Ferrari.cpp

#include <math.h>
#include <float.h>
#include <complex>
#include <iostream>
/******************************************************************************\
对一个复数 x 开 n 次方
\******************************************************************************/
std::complex<double>  sqrtn(const std::complex<double>&x,double n)
{
    double r = hypot(x.real(),x.imag()); //模
    if(r > 0.0)
    {
        double a = atan2(x.imag(),x.real()); //辐角
        n = 1.0 / n;
        r = pow(r,n);
        a *= n;
        return std::complex<double>(r * cos(a),r * sin(a));
    }
    return std::complex<double>();
}
/******************************************************************************\
使用费拉里法求解一元四次方程 a*x^4 + b*x^3 + c*x^2 + d*x + e = 0
\******************************************************************************/
std::complex<double> Ferrari(std::complex<double> x[4]
,std::complex<double> a
,std::complex<double> b
,std::complex<double> c
,std::complex<double> d
,std::complex<double> e)
{
    a = 1.0 / a;
    b *= a;
    c *= a;
    d *= a;
    e *= a;
    std::complex<double> P = (c * c + 12.0 * e - 3.0 * b * d) / 9.0;
    std::complex<double> Q = (27.0 * d * d + 2.0 * c * c * c + 27.0 * b * b * e - 72.0 * c * e - 9.0 * b * c * d) / 54.0;
    std::complex<double> D = sqrtn(Q * Q - P * P * P,2.0);
    std::complex<double> u = Q + D;
    std::complex<double> v = Q - D;
    if(v.real() * v.real() + v.imag() * v.imag() > u.real() * u.real() + u.imag() * u.imag())
    {
        u = sqrtn(v,3.0);
    }
    else
    {
        u = sqrtn(u,3.0);
    }
    std::complex<double> y;
    if(u.real() * u.real() + u.imag() * u.imag() > 0.0)
    {
        v = P / u;
        std::complex<double> o1(-0.5,+0.86602540378443864676372317075294);
        std::complex<double> o2(-0.5,-0.86602540378443864676372317075294);
        std::complex<double>&yMax = x[0];
        double m2 = 0.0;
        double m2Max = 0.0;
        int iMax = -1;
        for(int i = 0;i < 3;++i)
        {
            y = u + v + c / 3.0;
            u *= o1;
            v *= o2;
            a = b * b + 4.0 * (y - c);
            m2 = a.real() * a.real() + a.imag() * a.imag();
            if(0 == i || m2Max < m2)
            {
                m2Max = m2;
                yMax = y;
                iMax = i;
            }
        }
        y = yMax;
    }
    else
    {//一元三次方程,三重根
        y = c / 3.0;
    }
    std::complex<double> m = sqrtn(b * b + 4.0 * (y - c),2.0);
    if(m.real() * m.real() + m.imag() * m.imag() >= DBL_MIN)
    {
        std::complex<double> n = (b * y - 2.0 * d) / m;
        a = sqrtn((b + m) * (b + m) - 8.0 * (y + n),2.0);
        x[0] = (-(b + m) + a) / 4.0;
        x[1] = (-(b + m) - a) / 4.0;
        a = sqrtn((b - m) * (b - m) - 8.0 * (y - n),2.0);
        x[2] = (-(b - m) + a) / 4.0;
        x[3] = (-(b - m) - a) / 4.0;
    }
    else
    {
        a = sqrtn(b * b - 8.0 * y,2.0);
        x[0] =
        x[1] = (-b + a) / 4.0;
        x[2] =
        x[3] = (-b - a) / 4.0;
    }
return x[4];
}

int main()
{
std::complex<double> x[4];
x[4] = Ferrari(x,1,2,3,4,5); //验证费拉里法
std::cout<<"root1: "<<x[0]<<std::endl<<"root2: "<<x[1]<<std::endl<<"root3: "<<x[2]<<std::endl<<"root4: "<<x[3]<<std::endl;
return true;
}

测试系数:1,2,3,4,5
Alt

确保结果正确,使用matlab验证一下:

p = [1 2 3 4 5];
x = roots(p)

Alt
结果一致,OK。

2.方法二

使用多项式的伴随矩阵进行求解。

对于多项式: P ( x ) = x n + c n − 1 x n − 1 + c n − 2 x n − 2 + ⋯ + c 1 x + c 0 P(x) = x^n+c_{n-1}x^{n-1}+c_{n-2}x^{n-2}+\cdots+c_1x+c_0 P(x)=xn+cn1xn1+cn2xn2++c1x+c0
其伴随矩阵:

M x = [ 0 0 . . . 0 − c 0 1 0 . . . 0 − c 1 0 1 . . . 0 − c 2 . . . . . . . . . . . . . . . 0 0 . . . 1 − c n − 1 ] M_x = \left[\begin{array}{ccccc} 0 & {0} & {...} & {0} & {-c_0} \\ 1 &0 & {...} & {0} & {-c_1} \\ {0} & {1} & {...} & {0} & {-c_2} \\ { ...} & {...} & {...} & {...} & {...} \\ {0} & {0} & {...} & {1} & {-c_{n-1}} \end{array}\right] Mx=010...0001...0...............000...1c0c1c2...cn1
的特征值就是 P ( x ) P(x) P(x)的根。

于是使用Eigen进行多项式的求解:

#include <iostream>
#include <Eigen/Core>
// 稠密矩阵的代数运算(逆,特征值等)
#include <Eigen/Dense>
using namespace std;

int main( int argc, char** argv )
{

    Eigen::Matrix<double, 4, 4> matrix_44;
    //复数动态矩阵
	Eigen::Matrix<complex<double>, Eigen::Dynamic, Eigen::Dynamic> matrix_eigenvalues;
	//同样测试 12345
    matrix_44 << 0, 0, 0, -5,
				 1, 0, 0, -4,
				 0, 1, 0, -3,
				 0, 0, 1, -2;

	std::cout<<"matrix_44: "<<std::endl<<matrix_44<<std::endl<<std::endl;

	matrix_eigenvalues = matrix_44.eigenvalues();
	//std::cout<<"matrix_44.eigenvalues: "<<std::endl<<matrix_44.eigenvalues()<<std::endl;
	std::cout<<"matrix_eigenvalues: "<<std::endl<<matrix_eigenvalues<<std::endl;
	return 0;
}


结果与上面同样一致:

Alt

3.结论

数学功底越深厚,代码复杂度越低!!

  • 16
    点赞
  • 63
    收藏
  • 打赏
    打赏
  • 9
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:技术黑板 设计师:CSDN官方博客 返回首页
评论 9

打赏作者

古路

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值