Finding the real roots of an equation in a quartic using the ferrari method

I am currently trying to solve the quartz equation using Ferrari's method from Wikipedia . I want to get only real roots, discarding the imaginary. My implementation doesn't return a good value for real roots. I cannot find any errors in the formula.

My CubicEquation

works as expected and my biquadratic solution. Now I just missed Ferrari's method, but I can't get it to work!

Here's my class:

public class QuarticFunction {

    private final double a;
    private final double b;
    private final double c;
    private final double d;
    private final double e;

    public QuarticFunction(double a, double b, double c, double d, double e) {
        this.a = a;
        this.b = b;
        this.c = c;
        this.d = d;
        this.e = e;
    }

   public final double[] findRealRoots() {
        if (a == 0) {
            return new CubicEquation(b, c, d, e).findRealRoots();
        }

        if (isBiquadratic()) { //This part works as expected
            return solveUsingBiquadraticMethod();
        }

        return solveUsingFerrariMethodWikipedia();
    }

    private double[] solveUsingFerrariMethodWikipedia() {
        // http://en.wikipedia.org/wiki/Quartic_function#Ferrari.27s_solution
        // ERROR: Wrong numbers when Two Real Roots + Two Complex Roots
        // ERROR: Wrong numbers when Four Real Roots
        QuarticFunction depressedQuartic = toDepressed();
        if (depressedQuartic.isBiquadratic()) {
            return depressedQuartic.solveUsingBiquadraticMethod();
        }

        double y = findFerraryY(depressedQuartic);
        double originalRootConversionPart = -b / (4 * a);
        double firstPart = Math.sqrt(depressedQuartic.c + 2 * y);

        double positiveSecondPart = Math.sqrt(-(3 * depressedQuartic.c + 2 * y + 2 * depressedQuartic.d / Math.sqrt(depressedQuartic.c + 2 * y)));
        double negativeSecondPart = Math.sqrt(-(3 * depressedQuartic.c + 2 * y - 2 * depressedQuartic.d / Math.sqrt(depressedQuartic.c + 2 * y)));

        double x1 = originalRootConversionPart + (firstPart + positiveSecondPart) / 2;
        double x2 = originalRootConversionPart + (-firstPart + negativeSecondPart) / 2;
        double x3 = originalRootConversionPart + (firstPart - positiveSecondPart) / 2;
        double x4 = originalRootConversionPart + (-firstPart - negativeSecondPart) / 2;

        Set<Double> realRoots = findAllRealRoots(x1, x2, x3, x4);
        return toDoubleArray(realRoots);
    }

    private double findFerraryY(QuarticFunction depressedQuartic) {
        double a3 = 1;
        double a2 = 5 / 2 * depressedQuartic.c;
        double a1 = 2 * Math.pow(depressedQuartic.c, 2) - depressedQuartic.e;
        double a0 = Math.pow(depressedQuartic.c, 3) / 2 - depressedQuartic.c * depressedQuartic.e / 2
            - Math.pow(depressedQuartic.d, 2) / 8;

        //CubicEquation works as expected! No need to worry! It returns either 1 or 3 roots.
        CubicEquation cubicEquation = new CubicEquation(a3, a2, a1, a0);
        double[] roots = cubicEquation.findRealRoots();

        for (double y : roots) {
            if (depressedQuartic.c + 2 * y != 0) {
                return y;
            }
        }
        throw new IllegalStateException("Ferrari method should have at least one y");
    }

    public final boolean isBiquadratic() {
        return Double.compare(b, 0) == 0 && Double.compare(d, 0) == 0;
    }

    private double[] solveUsingBiquadraticMethod() {
        //It works as expected!
        QuadraticLine quadraticEquation = new QuadraticLine(a, c, e);
        if (!quadraticEquation.hasRoots()) {
            return new double[] {};
        }

        double[] quadraticRoots = quadraticEquation.findRoots();
        Set<Double> roots = new HashSet<>();
        for (double quadraticRoot : quadraticRoots) {
            if (quadraticRoot > 0) {
                roots.add(Math.sqrt(quadraticRoot));
                roots.add(-Math.sqrt(quadraticRoot));
            } else if (quadraticRoot == 0.00) {
                roots.add(0.00);
            }
        }

        return toDoubleArray(roots);
    }

