Find the extremum of a unimodal function — one that strictly increases then strictly decreases (or the reverse). evaluations.

The idea

Pick two interior points . If , the maximum cannot lie in , so discard it. Each step removes a third of the interval.

double ternaryMax(function<double(double)> f, double lo, double hi) {
    for (int it = 0; it < 200; it++) {
        double m1 = lo + (hi - lo) / 3;
        double m2 = hi - (hi - lo) / 3;
        if (f(m1) < f(m2)) lo = m1; else hi = m2;
    }
    return (lo + hi) / 2;
}

Use a fixed iteration count. 200 iterations shrink the interval by , far past double precision, and the loop always terminates.

long long lo = 0, hi = n;
while (hi - lo > 2) {
    long long m1 = lo + (hi - lo) / 3;
    long long m2 = hi - (hi - lo) / 3;
    if (f(m1) < f(m2)) lo = m1 + 1; else hi = m2;
}
long long best = lo;
for (long long i = lo; i <= hi; i++) if (f(i) > f(best)) best = i;

Always finish with a small linear scan — the boundary is where integer ternary search goes wrong, and scanning 3 values costs nothing.

Strict unimodality is required

Plateaus break it

If on a flat region, neither side can be safely discarded, and the search may converge to the wrong point. If the function has plateaus, use binary search on the derivative instead:

// find the last i with f(i+1) >= f(i)
while (lo < hi) { int mid = (lo+hi)/2; if (f(mid) < f(mid+1)) lo = mid+1; else hi = mid; }

This handles plateaus correctly and uses one evaluation per step instead of two.

Replace the thirds with the golden ratio and reuse one evaluation per iteration:

const double R = (sqrt(5.0) - 1) / 2;      // 0.618...
double m1 = hi - R * (hi - lo), m2 = lo + R * (hi - lo);
double f1 = f(m1), f2 = f(m2);
for (int it = 0; it < 200; it++) {
    if (f1 < f2) { lo = m1; m1 = m2; f1 = f2; m2 = lo + R*(hi-lo); f2 = f(m2); }
    else         { hi = m2; m2 = m1; f2 = f1; m1 = hi - R*(hi-lo); f1 = f(m1); }
}

One evaluation per iteration instead of two, with a slightly slower interval shrink — a net win of about 30% when is expensive. See Golden Section Search.

Where it applies

ProblemUnimodal in
Minimise a convex functionany convex
Optimal position on a line/segmentthe parameter
Best speed/time trade-offthe free variable
Maximum of (something increasing) (something decreasing)
Distance from a point to a convex curvethe parameter
Nested ternary search over 2 variables unimodal in for each , and the resulting unimodal
Extreme point on a convex polygonthe vertex index (cyclically unimodal)

For a 2D convex objective:

auto g = [&](double x) { return ternaryMin([&](double y){ return f(x,y); }, ylo, yhi); };
double bestX = ternaryMin(g, xlo, xhi);

evaluations. Valid when is convex in each variable and the partial minimum is unimodal — true for jointly convex . This solves the geometric median and similar facility-location problems to arbitrary precision.

Alternatives for finding an optimum

SituationMethod
Unimodal, continuousternary / golden section
Unimodal, integerternary + final scan, or binary search on the difference
Differentiable, want a root of Newton’s method
Convex, high dimensiongradient descent, or LP
Not unimodalsimulated annealing, grid search + local refinement
Discrete with monotone feasibilitybinary search on the answer
Convex sequence, DPAliens trick

See also: Binary Search · Golden Section Search · Newton’s Method