Ternary Search Algorithm — Finding Extrema in Unimodal Functions
- Ternary search finds extrema of unimodal functions in O(log n) by comparing two interior probe points.
- Unlike binary search, ternary search does not require a monotone predicate — only a unimodal function.
- Each iteration eliminates 1/3 of the search range, giving O(log_1.5 n) total iterations.
Imagine you're trying to find the sweetest spot on a hill — you don't know exactly where it is, but you know the hill only goes up and then back down. Instead of checking every single point, you pick two spots that divide the hill into three equal sections, taste both, and throw away the third that definitely can't be the sweetest. You keep doing this — splitting what's left into thirds — until you've zeroed in on the peak. That's ternary search: a way to find the best point on a single-humped curve by confidently eliminating a third of the possibilities each step.
Most engineers encounter binary search early and carry it like a trusty Swiss Army knife for the rest of their careers. But there's a whole class of problems — optimizing a physics simulation, tuning a cost function, finding the peak throughput of a network — where binary search simply can't help, because the data isn't sorted in the classical sense. It rises to a peak and then falls, forming a shape mathematicians call unimodal. Ternary search was designed exactly for this shape, and understanding it unlocks a family of optimization problems that would otherwise require calculus or brute-force scanning.
The core problem ternary search solves is this: given a unimodal function f(x) over a continuous or discrete domain, find the value of x that maximizes (or minimizes) f(x), without evaluating the entire domain. Binary search needs a sorted, monotonic sequence to compare against a target. Ternary search instead compares the function's output at two interior probe points — m1 and m2 — to decide which third of the search space cannot contain the optimum. Each iteration shrinks the search space to two-thirds of its previous size, converging on the answer exponentially fast.
By the end of this article you'll be able to implement both the iterative and recursive flavours of ternary search in Java, articulate exactly why it converges in O(log₃ n) iterations, know when to prefer it over binary search or golden-section search, avoid the three most common implementation bugs that cause infinite loops or missed answers, and answer the ternary search questions that show up in FAANG-level interviews. Let's build this from first principles.
What is Ternary Search? — Plain English
Ternary search finds the minimum or maximum of a unimodal function — a function that has exactly one peak (for maximum search) or one valley (for minimum search). Unlike binary search which requires a sorted array, ternary search works on unimodal functions: the function increases then decreases (for max) or decreases then increases (for min). The algorithm divides the current range into three equal parts using two midpoints m1 and m2. Comparing f(m1) and f(m2) eliminates one-third of the range: if f(m1) < f(m2), the maximum cannot be in [lo, m1]; if f(m1) > f(m2), the maximum cannot be in [m2, hi]. This continues until the range is small enough.
How Ternary Search Works — Step by Step
Ternary search for maximum of unimodal function f on range [lo, hi]:
- While hi - lo > epsilon (or for integers, while hi - lo > 2):
- a. m1 = lo + (hi - lo) / 3.
- b. m2 = hi - (hi - lo) / 3 (equivalently, lo + 2*(hi-lo)/3).
- c. If f(m1) < f(m2): maximum is not in [lo, m1-1] → set lo = m1 + 1.
- d. If f(m1) > f(m2): maximum is not in [m2+1, hi] → set hi = m2 - 1.
- e. If f(m1) == f(m2): maximum is in [m1+1, m2-1] → lo = m1+1, hi = m2-1.
- Check remaining elements in [lo, hi] for the maximum.
For minimum search, reverse the comparisons: if f(m1) > f(m2), minimum is not in [lo, m1-1].
Worked Example — Finding Maximum in Array [1,3,5,4,2]
Array: [1,3,5,4,2], indices 0-4. Maximum is at index 2 (value 5).
Round 1: lo=0, hi=4. m1=0+(4-0)/3≈1, m2=4-(4-0)/3≈2 (integer: m1=1, m2=3). f(m1)=arr[1]=3, f(m2)=arr[3]=4. f(m1)<f(m2) → maximum not in [0,m1]=[0,1] → lo=m1+1=2.
Round 2: lo=2, hi=4. m1=2+(4-2)/3≈2, m2=4-(4-2)/3≈3 (m1=2, m2=3). f(m1)=arr[2]=5, f(m2)=arr[3]=4. f(m1)>f(m2) → maximum not in [m2+1,hi]=[4,4] → hi=m2-1=2.
Round 3: lo=2, hi=2. hi-lo=0 <= 2 threshold. Check [2,2]: arr[2]=5. Maximum at index 2.
Each round eliminates ~1/3 of remaining range. Total iterations: O(log_1.5(n)) ≈ O(log n).
Implementation
Ternary search divides the range into thirds using two probe points. The implementation handles both discrete (integer index) and continuous (floating-point) variants. For continuous optimization, iterate until hi-lo < epsilon (e.g., 1e-9). For integer arrays, iterate until hi-lo <= 2 and check remaining elements directly. Ternary search is less common than binary search because it requires unimodal functions, but it is essential for competitive programming problems involving bitonic arrays or geometric optimization.
def ternary_search_max_array(arr): """Find index of maximum in unimodal array.""" lo, hi = 0, len(arr) - 1 while hi - lo > 2: m1 = lo + (hi - lo) // 3 m2 = hi - (hi - lo) // 3 if arr[m1] < arr[m2]: lo = m1 + 1 elif arr[m1] > arr[m2]: hi = m2 - 1 else: lo, hi = m1 + 1, m2 - 1 return max(range(lo, hi + 1), key=lambda i: arr[i]) def ternary_search_min_continuous(f, lo, hi, iterations=200): """Find x in [lo,hi] that minimizes f (unimodal, continuous).""" for _ in range(iterations): m1 = lo + (hi - lo) / 3 m2 = hi - (hi - lo) / 3 if f(m1) < f(m2): hi = m2 else: lo = m1 return (lo + hi) / 2 # Discrete: find max of [1,3,5,4,2] print(ternary_search_max_array([1,3,5,4,2])) # 2 (index) # Continuous: minimize (x-3)^2 on [0,10] f = lambda x: (x - 3) ** 2 print(round(ternary_search_min_continuous(f, 0, 10), 6)) # ~3.0
3.0
| Concept | Use Case | Example |
|---|---|---|
| Ternary Search Algorithm | Core usage | See code above |
🎯 Key Takeaways
- Ternary search finds extrema of unimodal functions in O(log n) by comparing two interior probe points.
- Unlike binary search, ternary search does not require a monotone predicate — only a unimodal function.
- Each iteration eliminates 1/3 of the search range, giving O(log_1.5 n) total iterations.
⚠ Common Mistakes to Avoid
Interview Questions on This Topic
- QWhat type of function can ternary search find the maximum of?
- QHow does ternary search differ from binary search?
- QWhat is the time complexity of ternary search?
Frequently Asked Questions
When does ternary search apply instead of binary search?
Ternary search applies when the function is unimodal (one peak/valley) but not monotone. Binary search requires a monotone predicate (all True then all False, or vice versa). Ternary search relaxes this to 'increases then decreases' (or decreases then increases). If the function is monotone, binary search is preferred because it converges faster (halves range vs thirds per step).
Why not just use binary search on the derivative for unimodal functions?
For continuous differentiable functions, binary search on f'(x) (where derivative changes sign at the extremum) is equivalent to ternary search and equally efficient. But computing derivatives is not always possible (discrete functions, black-box functions). Ternary search works on any function where you can evaluate f at any point.
Is ternary search faster than binary search?
No — ternary search is slower than binary search when binary search is applicable. Ternary search eliminates 1/3 of the range per step, requiring O(log_1.5 n) = O(1.7 * log2 n) iterations. Binary search eliminates 1/2 per step, requiring O(log2 n) iterations. Ternary search's niche is unimodal functions where binary search cannot be directly applied.
Developer and founder of TheCodeForge. I built this site because I was tired of tutorials that explain what to type without explaining why it works. Every article here is written to make concepts actually click.