Community JavaScript Snippet
Binary Search on the Answer Space
I had to bin-pack 5M jobs across N machines under a wall-time budget; sort-and-greedy timed out. The fix was binary searching the makespan: a `feasible(m)` predicate plus the standard lo/hi loop turns an NP-hard scheduler into O(N log range).
Binary Search on the Answer Space
I had to bin-pack 5M jobs across N machines under a wall-time budget; sort-and-greedy timed out. The fix was binary searching the makespan: a `feasible(m)` predicate plus the standard lo/hi loop turns an NP-hard scheduler into O(N log range).
By @rohaneriksson
January 12, 2026
·
Updated May 18, 2026
647 views
21
4.4 (13)
The template is the reason this pattern is in my permanent toolkit. The outer loop is the standard lo < hi binary search; the only project-specific logic is feasible(capacity), which decides whether the candidate answer is achievable. For makespan the predicate is greedy first-fit: walk jobs in order, push onto the current machine if it fits, otherwise start a new one. Because feasible is monotonic (if m works, every m+1 also works), binary search converges in log(sum - max) iterations. The bounds are deliberate: lo starts at the largest single job (you can never go below it) and hi starts at the sum (one machine taking everything is always feasible).
Koko is the example I use to teach the pattern because the predicate is a one-liner and the bounds are obvious. The lower bound is 1 (you must eat something each hour), the upper bound is max(piles) (eating any faster is wasted, since you cannot start a new pile mid-hour). hoursAt(speed) sums the ceiling of each pile divided by speed, which is the cost function the problem hands you. The same shape solves capacity-of-ship-to-ship-packages, split-array-largest-sum, and aggressive-cows; once you can write the predicate, the binary search is mechanical.
For continuous answer spaces I drop the integer-specific lo + 1 step and instead halve until the gap is below the requested precision. Capping at 64 iterations is a safety net: in pathological cases (denormals, near-zero targets) the relative epsilon never converges, and I would rather return an answer of bounded staleness than spin forever. Picking hi = target < 1 ? 1 : target covers the corner case where target = 0.25 and sqrt(target) > target. The same template handles minimize-distance-to-the-mean, minimize-max-distance-between-cows, and any other problem with a real-valued, monotonic predicate.