    public QuarticFunction toDepressed() {
        // http://en.wikipedia.org/wiki/Quartic_function#Converting_to_a_depressed_quartic
        double p = (8 * a * c - 3 * Math.pow(b, 2)) / (8 * Math.pow(a, 2));
        double q = (Math.pow(b, 3) - 4 * a * b * c + 8 * d * Math.pow(a, 2)) / (8 * Math.pow(a, 3));
        double r = (-3 * Math.pow(b, 4) + 256 * e * Math.pow(a, 3) - 64 * d * b * Math.pow(a, 2) + 16 * c * a
            * Math.pow(b, 2)) / (256 * Math.pow(a, 4));
        return new QuarticFunction(1, 0, p, q, r);
    }

    private Set<Double> findAllRealRoots(double... roots) {
        Set<Double> realRoots = new HashSet<>();
        for (double root : roots) {
            if (!Double.isNaN(root)) {
                realRoots.add(root);
            }
        }
        return realRoots;
    }

    private double[] toDoubleArray(Collection<Double> values) {
        double[] doubleArray = new double[values.size()];
        int i = 0;
        for (double value : values) {
            doubleArray[i] = value;
            ++i;
        }
        return doubleArray;
    }
}

      

I tried a simpler implementation I found here , but I got a new problem. Now half of the roots are good, but the other half are wrong. Here's my example:

private double[] solveUsingFerrariMethodTheorem() {
    // https://proofwiki.org/wiki/Ferrari's_Method
    CubicEquation cubicEquation = getCubicEquationForFerrariMethodTheorem();
    double[] cubicRoots = cubicEquation.findRealRoots();

    double y1 = findFirstNonZero(cubicRoots);

    double inRootOfP = Math.pow(b, 2) / Math.pow(a, 2) - 4 * c / a + 4 * y1;
    double p1 = b / a + Math.sqrt(inRootOfP);
    double p2 = b / a - Math.sqrt(inRootOfP);

    double inRootOfQ = Math.pow(y1, 2) - 4 * e / a;
    double q1 = y1 - Math.sqrt(inRootOfQ);
    double q2 = y1 + Math.sqrt(inRootOfQ);

    double x1 = (-p1 + Math.sqrt(Math.pow(p1, 2) - 8 * q1)) / 4;
    double x2 = (-p2 - Math.sqrt(Math.pow(p2, 2) - 8 * q1)) / 4;
    double x3 = (-p1 + Math.sqrt(Math.pow(p1, 2) - 8 * q2)) / 4;
    double x4 = (-p2 - Math.sqrt(Math.pow(p2, 2) - 8 * q2)) / 4;

    Set<Double> realRoots = findAllRealRoots(x1, x2, x3, x4);
    return toDoubleArray(realRoots);
}

private CubicEquation getCubicEquationForFerrariMethodTheorem() {
    double cubicA = 1;
    double cubicB = -c / a;
    double cubicC = b * d / Math.pow(a, 2) - 4 * e / a;
    double cubicD = 4 * c * e / Math.pow(a, 2) - Math.pow(b, 2) * e / Math.pow(a, 3) - Math.pow(d, 2)
            / Math.pow(a, 2);
    return new CubicEquation(cubicA, cubicB, cubicC, cubicD);
}

private double findFirstNonZero(double[] values) {
    for (double value : values) {
        if (Double.compare(value, 0) != 0) {
            return value;
        }
    }
    throw new IllegalArgumentException(values + " does not contain any non-zero value");
}

      

I don't know what I am missing. I spent hours debugging trying to see errors, but now I am completely lost (plus some headaches). What am I doing wrong? I don't care which formulas to use, as I want one to work.

+3


source to share


2 answers


I had two errors.

  • All my "integer constants" had to be in duplicate.
  • In the Wikipedia method, when the depressive quartic is biquadratic, we have to translate the roots to the original quartic. I brought back suppressed roots.


Here is my resulting implementation

public class QuarticFunction {

    private static final double NEAR_ZERO = 0.0000001;

    private final double a;
    private final double b;
    private final double c;
    private final double d;
    private final double e;

    public QuarticFunction(double a, double b, double c, double d, double e) {
        this.a = a;
        this.b = b;
        this.c = c;
        this.d = d;
        this.e = e;
    }

