First of all, there is no way to be sure that the Newton-Raphson method will converge for an initial input. The algorithm converges once the derivative of the function reaches abs (f') <= errThreshold
. This means that the algorithm fails for
- Very 'flat' functions or
- Regions in the function domain where the derivative gets flat, i.e. functions that are locally flat.
A second issue is that for functions with multiple maximas or minimas, there will be multiple points where the function flattens. So the outcome will actually depend on the starting point.
I give these initial warnings because unless you are totally 100% sure that the function is very well defined, you could well end up in an infinite loop. So the best approach would be to, instead of setting an infinite loop, allow a maximumNumberOfIterations
, after which the algorithm 'breaks down' and returns 'could not find some of the roots' ...
That said depending on what you are calculating, consider using the bissection method instead. It tends to have a slower convergence, but then again, the convergence is less dependent on the derivative, so it will tend to converge pretty much in all cases.
The algorithm you are looking for is the following (on Python):
from numpy.random import rand
#
# The maximum number of iterations before the
# algorithm breaks.
##
maximumNumberOfIterations : int
#
# The function that we are evaluating.
##
function : Callable
#
# The lower bound.
##
lowerBound : float;
#
# The upper bound.
##
upperBound : float;
#
# The tolerance.
##
tolerance : float;
#
# An array to store the outcomes
# for each iteration
##
roots: list[float] = []
#
# For each iteration DO:
##
for iteration in range (maximumNumberOfIterations):
#
# Generate a 'random' initial guess
# specified within the allowed boundary
##
initialGuess : float = (upperBound - lowerBound) * rand()
#
# Actually try to run the algorithm and append
# the root to the list.
##
try:
roots.append (
newraphson(function, guess, lowerBound,
upperBound, tolerance)
)
#
# It is likely that the algorithm will fail and
# return a RuntimeFault for 'some' cases. We
# must handle these... Not a big deal, just do
# nothing and continue.
##
except RuntimeError:
pass
#
# Now we just need to handle the repeated roots. Being
# lazy I can just use a set and display the results...
##
roots = set (roots)
for root in roots:
print (roots)
#
# If you were expecting a certain number of roots ...
##
expectedNumberOfRoots : int
if len (roots) < expectedNumberOfRoots:
print ({n} roots could not be found"
.format (n=expectedNumberOfRoots-len (roots)))