Reputation: 3860
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:
Then if the discriminant > 0 then root has value of r+s:
if discriminant == 0 then there are two roots:
if discriminant < 0 you can find three roots as follows:
After finding t you can transform it to x by taking:
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
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
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:
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:
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
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