    public final double[] findRealRoots() {
        if (Math.abs(a) < NEAR_ZERO) {
            return new CubicFunction(b, c, d, e).findRealRoots();
        }

        if (isBiquadratic()) {
            return solveUsingBiquadraticMethod();
        }

        return solveUsingFerrariMethodWikipedia();
    }

    private double[] solveUsingFerrariMethodWikipedia() {
        // http://en.wikipedia.org/wiki/Quartic_function#Ferrari.27s_solution
        QuarticFunction depressedQuartic = toDepressed();
        if (depressedQuartic.isBiquadratic()) {
            double[] depressedRoots = depressedQuartic.solveUsingBiquadraticMethod();
            return reconvertToOriginalRoots(depressedRoots);
        }

        double y = findFerraryY(depressedQuartic);
        double originalRootConversionPart = -b / (4.0 * a);
        double firstPart = Math.sqrt(depressedQuartic.c + 2.0 * y);

        double positiveSecondPart = Math.sqrt(-(3.0 * depressedQuartic.c + 2.0 * y + 2.0 * depressedQuartic.d
                / Math.sqrt(depressedQuartic.c + 2.0 * y)));
        double negativeSecondPart = Math.sqrt(-(3.0 * depressedQuartic.c + 2.0 * y - 2.0 * depressedQuartic.d
                / Math.sqrt(depressedQuartic.c + 2.0 * y)));

        double x1 = originalRootConversionPart + (firstPart + positiveSecondPart) / 2.0;
        double x2 = originalRootConversionPart + (-firstPart + negativeSecondPart) / 2.0;
        double x3 = originalRootConversionPart + (firstPart - positiveSecondPart) / 2.0;
        double x4 = originalRootConversionPart + (-firstPart - negativeSecondPart) / 2.0;

        Set<Double> realRoots = findOnlyRealRoots(x1, x2, x3, x4);
        return toDoubleArray(realRoots);
    }

    private double[] reconvertToOriginalRoots(double[] depressedRoots) {
        double[] originalRoots = new double[depressedRoots.length];
        for (int i = 0; i < depressedRoots.length; ++i) {
            originalRoots[i] = depressedRoots[i] - b / (4.0 * a);
        }
        return originalRoots;
    }

    private double findFerraryY(QuarticFunction depressedQuartic) {
        double a3 = 1.0;
        double a2 = 5.0 / 2.0 * depressedQuartic.c;
        double a1 = 2.0 * Math.pow(depressedQuartic.c, 2.0) - depressedQuartic.e;
        double a0 = Math.pow(depressedQuartic.c, 3.0) / 2.0 - depressedQuartic.c * depressedQuartic.e / 2.0
                - Math.pow(depressedQuartic.d, 2.0) / 8.0;

        CubicFunction cubicFunction = new CubicFunction(a3, a2, a1, a0);
        double[] roots = cubicFunction.findRealRoots();

        for (double y : roots) {
            if (depressedQuartic.c + 2.0 * y != 0.0) {
                return y;
            }
        }
        throw new IllegalStateException("Ferrari method should have at least one y");
    }

    private double[] solveUsingBiquadraticMethod() {
        QuadraticFunction quadraticFunction = new QuadraticFunction(a, c, e);
        if (!quadraticFunction.hasRoots()) {
            return new double[] {};
        }

        double[] quadraticRoots = quadraticFunction.findRoots();
        Set<Double> roots = new HashSet<>();
        for (double quadraticRoot : quadraticRoots) {
            if (quadraticRoot > 0.0) {
                roots.add(Math.sqrt(quadraticRoot));
                roots.add(-Math.sqrt(quadraticRoot));
            } else if (quadraticRoot == 0.00) {
                roots.add(0.00);
            }
        }

        return toDoubleArray(roots);
    }

    public QuarticFunction toDepressed() {
        // http://en.wikipedia.org/wiki/Quartic_function#Converting_to_a_depressed_quartic
        double p = (8.0 * a * c - 3.0 * Math.pow(b, 2.0)) / (8.0 * Math.pow(a, 2.0));
        double q = (Math.pow(b, 3.0) - 4.0 * a * b * c + 8.0 * d * Math.pow(a, 2.0)) / (8.0 * Math.pow(a, 3.0));
        double r = (-3.0 * Math.pow(b, 4.0) + 256.0 * e * Math.pow(a, 3.0) - 64.0 * d * b * Math.pow(a, 2.0) + 16.0 * c
                * a * Math.pow(b, 2.0))
                / (256.0 * Math.pow(a, 4.0));
        return new QuarticFunction(1.0, 0.0, p, q, r);
    }


