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).

JavaScript
Frontend
3 snippets
binary-search
problem-solving
code-template
rohaneriksson

By @rohaneriksson

January 12, 2026

·

Updated May 18, 2026

647 views

21

4.4 (13)

// Binary-search the smallest 'capacity' (makespan) such that we can pack all
// jobs into N machines without any one machine exceeding capacity.
// The trick: instead of searching for the right schedule, search for the
// answer (max load) and ask 'can we hit this?' as a yes/no predicate.

function minMakespan(jobs, machines) {
    // The answer lies between max(job) (one job alone) and sum(jobs) (one machine).
    let lo = 0;
    let hi = 0;
    for (const j of jobs) { if (j > lo) lo = j; hi += j; }

    function feasible(capacity) {
        let used = 1;
        let load = 0;
        for (const j of jobs) {
            if (load + j <= capacity) {
                load += j;
            } else {
                used += 1;
                load = j;
                if (used > machines) return false;
            }
        }
        return true;
    }

    while (lo < hi) {
        const mid = lo + ((hi - lo) >> 1);
        if (feasible(mid)) hi = mid;
        else lo = mid + 1;
    }
    return lo;
}

const jobs = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5];
console.log('1 machine :', minMakespan(jobs, 1));   // 44
console.log('2 machines:', minMakespan(jobs, 2));   // 23
console.log('3 machines:', minMakespan(jobs, 3));   // 17
console.log('5 machines:', minMakespan(jobs, 5));   // 11
console.log('11 machines:', minMakespan(jobs, 11)); // 9 (one job per machine, biggest singleton is 9)

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).