Reputation: 51
I need to write a Haskell function that resolves the Newton-Raphson algorithm by passing a function, its derivative and the initial point (x0) as arguments, and returns an approximation of the function root.
Example:
f(x) = x^2 − 2x + 1
f′(x) = 3x^2 − 2
x0 = −1.5
.
x3 = −1.618 = x2 − f′(x2)/f(x2)
I would appreciate a lot all of your help and suggestions.
What I have tried before is this:
newtonR f g x0 =
if (x0 - (f x0 / g x0)) /= 0 then
newtonR f g (x0 - f x0 / g x0)
else
x0
... and it returns the following error message:
No instance for (Show (Double -\> Double))
arising from a use of \`print'
(maybe you haven't applied a function to enough arguments?)
Upvotes: 2
Views: 178
Reputation: 51
Thanks a lot to 'jpmarinier' for helping me to resolve this. What i finaly did is:
{-# OPTIONS_GHC -Wno-unrecognised-pragmas #-}
{-# HLINT ignore "Redundant bracket" #-}
import Prelude hiding (words)
import qualified Data.Char as Char
-------------------------------
-- f's def
f :: Num a => a -> a
f x = (x^3)-7
-- f' = g
g :: Num a => a -> a
g x = 3*(x^2)
-- Aux func that works with x0
iterateF :: (Num a, Fractional a) => a -> a
iterateF a =
a - (f a)/(g a)
-- To make rounded comparisons
truncate' :: (RealFrac a, Integral p) => a -> p -> a
truncate' x n = (fromIntegral (floor (x * t))) / t
where t = 10^n
-- Main func
newtonR :: RealFrac t => t -> t
newtonR x0 = -- x0 specifies the start point
if truncate' (iterateF x0) 3 == truncate' x0 3 then
x0
else
newtonR (iterateF x0)
Notice that the accuracy level in the comparison is controlled by the algorythm.
Upvotes: 1
Reputation: 4733
One can start by writing a function that performs a single iteration of Newton-Raphson. Note that it is good and common practice to provide an explicit type signature before the body of the function:
nrIter :: (Double -> Double) -> (Double -> Double) -> Double -> Double
nrIter f f' x0 = x0 - (f x0 / f' x0)
It is one the idiosyncrasies of Haskell that f'
is a valid identifier, unlike in classic imperative languages.
As mentioned in the comments, testing floating-point numbers for equality is error prone, because of inevitable rounding errors. Instead, we can provide an explicit tolerance level, and write the upper level logic for Newton-Raphson like this:
newtonR :: (Double -> Double) -> (Double -> Double) -> Double -> Double -> Double
newtonR f f' tol x0 =
let
x1 = nrIter f f' x0
dx = abs (x1 -x0)
in
if (dx < tol) then x1
else newtonR f f' tol x1
Sample test code:
We can compute an approximation of the cubic root of 2 like this:
main :: IO ()
main = do
let cr2 = newtonR (\x -> x^3 - 2.0) (\x -> 3.0*x^2) 1.0e-9 1.0
putStrLn $ "Cubic root of 2 is close to: " ++ (show cr2)
Upvotes: 5