    private Set<Double> findOnlyRealRoots(double... roots) {
        Set<Double> realRoots = new HashSet<>();
        for (double root : roots) {
            if (Double.isFinite(root)) {
                realRoots.add(root);
            }
        }
        return realRoots;
    }


    private double[] toDoubleArray(Collection<Double> values) {
        double[] doubleArray = new double[values.size()];
        int i = 0;
        for (double value : values) {
            doubleArray[i] = value;
            ++i;
        }
        return doubleArray;
    }
}

      

+3


source


The Wikipedia article you mention is correct. However, it is not easy to apply formulas because of the limited floating point arithmetic precision. This is especially true when comparing the values ​​of the determinants with zero, since in some cases insufficient precision can change the sign.

Here is my naive C ++ implementation (work in progress) which, as you can see, is full of thresholds. I am planning on using high precision adaptive arithmetic to get rid of the thresholds in the future. This somehow works for equations with 1 - 4 real roots in the range [-100, 100]

:

template <class T = double, bool b_sort_roots = true,
    const int n_4th_order_coeff_log10_thresh = -6,
    const int n_depressed_1st_order_coeff_log10_thresh = -6,
    const int n_zero_discriminant_log10_thresh = -6,
    const int n_cubic_3rd_order_coeff_log10_thresh = -6,
    const int n_cubic_second_root_precision_abs_log10_thresh = -6,
    const int n_cubic_second_root_precision_rel_log10_thresh = 1,
    const int n_quad_zero_discriminant_log10_thresh = -5> // lower than above
