tags:

views:

148

answers:

6

I have a function that takes a floating point number and returns a floating point number. It can be assumed that if you were to graph the output of this function it would be 'n' shaped, ie. there would be a single maximum point, and no other points on the function with a zero slope. We also know that input value that yields this maximum output will lie between two known points, perhaps 0.0 and 1.0.

I need to efficiently find the input value that yields the maximum output value to some degree of approximation, without doing an exhaustive search.

I'm looking for something similar to Newton's Method which finds the roots of a function, but since my function is opaque I can't get its derivative.

A: 

You could reduce it to a simple linear fit on the delta's, finding the place where it crosses the x axis. Linear fit can be done very quickly.

Or just take 3 points (left/top/right) and fix the parabola.

It depends mostly on the nature of the underlying relation between x and y, I think.

edit this is in case you have an array of values like the question's title states. When you have a function take Newton-Raphson.

mvds
+3  A: 

Edit 2: The method described in Jive Dadson is a better way to go about this. I'm leaving my answer up since it's easier to implement, if speed isn't too much of an issue.

Use a form of binary search, combined with numeric derivative approximations.

Given the interval [a, b], let x = (a + b) /2 Let epsilon be something very small.

Is (f(x + epsilon) - f(x)) positive? If yes, the function is still growing at x, so you recursively search the interval [x, b] Otherwise, search the interval [a, x].

There might be a problem if the max lies between x and x + epsilon, but you might give this a try.

Edit: The advantage to this approach is that it exploits the known properties of the function in question. That is, I assumed by "n"-shaped, you meant, increasing-max-decreasing. Here's some Python code I wrote to test the algorithm:

def f(x):
    return -x * (x - 1.0)

def findMax(function, a, b, maxSlope):
    x = (a + b) / 2.0
    e = 0.0001
    slope = (function(x + e) - function(x)) / e
    if abs(slope) < maxSlope:
        return x
    if slope > 0:
        return findMax(function, x, b, maxSlope)
    else:
        return findMax(function, a, x, maxSlope)

Typing findMax(f, 0, 3, 0.01) should return 0.504, as desired.

Seth
+1 If you already know the general shape of the output, take advantage of it to simplify your logic. You know there is exactly one maximum, so a bisection method like this should find the solution relatively quickly. Yes, if the result is between x and (x + epsilon) you will get an inaccurate answer, but that inaccuracy decreases with epsilon (so for a sufficiently small epsilon, the inaccuracy is negligible).
bta
Curious (haven't tested myself), does this work with a function that has multiple peaks? Say, for instance, an amplitude modulated signal?
Nathan Ernst
Ah, nevermind, just reread the question, and it stated a single peak.
Nathan Ernst
Nathan: This would probably find a local max, but not a the global one.
Seth
Numerical derivative approximations are surprisingly tricky. It's better to fit some degree of polynomial. Parabolas (fitting three points) work just fine. See my answer.I would like to see how you folks who suggest bisection go about. Remember, he said derivatives are not available. :-)
Jive Dadson
A: 

The Levenberg-Marquardt algorithm is a Newton's method like optimizer. It has a C/C++ implementation levmar that doesn't require you to define the derivative function. Instead it will evaluate the objective function in the current neighborhood to move to the maximum.

BTW: this website appears to be updated since I last visited it, hope it's even the same one I remembered. Apparently it now also support other languages.

catchmeifyoutry
-1: Levenberg is overengineering in dimension 1.
Alexandre C.
A: 

Given that it's only a function of a single variable and has one extremum in the interval, you don't really need Newton's method. Some sort of line search algorithm should suffice. This wikipedia article is actually not a bad starting point, if short on details. Note in particular that you could just use the method described under "direct search", starting with the end points of your interval as your two points.

I'm not sure if you'd consider that an "exhaustive search", but it should actually be pretty fast I think for this sort of function (that is, a continuous, smooth function with only one local extremum in the given interval).

Tim Goodman
+2  A: 

For optimizing a concave function, which is the type of function you are talking about, without evaluating the derivative I would use the secant method.

Given the two initial values x[0]=0.0 and x[1]=1.0 I would proceed to compute the next approximations as:

def next_x(x, xprev):
    return x - f(x) * (x - xprev) / (f(x) - f(xprev))

and thus compute x[2], x[3], ... until the change in x becomes small enough.

Edit: As Jive explains, this solution is for root finding which is not the question posed. For optimization the proper solution is the Brent minimizer as explained in his answer.

Muhammad Alkarouri
Wrong problem for the solution. He's asking for a maximizer, not a root-finder.
Jive Dadson
+3  A: 

I would like to down-thumb all the other answers so far, for various reasons, but I won't.

An excellent and efficient method for minimizing (or maximizing) smooth functions when derivatives are not available is parabolic interpolation. It is common to write the algorithm so it temporarily switches to the golden-section search (Brent's minimizer) when parabolic interpolation does not progress as fast as golden-section would.

I wrote such an algorithm in C++. Any offers?

UPDATE: There is a C version of the Brent minimizer in GSL. The archives are here: ftp://ftp.club.cc.cmu.edu/gnu/gsl/ Note that it will be covered by some flavor of GNU "copyleft."

As I write this, the latest-and-greatest appears to be gsl-1.14.tar.gz. The minimizer is located in the file gsl-1.14/min/brent.c. It appears to have termination criteria similar to what I implemented. I have not studied how it decides to switch to golden section, but for the OP, that is probably moot.

UPDATE 2: I googled up a public domain java version, translated from FORTRAN. I cannot vouch for its quality. http://www1.fpl.fs.fed.us/Fmin.java I notice that the hard-coded machine efficiency ("machine precision" in the comments) is 1/2 the value for a typical PC today. Change the value of eps to 2.22045e-16.

Jive Dadson
Brent minimizer is implemented in a number of open source libraries such as gsl and scipy, in addition to other LGPL implementations on the web.
Muhammad Alkarouri
Brent minimizer is the slow fallback method when parabolic interpolation fails. If the OP's function is as described, Brent's minimizer will never kick in.
Jive Dadson
This method is indeed better than the one I proposed; my answer has been edited to reflect this. Thanks for refraining from down-voting. :P
Seth
Jive, I'd be interested in seeing a simple implementation of this - I'm using Java so if you are aware of a LGPL (or similar license) Java implementation that would be perfect.
sanity
as a matter of fact, [Brent minimizer](http://www.gnu.org/software/gsl/manual/html_node/Minimization-Algorithms.html) is itself the combination of parabolic interpolation with golden ratio algorithm, so it makes no sense to combine it with parabolic interpolation.
Muhammad Alkarouri
I stand corrected. In any case, that's what my algorithm does - parabolic interpolation with golden section as a fallback. It is loosely based on a version in a "recipe" book that was popular a decade or more ago. It is a definite improvement over that algorithm. Whether it is better than the one in the GNU math library, I do not know.
Jive Dadson
@Jive: The recipe book has a new version, and is still popular. And it describes perfectly Brent's method. You could've pointed to it.
Alexandre C.
The version in the recipe book that I was familiar with is not good, particularly in the termination criteria. I've never seen the one that is nominally written in C++.
Jive Dadson
@Jive: The recipe book definitely switched to C++ some years ago. Their C++ is not good for production, but their OO design is.
Alexandre C.