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 appr
(
f: RealFunc,
x0: float = 0,
max_iter: int = 100,
step: float = 1e-6,
max_error: float = 1e-6,
log_steps: bool = False,
)
| 33 | |
| 34 | |
| 35 | def newton_raphson( |
| 36 | f: RealFunc, |
| 37 | x0: float = 0, |
| 38 | max_iter: int = 100, |
| 39 | step: float = 1e-6, |
| 40 | max_error: float = 1e-6, |
| 41 | log_steps: bool = False, |
| 42 | ) -> tuple[float, float, list[float]]: |
| 43 | """ |
| 44 | Find a root of the given function f using the Newton-Raphson method. |
| 45 | |
| 46 | :param f: A real-valued single-variable function |
| 47 | :param x0: Initial guess |
| 48 | :param max_iter: Maximum number of iterations |
| 49 | :param step: Step size of x, used to approximate f'(x) |
| 50 | :param max_error: Maximum approximation error |
| 51 | :param log_steps: bool denoting whether to log intermediate steps |
| 52 | |
| 53 | :return: A tuple containing the approximation, the error, and the intermediate |
| 54 | steps. If log_steps is False, then an empty list is returned for the third |
| 55 | element of the tuple. |
| 56 | |
| 57 | :raises ZeroDivisionError: The derivative approaches 0. |
| 58 | :raises ArithmeticError: No solution exists, or the solution isn't found before the |
| 59 | iteration limit is reached. |
| 60 | |
| 61 | >>> import math |
| 62 | >>> tolerance = 1e-15 |
| 63 | >>> root, *_ = newton_raphson(lambda x: x**2 - 5*x + 2, 0.4, max_error=tolerance) |
| 64 | >>> math.isclose(root, (5 - math.sqrt(17)) / 2, abs_tol=tolerance) |
| 65 | True |
| 66 | >>> root, *_ = newton_raphson(lambda x: math.log(x) - 1, 2, max_error=tolerance) |
| 67 | >>> math.isclose(root, math.e, abs_tol=tolerance) |
| 68 | True |
| 69 | >>> root, *_ = newton_raphson(math.sin, 1, max_error=tolerance) |
| 70 | >>> math.isclose(root, 0, abs_tol=tolerance) |
| 71 | True |
| 72 | >>> newton_raphson(math.cos, 0) |
| 73 | Traceback (most recent call last): |
| 74 | ... |
| 75 | ZeroDivisionError: No converging solution found, zero derivative |
| 76 | >>> newton_raphson(lambda x: x**2 + 1, 2) |
| 77 | Traceback (most recent call last): |
| 78 | ... |
| 79 | ArithmeticError: No converging solution found, iteration limit reached |
| 80 | """ |
| 81 | |
| 82 | def f_derivative(x: float) -> float: |
| 83 | return calc_derivative(f, x, step) |
| 84 | |
| 85 | a = x0 # Set initial guess |
| 86 | steps = [] |
| 87 | for _ in range(max_iter): |
| 88 | if log_steps: # Log intermediate steps |
| 89 | steps.append(a) |
| 90 | |
| 91 | error = abs(f(a)) |
| 92 | if error < max_error: |
no test coverage detected