class CQuarticEq {
protected:
    T a; /**< @brief 4th order coefficient */
    T b; /**< @brief 3rd order coefficient */
    T c; /**< @brief the 2nd order coefficient */
    T d; /**< @brief the 1st order coefficient */
    T e; /**< @brief 0th order coefficient */
    T p_real_root[4]; /**< @brief list of the roots (real parts) */
    //T p_im_root[4]; // imaginary part of the roots
    size_t n_real_root_num; /**< @brief number of real roots */

public:
    /**
     *  @brief default constructor; solves for roots of \f$ax^4 + bx^3 + cx^2 + dx + e = 0\f$
     *
     *  This finds roots of the given equation. It tends to find two identical roots instead of one, rather
     *  than missing one of two different roots - the number of roots found is therefore orientational,
     *  as the roots might have the same value.
     *
     *  @param[in] _a is 4th order coefficient
     *  @param[in] _b is 3rd order coefficient
     *  @param[in] _c is the 2nd order coefficient
     *  @param[in] _d is the 1st order coefficient
     *  @param[in] _e is constant coefficient
     */
    CQuarticEq(T _a, T _b, T _c, T _d, T _e)
        :a(_a), b(_b), c(_c), d(_d), e(_e), n_real_root_num(0)
    {
        if(fabs(_a) < f_Power_Static(10, n_4th_order_coeff_log10_thresh)) { // otherwise division by a yields large numbers, this is then more precise
            CCubicEq<T, b_sort_roots, n_cubic_3rd_order_coeff_log10_thresh,
                n_cubic_second_root_precision_abs_log10_thresh,
                n_cubic_second_root_precision_rel_log10_thresh,
                n_quad_zero_discriminant_log10_thresh> eq3(_b, _c, _d, _e);
            n_real_root_num = eq3.n_RealRoot_Num();
            for(unsigned int i = 0; i < n_real_root_num; ++ i) {
                p_real_root[i] = eq3.f_RealRoot(i);
                //p_im_root[i] = eq3.f_ImagRoot(i);
            }
            return;
        }
        // the highest power is multiplied by 0, it is a cubic equation

        if(fabs(_e) == 0) {
            CCubicEq<T, b_sort_roots, n_cubic_3rd_order_coeff_log10_thresh,
                n_cubic_second_root_precision_abs_log10_thresh,
                n_cubic_second_root_precision_rel_log10_thresh,
                n_quad_zero_discriminant_log10_thresh> eq3(_a, _b, _c, _d);
            n_real_root_num = eq3.n_RealRoot_Num();
            for(unsigned int i = 0; i < n_real_root_num; ++ i) {
                p_real_root[i] = eq3.f_RealRoot(i);
                //p_im_root[i] = eq3.f_ImagRoot(i);
            }
            return;
        }
        // the constant is 0, divide the whole equation by x to get a cubic equation

        if(fabs(b) == 0 && fabs(d) == 0) { // this possibly needs to be done with a threshold
            CQuadraticEq<T, b_sort_roots, -6, n_quad_zero_discriminant_log10_thresh> eq2(a, c, e);
            for(int i = 0, n = eq2.n_RealRoot_Num(); i < n; ++ i) {
                T y = eq2.f_RealRoot(i);
                if(y < 0) {
                    if(y > -f_Power_Static(10, n_zero_discriminant_log10_thresh)) // only slightly negative
                        p_real_root[n_real_root_num ++] = 0;
                    continue; // this root would be imaginary
                }
                y = sqrt(y);
                if(y > 0)
                    p_real_root[n_real_root_num ++] = -y; // generate the roots in approximately sorted order, avoid negative zeros
                p_real_root[n_real_root_num ++] = y;
            }
            // solve a biquadratic equation a*x^4 + c*x^2 + e = 0
        } else {
#if 0 // this first branch seems to be slightly less precise in the worst case
            _b /= _a;
            _c /= _a;
            _d /= _a;
            _e /= _a;
            // normalize

            T f_roots_offset = _b / 4;
            T f_alpha = _c - 3.0 / 8 * _b * _b; // p // ok
            T f_beta = _b * _b * _b / 8 - _b * _c / 2 + _d; // q // ok
            T f_gamma = -3.0 / 256 * _b * _b * _b * _b + _e - 64.0 / 256 * _d * _b + 16.0 / 256 * _b * _b * _c; // r
#else // 0
            const T a4 = _a, a3 = _b, a2 = _c, a1 = _d, a0 = _e; // just rename, no normalization
            T f_roots_offset = a3 / (4 * a4);
            T f_alpha = (8 * a2 * a4 - 3 * a3 * a3) / (8 * a4 * a4);
            T f_beta = (a3 * a3 * a3 - 4 * a2 * a3 * a4 + 8 * a1 * a4 * a4) / (8 * a4 * a4 * a4);
            T f_gamma = (-3 * a3 * a3 * a3 * a3 + 256 * a0 * a4 * a4 * a4 -
                64 * a1 * a3 * a4 * a4 + 16 * a2 * a3 * a3 * a4) / (256 * a4 * a4 * a4 * a4);
#endif // 0
            // convert to a depressed quartic u^4 + f_alpha*u^2 + f_beta*u + f_gamma = 0 (where x = u - f_roots_offset)

            if(fabs(f_beta) < f_Power_Static(10, n_depressed_1st_order_coeff_log10_thresh)) {
                CQuadraticEq<T, b_sort_roots, -6, n_quad_zero_discriminant_log10_thresh> eq2(1, f_alpha, f_gamma);
                for(int i = 0, n = eq2.n_RealRoot_Num(); i < n; ++ i) {
                    T y = eq2.f_RealRoot(i);
                    if(y < 0) {
                        if(y > -f_Power_Static(10, n_zero_discriminant_log10_thresh)) // only slightly negative
                            p_real_root[n_real_root_num ++] = 0 - f_roots_offset;
                        continue; // this root would be imaginary
                    }
                    y = sqrt(y);
                    p_real_root[n_real_root_num ++] = -y - f_roots_offset;
                    if(y > 0)
                        p_real_root[n_real_root_num ++] = y - f_roots_offset; // generate the roots in approximately sorted order
                }
                // solve a depressed quartic u^4 + f_alpha*u^2 + f_gamma = 0
            } else {
                T f_cub_a = 1, f_cub_b = 5.0 / 2 * f_alpha, f_cub_c = 2 * f_alpha * f_alpha - f_gamma,
                    f_cub_d = (f_alpha * f_alpha * f_alpha - f_alpha * f_gamma) / 2 - f_beta * f_beta / 8;
                // form a cubic, which gets the parameter y for Ferrari method

                CCubicEq<T, b_sort_roots, n_cubic_3rd_order_coeff_log10_thresh,
                    n_cubic_second_root_precision_abs_log10_thresh,
                    n_cubic_second_root_precision_rel_log10_thresh,
                    n_quad_zero_discriminant_log10_thresh> eq3(f_cub_a, f_cub_b, f_cub_c, f_cub_d);
                for(int i = 0, n = eq3.n_RealRoot_Num(); i < n; ++ i) {
                    const T y = eq3.f_RealRoot(i);
                    _ASSERTE(fabs(f_alpha + 2 * y) > 0); // if this triggers, we have apparently missed to detect a biquadratic equation above (would cause a division by zero later on)
                    // get a root of the cubic equation

                    T f_a2y = f_alpha + 2 * y;
                    if(f_a2y < 0)
                        continue; // the root would be an imaginary one
                    f_a2y = (T)sqrt(f_a2y);
                    // get square root of alpha + 2y

                    {
                        T f_det0 = -(3 * f_alpha + 2 * y + 2 * f_beta / f_a2y);
                        if(f_det0 >= 0) {
                            double f_det_sqrt = sqrt(f_det0);
                            p_real_root[n_real_root_num ++] = (f_a2y + f_det_sqrt) / 2 - f_roots_offset;
                            if(f_det_sqrt > 0) // otherwise they are the same
                                p_real_root[n_real_root_num ++] = (f_a2y - f_det_sqrt) / 2 - f_roots_offset;
                        } else if(f_det0 > -f_Power_Static(10, n_zero_discriminant_log10_thresh)) { // only slightly negative
                            double f_det_sqrt = 0;
                            p_real_root[n_real_root_num ++] = (f_a2y + f_det_sqrt) / 2 - f_roots_offset;
                        }
                    }
                    {
                        T f_det1 = -(3 * f_alpha + 2 * y - 2 * f_beta / f_a2y);
                        if(f_det1 >= 0) {
                            double f_det_sqrt = sqrt(f_det1);
                            p_real_root[n_real_root_num ++] = (-f_a2y + f_det_sqrt) / 2 - f_roots_offset;
                            if(f_det_sqrt > 0) // otherwise they are the same
                                p_real_root[n_real_root_num ++] = (-f_a2y - f_det_sqrt) / 2 - f_roots_offset;
                        } else if(f_det1 > -f_Power_Static(10, n_zero_discriminant_log10_thresh)) { // only slightly negative
                            double f_det_sqrt = 0;
                            p_real_root[n_real_root_num ++] = (-f_a2y + f_det_sqrt) / 2 - f_roots_offset;
                        }
                    }

                    break; // the same results are obtained for any value of y
                }
                // find roots using Ferrari method
            }
        }

        if(b_sort_roots)
            std::sort(p_real_root, p_real_root + n_real_root_num);
        // sort the roots explicitly (we could use an ordered merge above,
        // but that would convolute the code somewhat)
    }

