mirror of
https://github.com/TheAlgorithms/Python.git
synced 2024-12-18 09:10:16 +00:00
Add algorithm for approximating the nth root with Newton's Method
This commit is contained in:
parent
ed1900f1b3
commit
a2b98cd9b4
151
maths/numerical_analysis/nth_root.py
Normal file
151
maths/numerical_analysis/nth_root.py
Normal file
|
@ -0,0 +1,151 @@
|
||||||
|
"""
|
||||||
|
Approximate the nth root of a real number using the Newton's Method.
|
||||||
|
|
||||||
|
The nth root of a real number R can be computed with Newton's method,
|
||||||
|
which starts with an initial guess x_0 and then iterates using the
|
||||||
|
recurrence relation:
|
||||||
|
|
||||||
|
x_{k + 1} = x_k - ((x_k)**n - R)/(n*(x_k)**(n-1))
|
||||||
|
|
||||||
|
The recurrence relation can be rewritten for computational efficiency:
|
||||||
|
|
||||||
|
x_{k + 1} = (n-1)/n*x_k + R/(n*(x_k)**(n-1))
|
||||||
|
|
||||||
|
Given a tolerance TOL, a stopping criterion can be set as:
|
||||||
|
|
||||||
|
abs(x_{k + 1} - x_k) < TOL
|
||||||
|
|
||||||
|
References:
|
||||||
|
- https://en.wikipedia.org/wiki/Nth_root#Using_Newton's_method
|
||||||
|
- Sauer, T. (2011): Numerical analysis.
|
||||||
|
USA. Addison-Wesley Publishing Company.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from math import pow
|
||||||
|
|
||||||
|
|
||||||
|
def nth_root(radicand: float, index: int, tolerance: float = 0.0001) -> float:
|
||||||
|
"""
|
||||||
|
Approximate the nth root of the radicand for the given index
|
||||||
|
|
||||||
|
Args:
|
||||||
|
radicand: number from which the root is taken
|
||||||
|
index: positive integer which is the degree of the root
|
||||||
|
tolerance: positive real number that establishes the stopping criterion
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
new_aproximation: approximation of the nth root of the radicand for the
|
||||||
|
given index
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
TypeError: radicand is not real number
|
||||||
|
TypeError: index is not integer
|
||||||
|
ValueError: index is not positive integer
|
||||||
|
TypeError: tolerance is not real number
|
||||||
|
ValueError: tolerance is not positive real number
|
||||||
|
ValueError: math domain error
|
||||||
|
|
||||||
|
>>> round(nth_root(9, 2),1)
|
||||||
|
3.0
|
||||||
|
|
||||||
|
>>> int(round(nth_root(-8, 3, 0.001)))
|
||||||
|
-2
|
||||||
|
|
||||||
|
>>> int(round(nth_root(256, 4, 0.001)))
|
||||||
|
4
|
||||||
|
|
||||||
|
>>> round(nth_root(2, 2), 5)
|
||||||
|
1.41421
|
||||||
|
|
||||||
|
>>> round(nth_root(0.25, 2, 0.00000001), 1)
|
||||||
|
0.5
|
||||||
|
|
||||||
|
>>> round(nth_root(-8/27, 3, 0.0000001), 5)
|
||||||
|
-0.66667
|
||||||
|
|
||||||
|
>>> nth_root(0, 2, 0.1)
|
||||||
|
0.0
|
||||||
|
|
||||||
|
>>> nth_root(0.0, 5)
|
||||||
|
0.0
|
||||||
|
|
||||||
|
>>> all(abs(nth_root(k, k, 0.00000001) - k**(1/k)) <= 1e-10 for k in range(1,10))
|
||||||
|
True
|
||||||
|
|
||||||
|
>>> nth_root('invalid input', 3, 0.0001)
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
TypeError: radicand must be real number, not str
|
||||||
|
|
||||||
|
>>> nth_root(4, 0.5, 0.0001)
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
TypeError: index must be integer, not float
|
||||||
|
|
||||||
|
>>> nth_root(16, -4, 0.001)
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
ValueError: index must be positive integer, -4 <= 0
|
||||||
|
|
||||||
|
>>> nth_root(4, 2, '0.000001')
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
TypeError: tolerance must be real number, not str
|
||||||
|
|
||||||
|
>>> nth_root(9, 2, -0.01)
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
ValueError: tolerance must be positive real number, -0.01 <= 0
|
||||||
|
|
||||||
|
>>> nth_root(-256, 4, 0.0001)
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
ValueError: math domain error, radicand must be nonnegative for even index
|
||||||
|
"""
|
||||||
|
if not isinstance(radicand, (int, float)):
|
||||||
|
error_message = f"radicand must be real number, not {type(radicand).__name__}"
|
||||||
|
raise TypeError(error_message)
|
||||||
|
|
||||||
|
if not isinstance(index, int):
|
||||||
|
error_message = f"index must be integer, not {type(index).__name__}"
|
||||||
|
raise TypeError(error_message)
|
||||||
|
|
||||||
|
if index <= 0:
|
||||||
|
error_message = f"index must be positive integer, {index} <= 0"
|
||||||
|
raise ValueError(error_message)
|
||||||
|
|
||||||
|
if not isinstance(tolerance, (int, float)):
|
||||||
|
error_message = f"tolerance must be real number, not {type(tolerance).__name__}"
|
||||||
|
raise TypeError(error_message)
|
||||||
|
|
||||||
|
if tolerance <= 0:
|
||||||
|
error_message = f"tolerance must be positive real number, {tolerance} <= 0"
|
||||||
|
raise ValueError(error_message)
|
||||||
|
|
||||||
|
if radicand < 0 and index % 2 == 0:
|
||||||
|
error_message = "math domain error, radicand must be nonnegative for even index"
|
||||||
|
raise ValueError(error_message)
|
||||||
|
|
||||||
|
if radicand == 0.0:
|
||||||
|
return 0.0
|
||||||
|
|
||||||
|
# Set initial guess
|
||||||
|
new_aproximation = radicand
|
||||||
|
# Set old_aproximation to enter the loop
|
||||||
|
old_aproximation = new_aproximation + tolerance + 0.1
|
||||||
|
|
||||||
|
# Iterate as long as the stop criterion is not satisfied
|
||||||
|
while tolerance <= abs(old_aproximation - new_aproximation):
|
||||||
|
old_aproximation = new_aproximation
|
||||||
|
# Compute new_approximation with the recurrence relation described above
|
||||||
|
first_summand = (index - 1) / index * old_aproximation
|
||||||
|
second_summand = radicand / (index * pow(old_aproximation, index - 1))
|
||||||
|
new_aproximation = first_summand + second_summand
|
||||||
|
|
||||||
|
return new_aproximation
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
import doctest
|
||||||
|
|
||||||
|
doctest.testmod()
|
Loading…
Reference in New Issue
Block a user