OrderAndChaos
OrderAndChaos

Reputation: 3860

Having trouble solving cubic equations in Java

I'm attempting to follow some psuedo code for solving cubic equations in Mathematics and Physics for Programmers, Chapter 3, as far as I can see I've followed it accurately, but I don't seem to be getting correct outputs.

For example: According to Wolfram Alpha 5x^3 + 4x^2 + 3x + 2 = 0 should give a root of x≈-0.72932, but I'm getting back -1.8580943294965526 from my script.

Can someone help me to work out what the hell I'm doing? I'm following the scripts to get a better understanding of maths and converting equations to code. But this is at the brink of my understanding so I'm finding it troublesome to debug. Coupled with the fact the book has no errata and many online reviews state the book has many errors, I'm struggling to see whether the issue is with my code, the books explanation or both.

The equation given in the book is:

eq

Then if the discriminant > 0 then root has value of r+s:

D1

if discriminant == 0 then there are two roots:

D2

if discriminant < 0 you can find three roots as follows:

D3

After finding t you can transform it to x by taking:

transform

package com.oacc.maths;

public class SolveCubic {

    public static double[] solveCubic(double a, double b, double c, double d) {
        double[] result;
        if (d != 1) {
            a = a / d;
            b = b / d;
            c = c / d;
        }

        double p = b / 3 - a * a / 9;
        double q = a * a * a / 27 - a * b / 6 + c / 2;
        double D = p * p * p + q * q;

        if (Double.compare(D, 0) >= 0) {
            if (Double.compare(D, 0) == 0) {
                double r = Math.cbrt(-q);
                result = new double[2];
                result[0] = 2 * r;
                result[1] = -r;
            } else {
                double r = Math.cbrt(-q + Math.sqrt(D));
                double s = Math.cbrt(-q - Math.sqrt(D));
                result = new double[1];
                result[0] = r + s;
            }
        } else {
            double ang = Math.acos(-q / Math.sqrt(-p * p * p));
            double r = 2 * Math.sqrt(-p);
            result = new double[3];
            for (int k = -1; k <= 1; k++) {
                double theta = (ang - 2 * Math.PI * k) / 3;
                result[k + 1] = r * Math.cos(theta);
            }

        }
        for (int i = 0; i < result.length; i++) {
            result[i] = result[i] - a / 3;
        }
        return result;
    }


    public static double[] solveCubic(double a, double b, double c) {
        double d = 1;
        return solveCubic(a, b, c, d);
    }

    public static void main(String args[]) {
        double[] result = solveCubic(5, 4, 3, 2);
        for (double aResult : result) {
            System.out.println(aResult);
        }
    }
}

I also found this code example from the book site(n.b. this is not the psuedo code from the book): http://www.delmarlearning.com/companions/content/1435457331/files/index.asp?isbn=1435457331

 on solveCubic(a,b,c,d)
 --! ARGUMENTS: 
a, b, c, d (all numbers). d is optional (default is 1)
 --! 
RETURNS: the list of solutions to dx^3+ax^2+bx+c=0


-- if d is defined then divide a, b and c by d 
 if not voidp(d) 
then
 if d=0 then return solveQuadratic(b,c,a)

set d to float(d)
 set a to a/d
 set b to b/d
 set 
c to c/d
 else
 set a to float(a)
 set b to float(b)

 set c to float(c)
 end if

 set p to b/3 - a*a/9

 set q to a*a*a/27 - a*b/6 + c/2
 set disc to p*p*p + q*q


 if abs(disc)<0.001 then 
 set r to cuberoot(-q)
 set ret to [2*r, -r]
 else if disc>0 then
 set r to cuberoot(-q+sqrt(disc))
 set s to cuberoot(-q-sqrt(disc))

set ret to [r+s]
 else

 set ang to acos(-q/sqrt(-p*p*p))
 set r to 2*sqrt(-p)

set ret to []
 repeat with k=-1 to 1
 set theta 
to (ang - 2*pi*k)/3
 ret.add(r*cos(theta))
 end repeat

end if
 set ret to ret-a/3 --NB: this adds the value to each 
element
 return ret
end 

Upvotes: 1

Views: 5290

Answers (3)

Ishaan Apte
Ishaan Apte

Reputation: 19

This works 100%. It has been tried and tested for all combinations.