    /**
     *  @brief gets number of real roots
     *  @return Returns number of real roots (0 to 3).
     */
    size_t n_RealRoot_Num() const
    {
        _ASSERTE(n_real_root_num >= 0);
        return n_real_root_num;
    }

    /**
     *  @brief gets value of a real root
     *  @param[in] n_index is zero-based index of the root
     *  @return Returns value of the specified root.
     */
    T f_RealRoot(size_t n_index) const
    {
        _ASSERTE(n_index < 4 && n_index < n_real_root_num);
        return p_real_root[n_index];
    }

    /**
     *  @brief evaluates the equation for a given argument
     *  @param[in] f_x is value of the argument \f$x\f$
     *  @return Returns value of \f$ax^4 + bx^3 + cx^2 + dx + e\f$.
     */
    T operator ()(T f_x) const
    {
        T f_x2 = f_x * f_x;
        T f_x3 = f_x2 * f_x;
        T f_x4 = f_x2 * f_x2;
        return f_x4 * a + f_x3 * b + f_x2 * c + f_x * d + e;
    }
};

      

You can use it as a starting point to debug your implementation. This gives the following results:



root response precision 1e-2147483648: 1774587 cases
root response precision 1e-24: 5 cases
root response precision 1e-23: 1 cases
root response precision 1e-22: 10 cases
root response precision 1e-21: 9 cases
root response precision 1e-20: 16 cases
root response precision 1e-19: 43 cases
root response precision 1e-18: 84 cases
root response precision 1e-17: 136 cases
root response precision 1e-16: 447 cases
root response precision 1e-15: 97499 cases
root response precision 1e-14: 394130 cases
root response precision 1e-13: 483321 cases
root response precision 1e-12: 435378 cases
root response precision 1e-11: 203487 cases
root response precision 1e-10: 69011 cases
root response precision 1e-9: 22429 cases
root response precision 1e-8: 6961 cases
root response precision 1e-7: 2368 cases
root response precision 1e-6: 744 cases
root response precision 1e-5: 208 cases
root response precision 1e-4: 24 cases

