----------------------------------------------------------------------------- -- | -- Module : Polynomial.Roots -- Copyright : (c) 1998 Numeric Quest Inc., All rights reserved -- License : GPL -- -- Maintainer : m.p.donadio@ieee.org -- Stability : experimental -- Portability : portable -- -- Root finder using Laguerre's method -- ----------------------------------------------------------------------------- -- This file was sucked out of the Wayback Machine at www.archive.org. -- This was orginally a HTML files containing literate Haskell. It has -- been modified to use the Polynomial library, and Haddock style comments -- have been added. As much as the original formatting has been retained -- as possible. --mpd -- Original comments are below {- Literate Haskell module Roots.lhs Jan Skibinski, Numeric Quest Inc., Huntsville, Ontario, Canada 1998.09.05, last modified 1998.09.24 This module implements Laguerre's method for finding complex roots of polynomials. According to [1], it is by far the most straightforward of these sure-fire methods. It does require that you perform complex arithmetic (even while converging to real roots), but it is guaranteed to converge to a root from any starting point. In some instances the complex arithmetic is no disadvantage, since the polynomial itself may have complex coefficients. [1] Numerical Recipes in Pascal, W.H. Press, B.P. Flannery, S.A. Teukolsky, W.T. Vetterling, Cambridge University Press, ISBN 0-521-37516-9 See also some other variations of the same book by the same authors: Numerical Recipes in C, Fortran, etc. I just happen to own [1], although I have never programmed in Pascal. :-) Example To solve the equation x^2 - 3 x + 2 = 0 form the list of coefficients [2, -3, 1] (notice the reverse order of coefficients) and execute roots 1.0e-6 300 [2,-3, 1] -- where -- 1.0e-6 is a required accuracy -- 300 is a count of permitted iterations -- (You set it to small number just in case you -- do not trust the algorithm. But if you do, -- then set it to something big, say 300) The answer is [2.0 :+ 0.0, 1.0 :+ 0.0]; that is, both roots are real and equal to 2 and 1: x^2 - 3 x + 2 = (x - 2) (x - 1) = 0 -} module Polynomial.Roots (roots) where import Data.Complex import Polynomial.Basic -- * Functions -- | Root finder using Laguerre's method roots :: RealFloat a => a -- ^ epsilon -> Int -- ^ iteration limit -> [Complex a] -- ^ the polynomial -> [Complex a] -- ^ the roots roots eps0 count0 as0 = -- -- List of complex roots of a polynomial -- a0 + a1*x + a2*x^2... -- represented by the list as=[a0,a1,a2...] -- where -- eps is a desired accuracy -- count is a maximum count of iterations allowed -- Require: list 'as' must have at least two elements -- and the last element must not be zero roots' eps0 count0 as0 [] where roots' eps count as xs | length as <= 2 = x:xs | otherwise = roots' eps count (deflate x bs [last as]) (x:xs) where x = laguerre eps count as 0 bs = drop 1 (reverse (drop 1 as)) deflate z bs' cs | bs' == [] = cs | otherwise = deflate z (tail bs') (((head bs')+z*(head cs)):cs) laguerre :: RealFloat a => a -> Int -> [Complex a] -> Complex a -> Complex a laguerre eps0 count as0 x0 -- -- One of the roots of the polynomial 'as', -- where -- eps is a desired accuracy -- count is a maximum count of iterations allowed -- x is initial guess of the root -- This method is due to Laguerre. -- | count <= 0 = x0 | magnitude (x0 - x0') < eps0 = x0' | otherwise = laguerre eps0 (count - 1) as0 x0' where x0' = laguerre2 eps0 as0 as0' as0'' x0 as0' = polyderiv as0 as0'' = polyderiv as0' laguerre2 eps as as' as'' x -- One iteration step | magnitude b < eps = x | magnitude gp < magnitude gm = if gm == 0 then x - 1 else x - n/gm | otherwise = if gp == 0 then x - 1 else x - n/gp where gp = g + delta gm = g - delta g = d/b delta = sqrt ((n-1)*(n*h - g2)) h = g2 - f/b b = polyeval as x d = polyeval as' x f = polyeval as'' x g2 = g^(2::Int) n = fromIntegral (length as) -- Original Copyright Notice ----------------------------------------------------------------------------- -- -- Copyright: -- -- (C) 1998 Numeric Quest Inc., All rights reserved -- -- Email: -- -- jans@numeric-quest.com -- -- License: -- -- GNU General Public License, GPL -- -----------------------------------------------------------------------------