These find the extremum of a unimodal function (strictly increasing then strictly decreasing, or the reverse) with evaluations. Convex and concave functions are the main source of unimodality.
Real ternary search
template<class F> double ternaryMin(F f, double lo, double hi) {
for (int it = 0; it < 100; it++) { // (2/3)^100 ~ 1e-18 of the range
double m1 = lo + (hi - lo) / 3, m2 = hi - (hi - lo) / 3;
if (f(m1) < f(m2)) hi = m2; else lo = m1;
}
return (lo + hi) / 2;
}Use a fixed iteration count, as in binary search.
Proof: discarding a third is safe for a unimodal (here, convex-shaped: decreasing then increasing)
Let be the minimiser. If with : claim . If it were, would be strictly decreasing on in particular at (both before the minimum, on the decreasing branch — wait, more carefully): since is decreasing then increasing with minimum at , if then both are on the decreasing branch, so (further left is higher) — contradicting . So , justifying
hi=m2. Symmetrically, if , , justifyinglo=m1. Either way the discarded third provably cannot contain , and the kept interval still contains it; after iterations the interval has shrunk by a factor (each step evaluates two new points dividing the interval into three parts and keeps two of them), reaching of the original range at .
Integer domain: binary search the slope
On integers, binary search the first with . That uses one comparison per step and handles plateaus at the optimum. Ternary search on integers needs a final linear scan of the last 3 candidates anyway.
ll lo = L, hi = R; // find argmin of convex f on [L, R]
while (lo < hi) {
ll mid = lo + (hi - lo) / 2;
if (f(mid) <= f(mid + 1)) hi = mid; else lo = mid + 1;
}Proof: binary search on the slope finds the argmin
For convex on integers, the difference is non-decreasing (discrete convexity, by definition). So is a monotone predicate ( in ): once the slope turns non-negative it stays non-negative. The code’s predicate
f(mid)<=f(mid+1)is exactly , and binary-searching its first true position finds the smallest with , i.e. the smallest where stops decreasing — which, for convex , is exactly where the (possibly flat) minimum begins.
Plateaus
Unimodality must be strict away from the optimum. If on a flat region that isn’t the optimum, neither side can be discarded and ternary search can converge to the wrong place. Convex functions are fine, since a flat part of a convex function is the minimum.
Golden-section search
This reuses one evaluation per iteration. The points sit at ratio , and because the surviving point is exactly where the next iteration needs it.
template<class F> double goldenMin(F f, double lo, double hi) {
const double R = (sqrt(5.0) - 1) / 2;
double m1 = hi - R * (hi - lo), m2 = lo + R * (hi - lo), f1 = f(m1), f2 = f(m2);
for (int it = 0; it < 100; it++) {
if (f1 < f2) { hi = m2; m2 = m1; f2 = f1; m1 = hi - R * (hi - lo); f1 = f(m1); }
else { lo = m1; m1 = m2; f1 = f2; m2 = lo + R * (hi - lo); f2 = f(m2); }
}
return (lo + hi) / 2;
}| Method | Shrink per evaluation | Evaluations for relative |
|---|---|---|
| ternary | about 103 | |
| golden section | about 43 |
Proof: golden-section search reuses the correct point
With , the construction places and . Say , so the code discards : the new interval is of length , and the old is reused as the new (the code does
m2 = m1) rather than recomputed. For this reuse to be correct, old ‘s offset from , which is , must equal what the construction rule would have placed at the new for the new interval, namely . Requiring gives exactly , i.e. , with positive root — so this specific is exactly the value making the reuse land correctly (the symmetric case, discarding the left side and reusing old as new , gives the same equation).
Golden section matters when is expensive: a nested search, a flow or DP per evaluation, or a simulation. The discrete analogue is Fibonacci search: with integer candidates it needs evaluations, which is optimal in the worst case.
Where unimodality comes from
| Source | Example |
|---|---|
| convex function (sum of convex terms) | , , , distance from a moving point |
| increasing + decreasing trade-off | ”spend on upgrades, then the rest of the time working” |
| minimum over a family of convex functions in a parameter | the lower envelope is not convex, so be careful |
| cost of groups in partition DPs | convex in (Aliens trick) |
| extreme point of a convex polygon in a direction | unimodal along the boundary (cyclically): binary search on edges |
| distance between two points moving linearly | is convex in |
The sum of convex functions is convex, the max of convex functions is convex, and convex composed with affine is convex. The sum of two unimodal functions is not unimodal in general. Prove convexity rather than assuming unimodality.
Nested ternary search
For a jointly convex , is convex, so
auto g = [&](double x) { return f(x, ternaryMin([&](double y) { return f(x, y); }, ylo, yhi)); };and a ternary search over finishes the job in evaluations. Typical uses: the geometric median (Weber point), the smallest enclosing circle of moving points, best meeting point under Euclidean distance, best launch angle / minimum-time trajectory problems, and parameter tuning inside a heuristic solver. Each extra dimension multiplies the cost by , so beyond 2-3 dimensions use gradient methods or random restarts plus hill climbing (Heuristics).
Alternatives
| Situation | Method |
|---|---|
| derivative available | Newton’s method or bisection on |
| discrete and convex | binary search on ; or slope trick if it’s DP-shaped |
| not unimodal, low dimension | grid sample, then refine around the best few points |
| convex in many variables, linear constraints | LP / simplex |
| piecewise linear convex | breakpoints are candidates: sort them, or use weighted median |
Weighted median: is minimised at any weighted median of the . No search needed. In Manhattan geometry, solve and independently.