solveQuartic.h

Go to the documentation of this file.
00001 
00016 #ifndef DLR_NUMERIC_SOLVEQUARTIC_H
00017 #define DLR_NUMERIC_SOLVEQUARTIC_H
00018 
00019 #include <complex>
00020 
00021 namespace dlr {
00022 
00023   namespace numeric {
00024   
00053     template <class Type>
00054     void
00055     solveQuartic(Type c0, Type c1, Type c2, Type c3,
00056                  std::complex<Type>& root0, std::complex<Type>& root1,
00057                  std::complex<Type>& root2, std::complex<Type>& root3);
00058 
00059   
00060   } // namespace numeric
00061 
00062 } // namespace dlr
00063 
00064 
00065 /* ======== Inline and template definitions below. ======== */
00066 
00067 #include <cmath>
00068 #include <dlrCommon/constants.h>
00069 #include <dlrNumeric/solveCubic.h>
00070 #include <dlrNumeric/solveQuadratic.h>
00071 
00072 // xxx
00073 #include <iostream>
00074 
00075 namespace dlr {
00076 
00077   namespace numeric {
00078 
00079     // This function computes the (possibly complex) roots of the
00080     // quartic polynomial x^4 + c0*x^3 + c1*x^2 + c2*x + c3 = 0.
00081     template <class Type>
00082     void
00083     solveQuartic(Type c0, Type c1, Type c2, Type c3,
00084                  std::complex<Type>& root0, std::complex<Type>& root1,
00085                  std::complex<Type>& root2, std::complex<Type>& root3)
00086     {
00087       // This solution method follows the "Quick and memorable
00088       // solution from first principles" algorithm contributed to
00089       // http://en.wikipedia.org/wiki/Quartic_equation by user
00090       // Goatchurch on Nov. 6, 2006.
00091       //
00092       // Start with the change of variables x = u - c0/4, to get a
00093       // depressed quartic:
00094       //
00095       //   u^4 + alpha*u^2 + beta*u + gamma = 0,
00096       //
00097       //   alpha = (-3*c0^2 / 8) + c1,
00098       //   beta = (c0^3 / 8) - ((c0 * c1) / 2) + c2,
00099       //   gamma = (-3*c0^4 / 256) + ((c0^2 * c1) / 16) - ((c0 * c2) / 4) + c3.
00100 
00101       Type c0Squared = c0 * c0;
00102       Type c0Cubed = c0Squared * c0;
00103       Type minus3C0Squared = Type(-3.0) * c0Squared;
00104 
00105       Type alpha = (minus3C0Squared / Type(8.0)) + c1;
00106       Type beta = (c0Cubed / Type(8.0)) - ((c0 * c1) / Type(2.0)) + c2;
00107       Type gamma = (((minus3C0Squared * c0Squared) / Type(256.0))
00108                     + ((c0Squared * c1) / Type(16.0)) - ((c0 * c2) / Type(4.0))
00109                     + c3);
00110 
00111       // We'll factor the depressed quartic into the product of two
00112       // quadratics:
00113       //
00114       //   (u^2 + p*u + q) * (u^2 + r*u + s) = 0,
00115       //
00116       // where r = -p (so that the cubed term drops out).
00117       //
00118       // Setting the two polynomials in u equal, we have:
00119       //
00120       //   alpha = s - p^2 + q
00121       //   beta = p * (s - q)
00122       //   gamma = sq
00123       // 
00124       // From which we get:
00125       //
00126       //   alpha + p^2 = s + q
00127       //   (alpha + p^2)^2 = (s + q)^2
00128       // 
00129       //   beta / p = s - q
00130       //   (beta / p)^2 = (s - q)^2
00131       //
00132       //   (alpha + p^2)^2 - (beta / p)^2 = (s + q)^2 - (s - q)^2
00133       //   = 4sq = 4*gamma
00134       //
00135       // Substituting P = p^2 gives:
00136       //
00137       //   P^2 + 2*alpha*P + alpha^2 - (beta^2 / P) = 4*gamma
00138       // 
00139       // or equivalently
00140       // 
00141       //   P^3 + 2*alpha*P^2 +(alpha^2 - 4*gamma)*P - beta^2 = 0
00142       //
00143       // which we solve for P.
00144 
00145       Type k0 = Type(2.0) * alpha;
00146       Type k1 = alpha * alpha - Type(4.0) * gamma;
00147       Type k2 = -(beta * beta);
00148       std::complex<Type> r0;
00149       std::complex<Type> r1;
00150       std::complex<Type> r2;
00151       solveCubic(k0, k1, k2, r0, r1, r2);
00152 
00153       // Pick the largest root of the polynomial.
00154       Type mag2R0 = r0.real() * r0.real(); // R1 is always real.
00155       Type mag2R1 = r1.real() * r1.real() + r1.imag() * r1.imag();
00156       Type mag2R2 = r2.real() * r2.real() + r2.imag() * r2.imag();
00157       if(mag2R1 > mag2R0) {
00158         std::swap(r1, r0);
00159         std::swap(mag2R1, mag2R0);
00160       }
00161       if(mag2R2 > mag2R0) {
00162         std::swap(r2, r0);
00163         std::swap(mag2R2, mag2R0);
00164       }
00165 
00166       // Recover p.
00167       std::complex<Type> pp = std::sqrt(r0);
00168 
00169       // Recover s and q from the equations
00170       //
00171       //   alpha + p^2 = s + q
00172       //   beta / p = s - q
00173       std::complex<Type> alphaPlusPpSquared = alpha + pp * pp;
00174       std::complex<Type> betaOverPp =
00175         mag2R0 ? (beta / pp) : std::complex<Type>(Type(0.0), Type(0.0));
00176       std::complex<Type> ss = (alphaPlusPpSquared + betaOverPp) / Type(2.0);
00177       std::complex<Type> qq = (alphaPlusPpSquared - betaOverPp) / Type(2.0);
00178 
00179       // Now find the roots of the polynomial in u, and change
00180       // variables back to x:
00181       // 
00182       //   (u^2 + p*u + q) * (u^2 + r*u + s) = 0,
00183       //   x = u - c0/4
00184       
00185       solveQuadratic(pp, qq, r0, r1);
00186       root0 = r0 - c0 / Type(4.0);
00187       root1 = r1 - c0 / Type(4.0);
00188 
00189       solveQuadratic(-pp, ss, r0, r1);
00190       root2 = r0 - c0 / Type(4.0);
00191       root3 = r1 - c0 / Type(4.0);
00192     }
00193 
00194   } // namespace numeric
00195 
00196 } // namespace dlr
00197 
00198 #endif /* #ifndef DLR_NUMERIC_UTILITIES_H */

Generated on Wed Nov 25 00:42:42 2009 for dlrUtilities Utility Library by  doxygen 1.5.8