public void solveCubicEquation(int A, int B, int C, int D) {


    double a = (double) B / A;
    double b = (double) C / A;
    double c = (double) D / A;

    System.out.println("Double values: ");
    System.out.println(a + " " + b + " " + c);
    double p = b - ((a * a) / 3.0);

    double q = (2 * Math.pow(a, 3) / 27.0) - (a * b / 3.0) + c;

    double delta = (Math.pow(q, 2) / 4) + (Math.pow(p, 3) / 27);

    if (delta > 0.001) {

        double mt1, mt2;

        double t1 = (-q / 2.0) + Math.sqrt(delta);
        double t2 = (-q / 2.0) - Math.sqrt(delta);

        if (t1 < 0) {
            mt1 = (-1) * (Math.pow(-t1, (double) 1 / 3));
        } else {
            mt1 = (Math.pow(t1, (double) 1 / 3));
        }

        if (t2 < 0) {
            mt2 = (-1) * (Math.pow(-t2, (double) 1 / 3));
        } else {
            mt2 = (Math.pow(t2, (double) 1 / 3));
        }

        x1 = mt1 + mt2 - (a / 3.0);

    } else if (delta < 0.001 && delta > -0.001) {

        if (q < 0) {

            x1 = 2 * Math.pow(-q / 2, (double) 1 / 3) - (a / 3);
            x2 = -1 * Math.pow(-q / 2, (double) 1 / 3) - (a / 3);

        } else {
            x1 = -2 * Math.pow(q / 2, (double) 1 / 3) - (a / 3);
            x2 = Math.pow(q / 2, (double) 1 / 3) - (a / 3);

        }

    } else {

        System.out.println("here");
        x1 = (2.0 / Math.sqrt(3)) * (Math.sqrt(-p) * Math.sin((1 / 3.0) * Math.asin(((3 * Math.sqrt(3) * q) / (2 * Math.pow(Math.pow(-p, (double) 1 / 2), 3)))))) - (a / 3.0);
        x2 = (-2.0 / Math.sqrt(3)) * (Math.sqrt(-p) * Math.sin((1 / 3.0) * Math.asin(((3 * Math.sqrt(3) * q) / (2 * Math.pow(Math.pow(-p, (double) 1 / 2), 3)))) + (Math.PI / 3))) - (a / 3.0);
        x3 = (2.0 / Math.sqrt(3)) * (Math.sqrt(-p) * Math.cos((1 / 3.0) * Math.asin(((3 * Math.sqrt(3) * q) / (2 * Math.pow(Math.pow(-p, (double) 1 / 2), 3)))) + (Math.PI / 6))) - (a / 3.0);

    }

}

Note: will not work for imaginary values

Upvotes: 2

Daniel Martin
Daniel Martin

Reputation: 23548

Okay. So, first off the equations from the book seem to be referring to this idea: If you have an equation of the form:

x^3 + ax^2 + bx + c = 0

Then by defining t as x - (a/3) you can transform this into an equation with no quadratic term, as verified by a bit of Mathematica:

Mathematica mumbling

Once you have no quadratic term, you can then apply the method given; let q be half the constant term, let p be one third the coefficient on the first power term, and define D (discriminant) as p*p*p - q*q.

All else then follows.

So why does your code not work? Because you've mislabeled the variables. a is the coefficient on x^2, not on x^3. If you call your method with the arguments (0.8, 0.6, 0.4, 1) or equivalently with (4, 3, 2, 5), you'll get the right answer.

(Or do as the other answer suggests and more around the variable names in the method declaration)

Upvotes: 1

Luke Woodward
Luke Woodward

Reputation: 64969

The error appears to be with the names of the parameters of your solveCubic method.

It seems your book is explaining how to solve the equation x3 + ax2 + bx + c = 0. You are calling your method thinking that the parameters a, b, c and d are for the equation ax3 + bx2 + cx + d = 0. However, it turns out that the body of your method is actually finding solutions to the equation dx3 + ax2 + bx + c = 0.

Aside from this naming error, the calculations appear to be correct. Try plugging your value ≈-1.858 into 2x3 + 5x2 + 4x + 3 if you don't believe me.

If you instead declare your solveCubic method as

public static double[] solveCubic(double d, double a, double b, double c) {

the parameters then correspond to the equation dx3 + ax2 + bx + c. You should then find that your method gives you the answer you expect.

Upvotes: 3

Related Questions