From 1ea02321083c279268a8425ac1d2bd1413c21449 Mon Sep 17 00:00:00 2001 From: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Date: Mon, 23 Oct 2023 06:09:27 +0000 Subject: [PATCH 1/8] updating DIRECTORY.md --- DIRECTORY.md | 1 + 1 file changed, 1 insertion(+) diff --git a/DIRECTORY.md b/DIRECTORY.md index 1e3711fe8dda..61823a241c8d 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -826,6 +826,7 @@ * [Shear Stress](physics/shear_stress.py) * [Speed Of Sound](physics/speed_of_sound.py) * [Speeds Of Gas Molecules](physics/speeds_of_gas_molecules.py) + * [Terminal Velocity](physics/terminal_velocity.py) ## Project Euler * Problem 001 From 96d300034261af1e9dac0f96d9dbbc914a82a10a Mon Sep 17 00:00:00 2001 From: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Date: Mon, 23 Oct 2023 14:02:00 +0000 Subject: [PATCH 2/8] updating DIRECTORY.md --- DIRECTORY.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/DIRECTORY.md b/DIRECTORY.md index 61823a241c8d..f4ba3061e8d9 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -394,6 +394,7 @@ * [Interest](financial/interest.py) * [Present Value](financial/present_value.py) * [Price Plus Tax](financial/price_plus_tax.py) + * [Simple Moving Average](financial/simple_moving_average.py) ## Fractals * [Julia Sets](fractals/julia_sets.py) @@ -706,6 +707,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) + * [Triangular Numbers](maths/special_numbers/triangular_numbers.py) * [Ugly Numbers](maths/special_numbers/ugly_numbers.py) * [Weird Number](maths/special_numbers/weird_number.py) * [Sum Of Arithmetic Series](maths/sum_of_arithmetic_series.py) From d4eb63bb6e28a452e08ccd582b94d927bef2ac2d Mon Sep 17 00:00:00 2001 From: Tianyi Zheng Date: Mon, 23 Oct 2023 15:19:30 -0400 Subject: [PATCH 3/8] Consolidate Newton-Raphson duplicates --- maths/numerical_analysis/newton_method.py | 54 ------- maths/numerical_analysis/newton_raphson.py | 45 ------ maths/numerical_analysis/newton_raphson_2.py | 138 ++++++++++++------ .../numerical_analysis/newton_raphson_new.py | 83 ----------- 4 files changed, 93 insertions(+), 227 deletions(-) delete mode 100644 maths/numerical_analysis/newton_method.py delete mode 100644 maths/numerical_analysis/newton_raphson.py delete mode 100644 maths/numerical_analysis/newton_raphson_new.py diff --git a/maths/numerical_analysis/newton_method.py b/maths/numerical_analysis/newton_method.py deleted file mode 100644 index 5127bfcafd9a..000000000000 --- a/maths/numerical_analysis/newton_method.py +++ /dev/null @@ -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)) diff --git a/maths/numerical_analysis/newton_raphson.py b/maths/numerical_analysis/newton_raphson.py deleted file mode 100644 index 8491ca8003bc..000000000000 --- a/maths/numerical_analysis/newton_raphson.py +++ /dev/null @@ -1,45 +0,0 @@ -# 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 - -from decimal import Decimal - -from sympy import diff, lambdify, symbols - - -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 - """ - 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) - - -if __name__ == "__main__": - import doctest - - 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)}") diff --git a/maths/numerical_analysis/newton_raphson_2.py b/maths/numerical_analysis/newton_raphson_2.py index f6b227b5c9c1..1969e50f4ae6 100644 --- a/maths/numerical_analysis/newton_raphson_2.py +++ b/maths/numerical_analysis/newton_raphson_2.py @@ -1,64 +1,112 @@ """ -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. -""" +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 + +x_{n + 1} = x_n + f(x_n) / f'(x_n) + +with the precision of the approximation increasing as the number of iterations increase. -import math as m +Reference: https://en.wikipedia.org/wiki/Newton%27s_method +""" from collections.abc import Callable -DerivativeFunc = Callable[[float], float] +RealFunc = Callable[[float], float] -def calc_derivative(f: DerivativeFunc, a: float, h: float = 0.001) -> float: +def calc_derivative(f: RealFunc, x: float, delta_x: float = 1e-3) -> float: """ - Calculates derivative at point a for function f using finite difference - method + Approximate the derivative of a function f(x) at a point x using the finite + difference method + + >>> import math + >>> derivative = calc_derivative(lambda x: x**2, 2) + >>> math.isclose(derivative, 4) + True + >>> derivative = calc_derivative(lambda x: math.sin(x), 0) + >>> math.isclose(derivative, 1) + True """ - return (f(a + h) - f(a - h)) / (2 * h) + return (f(x + delta_x / 2) - f(x - delta_x / 2)) / delta_x def newton_raphson( - f: DerivativeFunc, + f: RealFunc, x0: float = 0, - maxiter: int = 100, - step: float = 0.0001, - maxerror: float = 1e-6, - logsteps: bool = False, + max_iter: int = 100, + step: float = 1e-6, + max_error: float = 1e-6, + log_steps: 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: + """ + 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) - 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, [] + + 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__": - from matplotlib import pyplot as plt + import doctest + from math import exp, tanh + + doctest.testmod() + + def func(x: float) -> float: + return tanh(x) ** 2 - exp(3 * x) - 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 + solution, err, steps = newton_raphson( + func, x0=10, max_iter=100, step=1e-6, log_steps=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}}}") + print(f"{solution=}, {err=}") + print("\n".join(str(x) for x in steps)) diff --git a/maths/numerical_analysis/newton_raphson_new.py b/maths/numerical_analysis/newton_raphson_new.py deleted file mode 100644 index f61841e2eb84..000000000000 --- a/maths/numerical_analysis/newton_raphson_new.py +++ /dev/null @@ -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)}") From 609f23dd5297638e9950cf5b2a5dcd4ad0657427 Mon Sep 17 00:00:00 2001 From: Tianyi Zheng Date: Mon, 23 Oct 2023 15:20:08 -0400 Subject: [PATCH 4/8] Rename consolidated Newton-Raphson file --- .../numerical_analysis/{newton_raphson_2.py => newton_raphson.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename maths/numerical_analysis/{newton_raphson_2.py => newton_raphson.py} (100%) diff --git a/maths/numerical_analysis/newton_raphson_2.py b/maths/numerical_analysis/newton_raphson.py similarity index 100% rename from maths/numerical_analysis/newton_raphson_2.py rename to maths/numerical_analysis/newton_raphson.py From 5cdb06a90befa5a4519e7fa01e85dc4f79e153c3 Mon Sep 17 00:00:00 2001 From: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Date: Mon, 23 Oct 2023 19:24:30 +0000 Subject: [PATCH 5/8] updating DIRECTORY.md --- DIRECTORY.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/DIRECTORY.md b/DIRECTORY.md index f4ba3061e8d9..5d8aef83f270 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -641,10 +641,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) From 482b8f7ab9c3ce4c550198583e79094c0f99ff9b Mon Sep 17 00:00:00 2001 From: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Date: Mon, 23 Oct 2023 19:25:40 +0000 Subject: [PATCH 6/8] updating DIRECTORY.md --- DIRECTORY.md | 1 + 1 file changed, 1 insertion(+) diff --git a/DIRECTORY.md b/DIRECTORY.md index 5d8aef83f270..f0b1f7c13c2b 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -233,6 +233,7 @@ * [Deque Doubly](data_structures/linked_list/deque_doubly.py) * [Doubly Linked List](data_structures/linked_list/doubly_linked_list.py) * [Doubly Linked List Two](data_structures/linked_list/doubly_linked_list_two.py) + * [Floyds Cycle Detection](data_structures/linked_list/floyds_cycle_detection.py) * [From Sequence](data_structures/linked_list/from_sequence.py) * [Has Loop](data_structures/linked_list/has_loop.py) * [Is Palindrome](data_structures/linked_list/is_palindrome.py) From b0541e3fe9c70c325aa1431de6ba5ffc0ac187a7 Mon Sep 17 00:00:00 2001 From: Tianyi Zheng Date: Mon, 23 Oct 2023 15:51:43 -0400 Subject: [PATCH 7/8] Fix doctest precision --- maths/numerical_analysis/newton_raphson.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/maths/numerical_analysis/newton_raphson.py b/maths/numerical_analysis/newton_raphson.py index 1969e50f4ae6..8afecfc7c76b 100644 --- a/maths/numerical_analysis/newton_raphson.py +++ b/maths/numerical_analysis/newton_raphson.py @@ -20,10 +20,11 @@ def calc_derivative(f: RealFunc, x: float, delta_x: float = 1e-3) -> float: difference method >>> import math - >>> derivative = calc_derivative(lambda x: x**2, 2) + >>> dx = 1e-10 + >>> derivative = calc_derivative(lambda x: x**2, 2, delta_x=dx) >>> math.isclose(derivative, 4) True - >>> derivative = calc_derivative(lambda x: math.sin(x), 0) + >>> derivative = calc_derivative(math.sin, 0, delta_x=dx) >>> math.isclose(derivative, 1) True """ From c1a1857bf4182c0b0a9f6fdb5a9f7379d4a3461c Mon Sep 17 00:00:00 2001 From: Tianyi Zheng Date: Mon, 23 Oct 2023 15:59:51 -0400 Subject: [PATCH 8/8] Fix doctest precision again --- maths/numerical_analysis/newton_raphson.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/maths/numerical_analysis/newton_raphson.py b/maths/numerical_analysis/newton_raphson.py index 8afecfc7c76b..feee38f905dd 100644 --- a/maths/numerical_analysis/newton_raphson.py +++ b/maths/numerical_analysis/newton_raphson.py @@ -20,12 +20,12 @@ def calc_derivative(f: RealFunc, x: float, delta_x: float = 1e-3) -> float: difference method >>> import math - >>> dx = 1e-10 - >>> derivative = calc_derivative(lambda x: x**2, 2, delta_x=dx) - >>> math.isclose(derivative, 4) + >>> 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, delta_x=dx) - >>> math.isclose(derivative, 1) + >>> 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