一元四次方程的求解

一元四次方程的求解

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)=xn+cn1xn1+cn2xn2++c1x+c0P(x) = x^n+c_{n-1}x^{n-1}+c_{n-2}x^{n-2}+\cdots+c_1x+c_0
其伴隨矩陣:

Mx=[00...0c010...0c101...0c2...............00...1cn1]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]
的特徵值就是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.結論

數學功底越深厚,代碼複雜度越低!!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章