Talk:Brent's method

message: I am wondering whether, when we swap the most recent a with b, we should also swap the earlier values? So that instead of "if |f(a)| < |f(b)| then swap (a,b) end if" we would have "if |f(a)| < |f(b)| then swap (a,b) and swap (p,c) and swap (q,d) end if" where p would be the old a and q would be the old p. JRSpriggs 11:37, 18 June 2006 (UTC)
replies: text: No, just the last. So "if |f(a)| < |f(b)| then swap (a,b) end if" is correct. -- Jitse Niesen (talk) 12:56, 18 June 2006 (UTC)
thread_title:  Swap all a<sub>k</sub> with b<sub>k</sub> or just the last? 

message: The condition "f(a)*f(s) <= 0" in the if-else at the end of the pseudocode was originally "f(a)*f(s) < 0" which I believe did not work for the following reason: if f(s)=0, then f(a)*f(s)=0 and we would land in the 'else' side, hence setting a := s. Upon the next repeat until we would then miss that s was actually a root.
On the other hand, if the test is f(a)*f(s) <= 0, we have the following possibilities:
:1. if f(s)=0 then b := s, the next repeat until is the last since f(b)=0, and b is returned
:2. if f(a)=0 then
::2.a if f(b)!=0 then we swap a and b just after in "if |f(a)| <= |f(b)| then swap (a,b) end if" and a will therefore be identified as the root in the next repeat until test.
::2.b or f(b)=0 and b is a root as well.
Erwan.lemonnier 14:07, 15 December 2006 (UTC)
replies: text: If f(s)=0, then a would become s and the argument which you gave for case 2 would work for case 1 as well. I am reverting both additions of equal signs. JRSpriggs 06:14, 16 December 2006 (UTC)
thread_title:  "f(a)*f(s) <= 0" 

message: ** d := c
** c := b
** if f(a) f(s) < 0 then b := s else a := s end if
** if |f(a)| < |f(b)| then swap (a,b) end if
At the end, it seems that the previous value of a could end up in c instead of b, whereas the discussion of Brent's improvement discusses using the old values of b. To match that discussion, surely the top 2 lines I've quoted should be after the other 2, not before? 193.133.239.201 09:38, 24 April 2007 (UTC)
replies: text: The algorithm is correct. Do not take the words too seriously. a itself is usually a previous value of b as are c and d. s is a probable future value. JRSpriggs 10:17, 24 April 2007 (UTC)
thread_title:  Discrepancy between discussion and example algorithm? 

message: Hello,
Towards the end of the algorithm there is the statement:
: d := c
however d is never mentioned earlier or later as a variable. Is it possible that it's supposed to be 's'? I see that in earlier incarnations of the page 's' was called 'd'. —Preceding unsigned comment added by 132.206.77.89 (talk • contribs) 
:No. The variable d is used in one line 
** if s is not between (3a + b)/4 and b or (mflag is set and |s−b| ≥ |b−c| / 2) or (mflag is cleared and |s−b| ≥ |c−d| / 2) then
:And it is supposed to be there and is distinct from s. JRSpriggs 03:11, 6 July 2007 (UTC)
thread_title:  Possible algorithm error 

message: Hello,
The article states a few things without giving a precise reason for them, could you pease explain the following?
# Why does the "convergence speed test" depend on whether or not interpolation was used in the previous step? Why is it not possible to require  |s-b_k| < \begin{matrix} \frac12 \end{matrix} |b_k - b_{k-1}|  whatever the method used in the previous step, even if interpolation was used?
# When using inverse quadratic interpolation, why does "the condition for accepting s (the value proposed by either linear interpolation or inverse quadratic interpolation)" have to be changed to "s has to lie between (3ak + bk) / 4 and bk"? (which is less constraining than "s has to lie between (ak + bk) / 2 and bk")
Thanks for your help. laug (talk) 12:47, 10 May 2008 (UTC)
replies: text: I do not know. It has been a long time since I worked on this article, and I do not have the reference. But remember, this is Brent's method, not Niesen's method, Spriggs's method, or Laug's method. If I were writing the program freely for my own use, I would change the algorithm in some ways. But it would not then be Brent's method. And an article on Spriggs's method would be Original Research which is frowned on here. JRSpriggs (talk) 19:58, 14 May 2008 (UTC)
replies: text: Also, notice that the only way to guarantee that the convergence will not be slower than the Bisection method is to use the Bisection method itself which also guarantees that it will not converge faster. JRSpriggs (talk) 00:36, 18 May 2008 (UTC)
thread_title:  Question on Brent's extra tests 

