Skip to content

Commit 06586d7

Browse files
committed
brent's_method
1 parent a71618f commit 06586d7

1 file changed

Lines changed: 89 additions & 0 deletions

File tree

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
import math
2+
from collections.abc import Callable
3+
4+
def brent_method(
5+
f: Callable[[float], float],
6+
a: float,
7+
b: float,
8+
tol: float = 1e-14,
9+
max_iter: int = 100
10+
) -> float:
11+
"""
12+
Root finding using Brent's method.
13+
14+
>>> brent_method(lambda x: x**3 - 1, -5, 5)
15+
1.0
16+
>>> brent_method(lambda x: x**2 - 4*x + 3, 0, 2)
17+
1.0
18+
>>> brent_method(lambda x: x**2 - 4*x + 3, 2, 4)
19+
3.0
20+
>>> brent_method(lambda x: x**2 - 4*x + 3, 4, 1000)
21+
Traceback (most recent call last):
22+
...
23+
ValueError: Root is not bracketed.
24+
"""
25+
26+
fa = f(a)
27+
fb = f(b)
28+
if fa * fb >= 0:
29+
raise ValueError("Root is not bracketed.")
30+
31+
if abs(fa) < abs(fb):
32+
a, b = b, a
33+
fa, fb = fb, fa
34+
35+
c = a
36+
fc = fa
37+
d = e = b - a
38+
mflag = True
39+
40+
for _ in range(max_iter):
41+
if fb == 0:
42+
return b
43+
if fa != fc and fb != fc:
44+
# Inverse quadratic interpolation
45+
s = (
46+
a * fb * fc / ((fa - fb) * (fa - fc)) +
47+
b * fa * fc / ((fb - fa) * (fb - fc)) +
48+
c * fa * fb / ((fc - fa) * (fc - fb))
49+
)
50+
else:
51+
# Secant method
52+
s = b - fb * (b - a) / (fb - fa)
53+
54+
conditions = [
55+
not ((3 * a + b) / 4 < s < b if b > a else b < s < (3 * a + b) / 4),
56+
mflag and abs(s - b) >= abs(b - c) / 2,
57+
not mflag and abs(s - b) >= abs(c - d) / 2,
58+
mflag and abs(b - c) < tol,
59+
not mflag and abs(c - d) < tol,
60+
]
61+
if any(conditions):
62+
s = (a + b) / 2
63+
mflag = True
64+
else:
65+
mflag = False
66+
67+
fs = f(s)
68+
d, c = c, b
69+
fc = fb
70+
71+
if fa * fs < 0:
72+
b = s
73+
fb = fs
74+
else:
75+
a = s
76+
fa = fs
77+
78+
if abs(fa) < abs(fb):
79+
a, b = b, a
80+
fa, fb = fb, fa
81+
82+
if abs(b - a) < tol or fb == 0:
83+
return b
84+
85+
return b
86+
87+
if __name__ == "__main__":
88+
from doctest import testmod
89+
testmod()

0 commit comments

Comments
 (0)