Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions DIRECTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -712,14 +712,17 @@
* [Loss Functions](machine_learning/loss_functions.py)
* Lstm
* [Lstm Prediction](machine_learning/lstm/lstm_prediction.py)
* [Mean Shift](machine_learning/mean_shift.py)
* [Mfcc](machine_learning/mfcc.py)
* [Mini Batch Gradient Descent](machine_learning/mini_batch_gradient_descent.py)
* [Multilayer Perceptron Classifier](machine_learning/multilayer_perceptron_classifier.py)
* [Naive Bayes Text Classification](machine_learning/naive_bayes_text_classification.py)
* [Polynomial Regression](machine_learning/polynomial_regression.py)
* [Principle Component Analysis](machine_learning/principle_component_analysis.py)
* [Q Learning](machine_learning/q_learning.py)
* [Random Forest Classifier](machine_learning/random_forest_classifier.py)
* [Random Forest Regressor](machine_learning/random_forest_regressor.py)
* [Rmsprop](machine_learning/rmsprop.py)
* [Scoring Functions](machine_learning/scoring_functions.py)
* [Self Organizing Map](machine_learning/self_organizing_map.py)
* [Sequential Minimum Optimization](machine_learning/sequential_minimum_optimization.py)
Expand All @@ -736,6 +739,7 @@
* [Arc Length](maths/arc_length.py)
* [Area](maths/area.py)
* [Area Under Curve](maths/area_under_curve.py)
* [Autocorrelation](maths/autocorrelation.py)
* [Average Absolute Deviation](maths/average_absolute_deviation.py)
* [Average Mean](maths/average_mean.py)
* [Average Median](maths/average_median.py)
Expand Down Expand Up @@ -781,6 +785,7 @@
* [Fibonacci](maths/fibonacci.py)
* [Find Max](maths/find_max.py)
* [Find Min](maths/find_min.py)
* [First Fundamental Form](maths/first_fundamental_form.py)
* [Floor](maths/floor.py)
* [Gamma](maths/gamma.py)
* [Gaussian](maths/gaussian.py)
Expand Down Expand Up @@ -833,6 +838,7 @@
* [Nevilles Method](maths/numerical_analysis/nevilles_method.py)
* [Newton Forward Interpolation](maths/numerical_analysis/newton_forward_interpolation.py)
* [Newton Raphson](maths/numerical_analysis/newton_raphson.py)
* [Nth Root](maths/numerical_analysis/nth_root.py)
* [Numerical Integration](maths/numerical_analysis/numerical_integration.py)
* [Proper Fractions](maths/numerical_analysis/proper_fractions.py)
* [Runge Kutta](maths/numerical_analysis/runge_kutta.py)
Expand All @@ -843,6 +849,7 @@
* [Square Root](maths/numerical_analysis/square_root.py)
* [Weierstrass Method](maths/numerical_analysis/weierstrass_method.py)
* [Odd Sieve](maths/odd_sieve.py)
* [Padovan Sequence](maths/padovan_sequence.py)
* [Pell Number](maths/pell_number.py)
* [Perfect Cube](maths/perfect_cube.py)
* [Perfect Number](maths/perfect_number.py)
Expand Down Expand Up @@ -872,6 +879,8 @@
* [Reverse Factorial Recursive](maths/reverse_factorial_recursive.py)
* [Segmented Sieve](maths/segmented_sieve.py)
* Series
* [Alternate Harmonic Series](maths/series/alternate_harmonic_series.py)
* [Alternating Harmonic Series](maths/series/alternating_harmonic_series.py)
* [Arithmetic](maths/series/arithmetic.py)
* [Geometric](maths/series/geometric.py)
* [Geometric Series](maths/series/geometric_series.py)
Expand Down Expand Up @@ -911,6 +920,7 @@
* [Polygonal Numbers](maths/special_numbers/polygonal_numbers.py)
* [Pronic Number](maths/special_numbers/pronic_number.py)
* [Proth Number](maths/special_numbers/proth_number.py)
* [Spy Number](maths/special_numbers/spy_number.py)
* [Triangular Numbers](maths/special_numbers/triangular_numbers.py)
* [Trimorphic Number](maths/special_numbers/trimorphic_number.py)
* [Ugly Numbers](maths/special_numbers/ugly_numbers.py)
Expand Down
151 changes: 151 additions & 0 deletions maths/numerical_analysis/nth_root.py
Original file line number Diff line number Diff line change
@@ -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

Check failure on line 24 in maths/numerical_analysis/nth_root.py

View workflow job for this annotation

GitHub Actions / ruff

ruff (A004)

maths/numerical_analysis/nth_root.py:24:18: A004 Import `pow` is shadowing a Python builtin


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