message: OK, so the secant results are rejected, according to the "Brent's Method" section in the article, based on two inequalities, depending on the method used in the previous iteration of the algorithm:
Dekker's method performs well if the function f is reasonably well-behaved. However, there are circumstances in which every iteration employs the secant method, but the iterates bk converge very slowly (in particular, |bk − bk−1| may be arbitrarily small). Dekker's method requires far more iterations than the bisection method in this case. 
Brent proposed a small modification to avoid this problem. He inserted an additional test which must be satisfied before the result of the secant method is accepted as the next iterate. Specifically, if the previous step used the bisection method, the inequality
:  |s-b_k| < \begin{matrix} \frac12 \end{matrix} |b_k - b_{k-1}| 
must hold, otherwise the midpoint m is used for the next iterate. If the previous step used interpolation, then the inequality
:  |s-b_k| < \begin{matrix} \frac12 \end{matrix} |b_{k-1} - b_{k-2}| 
is used instead.
I believe these inequalities are conditions for scrapping the secant results, rather than accepting them. Since earlier in the paragraph, the flaw raised in Dekker's method was that it does not converge fast enough in certain cases, since " |b_k - b_{k-1}| may be arbitrarily small", then one needs to use the bisection method when the result generated by the secant method makes  |b_k - b_{k-1}| very small. But if we accept secant results when the above inequalities hold, we are saying that "OK, only when  |b_{k+1} - b_{k}| is smaller than half of  |b_k - b_{k-1}| or  |b_{k-1} - b_{k-2}| will I be happy with it". Wouldn't that be the exact opposite of what we would like to do? I will check Brent's work later, but I believe that one should use the bisection result when the inequalities hold for the secant result. I will confirm this with research and possibly correct the article soon.  Yangli2 (talk) 21:09, 1 October 2008 (UTC)
replies: text: OK, never mind, it is as the article says in Brent's book, but with an extra condition: when |c-d| is smaller than a certain tolerance value given a priori, a bisection also needs to be performed. This makes the whole thing make sense, because when the secant method gets trapped in situation where every step it takes becomes vanishingly small and underflows the processor, there is nothing to force a bisection in the algorithm given in the article. This condition, combined with the inequalities in the article, is basically saying "I will only accept step sizes made by the secant method that are halved every two iterations; that way the step sizes will vanish with O(log_2), and if convergence does not happen, eventually the step sizes will become smaller than the tolerance value, at which point I use bisection method". The article is missing a very important condition which guarantees numerical convergence, and which makes sure that the bisection method is performed at least once every 2log_2(|c-d|/tolerance) iterations, so that the algorithm is never much slower than bisection. I will now make the necessary adjustments to the article.  Yangli2 (talk) 19:17, 2 October 2008 (UTC)
replies: text: Thanks. That was probably my fault. I found Brent's description quite a hard read and I had to refer a lot to the actual implementation to make sense of it. It's eminently possible I missed some details. I do remember that there were a couple of things that I didn't understand at all (I believe the second question by laug above was one of the things that mystified me). -- Jitse Niesen (talk) 23:17, 2 October 2008 (UTC)
thread_title:  Serious Error in Secant Result Rejection Criterion? 

message: The pseudocode cannot be right, because near the bottom of the loop, c is set to b, and at the top of the loop, inverse quadratic interpolation is only done if f(c)!=f(b).  So it never happens.  —Preceding unsigned comment added by 193.113.37.7 (talk) 11:55, 13 May 2010 (UTC)
thread_title:  pseudocode wrong 

message: Did Brent develop two methods for one-dimensional functions with derivatives?  This page talks about a root-finding algorithm.  But I thought Brent's method was a minimization algorithm, a variant on golden section search.  The link from Powell's method to Brent's method also invokes Brent's method as a 1-dimensional minimization algorithm, but that is not what this page describes.  Flipping through the Numerical Recipes book on Google Books, it looks like both of Brent's algorithms are given, without comment on the fact that they have the same name.  The minimization algorithm appears in chapter 10.   Eclecticos (talk) 21:06, 3 October 2011 (UTC)
replies: text:  His code zero is root finding. The code localmin in the same book is for 1d minimisation. Bendel boy (talk) 16:02, 1 May 2019 (UTC)
thread_title:  How about Brent's method for minimization? 

message: Why on earth is the example in C# ? Thta is only supported on Microstft systems and is a niche anyway only used by a small amount of users.
With only minor changes this would be a generic C program. (In fact I already made that C program).
130.161.210.156 (talk) 10:36, 16 May 2014 (UTC)
replies: text: If you actually had read the top of the source code box, you would have seen that the intended language was (and still is?) Java, or at least there was nothing preventing the code from being used in Java. Besides, Mono is an open source implementation of C# that certainly covers all numerical computations. Source code examples are never intended to be complete programs or modules, so standard include or import statements are superfluous.--LutzL (talk) 09:40, 18 May 2014 (UTC)