4-root solution precision 1e-2147483648: 73928 cases
4-root solution precision 1e-23: 1876 cases
4-root solution precision 1e-22: 81123 cases
4-root solution precision 1e-21: 293684 cases
4-root solution precision 1e-20: 745332 cases
4-root solution precision 1e-19: 919578 cases
4-root solution precision 1e-18: 597248 cases
4-root solution precision 1e-17: 268523 cases
4-root solution precision 1e-16: 99841 cases
4-root solution precision 1e-15: 33995 cases
4-root solution precision 1e-14: 11962 cases
4-root solution precision 1e-13: 4366 cases
4-root solution precision 1e-12: 1609 cases
4-root solution precision 1e-11: 547 cases
4-root solution precision 1e-10: 194 cases
4-root solution precision 1e-9: 64 cases
4-root solution precision 1e-8: 4 cases
4-root solution precision 1e-7: 2 cases

3-root solution precision 1e-2147483648: 75013 cases
3-root solution precision 1e-23: 321 cases
3-root solution precision 1e-22: 3281 cases
3-root solution precision 1e-21: 4961 cases
3-root solution precision 1e-20: 5543 cases
3-root solution precision 1e-19: 3248 cases
3-root solution precision 1e-18: 1424 cases
3-root solution precision 1e-17: 654 cases
3-root solution precision 1e-16: 177 cases
3-root solution precision 1e-15: 96 cases
3-root solution precision 1e-14: 372 cases
3-root solution precision 1e-13: 874 cases
3-root solution precision 1e-12: 7575 cases
3-root solution precision 1e-11: 14236 cases
3-root solution precision 1e-10: 11076 cases
3-root solution precision 1e-9: 5020 cases
3-root solution precision 1e-8: 2202 cases
3-root solution precision 1e-7: 1044 cases
3-root solution precision 1e-6: 326 cases
3-root solution precision 1e-5: 39 cases
3-root solution precision 1e-4: 4 cases
3-root solution precision 1e-3: 1 cases

2-root solution precision 1e-2147483648: 35077 cases
2-root solution precision 1e-23: 399 cases
2-root solution precision 1e-22: 2054 cases
2-root solution precision 1e-21: 3016 cases
2-root solution precision 1e-20: 3236 cases
2-root solution precision 1e-19: 2340 cases
2-root solution precision 1e-18: 1337 cases
2-root solution precision 1e-17: 687 cases
2-root solution precision 1e-16: 356 cases
2-root solution precision 1e-15: 3596 cases
2-root solution precision 1e-14: 12063 cases
2-root solution precision 1e-13: 8287 cases
2-root solution precision 1e-12: 7902 cases
2-root solution precision 1e-11: 11479 cases
2-root solution precision 1e-10: 4458 cases
2-root solution precision 1e-9: 1212 cases
2-root solution precision 1e-8: 142 cases
2-root solution precision 1e-7: 21 cases
2-root solution precision 1e-6: 2 cases
2-root solution precision 1e-4: 1 cases
2-root solution precision 1e-3: 1 cases

1-root solution precision 1e-2147483648: 111525 cases
1-root solution precision 1e-23: 867 cases
1-root solution precision 1e-22: 3505 cases
1-root solution precision 1e-21: 2735 cases
1-root solution precision 1e-20: 1888 cases
1-root solution precision 1e-19: 686 cases
1-root solution precision 1e-18: 497 cases
1-root solution precision 1e-17: 138 cases
1-root solution precision 1e-16: 26 cases
1-root solution precision 1e-14: 2 cases

had 0 quartic equations with 0 roots
had 121869 quartic equations with 1 roots
had 48833 quartic equations with 2 roots
had 45829 quartic equations with 3 roots
had 783469 quartic equations with 4 roots

      

Where "the accuracy of the root response" means the absolute value of the function in the found root (error in y

), and the "accuracy of the solution of the root" is the distance from the found root to the root true root (error in x

).

Please note that I do not give CCubicEq

and CQuadraticEq

, since you already have one.

+1


source







All Articles