Scheduling
Scheduling problems share one abstract shape: a fixed set of tasks that need to get done, a fixed set of machines that can do them, and a question of when to put each task on which machine so that the whole job finishes as well as possible. “As well as possible” depends on the situation — most often it means finishing in the shortest possible total time — but the underlying structure stays the same: an ordering decision under constraints.
This is a different flavor of discrete modeling from the decision models of the previous chapter. There the discreteness lived in the choice space — finitely many actions, finitely many states — and the decision came down to a single pick from a matrix. Here the choice space is itself an arrangement: tasks laid out over time and across machines. The questions are no longer which row of a matrix to pick; they are how to lay things out in time.
Problem setup
A scheduling problem consists of:
- a process — an overall piece of work to be carried out — decomposed into tasks (also called jobs) ;
- a pool of machines that can execute those tasks;
- an execution time for each (task, machine) pair — the time machine takes to complete task ;
- three constraints on how tasks and machines may be paired up over time:
- no task is carried out on more than one machine at a time,
- no machine handles more than one task at a time,
- but several machines may run in parallel.
The execution-time table is the most general form: the time a task takes can depend both on which task it is and on which machine runs it. Later sections specialize this — for instance when all machines are interchangeable, collapses to a single number per task — but for now leave both indices free.
The three constraints together carve out the legal moves of the problem. The first two are exclusion clauses: while a task runs it occupies one machine, and while a machine runs it occupies one task. The third is permission rather than prohibition — distinct machines may be busy at the same wall-clock moment, doing distinct tasks. This parallelism across machines is what makes the problem interesting; without it the model would collapse to choosing a total order on tasks for one resource.
Model parameters
A scheduling model is described by three kinds of parameters:
- task parameters — what each is, what it requires, how long it takes;
- machine parameters — what each is capable of and how it processes tasks (e.g. the execution times );
- a target function (also called objective function or optimality criterion) — a rule that turns a candidate schedule into a single number to be optimized.
The first two are descriptive: they pin down the system being modeled. The third is the normative piece — it says what optimal means for the problem at hand, and the answer changes with the situation.
Goal
The goal is to find an optimal schedule: an assignment of tasks to machines and time slots that best satisfies the target function while respecting the constraints. At its heart this is an ordering problem — every candidate schedule comes down to which task runs where, and in what order.
The most common target is to minimize the overall time — the moment when the last task finishes — i.e. the schedule that gets everything done as early as possible. Other choices are possible (minimize total cost, maximize machine utilization, balance load across machines), and much of what follows in this chapter is about how the model and the algorithms change depending on which target is in play.
Machine characterization
The structure of the execution-time table — how the times relate to the tasks and to the machines — distinguishes one scheduling problem from another. Four classes cover the spectrum from the most restrictive setup to the fully general one.
A single machine pool has . Every task is processed strictly serially on one resource — no parallelism is possible.
The single-machine case is the simplest version of the problem. Without parallelism, the only choice is the order in which tasks are processed.
Machines are identical when they are both qualitatively identical — every machine offers the same range of functionality and can handle every task — and quantitatively identical — every machine runs at the same speed. The execution time then depends only on the task:
The superscript drops because the machine identity is irrelevant.
Think of copies of the same industrial robot. Any task can be assigned to any robot, and each one finishes in the same time. The scheduling problem reduces to deciding how to spread the tasks across the machines.
Machines are qualitatively identical when they all support the same set of tasks but run at different speeds. Each machine carries a per-machine proportionality factor , and the execution time scales accordingly:
A larger marks a slower machine for every task it runs; setting all recovers the identical-machines case.
This is the “same model, different generations” picture — a workshop with old and new versions of the same machine. The qualitative ability is uniform, but a job that takes 4 hours on the newest machine might take 6 hours on the previous generation.
Machines are arbitrary when both their functionality and their performance vary across the pool. The execution times are then arbitrary, with no algebraic relationship tying the rows or columns of the table together.
This is the fully general setup: each (task, machine) pair carries its own number, with no shared structure across the table. Most real-world scheduling problems live here — different machines with different abilities, running different jobs at different rates.
The four classes line up along a single trade-off: more general means more powerful, but also more difficult to analyze. The more general the machine model, the wider the range of real-world setups it can describe — and the less algebraic structure there is to exploit when searching for an optimum. The simpler classes often admit clean, direct algorithms; the more general ones typically force a combinatorial search over many candidate schedules.
A separate axis of generality is uncontrollable influences on the execution times — noise, machine failures, weather, the unforeseen. Modelling those requires replacing the deterministic with a random variable, and we return to that direction later in the chapter.
Process scheduling
We now narrow to the simplest setting: the execution time of each task is purely task-dependent, so every carries a single duration that doesn’t depend on the machine. We also assume machine availability is not a binding constraint — there are always enough machines to start a task the instant its prerequisites finish, no matter how many other tasks are already running. The only thing that ever forces one task to wait for another is a precedence arrow, and the natural language for those arrows is a directed graph.
This “unlimited resources” assumption will be relaxed later in the chapter. With finitely many machines, two precedence-unrelated tasks can still have to take turns when they target the same machine — that case is the job-shop problem of the next few sections.
The start time of task is the moment at which is launched. The completion time is
the moment at which finishes. We assume that once started, a task cannot be interrupted — it runs through to completion.
With every task carrying a start time and a completion time, “task has to wait for task ” becomes a clean numerical condition.
A precedence condition (also called a precedence constraint) from to , written , requires that start only after has finished:
Equivalently, ‘s completion must precede (or coincide with) ‘s start.
The precedence graph
A list of precedence conditions has a natural picture: draw one node per task, and one arrow per precedence condition. The picture is a directed graph, and it captures the entire ordering structure of the process at a glance.
The precedence graph of a process is a directed graph where:
- the vertex set is — one vertex per task — and each vertex is labeled with its execution time ;
- the edge set is — one directed edge per precedence condition. The edges themselves carry no labels.
Two extra vertices close the graph at its endpoints:
- an initial vertex with index and , joined by an edge to every that has no incoming edge in — every task that depends on no other task;
- a final vertex with index and , joined by an edge from every that has no outgoing edge in — every task that nothing else depends on.
Putting the duration on the vertex and leaving the edge bare is a deliberate split. The vertex carries the quantitative data — how long the task takes — while the edge carries pure ordering information: ” must come before ”, no number attached. In graph-theoretic terms this is a node-weighted directed graph; only the vertices carry numbers.
The two bookend vertices and have zero cost (). is a single common starting point that every otherwise-unconstrained task must follow; is a single common endpoint that every otherwise-unconstrained task must reach. The payoff comes later — the moment at which the entire process is finished is just the completion time of , one number instead of a maximum across many independent endpoints.
Admissible schedules and DAGs
With the precedence graph in hand, we can now write down what a schedule is — and what it means for one to actually respect the dependencies.
A schedule for a process is an assignment of a start time to every task . The completion times then follow from .
A schedule on its own is just numbers — there is no built-in guarantee that it respects the precedence arrows. That is a separate property.
A schedule is admissible when every precedence condition in is satisfied — that is, for every edge .
Not every precedence graph admits a non-trivial admissible schedule, though. A graph that contains a cycle — a path that returns to its starting vertex — forces
so , and since every , every one of them must be zero.
Where each inequality comes from
The first inequality is just the closing edge of the cycle: the precedence condition reads .
The second inequality comes from walking forward through the cycle starting at :
- ;
- (the edge ), so ;
- continuing the same step inductively along gives .
The only cycles a precedence graph can carry are therefore cycles of zero-duration tasks — mathematically degenerate. So we restrict attention to graphs with no cycles at all.
A directed acyclic graph (commonly abbreviated DAG) is a directed graph that contains no cycles. From this point on, every precedence graph is assumed to be a DAG.
Precedence is transitive. If and , then , so holds automatically. Long chains of dependencies imply pairwise precedence between any earlier and later task in the chain, regardless of whether those pairwise edges are drawn explicitly in .
Once is a DAG, the tasks admit a clean linear ordering that is compatible with every precedence arrow.
A topological sort (also called a topological ordering) of a DAG is a reindexing of the vertices such that every edge has — every task receives an index strictly larger than those of all its prerequisites. A depth-first search of the graph — a recursive traversal that follows each branch to its end before backtracking — produces one such ordering, and every DAG admits at least one.
Reindexing the vertices into a topological order makes constructing schedules straightforward: walking the tasks in order , every prerequisite of the current task has already been visited, so its start and completion times are already known. The next section turns this observation into an explicit “earliest-possible” schedule that always exists and turns out to be optimal as well.
Constructing schedules
With the precedence graph reduced to a DAG and the vertices renumbered in topological order, we can build a concrete admissible schedule task by task. Two natural constructions present themselves — the “as early as possible” schedule and the “as late as possible” schedule — and both turn out to achieve the same overall completion time.
Earliest schedule: lead times
The first construction follows the simplest possible policy: start every task the moment it is allowed to start — i.e. the instant all of its prerequisites have completed.
The lead time (also called the earliest start time) of task is the moment at which all of ‘s predecessors have completed — the earliest time may begin. Equivalently, it is the maximum completion time across the predecessors:
The corresponding earliest completion time is then
Lead times are computed by a single forward pass through the DAG in topological order, so that every on the right-hand side of the recurrence is already known by the time we reach :
- Initialize at the source with (no predecessors, zero duration).
- Walk forward through in topological order, applying the recurrence at each step to compute first and then .
- Terminate at . The final value is the earliest possible completion time of the entire process.
The in the recurrence enforces precedence: can only start once every predecessor has finished, and the bottleneck predecessor is whichever finishes latest. Acyclicity of guarantees the recursion is well-defined — when we reach , every predecessor has index in the topological order, so is already in hand.
Latest schedule: remaining times
A second construction asks the opposite question: how late can each task afford to be without slipping past the optimal overall completion time ?
The remaining time (also called the latest completion time) of task is the latest moment at which can finish while still allowing the whole process to complete at . Equivalently, it is the minimum latest-start across the successors:
The corresponding latest start time is then
The name “remaining time” is a touch misleading. is not a duration — it is a deadline, an absolute moment on the same time axis as and . The “remaining” refers to the downstream time budget: once has finished at , the quantity is what remains on the clock for every task that comes after . Slip past , and that budget isn’t enough.
Computing remaining times depends on , the output of the forward pass — so the reverse pass must run after the lead-time pass has finished. With in hand, we walk the DAG backward in reverse topological order:
- Initialize at the sink with (no successors, zero duration).
- Walk backward through in reverse topological order, applying the recurrence at each step to compute first and then .
- Terminate at .
The over successors mirrors the over predecessors. must finish in time for every successor to start on its own latest schedule, and the tightest of those deadlines binds.
The forward pass gives each task its earliest ; the reverse pass gives each task its latest . Setting for every defines one admissible schedule; setting defines another. Both finish the entire process at exactly , so both achieve the same overall completion time. What the gap tells us about each task — and why is in fact the best any schedule can do — is the subject of the next section.
Critical paths
With both the lead times and the remaining times in hand, every vertex of the DAG falls into exactly one of two cases. By construction — the latest acceptable start cannot precede the earliest possible start, since the earliest possible start is the minimum — so the two cases are:
A vertex is critical when its lead time and latest start time coincide:
(Equivalently, .) In every optimal schedule, a critical task must be assigned the start time — it has no leeway.
The slack of a non-critical vertex is the gap
In any optimal schedule, may begin anywhere in the interval without affecting the overall completion time.
Slack is individual, not joint. If are both non-critical, delaying to its latest start while letting begin at its earliest can violate the precedence condition — may still be running when wants to start, even though both choices sit inside their own slack intervals. The slack interval describes the freedom of on its own — not joint freedom across all non-critical tasks at once.
Critical vertices stitch together into paths from to .
A critical path is a path in consisting entirely of critical vertices, joined by critical edges — edges along the path at which ‘s two completion values and ‘s two start values all coincide:
So must start the very instant finishes, with no gap. (The outer equalities and are automatic from the criticality of the endpoints; the operative new condition is the middle equality, written compactly in the literature as .)
At least one critical path always exists. is by definition the earliest possible finish, so there must be at least one chain of tasks of total duration — every vertex on that chain has no room to delay without postponing the project, hence is critical. (If every task had slack, the whole schedule could be shifted earlier and finish before , contradicting being the earliest possible.) Formally, the forward pass’s at each vertex picks out the predecessor that determines that vertex’s earliest start; tracing these bottleneck predecessors back from recovers a critical path. If multiple chains achieve total duration , multiple critical paths exist — and all of them have the same total duration .
The makespan and CPM
The makespan (also called the entire completion time, or the overall time of the informal problem setup) of a schedule is the value — the moment at which the final vertex finishes, i.e. when the whole process is done. For the lead-time and remaining-time schedules, attains its minimum value, with .
Along any path in , the cumulative duration — the sum of the execution times of all vertices on the path — is a lower bound on the makespan of every admissible schedule. (This cumulative duration is also called the path’s length in weighted-graph shorthand — the phrase “the length of a critical path” refers to exactly this total duration, not to the number of edges or vertices on the path.) Every task on the path must finish before the next can start, so the path is traversed strictly sequentially. Among all such paths, the one with the largest cumulative duration gives the tightest lower bound — and that largest cumulative duration is exactly (equivalently , since the reverse pass set as its boundary).
Both schedules actually achieve this lower bound: the lead-time schedule gives directly, and the remaining-time schedule gives . So in either schedule — is simultaneously a lower bound on the makespan and an upper bound realized by explicit construction, making both schedules makespan-optimal.
The Critical Path Method, abbreviated CPM, is the procedure for finding the critical paths of a precedence DAG: compute the lead times by a forward pass, the remaining times by a reverse pass, classify each vertex as critical or non-critical from the gap , and walk backward from to through critical edges to extract one or more critical paths.
The forward pass alone already produces a makespan-optimal schedule — every task has a start time, and the project finishes at . Identifying the critical paths is the additional output that CPM is named after, and it earns its name through two concrete real-world uses:
- To shorten the makespan, you must shorten a task that lies on a critical path. Shortening any other task only widens its slack and leaves unchanged — so when investing effort (people, money, parallelism) to speed up a project, the critical path tells you where that effort actually pays off.
- To know which tasks can absorb a delay, look at the non-critical vertices: each one can start as late as — i.e. slip by up to its slack — without pushing later. Critical tasks have no such buffer; any slip on them pushes the entire project’s deadline.
A worked example
To make the whole CPM pipeline concrete, consider seven tasks with execution times
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | |
|---|---|---|---|---|---|---|---|
| 3 | 2 | 2 | 3 | 2 | 4 | 4 |
and the precedence set
So we get the following precedence graph:
Three tasks () have no prerequisites in this list, so each receives an edge from the initial vertex ; three tasks () appear as nobody’s prerequisite, so each gains an edge to the final vertex . The natural indexing already forms a topological order, so we can walk the algorithm vertex by vertex in index order.
Forward pass: lead times
Taking the over predecessors at each step:
- : , .
- : , .
- : , .
- : , .
- : , .
- : , .
- : , .
- : , .
- : , .
The earliest possible overall completion time is .
Reverse pass: remaining times
Starting from and walking the DAG in reverse index order, taking the over successors:
- : , .
- : , .
- : , .
- : , .
- : , .
- : , .
- : , .
- : , .
- : , .
Critical vertices and critical paths
Combining the two passes, shading the critical vertices and the critical edges, and coloring each set of four matching values at the endpoints of a critical edge:
Seven of the nine vertices are critical, and they line up into two distinct critical paths from to :
Both paths have total duration exactly — exactly as the existence argument promised: when several critical paths exist, they share the same total duration. The only tasks with leeway are and , each carrying one unit of slack (). In any optimal schedule, may start anywhere in and anywhere in — but, by the earlier remark on joint slack, these two choices are not entirely independent of each other or of the surrounding critical structure. Every other task has its start time pinned to a unique value.
Job-shop problems
Process scheduling rested on a comfortable assumption: machine availability was never the bottleneck. There were always enough machines to start any task the instant its precedences cleared. We now drop that assumption. Machines become a scarce resource — each one runs at most one job at a time, and a job can be blocked not only by an unfinished prerequisite but also by another job sitting on the machine it needs.
The canonical formalization of this regime is the job-shop model, in which each job is itself an ordered pipeline of machine-specific steps.
A job-shop model describes a process of jobs — the term “job” replaces “task” of the earlier scheduling problem setup, both naming the same kind of object — to be run on machines , with the following structure:
- Each job decomposes into a sequence of subjobs which must run strictly in this order.
- Subjob has execution time and is bound to a specific machine .
- Each machine processes at most one subjob at a time, so subjobs assigned to the same machine must be put into some order.
- No recirculation: the machine indices along one job’s sequence are pairwise distinct — every job uses every machine at most once.
The first two bullets pin down the internal anatomy of a job. The order of its subjobs and which machine each one runs on are given as input — the scheduler doesn’t reorder the steps within a job or move a subjob to a different machine. The model also imposes no precedence between different jobs: every job’s first subjob is allowed to start at , regardless of any other job’s state.
The third bullet is where the new scheduling decision lives. Because each machine is serial, every group of subjobs targeting the same machine has to be put into a single sequence — and choosing those sequences, one per machine, is the combinatorial layer that did not appear in process scheduling. Two subjobs from different jobs, otherwise independent, can now still have to wait their turn for a shared machine.
The fourth bullet — no recirculation — limits how convoluted one job’s path can be: it never revisits a machine it has already used. Concretely, since one job’s machines are pairwise distinct, for every job. The word recirculation names the forbidden behavior (a job circling back to an earlier machine), and the assumption is its absence. Some real-world variants drop this restriction; we keep it as a baseline simplification.
Two close relatives of the job-shop model relax or tighten the per-job step order, and are worth knowing by name even though we don’t pursue them further:
- Open shop — same data as the job-shop model, but the subjobs of one job may be processed in any order. The scheduler picks both the per-job order and the per-machine sequences.
- Flow shop — same data, but every job passes through the machines in the same fixed order. Every job follows one universal pipeline.
The three setups line up by how much per-job flexibility the model carries: open shop is the most permissive, flow shop the most rigid, job shop in between. The rest of this chapter develops the job-shop case.
Admissibility
Admissibility in process scheduling boiled down to a single rule: every precedence edge in the DAG must hold as a inequality. In the job-shop setting the same principle applies but the constraints come in two flavors — per-job ordering and per-machine exclusion.
A schedule in a job-shop model, assigning a start time to every subjob , is admissible when all of the following hold:
- Within-job ordering — for every job , the first subjob begins at or after time zero and every later subjob waits for its predecessor:
- Machine exclusion — no two subjobs sharing a machine overlap in time: for every machine , every pair of distinct subjobs in its subjob set must be processed one after the other. Formally, for any with ,
The within-job condition looks just like the process-scheduling precedence constraints — a list of inequalities, each one a directed edge waiting to be drawn. The machine-exclusion condition has a different shape: it asserts a disjunction — one of two precedence inequalities must hold for each pair, but the model doesn’t say which. That disjunction is the new combinatorial choice introduced by job-shop scheduling.
Conjunctive and disjunctive edges
To keep the precedence graph machinery from process scheduling, we encode both kinds of constraints as edges — splitting them into two families that behave differently.
A conjunctive edge in the precedence graph of a job-shop model is a directed edge whose direction is fixed by the problem data. There are three families, together encoding the within-job admissibility condition:
- Internal-sequence edges for every and — the strict order of subjobs within a job;
- Source edges for every — every job’s first subjob hangs off the common starting vertex;
- Sink edges for every — every job’s last subjob feeds the common ending vertex.
The vertex set is , with each labeled by its execution time and .
If the conjunctive edges were all the precedence graph had, we would be back in process scheduling: a fixed DAG with no orientation choices, ready for CPM. What’s new is that machine exclusion still has to be encoded, and it cannot be expressed as a single directed edge — the constraint is symmetric between the two subjobs sharing a machine, and only the scheduler decides which runs first.
A disjunctive edge in the precedence graph of a job-shop model is an unordered pair of subjobs that share the same machine , drawn as the pair of opposite-direction arrows
The two arrows represent the two possible orderings of the pair on machine — one of them must hold in any admissible schedule, but which one is the scheduler’s decision. Picking a single direction for every disjunctive edge is called choosing an orientation.
For a machine holding subjobs, the orientation problem on that machine can be sized two ways — by individual arrows on the diagram, or by whole disjunctive edges. The two are different granularities of the same picture: each disjunctive edge bundles two arrows, so counting arrows is finer-grained and counting disjunctive edges is coarser-grained, but both describe the same configuration.
- Counting arrows. Every ordered pair of distinct subjobs contributes one directed arrow, so the diagram carries arrows in total. They group into opposing couples — for every arrow there is a counterpart between the same two subjobs. The scheduler keeps exactly one arrow from each opposing couple and deletes the other.
- Counting disjunctive edges. Each opposing couple of arrows is one disjunctive edge, per the definition above — the pair-of-arrows is the edge. So the machine carries disjunctive edges, and the scheduler orients each one (chooses which of its two arrows to keep).
Either way of counting hands the scheduler the same workload: binary decisions per machine. Writing for the subjob count of machine , the total disjunctive-edge count across all machines is
and the global orientation space is — one binary choice per disjunctive edge, holding candidate orientations to search through.
Once an orientation has been picked, every edge in the graph carries a single direction, and the result is a directed graph that we can analyze with the existing CPM toolkit — provided it has no cycles.
The augmented precedence graph of a job-shop model under a given orientation is the directed graph
where is the vertex set of the precedence graph, is the set of conjunctive edges, and contains one directed edge per disjunctive edge, chosen according to the orientation. An orientation is admissible when is a DAG.
With an admissible orientation in hand, every job-shop admissibility condition reduces to a single statement: the schedule satisfies for every edge . That is exactly the process-scheduling notion of admissibility, applied to . So the Critical Path Method runs on unchanged — forward pass, reverse pass, critical paths, makespan — and produces the optimal schedule for that particular orientation.
The hard part has been pushed up one level: choosing the orientation in the first place. That choice is the topic of the next section.
Optimization
The optimization goal of the job-shop model is to find an admissible schedule with minimal makespan . With the augmented precedence graph in place, the search splits into two nested subproblems:
- Inner question — given a fixed orientation of the disjunctive edges, find the optimal admissible schedule. The Critical Path Method solves this in time linear in the size of .
- Outer question — among all admissible orientations, find the one whose resulting CPM makespan is smallest.
The inner question is already solved. Everything in this section concerns the outer one.
Disjunctive edge assignments
What we have been calling an “orientation” has a formal name: a disjunctive edge assignment.
A disjunctive edge assignment for a job-shop model is a set of directed arrows that contains exactly one arrow from each disjunctive edge — the chosen orientation of that pair. Together with the conjunctive edges , the assignment forms the augmented precedence graph .
The assignment is admissible when is acyclic (a DAG). Admissibility is not automatic: many assignments produce cycles by routing disjunctive arrows back into the conjunctive flow.
At least one admissible assignment always exists. Take any topological ordering of the conjunctive-only subgraph — itself a DAG by construction — and orient every disjunctive edge to agree with . Concretely: imagine the subjobs lined up in ‘s order; for every disjunctive edge, let the subjob earlier in the lineup be the source and the later one be the target. Every edge of the resulting augmented graph then points forward in , and a graph whose arrows all run in one direction along a fixed order cannot loop back on itself — so is acyclic. Existence is structural, not accidental: the outer search always has something to find.
Why the outer search is hard
We already noted that the orientation space has candidate assignments, with . Naive enumeration means filtering each candidate for admissibility, then running CPM on every survivor — at most CPM runs in total. CPM itself is cheap (linear in the size of ), but grows exponentially in , and itself scales quadratically with the per-machine subjob counts. The bottleneck is entirely in the outer search.
Two structural facts compound the cost beyond raw exponentiality:
- The problem does not decompose. Re-orienting one disjunctive edge on machine can lengthen or shorten critical paths anywhere in the graph — different machines are coupled through the within-job conjunctive chains. The optimal assignment on one machine cannot be chosen independently of the others. This refusal to decompose into independent subproblems is the signature of discrete optimization and recurs across the field — the traveling salesman problem (find the shortest tour visiting every city once), bin-packing, the knapsack problem, Boolean satisfiability. Local optimality is no guarantee of global optimality.
- Many candidates are inadmissible. A large fraction of the assignments produce cycles in and have to be discarded before CPM ever runs. Filtering for admissibility helps a little, but the surviving set is still exponential.
Heuristics in practice
Exact enumeration is intractable for any realistic , so production scheduling uses heuristics — algorithms that produce a good admissible assignment in reasonable time, with no guarantee of global optimality. The next section walks through one representative heuristic family and exhibits a counter-intuitive failure mode that is characteristic of the entire field: shortening one task’s execution time can make the overall schedule longer. That paradox is the running illustration of why naive intuition fails in discrete optimization.
Precedence-list scheduling
The previous section motivated heuristics for hard scheduling problems. This section walks through one canonical greedy heuristic, then runs it on a small instance that exhibits the counter-intuitive failure mode greedy methods are exposed to across discrete optimization.
The heuristic
Each task carries a priority; the tasks ordered from highest to lowest priority form a priority list (also called a precedence list). Given such a list, the algorithm is essentially “always do the highest-priority thing you legally can”:
Priority-list scheduling (also called list scheduling) is a greedy heuristic for assigning tasks to machines under precedence constraints. Given a priority list:
- Maintain the set of ready tasks — tasks whose precedence requirements have all completed.
- Whenever a machine becomes idle and at least one ready task exists, assign that machine the highest-priority ready task. The task then runs to completion without interruption.
- If no ready task exists when a machine is idle, the machine waits.
The algorithm produces a complete precedence-respecting schedule in a single forward sweep — linear time, no backtracking, no lookahead.
A worked example
The example is not in the job-shop model. It is the simpler identical-machines case — every machine interchangeable, every task with a single execution time — plus task-level precedence as in process scheduling. Six tasks , two machines , and the precedence DAG given below:
The priority list is the natural one — lower index = higher priority:
We compare two scenarios that differ in a single execution time:
| Task | ||||||
|---|---|---|---|---|---|---|
| Scenario A: | 5 | 5 | 5 | 5 | 11 | 6 |
| Scenario B: | 5 | 5 | 4 | 5 | 11 | 6 |
Common sense says scenario B (less total work) should finish at least as soon as scenario A. The heuristic disagrees: scenario A reaches , scenario B drags out to .
Scenario A. Both machines stay productive from onward. runs ; runs . Every transition happens at exactly the moment the next task becomes ready, and both machines finish together at .
Scenario B. Task finishes one unit early, at . The only ready task then is — still need to finish at . The heuristic, with no lookahead, commits to for six units. By the time finally gets picked up at , its 11-unit run pushes the makespan all the way out to .
Why shortening hurt
Making one input strictly better made the global result strictly worse. The mechanism is just timing: in scenario A, ‘s pause until was useful — it let the high-priority become ready before had to commit. In scenario B the pause closed by one unit, and committed to the wrong task before the wave arrived.
This non-monotonicity — making an input “better” can make the result “worse” — is the signature failure mode of greedy heuristics under coupling. It recurs wherever local-rational moves chain into global outcomes: the job-shop outer search, the traveling salesman problem, knapsack, bin-packing. The example is small but the lesson generalizes.
Randomization
Every quantity in the scheduling models so far has been a fixed number known up front — execution times , costs, completion times, the resulting makespan. That works when the data is genuinely deterministic, but planning large projects rarely is. Unforeseeable influences — machine breakdowns, weather, supply hiccups, a key engineer falling ill — push real execution times around their planned values in ways no schedule can predict in advance.
The empirical consequence, observed across decades of project case studies, is underestimation: the realized duration and cost typically exceed the deterministic planning estimate. Famous public examples include the move of the German federal government from Bonn to Berlin and the police information-system project INPOL-neu, both of which ran well over their planned budgets and timelines. Political incentives and incomplete information contribute their own share to the gap, but those are human mechanisms — there is also a mathematical mechanism that bites even when everyone is honest and well-informed, and that mechanism is what this section is about.
The remedy is randomization — replace each deterministic quantity with a random variable whose distribution captures the spread of plausible realizations. Execution times become , costs become , and the apparatus of expected values and distributions becomes available. The scheduling pipeline of the previous sections still runs; it just operates on expectations instead of fixed numbers. And one single inequality already explains a large chunk of the underestimation phenomenon.
Let be subprojects with individual costs (or execution times) — random variables in the stochastic case, with deterministic constants as a special case. Then
In words: the maximum of the expectations is no larger than the expectation of the maximum. Max and expectation cannot be exchanged in general.
Reading the two sides side by side:
- The left, , is what a deterministic planner produces — replace each random by its expected value , then compute the bottleneck across subprojects.
- The right, , is the expected value of the actual project bottleneck — the average, over many possible runs, of the worst subproject in each run.
The theorem says the second is always at least the first, so the deterministic estimate systematically underestimates the stochastic reality. With fixed , the two sides coincide trivially: . The moment the carry any spread, the right side starts pulling away — and across many parallel subprojects, that spread compounds rather than averaging out. A small worked example makes the gap concrete.
A worked example
Take the simplest possible randomization: independent subprojects — picture parallel pieces of work that all feed into one common deadline — each with processing time drawn independently from the uniform distribution on .
Read the interval as a normalized time scale: means no time at all, means the longest a subproject could possibly take (its hard upper bound), and any intermediate is a fraction of that worst case. The actual units (seconds, days, weeks) don’t matter for the punchline — the result is about how the worst-case fraction behaves as the number of parallel subprojects grows.
“Uniform on ” then pins down the distribution: the density is flat at on the interval (zero outside), the CDF is the linear , and the expected value sits at the midpoint, .
Why the density is flat at , and what the CDF really is
The density is the continuous analog of a histogram — picture binning a large sample of values and letting the bins shrink: the bar heights settle into the curve , and the area of any slice under equals the probability of landing in that slice. For the uniform case on , that shape is a flat horizontal segment at stretching from to , and zero everywhere outside the interval.
The "" is not arbitrary: the total area under any density has to equal — every realization lands somewhere — and a flat segment of width must be at height to enclose unit area. Equal height across the interval is the picture-form of “every value is equally likely”.
The cumulative distribution function (CDF) accumulates that area from the left: is the area beneath between and . For the flat- density, the running area grows linearly, so , climbing from at to at .
The deterministic baseline. Treat each as a fixed number equal to its expected value . Then the deterministic planner reports
a single number that doesn’t even see as a relevant variable.
The stochastic reality. Define , the actual worst finishing time across the subprojects. Because the are independent, the CDF of factorizes:
In short: is just the largest of the ‘s (the symbol is a dummy index, as in a sum) — and “the largest one is ” is the same event as “every ”, a logical and. Independence turns the and into a product, each factor is just , and the product is .
The chain of equalities, walked link by link
Each link carries a different idea — worth pulling apart one step at a time.
-
. Pure substitution. We defined a sentence ago, so the two expressions name the same event and therefore have the same probability.
-
. The key logical move. The maximum of a list of numbers is at most exactly when every one of them is at most — if even a single exceeded , the max (being at least that ) would too; conversely, if all of them stay below , then the max — itself one of the — stays below as well. “The max is ” and “all of them are ” describe the same event, and same event implies same probability.
-
. Independence. The defining property of independent random variables is exactly that the probability of a joint event factors into the product of the individual probabilities. Intuitively, knowing whether subproject finishes by tells you nothing about whether subproject does, so the events don’t interact and the probabilities just multiply — much like the chance of two fair coins both landing heads is , not .
-
. Substitute the uniform CDF from above — each — and the product of copies of is .
A quick reality check on the formula: with and , — even though each subproject has a chance of finishing by time on its own, the joint event “both finished by then” has only a chance. With , that drops to . The only way for all subprojects to stay below a threshold is an increasingly unlikely conjunction of “every single one of them got lucky” — and that’s the structural reason the worst subproject’s expected finish time creeps toward as grows.
What does this formula actually say about as grows? For any fixed threshold , the probability shrinks to zero — vanishingly small chance lands at or below . Yet always, since has to land somewhere in . The only way to reconcile those two facts is for to crowd up against , the worst case: with many subprojects, the worst finishing time is almost certainly just below . On the CDF curve this reads as the flat-near-zero region growing wider with and the rise to getting crammed into an ever-thinner sliver next to the right edge. Pushing that observation through to the expectation:
The expected-value integral, step by step
For a non-negative continuous random variable supported on , the expected value is
where is the density — the derivative of the CDF . From ,
Substituting and integrating:
For the limit, write — the subtracted term shrinks to as .
The two answers side by side.
The qualitative gap is striking. The deterministic estimate sits at regardless of ; the stochastic one climbs toward the worst case as grows, because a project that could take up to time effectively does once enough parallel subprojects are stacked. The expected completion drifts toward the imaginable worst case, and there is no escape from this drift — exactly the structural underestimation Fulkerson’s theorem warned about in the abstract, now made unmistakable by a concrete distribution. The deterministic model is blind to that drift; randomization is the lens that lets the planner see it coming.
Hypergraphs
The chapter has, so far, leaned on directed graphs — vertices for tasks, edges for precedence or machine-sharing. Every edge connected exactly two vertices, and every machine handled at most one task at a time. As a closing outlook, relax both restrictions, and the natural setting becomes a hypergraph.
The new idea is small but powerful. In a regular graph an edge is always a pair of vertices. In a hypergraph the edge is generalized to a hyperedge — a subset of vertices, of any cardinality. A hyperedge can group two vertices (recovering an ordinary edge), three vertices, twenty, or any number. For instance, with , a hypergraph might carry hyperedges , , , and — four hyperedges of cardinalities . Picture-wise: in a graph you draw lines between pairs of dots; in a hypergraph you draw closed blobs enclosing any number of dots at once.
Formally, a hypergraph is a pair
where is a vertex set and is the power set of (the set of all subsets of , also written ). So just says “each hyperedge is some subset of vertices, and we pick a collection of them”. When every hyperedge has exactly two vertices, the hypergraph reduces to an ordinary graph — graphs are the cardinality- special case.
For scheduling, the re-interpretation is the punchline. Vertices = tasks, as before. Hyperedges = machines: each machine corresponds to the hyperedge containing all the tasks that need to run on it — a single set summarizing “these are the tasks competing for this machine”. A task that needs several machines appears as a vertex in several hyperedges. The new degree of freedom over the job-shop model is that a machine can run several tasks simultaneously, up to some capacity, instead of one at a time.
The natural scheduling problem on this object is vertex coloring, with one mapping at its core:
- Color = time slot. Two vertices sharing a color means two tasks running in the same time slot — simultaneously.
- Hyperedge = machine. Two vertices in the same hyperedge means two tasks competing for the same machine.
So same color and same hyperedge means two tasks trying to run on the same machine at the same time — and the cap on how many of those a hyperedge can hold is exactly the machine’s capacity. Vertices in different hyperedges (tasks on different machines) are free to share a color; they don’t compete. The optimization goal is fewest colors possible — fewer colors mean fewer time slots mean a shorter overall schedule — which puts pressure to pack as many same-colored vertices into each hyperedge as capacity permits. (Forcing every vertex to take a distinct color would just mean every task gets its own time slot in serial, which is the worst schedule, not the best.)
The hypergraph machinery is needed precisely because capacity is now allowed to exceed — the job-shop model’s “one task per machine at a time” rule is being relaxed. With capacity and every hyperedge of size , the per-hyperedge constraint “at most one same-colored vertex” forces every edge’s two endpoints to take different colors — exactly the classical graph-coloring problem (paint the vertices of a graph so no two adjacent ones share a color, using the fewest colors possible), itself NP-hard in general. Allowing larger hyperedges and capacities above piles more structure on top, and the closing message is the same one the chapter has been making throughout: a richer modeling target lands in a harder optimization regime, and heuristics carry the practical load.
That’s where the scheduling chapter stops. The arc traced — DAG-based process scheduling, job-shop with disjunctive choices, randomization for unforeseen variation, and hypergraphs for capacity-sharing machines — sketches one lesson at four scales: each modeling class trades expressiveness against tractability, and picking the right class is half the discrete-modeling problem.