text: I went ahead and removed the code (which is copied below). Glrx (talk) 21:38, 21 May 2014 (UTC)
thread_title:  C# code 

message: I'm trying to implement Brent's method and running into problems. In some parts of the program I can't easily make a function to pass to a Brent function, and in one part, the domain of the function is the 32-bit integers, so I can't use code that's out there. Instead I made an object of a Brent class and initialize it with two arguments and two values, then it returns the next argument to try, and I pass it back the value, until it's done. This works, most of the time, but occasionally encounters some weirdness.
There are two problems with the pseudocode:
*There are 18 calls to f on just the one line that computes the inverse parabolic interpolation. There should be just one call to f; since I return s and expect f(s) to be passed to the next call to the object, I need to know where it is.
*There is an uninitialized variable δ.
The main problem with the worked example is that it uses subscripts instead of the variables a, b, c, and d, which the code uses. It also computes the function of the same argument twice in a row, which shouldn't happen unless you're within two ulps of the root. phma (talk) 18:59, 16 October 2016 (UTC)
thread_title: Pseudocode and worked example both unclear

message: I just wrote an algorithm to use brent's method and I was testing it following the reported example.
I could follow it without errors until step 7. At step 6, you have f(a) = −6.78239 and the resulting f(s) =  -0.58418.
Since it result f(a)*f(s), it result A = S and since 0.58418 < f(b) = 3.93934, it swap A and B.
At this point, the example says: "In the seventh iteration, we can again use inverse quadratic interpolation", but this is not the case, since C = A (or C = previous swap B, to be accurate) and the condition f(a) ≠ f(c) cannot be satisfied. The code calculate the new S with the secant method instead, but since it fails the conditions, then bisection is used in the end.
Is it just me missing something or is it an error? can someone check this, please.
Nschloe (talk) 10:25, 21 February 2024 (UTC)
thread_title:  possible error in the example? 

message: I needed to use Brent's method and I ended up adapting the code found in Numerical Recipes after cross-referencing it with Brent's publication. I have now a working function which I believe to be correct. I have the code both in python and C++. This is the python code:
def brents_root_finder(func, x1, x2, tol, maxiter):
    EPS = sys.float_info.epsilon
    a = x1
    b = x2
    c = x2
    fa = func(a)
    fb = func(b)
    if (fa > 0 and fb > 0) or (fa < 0 and fb < 0):
        raise Exception(f"The interval [{x1},{x2}] is not guaranteed to contain a root")
    fc = fb
    d = b - a
    e = d
    for i in range(maxiter):
        # the bracketing interval is (b,c) with b being the root candidate and a being the previous root candidate
        if fb * fc > 0:
            # if fb and fc have the same sign, then c becomes a since the root is within (a,b)
            c = a
            fc = fa
            d = b - a
            e = d
        if abs(fc) < abs(fb):
            # if abs(fc) < abs(fb), then c is a better root candidate , so they are flipped
            # This has the side effect of a == c
            a = b
            b = c
            c = a
            fa = fb
            fb = fc
            fc = fa
        tol1 = 2*EPS*abs(b) + 0.5*tol
        m = 0.5*(c - b)
        if abs(m) <= tol1 or fb == 0.0:
            # tolerance reached, return root
            return b
        if abs(e) < tol1 or abs(fa) <= abs(fb):
            # if the previous step was small or the previous root candidate seems better, do bisection
            d = m
        else:
            s = fb/fa
            if a == c:
                # if a == c, then do secant
                p = 2 * m * s
                q = 1 - s
            else:
                # else do inverse quadratic interpolation
                q = fa / fc
                r = fb / fc
                p = s * (2 * m * q * (q - r) - (b - a) * (r - 1))
                q = (q - 1) * (r - 1) * (s - 1)
            # the following conditional makes sure that p > 0 and if secant was what just happened
            #     then sign(m) == sign(q)
            if p > 0:
                q = -q
            else:
                p = -p
            if 2 * p < 3 * m * q - abs(tol1 * q) and p < abs(e * q / 2):
                # if the new root candidate is not more than 3/4 the distance from b to c
                #    and p/q is not bigger than half of the previous step
                #    then accept the previous step
                # note that if sign(m) != sign(q) then the first check will fail
                d = p / q
            else:
                # else do a bisection
                d = m
        e = d
        a = b
        fa = fb
        # if d is smaller than tol1, then just take a tol1 step towards the middle
        b += d if abs(d) > tol1 else math.copysign(tol1, m)
        fb = func(b)
    raise Exception(f"Iteration limit [{maxiter}] reached")
I didn't want to just go and add it, but I think it would be nice to have the code in the article. Gtusus (talk) 13:13, 9 January 2023 (UTC)
thread_title:  Working code