- 1. Реалізація
- 2. Висновок
Як відомо, рішення кубічного рівняння було відоме ще з 16 століття. Однак навіть у наші дні інженери можуть зіткнутися з проблемою при його вирішенні. Ця складність обумовлена необхідністю вилучення кореня з комплексних чисел. Найбільш зручним рішенням є тригонометрична формула Вієта , яка значно менш відома в порівнянні з формулою Кардано. Для привернення уваги до теми я написав маленьку бібліотеку на C++ .
Реалізація
Бібліотека містить 3 сімейства класів: поліноміальне рівняння, коріння поліноміального рівняння та корінь поліноміального рівняння. Класи наведених рівнянь 1-го, 2-го і 3-го ступенів LinerNormalEquation, QuadraticNormalEquation і CubicNormalEquation успадковані від загального інтерфейсу AbstractPolynomialEquation. У цьому прикладі я обмежився розповсюдженням лише наведених рівнянь. Їхні основні методи roots() — повертає покажчик на клас, що зберігає рішення рівняння, root(int n) — повертає n корінь рівняння і value(…) — повертає значення рівняння при підстановці в нього заданого аргументу. Функція decomposed() повертає розкладання поточного полінома на множники. Основна теорема алгебри стверджує, що будь-який многочлен можна розкласти на множники ступеня, що не перевищує 2. Тому рівняння третього ступеня може бути розкладене або на 3 множника першого ступеня, або на 1 першого і 1 другого ступеня.
Інтерфейс
class AbstractPolynomialEquation { public: virtual ~AbstractPolynomialEquation() { deleteRoots(); } virtual int equationType() const = 0; virtual std::list<double> coefficients() const = 0; virtual double value( const double &x ) const = 0; virtual std::complex<double> value( const std::complex<double> &x ) const = 0; virtual std::complex<double> value( const std::unique_ptr<AbstractPolynomialRoot> &x ) { if(x->isReal()) return std::complex<double>( value( x->real() ) ); return value( x->toComplex() ); } virtual const AbstractPolynomialEquationRoots *roots() const = 0; const std::unique_ptr<AbstractPolynomialRoot> root(int number) const { return roots()->root(number); } virtual std::list<std::unique_ptr<AbstractPolynomialEquation>> decomposed() const = 0; virtual bool isNormal() const = 0; friend std::ostream &operator<< (std::ostream &out, const AbstractPolynomialEquation &equation) { out << equation.toString() + " = 0."; return out; } protected: virtual std::string toString() const = 0; mutable AbstractPolynomialEquationRoots *myroots = nullptr; void deleteRoots() const { if(myroots) { delete myroots; myroots = nullptr; } } };
Лінійне рівняння
class LinerNormalEquation : public AbstractPolynomialEquation { public: explicit LinerNormalEquation(const double &coef0); void setCoef0(const double &coef0); LinerNormalEquation operator+( const double &val ); LinerNormalEquation operator-( const double &val ); LinerNormalEquation operator+( const LinerNormalEquation &eq ); double operator-( const LinerNormalEquation &eq ) const; virtual int equationType() const override {return LINER;} virtual std::list<double> coefficients() const override { std::list<double> list; list.push_back( my0 ); list.push_back( 1 ); return list; } virtual double value( const double &x ) const override { return x + my0; } virtual std::complex<double> value( const std::complex<double> &x ) const override { return x + my0; } virtual const AbstractPolynomialEquationRoots *roots() const override; virtual std::list<std::unique_ptr<AbstractPolynomialEquation>> decomposed() const override; virtual bool isNormal() const override {return true;} protected: virtual std::string toString() const override; private: double my0; };
Реалізація
LinerNormalEquation::LinerNormalEquation(const double &coef0) { my0 = coef0; } void PolynomialEquation::LinerNormalEquation::setCoef0(const double &coef0) { my0 = coef0; deleteRoots(); } LinerNormalEquation LinerNormalEquation::operator+(const double &val) { return LinerNormalEquation( this->my0 + val ); } LinerNormalEquation LinerNormalEquation::operator-(const double &val) { return LinerNormalEquation( this->my0 - val ); } LinerNormalEquation LinerNormalEquation::operator+(const LinerNormalEquation &eq) { return LinerNormalEquation( (this->my0 + eq.my0)/2 ); } double LinerNormalEquation::operator-(const LinerNormalEquation &eq) const { return this->my0 - eq.my0; } const AbstractPolynomialEquationRoots *PolynomialEquation::LinerNormalEquation::roots() const { if(!myroots) myroots = new LinerEquationRoot( -my0 ); return myroots; } std::list<std::unique_ptr<AbstractPolynomialEquation> > LinerNormalEquation::decomposed() const { std::list<std::unique_ptr<AbstractPolynomialEquation> > root; root.push_back( std::make_unique<LinerNormalEquation>( this->my0 ) ); return root; } std::string LinerNormalEquation::toString() const { return std::string("x") + ( my0 > 0 ? std::string("+") : std::string() ) + ( my0 == 0 ? std::string() : std::to_string( my0 ) ); }
Квадратне рівняння
class QuadraticNormalEquation : public AbstractPolynomialEquation { public: QuadraticNormalEquation( double coef1, double coef0); virtual ~QuadraticNormalEquation() {} void setCoef0(const double &coef0); void setCoef1(const double &coef1); QuadraticNormalEquation operator+( const double &val ); QuadraticNormalEquation operator-( const double &val ); QuadraticNormalEquation operator+( const QuadraticNormalEquation &eq ); QuadraticNormalEquation operator+( const LinerNormalEquation &eq ); QuadraticNormalEquation operator-( const LinerNormalEquation &eq ); double discriminant() const; virtual int equationType() const override { return QUADRATIC; } virtual std::list<double> coefficients() const override { std::list<double> list; list.push_back( my0 ); list.push_back( my1 ); list.push_back( 1 ); return list; } virtual double value( const double &x ) const override { return (x + my1)*x + my0; } virtual std::complex<double> value( const std::complex<double> &x ) const override { return (x + my1)*x + my0; } virtual const AbstractPolynomialEquationRoots *roots() const override; virtual std::list< std::unique_ptr<AbstractPolynomialEquation> > decomposed() const override; virtual bool isNormal() const override {return true;} protected: virtual std::string toString() const override; private: double my0; double my1; };
Реалізація
QuadraticNormalEquation::QuadraticNormalEquation( double coef1, double coef0) : my0(coef0), my1(coef1) { } void QuadraticNormalEquation::setCoef0(const double &coef0) { my0 = coef0; deleteRoots(); } void QuadraticNormalEquation::setCoef1(const double &coef1) { my1 = coef1; deleteRoots(); } QuadraticNormalEquation QuadraticNormalEquation::operator+( const double &val ) { return QuadraticNormalEquation( this->my1, this->my0 + val ); } QuadraticNormalEquation QuadraticNormalEquation::operator-( const double &val ) { return QuadraticNormalEquation( this->my1, this->my0 - val ); } QuadraticNormalEquation QuadraticNormalEquation::operator+(const QuadraticNormalEquation &eq) { return QuadraticNormalEquation( (this->my1+eq.my1)/2, (this->my0+eq.my0)/2 ); } QuadraticNormalEquation QuadraticNormalEquation::operator-(const LinerNormalEquation &eq) { return QuadraticNormalEquation( this->my1 - 1, this->my0 - eq.coefficients().front() ); } QuadraticNormalEquation QuadraticNormalEquation::operator+(const LinerNormalEquation &eq) { return QuadraticNormalEquation( this->my1 + 1, this->my0 + eq.coefficients().front() ); } double QuadraticNormalEquation::discriminant() const { return my1*my1 - 4*my0; } const AbstractPolynomialEquationRoots *QuadraticNormalEquation::roots() const { if(!myroots) { auto d = discriminant(); myroots = new QuadraticImageEquationRoots( -my1/2, sqrt(-d)/2 ); } return myroots; } std::list<std::unique_ptr<AbstractPolynomialEquation> > QuadraticNormalEquation::decomposed() const { std::list<std::unique_ptr<AbstractPolynomialEquation> > eqs; if( this->roots()->rootsType() == QUADRAT_REAL ) { eqs.push_back( std::make_unique<LinerNormalEquation>( -this->roots()->root( 1 )->real() ) ); eqs.push_back( std::make_unique<LinerNormalEquation>( -this->roots()->root( 2 )->real() ) ); } else if( this->roots()->rootsType() == QUADRAT_IMAGE ) { eqs.push_back( std::make_unique<QuadraticNormalEquation>( *this ) ); } return eqs; } std::string QuadraticNormalEquation::toString() const { return std::string("x^2") + ( my1 > 0 ? std::string("+") : std::string("") ) + ( my1==0 ? std::string("") : std::to_string( my1 ) + std::string("*x") ) + ( my0 > 0 ? std::string("+") : std::string("") ) + ( my0==0 ? std::string("") : std::to_string( my0 ) ); }
Кубічне рівняння
class CubicNormalEquation : public AbstractPolynomialEquation { public: CubicNormalEquation(double coef2, double coef1, double coef0); void setCoef0(const double &coef0); void setCoef1(const double &coef1); void setCoef2(const double &coef2); CubicNormalEquation operator+( const CubicNormalEquation &eq ); CubicNormalEquation operator+( const LinerNormalEquation &eq ); CubicNormalEquation operator-( const LinerNormalEquation &eq ); CubicNormalEquation operator+( const QuadraticNormalEquation &eq ); CubicNormalEquation operator-( const QuadraticNormalEquation &eq ); double discriminant() const; double qu() const; double re() const; double fi() const; virtual int equationType() const override {return CUBIC;} virtual std::list<double> coefficients() const override { std::list<double> list; list.push_back( my0 ); list.push_back( my1 ); list.push_back( my2 ); list.push_back( 1 ); return list; } virtual double value( const double &x ) const override { return ( (x + my2)*x + my1 ) * x + my0; } virtual std::complex<double> value( const std::complex<double> &x ) const override { return ( (x + my2)*x + my1 ) * x + my0; } virtual const AbstractPolynomialEquationRoots *roots() const override; virtual std::list<std::unique_ptr<AbstractPolynomialEquation>> decomposed() const override; virtual bool isNormal() const override {return true;} protected: virtual std::string toString() const override; private: double my0; double my1; double my2; };
Реалізація
CubicNormalEquation::CubicNormalEquation( double coef2, double coef1, double coef0 ) : my0(coef0), my1(coef1), my2(coef2) { } void CubicNormalEquation::setCoef0(const double &coef0) { my0 = coef0; deleteRoots(); } void CubicNormalEquation::setCoef1(const double &coef1) { my1 = coef1; deleteRoots(); } void CubicNormalEquation::setCoef2(const double &coef2) { my2 = coef2; deleteRoots(); } CubicNormalEquation CubicNormalEquation::operator+(const CubicNormalEquation &eq) { return CubicNormalEquation( (this->my2+eq.my2)/2, (this->my1+eq.my1)/2, (this->my0+eq.my0)/2 ); } CubicNormalEquation CubicNormalEquation::operator+( const LinerNormalEquation &eq ) { auto coef = eq.coefficients(); return CubicNormalEquation( this->my2, this->my1 + 1, this->my0 + coef.front() ); } CubicNormalEquation CubicNormalEquation::operator-( const LinerNormalEquation &eq ) { auto coef = eq.coefficients(); return CubicNormalEquation( this->my2, this->my1 - 1, this->my0 - coef.front() ); } CubicNormalEquation CubicNormalEquation::operator+( const QuadraticNormalEquation &eq ) { auto coef = eq.coefficients(); return CubicNormalEquation( this->my2 + 1, this->my1 + *next( coef.begin() ), this->my0 + coef.front() ); } CubicNormalEquation CubicNormalEquation::operator-( const QuadraticNormalEquation &eq ) { auto coef = eq.coefficients(); return CubicNormalEquation( this->my2 - 1, this->my1 - *next( coef.begin() ), this->my0 - coef.front() ); } double CubicNormalEquation::discriminant() const // { auto q = qu(); auto r = re(); return q*q*q - r*r; } double CubicNormalEquation::qu() const { return (my2*my2 - my1*3) / 9; } double CubicNormalEquation::re() const { return (my2 * (my2*my2*2 - my1*9) + 27*my0) / 54; } double CubicNormalEquation::fi() const { auto q = qu(); auto q_cub = q*q*q; auto r = re(); auto s = q*q*q - r*r; if( s > 0 ) return acos( r/sqrt(q_cub) ) / 3; else { if( q > 0 ) { return acosh( fabs( r )/sqrt( q_cub ) ) / 3; } else { return asinh( fabs( r )/sqrt( -q_cub ) ) / 3; } } } const AbstractPolynomialEquationRoots *CubicNormalEquation::roots() const { if(!myroots) { auto r = re(); auto q = qu(); auto a_3 = my2 / 3; auto s = q*q*q - r*r; if(r==0 && q==0) { auto x1 = - a_3; myroots = new CubicRealEquationRoots( x1, x1, x1 ); return myroots; } if( s == 0 ) { auto x1 = -2 * cbrt(r) - a_3; auto x23 = cbrt(r) - a_3; myroots = new CubicRealEquationRoots( x1, x23, x23 ); return myroots; } auto f = fi(); if( s > 0 ) { auto q_sqrt = sqrt( q ); auto x1 = - 2 * q_sqrt * cos( f ) - a_3; auto x2 = - 2 * q_sqrt * cos( f + 2.0 * M_PI / 3 ) - a_3; auto x3 = - 2 * q_sqrt * cos( f - 2.0 * M_PI / 3 ) - a_3; myroots = new CubicRealEquationRoots( x1, x2, x3 ); } else { if( q == 0) { auto x = - cbrt( my0 - a_3*a_3*a_3 ) - a_3; auto re = -( my2 + x )/2; auto im = sqrt( fabs( (my2 - 3*x)*(my2 + x) - 4*my1 ) )/2; myroots = new CubicImageEquationRoots( x, re, im ); return myroots; } if( q > 0 ) { auto q_sqrt = sqrt( q ); auto x = - q_sqrt * 2.0 * sgn(r) * cosh( f ) - a_3; auto re = q_sqrt * sgn(r) * cosh( f ) - a_3; auto im = SQRT_3 * q_sqrt * sinh( f ); myroots = new CubicImageEquationRoots( x, re, im ); } else { auto q_sqrt = sqrt( -q ); auto x = - q_sqrt * 2.0 * sgn(r) * sinh( f ) - a_3; auto re = q_sqrt * sgn(r) * sinh( f ) - a_3; auto im = SQRT_3 * q_sqrt * cosh( f ); myroots = new CubicImageEquationRoots( x, re, im ); } } } return myroots; } std::list<std::unique_ptr<AbstractPolynomialEquation> > CubicNormalEquation::decomposed() const { std::list<std::unique_ptr<AbstractPolynomialEquation> > eqs; eqs.push_back( std::make_unique<LinerNormalEquation>( -this->roots()->root( 1 )->real() ) ); if( roots()->rootsType() == CUB_REAL ) { eqs.push_back( std::make_unique<LinerNormalEquation>( -this->roots()->root( 2 )->real() ) ); eqs.push_back( std::make_unique<LinerNormalEquation>( -this->roots()->root( 3 )->real() ) ); } else if( roots()->rootsType() == CUB_IMAGE ) { eqs.push_back( std::make_unique<QuadraticNormalEquation>( -2.0*this->roots()->root( 2 )->real(), fabs( this->roots()->root( 2 )->toComplex() ) ) ); } return eqs; } std::string CubicNormalEquation::toString() const { return std::string("x^3") + ( my2 > 0 ? std::string("+") : std::string() ) + ( my2==0 ? std::string() : ( std::to_string( my2 ) + std::string("*x^2") ) ) + ( my1 > 0 ? std::string("+") : std::string() ) + ( my1==0 ? std::string() : ( std::to_string( my1 ) + std::string("*x") ) ) + ( my0 > 0 ? std::string("+") : std::string() ) + ( my0==0 ? std::string() : std::to_string( my0 ) ); }
Класи зберігачі рішень рівнянь LinerEquationRoot, QuadraticRealEquationRoots, QuadraticImageEquationRoots. CubicRealEquationRoots, CubicImageEquationRoots успадковані від загального інтерфейсу AbstractPolynomialEquationRoots і класифікуються за двома ознаками. Перший – ступінь рівняння, другий – характеристика коренів рівняння. Лінійне рівняння є тривіальним випадком — має один дійсний корінь. Квадратне і кубічне рівняння може мати комплексне коріння. Таке коріння завжди існує парами x 1 = R + i M і x 2 = R – i M. Тому зберігаються вони як дійсна та уявна частини. Справжнє коріння зберігається як безпосередньо свої значення.
Інтерфейс
class AbstractPolynomialEquationRoots { public: virtual ~AbstractPolynomialEquationRoots() { /*std::cout << "delete AbstractPolynomialEquationRoots";*/ } virtual int rootsType() const = 0; virtual std::unique_ptr<AbstractPolynomialRoot> root(int number) const = 0; friend std::ostream &operator<< (std::ostream &out, const AbstractPolynomialEquationRoots *root) { out << root->toString(); return out; } protected: virtual std::string toString() const = 0; };
Коріння лінійного рівняння
class LinerEquationRoot : public AbstractPolynomialEquationRoots { public: explicit LinerEquationRoot(const double &root); virtual ~LinerEquationRoot() {} virtual int rootsType() const override {return LINE;} virtual std::unique_ptr<AbstractPolynomialRoot> root(int number) const override { return number == 1 ? std::make_unique<RealPolynomialRoot>(myx1) : nullptr; } protected: virtual std::string toString() const override; private: double myx1; };
Реалізація
LinerEquationRoot::LinerEquationRoot(const double &root) : myx1(root) { } std::string LinerEquationRoot::toString() const { return "x = " + std::to_string(myx1) + "."; }
Коріння квадратного рівняння
class QuadraticRealEquationRoots : public AbstractPolynomialEquationRoots { public: QuadraticRealEquationRoots(const double &root1, const double &root2); virtual ~QuadraticRealEquationRoots() {} virtual int rootsType() const override {return QUADRAT_REAL;} virtual std::unique_ptr<AbstractPolynomialRoot> root(int number) const override; protected: virtual std::string toString() const override; private: double myx1; double myx2; }; class QuadraticImageEquationRoots : public AbstractPolynomialEquationRoots { public: QuadraticImageEquationRoots( double real, double image); virtual ~QuadraticImageEquationRoots() {} virtual int rootsType() const override {return QUADRAT_IMAGE;} virtual std::unique_ptr<AbstractPolynomialRoot> root(int number) const override; protected: virtual std::string toString() const override; private: double myre; double myim; };
Реалізація
QuadraticRealEquationRoots::QuadraticRealEquationRoots(const double &root1, const double &root2) : myx1( root1 ), myx2( root2 ) { } std::unique_ptr<AbstractPolynomialRoot> QuadraticRealEquationRoots::root(int number) const { switch (number) { case 1: return std::make_unique<RealPolynomialRoot>(myx1); case 2: return std::make_unique<RealPolynomialRoot>(myx2); default: return nullptr; } } std::string QuadraticRealEquationRoots::toString() const { return "x1 = " + std::to_string( myx1 ) + "; x2 = " + std::to_string( myx2 ) + "."; } QuadraticImageEquationRoots::QuadraticImageEquationRoots(double real, double image) : myre( real ), myim( image ) { } std::unique_ptr<AbstractPolynomialRoot> QuadraticImageEquationRoots::root(int number) const { switch (number) { case 1: return std::make_unique<ImagePolynomialRoot>(myre, myim); case 2: return std::make_unique<ImagePolynomialRoot>(myre, -myim); default: return nullptr; } } std::string QuadraticImageEquationRoots::toString() const { return "x1 = " + std::to_string( myre ) + " + j" + std::to_string( myim ) + "; x2 = " + std::to_string( myre ) + " - j" + std::to_string( myim ) + "."; }
Коріння кубічного рівняння
class CubicRealEquationRoots : public AbstractPolynomialEquationRoots { public: CubicRealEquationRoots(const double &root1, const double &root2, const double &root3); virtual ~CubicRealEquationRoots() {} virtual int rootsType() const override {return CUB_REAL;} virtual std::unique_ptr<AbstractPolynomialRoot> root(int number) const override; protected: virtual std::string toString() const override; private: double myx1; double myx2; double myx3; }; class CubicImageEquationRoots : public AbstractPolynomialEquationRoots { public: CubicImageEquationRoots(const double &root, const double &real, const double &image); virtual ~CubicImageEquationRoots() {} virtual int rootsType() const override {return CUB_IMAGE;} virtual std::unique_ptr<AbstractPolynomialRoot> root(int number) const override; protected: virtual std::string toString() const override; private: double myx1; double myre; double myim; };
Реалізація
CubicRealEquationRoots::CubicRealEquationRoots(const double &root1, const double &root2, const double &root3) : myx1( root1 ), myx2( root2 ), myx3( root3 ) { } std::unique_ptr<AbstractPolynomialRoot> CubicRealEquationRoots::root(int number) const { switch (number) { case 1: return std::make_unique<RealPolynomialRoot>(myx1); case 2: return std::make_unique<RealPolynomialRoot>(myx2); case 3: return std::make_unique<RealPolynomialRoot>(myx3); default: return nullptr; } } std::string CubicRealEquationRoots::toString() const { return "x1 = " + std::to_string( myx1 ) + "; x2 = " + std::to_string( myx2 ) + "; x3 = " + std::to_string( myx3 ); } CubicImageEquationRoots::CubicImageEquationRoots(const double &root, const double &real, const double &image) : myx1( root ), myre( real ), myim( image ) { } //----------------------------------------------------------- std::unique_ptr<AbstractPolynomialRoot> CubicImageEquationRoots::root(int number) const { switch (number) { case 1: return std::make_unique<RealPolynomialRoot>(myx1); case 2: return std::make_unique<ImagePolynomialRoot>(myre, myim); case 3: return std::make_unique<ImagePolynomialRoot>(myre, -myim); default: return nullptr; } } std::string CubicImageEquationRoots::toString() const { return "x1 = " + std::to_string( myx1 ) + "; x2 = " + (myre == 0 ? std::string() : std::to_string( myre ) + std::string(" + ")) + "j" + std::to_string( myim ) + "; x3 = " + (myre == 0 ? std::string() : std::to_string( myre ) + std::string(" ")) + "- j" + std::to_string( myim ) + "."; }
Для доступу до величин коренів створені об'єкти RealPolynomialRoot та ImagePolynomialRoot, успадковані від AbstractPolynomialRoot. Корінь повертається як розумного покажчика unique_ptr
Інтерфейс
class AbstractPolynomialRoot { public: virtual std::complex<double> toComplex() const = 0; virtual double real() const = 0; virtual double image() const = 0; virtual bool isReal() const = 0; };
Справжній корінь
class RealPolynomialRoot : public AbstractPolynomialRoot { public: explicit RealPolynomialRoot( const double &x ) : myx(x) {} virtual std::complex<double> toComplex() const override { return std::complex<double>(myx, 0); } virtual double real() const override { return myx; } virtual double image() const override { return 0; } virtual bool isReal() const override { return true; } private: double myx; };
Уявний корінь
class ImagePolynomialRoot : public AbstractPolynomialRoot { public: ImagePolynomialRoot( const double &r, const double &i ) : myr(r), myi(i) {} virtual std::complex<double> toComplex() const override { return std::complex<double>(myr, myi); } virtual double real() const override { return myr; } virtual double image() const override { return myi; } virtual bool isReal() const override { return false; } private: double myr, myi; };
Висновок
Незважаючи на простоту, формули розв'язання поліноміальних рівнянь є дуже складними виразами. Крім рішення рівняння третього ступеня відоме рішення Ferrari для рівнянь четвертого ступеня. За інших випадках доводиться використовувати чисельні методи. При цьому для пошуку комплексного коріння необхідні спеціальні методи. На ці теми планую написати такі статті.