mirror of
https://github.com/TheAlgorithms/Python.git
synced 2024-11-23 21:11:08 +00:00
Consolidate Newton-Raphson implementations (#10859)
* updating DIRECTORY.md * updating DIRECTORY.md * Consolidate Newton-Raphson duplicates * Rename consolidated Newton-Raphson file * updating DIRECTORY.md * updating DIRECTORY.md * Fix doctest precision * Fix doctest precision again --------- Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com>
This commit is contained in:
parent
e5d6969f38
commit
b98312ca9f
|
@ -642,10 +642,7 @@
|
|||
* [Intersection](maths/numerical_analysis/intersection.py)
|
||||
* [Nevilles Method](maths/numerical_analysis/nevilles_method.py)
|
||||
* [Newton Forward Interpolation](maths/numerical_analysis/newton_forward_interpolation.py)
|
||||
* [Newton Method](maths/numerical_analysis/newton_method.py)
|
||||
* [Newton Raphson](maths/numerical_analysis/newton_raphson.py)
|
||||
* [Newton Raphson 2](maths/numerical_analysis/newton_raphson_2.py)
|
||||
* [Newton Raphson New](maths/numerical_analysis/newton_raphson_new.py)
|
||||
* [Numerical Integration](maths/numerical_analysis/numerical_integration.py)
|
||||
* [Runge Kutta](maths/numerical_analysis/runge_kutta.py)
|
||||
* [Runge Kutta Fehlberg 45](maths/numerical_analysis/runge_kutta_fehlberg_45.py)
|
||||
|
|
|
@ -1,54 +0,0 @@
|
|||
"""Newton's Method."""
|
||||
|
||||
# Newton's Method - https://en.wikipedia.org/wiki/Newton%27s_method
|
||||
from collections.abc import Callable
|
||||
|
||||
RealFunc = Callable[[float], float] # type alias for a real -> real function
|
||||
|
||||
|
||||
# function is the f(x) and derivative is the f'(x)
|
||||
def newton(
|
||||
function: RealFunc,
|
||||
derivative: RealFunc,
|
||||
starting_int: int,
|
||||
) -> float:
|
||||
"""
|
||||
>>> newton(lambda x: x ** 3 - 2 * x - 5, lambda x: 3 * x ** 2 - 2, 3)
|
||||
2.0945514815423474
|
||||
>>> newton(lambda x: x ** 3 - 1, lambda x: 3 * x ** 2, -2)
|
||||
1.0
|
||||
>>> newton(lambda x: x ** 3 - 1, lambda x: 3 * x ** 2, -4)
|
||||
1.0000000000000102
|
||||
>>> import math
|
||||
>>> newton(math.sin, math.cos, 1)
|
||||
0.0
|
||||
>>> newton(math.sin, math.cos, 2)
|
||||
3.141592653589793
|
||||
>>> newton(math.cos, lambda x: -math.sin(x), 2)
|
||||
1.5707963267948966
|
||||
>>> newton(math.cos, lambda x: -math.sin(x), 0)
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ZeroDivisionError: Could not find root
|
||||
"""
|
||||
prev_guess = float(starting_int)
|
||||
while True:
|
||||
try:
|
||||
next_guess = prev_guess - function(prev_guess) / derivative(prev_guess)
|
||||
except ZeroDivisionError:
|
||||
raise ZeroDivisionError("Could not find root") from None
|
||||
if abs(prev_guess - next_guess) < 10**-5:
|
||||
return next_guess
|
||||
prev_guess = next_guess
|
||||
|
||||
|
||||
def f(x: float) -> float:
|
||||
return (x**3) - (2 * x) - 5
|
||||
|
||||
|
||||
def f1(x: float) -> float:
|
||||
return 3 * (x**2) - 2
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
print(newton(f, f1, 3))
|
|
@ -1,45 +1,113 @@
|
|||
# Implementing Newton Raphson method in Python
|
||||
# Author: Syed Haseeb Shah (github.com/QuantumNovice)
|
||||
# The Newton-Raphson method (also known as Newton's method) is a way to
|
||||
# quickly find a good approximation for the root of a real-valued function
|
||||
from __future__ import annotations
|
||||
"""
|
||||
The Newton-Raphson method (aka the Newton method) is a root-finding algorithm that
|
||||
approximates a root of a given real-valued function f(x). It is an iterative method
|
||||
given by the formula
|
||||
|
||||
from decimal import Decimal
|
||||
x_{n + 1} = x_n + f(x_n) / f'(x_n)
|
||||
|
||||
from sympy import diff, lambdify, symbols
|
||||
with the precision of the approximation increasing as the number of iterations increase.
|
||||
|
||||
Reference: https://en.wikipedia.org/wiki/Newton%27s_method
|
||||
"""
|
||||
from collections.abc import Callable
|
||||
|
||||
RealFunc = Callable[[float], float]
|
||||
|
||||
|
||||
def newton_raphson(func: str, a: float | Decimal, precision: float = 1e-10) -> float:
|
||||
"""Finds root from the point 'a' onwards by Newton-Raphson method
|
||||
>>> newton_raphson("sin(x)", 2)
|
||||
3.1415926536808043
|
||||
>>> newton_raphson("x**2 - 5*x + 2", 0.4)
|
||||
0.4384471871911695
|
||||
>>> newton_raphson("x**2 - 5", 0.1)
|
||||
2.23606797749979
|
||||
>>> newton_raphson("log(x) - 1", 2)
|
||||
2.718281828458938
|
||||
def calc_derivative(f: RealFunc, x: float, delta_x: float = 1e-3) -> float:
|
||||
"""
|
||||
x = symbols("x")
|
||||
f = lambdify(x, func, "math")
|
||||
f_derivative = lambdify(x, diff(func), "math")
|
||||
x_curr = a
|
||||
while True:
|
||||
x_curr = Decimal(x_curr) - Decimal(f(x_curr)) / Decimal(f_derivative(x_curr))
|
||||
if abs(f(x_curr)) < precision:
|
||||
return float(x_curr)
|
||||
Approximate the derivative of a function f(x) at a point x using the finite
|
||||
difference method
|
||||
|
||||
>>> import math
|
||||
>>> tolerance = 1e-5
|
||||
>>> derivative = calc_derivative(lambda x: x**2, 2)
|
||||
>>> math.isclose(derivative, 4, abs_tol=tolerance)
|
||||
True
|
||||
>>> derivative = calc_derivative(math.sin, 0)
|
||||
>>> math.isclose(derivative, 1, abs_tol=tolerance)
|
||||
True
|
||||
"""
|
||||
return (f(x + delta_x / 2) - f(x - delta_x / 2)) / delta_x
|
||||
|
||||
|
||||
def newton_raphson(
|
||||
f: RealFunc,
|
||||
x0: float = 0,
|
||||
max_iter: int = 100,
|
||||
step: float = 1e-6,
|
||||
max_error: float = 1e-6,
|
||||
log_steps: bool = False,
|
||||
) -> tuple[float, float, list[float]]:
|
||||
"""
|
||||
Find a root of the given function f using the Newton-Raphson method.
|
||||
|
||||
:param f: A real-valued single-variable function
|
||||
:param x0: Initial guess
|
||||
:param max_iter: Maximum number of iterations
|
||||
:param step: Step size of x, used to approximate f'(x)
|
||||
:param max_error: Maximum approximation error
|
||||
:param log_steps: bool denoting whether to log intermediate steps
|
||||
|
||||
:return: A tuple containing the approximation, the error, and the intermediate
|
||||
steps. If log_steps is False, then an empty list is returned for the third
|
||||
element of the tuple.
|
||||
|
||||
:raises ZeroDivisionError: The derivative approaches 0.
|
||||
:raises ArithmeticError: No solution exists, or the solution isn't found before the
|
||||
iteration limit is reached.
|
||||
|
||||
>>> import math
|
||||
>>> tolerance = 1e-15
|
||||
>>> root, *_ = newton_raphson(lambda x: x**2 - 5*x + 2, 0.4, max_error=tolerance)
|
||||
>>> math.isclose(root, (5 - math.sqrt(17)) / 2, abs_tol=tolerance)
|
||||
True
|
||||
>>> root, *_ = newton_raphson(lambda x: math.log(x) - 1, 2, max_error=tolerance)
|
||||
>>> math.isclose(root, math.e, abs_tol=tolerance)
|
||||
True
|
||||
>>> root, *_ = newton_raphson(math.sin, 1, max_error=tolerance)
|
||||
>>> math.isclose(root, 0, abs_tol=tolerance)
|
||||
True
|
||||
>>> newton_raphson(math.cos, 0)
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ZeroDivisionError: No converging solution found, zero derivative
|
||||
>>> newton_raphson(lambda x: x**2 + 1, 2)
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ArithmeticError: No converging solution found, iteration limit reached
|
||||
"""
|
||||
|
||||
def f_derivative(x: float) -> float:
|
||||
return calc_derivative(f, x, step)
|
||||
|
||||
a = x0 # Set initial guess
|
||||
steps = []
|
||||
for _ in range(max_iter):
|
||||
if log_steps: # Log intermediate steps
|
||||
steps.append(a)
|
||||
|
||||
error = abs(f(a))
|
||||
if error < max_error:
|
||||
return a, error, steps
|
||||
|
||||
if f_derivative(a) == 0:
|
||||
raise ZeroDivisionError("No converging solution found, zero derivative")
|
||||
a -= f(a) / f_derivative(a) # Calculate next estimate
|
||||
raise ArithmeticError("No converging solution found, iteration limit reached")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import doctest
|
||||
from math import exp, tanh
|
||||
|
||||
doctest.testmod()
|
||||
|
||||
# Find value of pi
|
||||
print(f"The root of sin(x) = 0 is {newton_raphson('sin(x)', 2)}")
|
||||
# Find root of polynomial
|
||||
print(f"The root of x**2 - 5*x + 2 = 0 is {newton_raphson('x**2 - 5*x + 2', 0.4)}")
|
||||
# Find value of e
|
||||
print(f"The root of log(x) - 1 = 0 is {newton_raphson('log(x) - 1', 2)}")
|
||||
# Find root of exponential function
|
||||
print(f"The root of exp(x) - 1 = 0 is {newton_raphson('exp(x) - 1', 0)}")
|
||||
def func(x: float) -> float:
|
||||
return tanh(x) ** 2 - exp(3 * x)
|
||||
|
||||
solution, err, steps = newton_raphson(
|
||||
func, x0=10, max_iter=100, step=1e-6, log_steps=True
|
||||
)
|
||||
print(f"{solution=}, {err=}")
|
||||
print("\n".join(str(x) for x in steps))
|
||||
|
|
|
@ -1,64 +0,0 @@
|
|||
"""
|
||||
Author: P Shreyas Shetty
|
||||
Implementation of Newton-Raphson method for solving equations of kind
|
||||
f(x) = 0. It is an iterative method where solution is found by the expression
|
||||
x[n+1] = x[n] + f(x[n])/f'(x[n])
|
||||
If no solution exists, then either the solution will not be found when iteration
|
||||
limit is reached or the gradient f'(x[n]) approaches zero. In both cases, exception
|
||||
is raised. If iteration limit is reached, try increasing maxiter.
|
||||
"""
|
||||
|
||||
import math as m
|
||||
from collections.abc import Callable
|
||||
|
||||
DerivativeFunc = Callable[[float], float]
|
||||
|
||||
|
||||
def calc_derivative(f: DerivativeFunc, a: float, h: float = 0.001) -> float:
|
||||
"""
|
||||
Calculates derivative at point a for function f using finite difference
|
||||
method
|
||||
"""
|
||||
return (f(a + h) - f(a - h)) / (2 * h)
|
||||
|
||||
|
||||
def newton_raphson(
|
||||
f: DerivativeFunc,
|
||||
x0: float = 0,
|
||||
maxiter: int = 100,
|
||||
step: float = 0.0001,
|
||||
maxerror: float = 1e-6,
|
||||
logsteps: bool = False,
|
||||
) -> tuple[float, float, list[float]]:
|
||||
a = x0 # set the initial guess
|
||||
steps = [a]
|
||||
error = abs(f(a))
|
||||
f1 = lambda x: calc_derivative(f, x, h=step) # noqa: E731 Derivative of f(x)
|
||||
for _ in range(maxiter):
|
||||
if f1(a) == 0:
|
||||
raise ValueError("No converging solution found")
|
||||
a = a - f(a) / f1(a) # Calculate the next estimate
|
||||
if logsteps:
|
||||
steps.append(a)
|
||||
if error < maxerror:
|
||||
break
|
||||
else:
|
||||
raise ValueError("Iteration limit reached, no converging solution found")
|
||||
if logsteps:
|
||||
# If logstep is true, then log intermediate steps
|
||||
return a, error, steps
|
||||
return a, error, []
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from matplotlib import pyplot as plt
|
||||
|
||||
f = lambda x: m.tanh(x) ** 2 - m.exp(3 * x) # noqa: E731
|
||||
solution, error, steps = newton_raphson(
|
||||
f, x0=10, maxiter=1000, step=1e-6, logsteps=True
|
||||
)
|
||||
plt.plot([abs(f(x)) for x in steps])
|
||||
plt.xlabel("step")
|
||||
plt.ylabel("error")
|
||||
plt.show()
|
||||
print(f"solution = {{{solution:f}}}, error = {{{error:f}}}")
|
|
@ -1,83 +0,0 @@
|
|||
# Implementing Newton Raphson method in Python
|
||||
# Author: Saksham Gupta
|
||||
#
|
||||
# The Newton-Raphson method (also known as Newton's method) is a way to
|
||||
# quickly find a good approximation for the root of a functreal-valued ion
|
||||
# The method can also be extended to complex functions
|
||||
#
|
||||
# Newton's Method - https://en.wikipedia.org/wiki/Newton's_method
|
||||
|
||||
from sympy import diff, lambdify, symbols
|
||||
from sympy.functions import * # noqa: F403
|
||||
|
||||
|
||||
def newton_raphson(
|
||||
function: str,
|
||||
starting_point: complex,
|
||||
variable: str = "x",
|
||||
precision: float = 10**-10,
|
||||
multiplicity: int = 1,
|
||||
) -> complex:
|
||||
"""Finds root from the 'starting_point' onwards by Newton-Raphson method
|
||||
Refer to https://docs.sympy.org/latest/modules/functions/index.html
|
||||
for usable mathematical functions
|
||||
|
||||
>>> newton_raphson("sin(x)", 2)
|
||||
3.141592653589793
|
||||
>>> newton_raphson("x**4 -5", 0.4 + 5j)
|
||||
(-7.52316384526264e-37+1.4953487812212207j)
|
||||
>>> newton_raphson('log(y) - 1', 2, variable='y')
|
||||
2.7182818284590455
|
||||
>>> newton_raphson('exp(x) - 1', 10, precision=0.005)
|
||||
1.2186556186174883e-10
|
||||
>>> newton_raphson('cos(x)', 0)
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ZeroDivisionError: Could not find root
|
||||
"""
|
||||
|
||||
x = symbols(variable)
|
||||
func = lambdify(x, function)
|
||||
diff_function = lambdify(x, diff(function, x))
|
||||
|
||||
prev_guess = starting_point
|
||||
|
||||
while True:
|
||||
if diff_function(prev_guess) != 0:
|
||||
next_guess = prev_guess - multiplicity * func(prev_guess) / diff_function(
|
||||
prev_guess
|
||||
)
|
||||
else:
|
||||
raise ZeroDivisionError("Could not find root") from None
|
||||
|
||||
# Precision is checked by comparing the difference of consecutive guesses
|
||||
if abs(next_guess - prev_guess) < precision:
|
||||
return next_guess
|
||||
|
||||
prev_guess = next_guess
|
||||
|
||||
|
||||
# Let's Execute
|
||||
if __name__ == "__main__":
|
||||
# Find root of trigonometric function
|
||||
# Find value of pi
|
||||
print(f"The root of sin(x) = 0 is {newton_raphson('sin(x)', 2)}")
|
||||
|
||||
# Find root of polynomial
|
||||
# Find fourth Root of 5
|
||||
print(f"The root of x**4 - 5 = 0 is {newton_raphson('x**4 -5', 0.4 +5j)}")
|
||||
|
||||
# Find value of e
|
||||
print(
|
||||
"The root of log(y) - 1 = 0 is ",
|
||||
f"{newton_raphson('log(y) - 1', 2, variable='y')}",
|
||||
)
|
||||
|
||||
# Exponential Roots
|
||||
print(
|
||||
"The root of exp(x) - 1 = 0 is",
|
||||
f"{newton_raphson('exp(x) - 1', 10, precision=0.005)}",
|
||||
)
|
||||
|
||||
# Find root of cos(x)
|
||||
print(f"The root of cos(x) = 0 is {newton_raphson('cos(x)', 0)}")
|
Loading…
Reference in New Issue
Block a user