]> gitweb.michael.orlitzky.com - numerical-analysis.git/blobdiff - src/Roots/Fast.hs
Update numeric-prelude and fixed-vector.
[numerical-analysis.git] / src / Roots / Fast.hs
index 78c299ad6a2cc39629b409bff5eed86787a3ca20..f7cde8a82a70f3f98605ab87fc94fae5a59147dc 100644 (file)
@@ -14,7 +14,8 @@ import Normed
 
 import NumericPrelude hiding (abs)
 import qualified Algebra.Absolute as Absolute
-import qualified Algebra.Field as Field
+import qualified Algebra.Additive as Additive
+import qualified Algebra.Algebraic as Algebraic
 import qualified Algebra.RealRing as RealRing
 import qualified Algebra.RealField as RealField
 
@@ -98,6 +99,54 @@ bisect f a b epsilon f_of_a f_of_b
 
 
 
+trisect :: (RealField.C a,
+            RealRing.C b,
+            Absolute.C b)
+        => (a -> b) -- ^ The function @f@ whose root we seek
+        -> a       -- ^ The \"left\" endpoint of the interval, @a@
+        -> a       -- ^ The \"right\" endpoint of the interval, @b@
+        -> a       -- ^ The tolerance, @epsilon@
+        -> Maybe b -- ^ Precomputed f(a)
+        -> Maybe b -- ^ Precomputed f(b)
+        -> Maybe a
+trisect f a b epsilon f_of_a f_of_b
+  -- We pass @epsilon@ to the 'has_root' function because if we want a
+  -- result within epsilon of the true root, we need to know that
+  -- there *is* a root within an interval of length epsilon.
+  | not (has_root f a b (Just epsilon) (Just f_of_a') (Just f_of_b')) = Nothing
+  | f_of_a' == 0 = Just a
+  | f_of_b' == 0 = Just b
+  | otherwise =
+      -- Use a 'prime' just for consistency.
+    let (a', b', fa', fb') =
+          if (has_root f d b (Just epsilon) (Just f_of_d') (Just f_of_b'))
+          then (d, b, f_of_d', f_of_b')
+          else
+            if (has_root f c d (Just epsilon) (Just f_of_c') (Just f_of_d'))
+            then (c, d, f_of_c', f_of_d')
+            else (a, c, f_of_a', f_of_c')
+    in
+      if (b-a) < 2*epsilon
+      then Just ((b+a)/2)
+      else trisect f a' b' epsilon (Just fa') (Just fb')
+  where
+    -- Compute f(a) and f(b) only if needed.
+    f_of_a'  = case f_of_a of
+                 Nothing -> f a
+                 Just v -> v
+
+    f_of_b'  = case f_of_b of
+                 Nothing -> f b
+                 Just v -> v
+
+    c = (2*a + b) / 3
+
+    d = (a + 2*b) / 3
+
+    f_of_c' = f c
+    f_of_d' = f d
+
+
 
 -- | Iterate the function @f@ with the initial guess @x0@ in hopes of
 --   finding a fixed point.
@@ -116,9 +165,9 @@ fixed_point_iterations f x0 =
 --   We also return the number of iterations required.
 --
 fixed_point_with_iterations :: (Normed a,
-                                Field.C b,
-                                Absolute.C b,
-                                Ord b)
+                                Additive.C a,
+                                RealField.C b,
+                                Algebraic.C b)
                             => (a -> a)  -- ^ The function @f@ to iterate.
                             -> b        -- ^ The tolerance, @epsilon@.
                             -> a        -- ^ The initial value @x0@.