From b55c792bd9e2d439c5f1aebae160c92941a87e4e Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Mon, 24 Sep 2012 12:14:23 -0400 Subject: [PATCH] Add the secant method to Roots.Simple. --- src/Roots/Simple.hs | 80 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/src/Roots/Simple.hs b/src/Roots/Simple.hs index 101b7e2..2689163 100644 --- a/src/Roots/Simple.hs +++ b/src/Roots/Simple.hs @@ -90,6 +90,8 @@ newton_iterations f f' x0 = +-- | Use Newton's method to find a root of @f@ near the initial guess +-- @x0@. If your guess is bad, this will recurse forever! newtons_method :: (Fractional a, Ord a) => (a -> a) -- ^ The function @f@ whose root we seek -> (a -> a) -- ^ The derivative of @f@ @@ -100,3 +102,81 @@ newtons_method f f' epsilon x0 = find (\x -> abs (f x) < epsilon) x_n where x_n = newton_iterations f f' x0 + + + +-- | Takes a function @f@ of two arguments and repeatedly applies @f@ +-- to the previous two values. Returns a list containing all +-- generated values, f(x0, x1), f(x1, x2), f(x2, x3)... +-- +-- Examples: +-- +-- >>> let fibs = iterate2 (+) 0 1 +-- >>> take 15 fibs +-- [0,1,1,2,3,5,8,13,21,34,55,89,144,233,377] +-- +iterate2 :: (a -> a -> a) -- ^ The function @f@ + -> a -- ^ The initial value @x0@ + -> a -- ^ The second value, @x1@ + -> [a] -- ^ The result list, [x0, x1, ...] +iterate2 f x0 x1 = + x0 : x1 : (go x0 x1) + where + go prev2 prev1 = + let next = f prev2 prev1 in + next : go prev1 next + +-- | The sequence x_{n} of values obtained by applying the secant +-- method on the function @f@ and initial guesses @x0@, @x1@. +-- +-- The recursion more or less implements a two-parameter 'iterate', +-- although one list is passed to the next iteration (as opposed to +-- one function argument, with iterate). At each step, we peel the +-- first two elements off the list and then compute/append elements +-- three, four... onto the end of the list. +-- +-- Examples: +-- +-- Atkinson, p. 67. +-- >>> let f x = x^6 - x - 1 +-- >>> take 4 $ secant_iterations f 2 1 +-- [2.0,1.0,1.0161290322580645,1.190577768676638] +-- +secant_iterations :: (Fractional a, Ord a) + => (a -> a) -- ^ The function @f@ whose root we seek + -> a -- ^ Initial guess, x-naught + -> a -- ^ Second initial guess, x-one + -> [a] +secant_iterations f x0 x1 = + iterate2 g x0 x1 + where + g prev2 prev1 = + let x_change = prev1 - prev2 + y_change = (f prev1) - (f prev2) + in + (prev1 - (f prev1 * (x_change / y_change))) + + +-- | Use the secant method to find a root of @f@ near the initial guesses +-- @x0@ and @x1@. If your guesses are bad, this will recurse forever! +-- +-- Examples: +-- +-- Atkinson, p. 67. +-- >>> let f x = x^6 - x - 1 +-- >>> let Just root = secant_method f (1/10^9) 2 1 +-- >>> root +-- 1.1347241384015196 +-- >>> abs (f root) < (1/10^9) +-- True +-- +secant_method :: (Fractional a, Ord a) + => (a -> a) -- ^ The function @f@ whose root we seek + -> a -- ^ The tolerance epsilon + -> a -- ^ Initial guess, x-naught + -> a -- ^ Second initial guess, x-one + -> Maybe a +secant_method f epsilon x0 x1 + = find (\x -> abs (f x) < epsilon) x_n + where + x_n = secant_iterations f x0 x1 -- 2.43.2