Discrete Modeling
Two Views of Traffic
Traffic can be studied with two different kinds of models, and this chapter leans on both. A discrete model tracks individual, countable objects — data packets hopping between machines, customers stepping up to a counter. A continuous model smears those objects into a flowing quantity — a density of cars along a road that thins and thickens like a fluid.
Both kinds of model describe the same handful of quantities: the throughput (how much traffic passes through per unit time), the maximum throughput (the most the system can handle per unit time), the capacity (how many jobs can be in service at once), and the filling (how much is inside right now). We make each of these precise later in the chapter. What separates the two views is not what they measure but the mathematics used to get at it.
- Discrete consideration — data traffic in computing systems. A computing system moves data packets between components, and we model it as a network of queues, analyzed with probability theory, stochastic processes, and random variables. This is the subject of the present chapter.
- Continuous consideration — traffic flow. Road traffic is modeled as a continuous medium, described with integrals, derivatives, and partial differential equations. That is the subject of Traffic Flow Models.
It helps to keep both pictures in mind at once — internet data traffic and road traffic — because the same optimization questions (push more through, avoid jams) recur in both.
The Modeling Problem
The goal is to describe — and then optimize — a computing system built from several service units, each with its own functionality, and to model and simulate the traffic of data packets flowing through the network they form. “Optimize” here is concrete: improving an existing system, or designing a better one from scratch.
The road-traffic analogy makes the aims tangible. In the city, traffic lights are controlled to maximize the throughput of the intersections; on highways, a traffic-management system heads off congestion before it forms. The same two goals — move more traffic, avoid jams — carry straight over to data packets moving through a network.
We model such a system as a queuing network: queues sitting in front of service units, studied with probability theory, queues, and stochastic processes. Why bring in randomness at all? Because the model is inherently stochastic, for two reasons:
- The events are unpredictable when we can see only part of the system’s components.
- We rarely need the exact fate of every individual packet — what we want are mean values and averages: the average waiting time, the average throughput, and so on.
A Simple Example: The Post Office
Before touching computers, it helps to look at an everyday system with exactly the same structure. The classic one is a post office — or, just as well, a single shop: one facility with its own queues and counters, serving a stream of arriving customers. That single shop is the right unit for now. Later, when many such shops are wired together so that a job is split into partial tasks and routed through several shops in turn — each shop handling a different part — the picture grows into a whole shopping center, and that network of shops is exactly what a queuing network will be. We are not there yet; for this first example, one shop is enough.
A post office has one entrance , one exit , one or several counters where customers are served, and one or several queues where they wait. The aim is to make everyone — customers and employees — happy.
But “happy” pulls in two directions, and that tension is the whole problem:
- Customers want a short average waiting time.
- The operator wants a short expected idle time for the personnel — staff standing around with no one to serve is wasted money.
These two aims conflict. Idle time is smallest with a single employee, who is then almost never idle — but that lone counter makes the average customer wait the longest. Push the other way, opening a counter for every customer, and nobody waits while the staff sit idle most of the day. What makes the trade-off genuinely hard is the randomness in when customers arrive and how long each one takes: the stochasticity is unavoidable.
To turn this into a model, we pick out the objects involved and the parameters that describe each:
- customers — type of job, frequency and intervals of arrival, execution time;
- counters — open or closed;
- employees — functionality (which jobs they can handle), promptness, reliability.
The same post office can also be laid out in two different ways, and the choice matters:
- one queue per counter — each arriving customer picks a line and commits to it (the classic supermarket layout);
- a single shared queue — everyone waits in one line, and whoever is at the front goes to whichever counter frees up next (the modern bank or airport layout).
Both are built from the same entrance, queues , and counters , yet they behave differently — a difference we come back to once we can measure waiting times.
Interfaces to the Environment
A post office cannot be modeled as a stand-alone system. It sits inside a wider context — the town, people’s daily routines, the surrounding economy — that is impossible to capture completely. So we restrict the model to the interfaces between the system and its environment, and decide what has to happen at each. Three tasks fall out.
Modeling the environment
The environment supplies the system’s input, and two questions define it: how often, and at what intervals, do customers arrive, and what exactly do they want? Arrival patterns are not uniform — different kinds of customer show up at different times of day. There are two ways to feed this into the model:
- Use measured data directly. Take empirical measurements — the famous “car counters” stationed along highways are the canonical example — and drive the simulation straight from the recorded samples.
- Fit a distribution and synthesize. Use the same measurements to derive a probability distribution, then generate fresh, synthetic input data that is statistically representative of the real thing.
The second route is the more flexible one: instead of replaying the recording verbatim, we extrapolate it into a distribution and sample new data from that. It also raises a question we return to at length later — how do you get a computer to generate randomness in the first place?
Modeling the system behavior
With the input pinned down, the real object of interest is the system behavior — what happens inside the post office once customers are through the door. This is where the modeling effort goes, and it is what the rest of the chapter builds toward.
Analyzing the results
Reading answers off a run is not the end. The output of a stochastic simulation is itself random, so the analysis leans on statistics: running suitable tests and checking that the observations are independent of one another before trusting any average computed from them.
Some effects are genuinely hard to pin down — the feedback effects, where the system’s own performance changes the demand it faces:
- If word gets around that a post office serves people quickly, more customers travel in from other districts — which slows it back down.
- Customers keep switching lines, convinced the other line always moves faster than their own.
These loops are the hardest part to model faithfully, precisely because cause and effect run in a circle.
The Instruments: Random Variables
Everything the model deals with is random events happening in time, and therefore time intervals of random length — the gap between one customer arriving and the next, the time a counter spends serving one job. Before building anything, we need the vocabulary for describing such a quantity. It all hangs off a single object and the two functions attached to it.
A random variable is a quantity whose value is decided by chance. In the continuous case it is described by two functions. The distribution gives the probability that does not exceed a value , and the density is the function it accumulates:
The second equation just says that some value certainly occurs, so the total probability is .
It is worth being clear about which of these two functions is which, because the picture that usually comes to mind belongs to only one of them. The famous bell curve of the normal distribution is a graph of the density , not the distribution : its defining feature is that the whole area underneath it equals , which is exactly the normalization . The density is a local statement — the area under a stretch of the curve is the probability that lands in that stretch, so where the bell is tall the values are likely and where it flattens into the tails they are rare. The distribution is the running total of that same area: sweep from left to right accumulating the density as you go, and is how much has piled up by the time you reach . That accumulating curve starts at , only ever climbs, and levels off at once all the area is behind it. So the two functions carry the same information in two shapes — the density says how likely each value is at a point, and the distribution says how much probability sits at or below that point.
The random variables here measure durations, so almost always , and the effective lower bound of every integral is then rather than .
Single-number summaries
A whole density is more information than we usually want. From it we pull out a few single numbers — its moments — that capture the distribution’s location and spread. The first-order moment is the one everybody knows.
The expected value (or mean) of is the density-weighted average of its values:
The variance measures how far typically lands from its mean, averaging the squared deviation:
Its square root is the standard deviation , back in the same units as itself.
The two numbers answer different questions. The expected value is your best guess for the size of — where the value will land on average. The variance and standard deviation tell you how much to trust that guess: a small spread means is nearly deterministic, a large one means it scatters widely around the mean.
If the integral form looks unfamiliar, it is the continuous version of the variance you would compute by hand from a finite list of measurements, . There, each of the data points chips in its squared deviation with the same weight . In the integral the density takes over that weighting job: the factor stands in for , so each squared deviation is scaled by how likely that value is before being accumulated across all . It is the same averaging operation, lifted from a finite sample to a continuous distribution — and the expected value reads the same way, as the continuous counterpart of the plain average .
There is a catch with the standard deviation, though: it is an absolute figure. Told that a measured length has a standard deviation of two kilometers, you cannot say whether that is a lot or a little without knowing the typical length being measured — two kilometers is enormous for a footpath and negligible for a continent. Normalizing the spread against the expected value fixes this.
The coefficient of variation puts the standard deviation in relation to the mean:
Because it divides one length by another, is a pure number with no units — a scale-free measure of relative spread that can be compared across completely different quantities. It becomes a running yardstick later on.
The dynamical view
The summaries above look at from the outside, as a finished quantity. The queuing model needs a second, dynamical view: given that we have already been waiting a while, how likely is the interval to end now? That question is built from a conditional probability.
The conditional probability of given that has occurred is
The form we lean on asks whether the interval ends within the next small step , given that it has already outlasted :
Picture a hot-dog stand: what is the probability that a customer walks up in the next five seconds, given that nobody has come for the last ten minutes? The numerator is the chance the interval ends inside the little window ; the denominator is the chance it survived past in the first place, which is what “given no one has come yet” restricts us to.
Now shrink the window. As the conditional probability itself goes to zero — wait a short enough instant and nothing happens in it — so the bare limit tells us nothing. The fix is exactly the one that defines a derivative: divide by before taking the limit, turning a vanishing probability into a finite rate.
The hazard rate is the instantaneous risk that the interval ends right now, given that it has lasted until :
Why the limit collapses to the density over the survival term
The denominator carries no , so it is a constant as far as the limit is concerned and can be pulled out in front. What is left inside the limit is precisely the difference quotient of :
That remaining limit is the definition of the derivative , and the derivative of a distribution is its density . So the whole expression collapses to .
It helps to read the final form back in words. The numerator is the chance per unit time that the interval ends right around , taken over all intervals. Dividing by , the fraction that have survived past , restricts that to the survivors: of the intervals still running at time , what fraction finish per unit time right now? A high hazard rate means the end is imminent given how long the interval has already lasted; a low one means it is in no hurry to finish. It is a conditional failure rate, and it turns out to be the most telling fingerprint of a distribution — the two examples that follow are told apart entirely by the shape of their hazard rate.
A Simple Example: The Uniform Distribution
The plainest instrument is the one where nothing is favored: every outcome in a range is equally likely.
A random variable is uniformly distributed on when its density is constant across the interval and zero outside it:
A natural scenario is the remaining rotation time of a spinning disk: after a task arrives at a random moment, the wanted block is equally likely to be anywhere along its circular path, so the latency until it passes under the read head is spread evenly from up to one full revolution .
Its summaries work out to
The expected latency sits at the midpoint , as symmetry demands. The coefficient of variation is the interesting one: it comes out to with no in sight. Widening the interval scales the spread and the mean by the same factor, so their ratio is fixed — every uniform distribution has the same relative spread regardless of size.
Where these numbers come from
Each summary is a one-line integral against the constant density on .
Taking the square root gives , and dividing by cancels the to leave .
The hazard rate exposes the distribution’s character:
As climbs toward the denominator shrinks and the hazard rate runs off to infinity: the longer the interval has already lasted, the more certain it is to end at any moment. It is the mood of the days before a fixed date on the calendar — the closer Christmas comes, the greater the chance from one day to the next that it is finally here. This rising hazard is exactly the behavior the next example strips away.
A More Exciting Example: Constant Hazard Rate
Turn the previous question around: what must a distribution look like for its hazard rate to stay constant, , never rising or falling with ?
A constant hazard means the risk of the event sits at the same level for all time. In particular it does not depend on the interval’s own past — how long we have already waited says nothing about how much longer we must wait. That is a property worth naming.
A distribution is memoryless when the time already elapsed has no effect on what remains: for any waited-out duration and any further duration ,
Equivalently, its hazard rate is constant.
Read the two sides against each other: the left is the chance of lasting an additional given the interval has already run to , while the right is the chance that a fresh interval lasts at all. Setting them equal says the elapsed drops out completely — the clock effectively resets, and only the extra duration matters. (This is why the right side is and not : it describes a brand-new interval, not the original one carried further.)
Back at the hot-dog stand: whether you have stood through five quiet hours or just served twenty-five customers in the past hour, the chance that the next customer appears in the next five seconds is exactly the same. The distribution keeps no memory of what came before. A constant hazard is realistic whenever an event can be sourced to many independent, individually unlikely causes — the time until the next system error or user login, the time until an atom’s next spontaneous decay, even the time until the next death in the Prussian army from a horse’s kick, the data set that first popularized this distribution.
Working out which density holds the hazard rate flat produces a familiar pair.
The negative exponential distribution with rate has density and distribution
It is the only continuous distribution that is memoryless.
Check that its hazard rate is constant
Feed the exponential into the hazard-rate formula. The survival probability is , so
The exponential factors cancel exactly, leaving the constant — the flat hazard we started from.
The exponential times the intervals between events. Its discrete companion counts how many events land in a fixed stretch of time.
The Poisson distribution gives the probability of exactly events:
With , the variable counts the number of events occurring in an interval of length when the gaps between them are negative-exponentially distributed with rate .
The two describe the same memoryless stream from opposite ends: the negative exponential distribution measures the time between successive events, while the Poisson distribution counts how many fall inside a window. Their characteristic values are
The coefficient of variation lands exactly on . That makes the exponential a natural reference point for the yardstick introduced above: distributions with are more regular than the memoryless case, those with burstier — a comparison we return to once random processes enter the picture.
Stochastic Processes
The instruments from the previous section each describe a single random quantity — one interval, one latency, one wait. But traffic is never a single event; it is the same event happening over and over: customer after customer arrives, task after task finishes, error after error strikes. To model such a stream we need not one random variable but a whole sequence of them.
There are two ways to pin a stream down: by the points in time at which events happen, or by the spacings between them — the gaps from one event to the next. The second is almost always the better handle. A list of absolute timestamps says little on its own, but the gaps between them recur under the same conditions, and a recurring quantity is one you can fit a distribution to. So from here on the object of study is the inter-arrival time, the interval between consecutive events, rather than the raw clock reading.
A stochastic process is a sequence of random variables indexed by their position in time — one for each successive interval between events. Because the same kind of event keeps recurring, a single random variable is replaced by an indexed family of them, one per repetition.
When the intervals share not just a common nature but a common rule, the process is especially well-behaved.
A stochastic process is stationary when the statistical law generating its intervals does not change over time. The strongest form of this — and the case treated here — has the iid: independent (no interval influences another) and identically distributed (every is drawn from the same distribution ).
The name is apt: what stays stationary is not the individual outcomes — those still vary from one interval to the next — but the underlying probability law. Every interval is drawn from the same distribution, and no interval carries information about its neighbors.
Plenty of real streams break one of the two conditions:
- Cars passing a checkpoint are famously companionable: a single slow car gathers a pack behind it, so a long gap tends to be followed by a cluster of short ones. The intervals are not independent — a jam remembers itself.
- The daily average temperature across a year is not identically distributed: a July reading and a January reading come from visibly different distributions, so the law drifts with the season.
One stationary process matters more than any other here — the one whose intervals are memoryless.
A Poisson process is a stationary process whose inter-arrival times are negative-exponentially distributed. Equivalently, the number of events it produces in a fixed window follows the Poisson distribution introduced earlier — which is where the process gets its name.
Because its intervals are exponential, the Poisson process inherits their defining trait: memorylessness. That single property — the process forgets how long it has already been waiting — has consequences that run squarely against intuition, as the next example shows.
The Hitchhiker’s Paradox
A hitchhiker stands at the roadside. Twenty minutes pass with no car. Surely, they reason, a car must be due any moment now — the longer the drought, the closer the next arrival ought to be. If cars pass as a Poisson process, this reasoning is exactly wrong, and pinning down why is worth doing slowly.
Fix attention on a single gap between two consecutive cars. The length of that gap is the random variable : some gaps are short, some long, and each new gap draws its own from the same distribution. Now the hitchhiker arrives just as a car goes by and starts waiting somewhere inside one such gap. Write for how long they have waited so far with still no car. That wait tells us one thing for certain: the gap they are stuck in must be longer than (in symbols, ) — otherwise the next car would already have come.
What the hitchhiker actually wants to know is about the rest of the wait, and we get at it by asking how the full gap length is distributed once we condition on knowing . Call that conditional distribution : the probability that the gap’s whole length turns out to be at most , given that it already exceeds . So the threshold is a candidate value for the total length, and only values make sense — we have already ruled out every gap shorter than what has elapsed. The first line below just restates this; the remaining lines evaluate it for the exponential gap:
The first three lines are general — the definition of conditional probability, then its rewrite through the distribution function . From the fourth line on we plug in the negative-exponential form : the survival term collapses to , and dividing through leaves a single exponential in .
The result reads most clearly once you name the leftover explicitly. Since is the mark measured from the start of the gap and is how much has already elapsed, is the time still to come from the moment the hitchhiker checks. Writing that remaining time as its own random variable , the last line says that for any leftover duration ,
the leftover has exactly the same distribution as a brand-new gap . The elapsed only shifts where the clock starts; it does not reshape the distribution, which simply begins again from .
This is not the much stronger — and false — claim that every remaining wait is equally likely. Keep the same gap and say the hitchhiker has already waited minutes. The chance the gap ends by its mark, i.e. within more minutes, is , fairly modest; the chance it ends by the mark, within more, is , all but certain. A longer remaining wait is still far likelier to have finished than a shorter one, exactly as for a fresh gap — the shape of is unchanged. What memorylessness removes is only the dependence on how long you have already waited: whether that is minutes or , the odds of lasting another come out identical. That is precisely the hitchhiker’s error: the wait already endured says nothing about the wait still to come. The expectation says the same thing plainly:
Given that you have already waited , the expected total is plus a full mean interval — not the mean minus what you have already endured. The hitchhiker’s twenty minutes buy nothing; the expected remaining wait is still the whole .
This invites a sharper question. Drop an outside observer onto the timeline at a random moment — not synchronized to the events in any way — and ask: how long until the next event, and how long since the last?
The forward recurrence time (FRT, also VRZ) is the time still remaining, measured from the observer’s random arrival instant, until the next event.
The backward recurrence time (BRT, also RRZ) is the time that has already elapsed, at the observer’s random arrival instant, since the previous event.
Track these two clocks across the timeline of events at with intervals . Over each interval the forward recurrence time starts at the full interval length the instant after an event and ramps down to zero as the next event approaches — a downward ramp of slope . The backward recurrence time is its mirror image: it starts at zero and ramps up to the full interval length, slope . Plotted over the whole timeline, the forward recurrence time traces a row of right-triangular “saw teeth,” one per interval, each of height and width . The observer, arriving at a random instant, lands somewhere under these teeth, and the recurrence time they measure is the height of the tooth at that point.
Now the setup that gives the paradox its bite. Imagine two restaurants on the same street. The first records the true intervals between its own customers — the full . A rival across the road does something subtly different: at scattered moments it starts a stopwatch and times how long until the next customer walks in, sampling the forward recurrence time. How does the rival’s average compare to the true average interval?
Intuition says the rival must measure less: each of its stopwatch readings is only the tail end of some interval, a piece of a rather than the whole of it. So the rival’s average ought to fall below the true mean. That intuition is about to be tested — and for bursty traffic it fails outright.
The Outside Observer
Whatever the observer’s intuition, the actual average forward recurrence time is fixed by the distribution of , and it comes out equal to the average backward recurrence time:
The three forms are one number written three ways. The middle step uses — the second moment splits into variance plus mean-squared — and the last collects everything into the coefficient of variation , which is where the entire answer concentrates.
Where the formula comes from — the saw-tooth area
The observer’s average forward recurrence time is just the average height of the saw-tooth curve over a long stretch of timeline: total area under the teeth divided by total base length.
Each forward-recurrence tooth over interval is a right triangle with both legs equal to , so its area is . Across intervals from to the teeth cover a total area sitting on a base . The mean height the observer encounters is the quotient:
Multiplying top and bottom by turns the two sums into sample averages. As they converge to and , leaving
which is the first form of the general result.
Read through , the formula sorts every process into three regimes against the true mean :
- uniform distribution on (, so ): the observer sees , which is less than . Here intuition holds — the rival really does measure below the true mean.
- Poisson process (): exactly. The observer measures neither more nor less than the true average.
- any process with : . The outside observer measures more than the average interval — the “subset” intuition is not just wrong but backward.
The Poisson case is the one to hold onto, and memorylessness explains it in a sentence. Because the process forgets, the instant the observer arrives is statistically no different from an instant just after an event: the remaining time is distributed like a whole fresh interval, so its mean is the whole — exactly what the hitchhiker’s conditional expectation already told us. Landing mid-interval gains the observer nothing and costs them nothing.
The case is the genuinely strange one, and its resolution is worth spelling out. A large coefficient of variation means the intervals are wildly uneven — many short gaps punctuated by a few very long ones. The true average weights every interval equally, short and long alike. But the outside observer does not sample intervals equally: dropping in at a uniformly random instant, they are far more likely to land inside a long interval than a short one, simply because a long interval occupies more of the timeline. The observer oversamples exactly the intervals that stretch out and undersamples the brief ones that flicker past, so their average is dragged above the true mean. The stochasticity is quietly misleading them.
The interval you land in is longer than the average interval
Length-biased sampling has a clean consequence for the Poisson process itself. There , so the interval the observer lands in — its length being the time since the last event plus the time to the next — has expected length , twice the mean interval. Picking an interval by landing in it at random is not the same as picking one uniformly from the list: the random landing favors the long intervals, and here that favoritism doubles the expected length. The observer is not measuring a typical interval at all; they are measuring an atypically long one, seen from a random point inside it.
Model Parameters
With the random-variable toolkit in hand, we can go back into the post office — a single shop, one facility with its own counters and queues — and give its parts and its timings precise names. Everything that follows is a shared vocabulary of model parameters: the quantities any such facility is described by, whether it is a post office, a disk drive, or a processor.
Start with what the facility is built from. Its building blocks are all functional units, and they split into two kinds by what they do.
A functional unit (FU) is any component of the system delimited by its role — by its impact or its assignment. Functional units come in two kinds: those that process jobs and those that transport them.
A service unit (SU), also called an instance, is a functional unit that does the actual work on a job; its functionality does not include moving data around. In the post office it is a counter — it serves the customer in front of it but carries no one from place to place.
A canal (C) is a functional unit whose sole function is data transport: carrying jobs between service units. In the post office it is the queue that feeds customers to the counters — it moves them along but serves no one.
Bundle several of these together and the bundle is itself a unit.
A system is a functional unit built from several service units, together with the canals connecting them, and treated as a single whole. The post office — its counters and its queues, taken together — is one system.
A job is a unit of work that occupies a service unit and needs processing — the customer at the post office, the data packet in a network. Jobs are the load the system exists to serve.
With the parts named, we can measure them. The first quantity is a duration.
The dwelling time of a job is the total time it spends in the system, from the moment it enters until its processing completes: the time spent waiting in a canal plus the time spent actually being served.
The capacity of a service unit is the maximal number of jobs it can process at the same time. A post-office counter that serves one customer at a time has ; four counters bundled and treated as a single service unit have . A service unit with is called simple.
The filling of a service unit is the number of jobs it currently holds. Three cases get their own names:
- : the unit is empty.
- : the unit is busy.
- : the unit is occupied, full to capacity.
The ratio is the relative filling, the fraction of capacity in use.
The next three quantities describe flow rather than contents.
The throughput of a service unit is the average number of jobs it completes per unit of time. It is an observational quantity — a snapshot of how much work is actually getting done.
The maximum throughput of a service unit is the largest throughput it can possibly sustain. It is a characteristic of the unit itself — how fast the clerk can work — not of the current load.
Capacity and maximum throughput are different quantities, easy to conflate because both sound like “how much the unit can handle.” Capacity counts how many jobs a unit holds at once — a structural limit, how many service windows there are. Maximum throughput counts how many it can finish per unit time — a speed limit, how fast each window works. A single fast counter has but a large ; a row of sluggish counters has a large but a small .
The utilization of a service unit is its throughput relative to its maximum — the fraction of its capability actually in use. A utilization near is a nearly idle unit; a utilization near is one running flat out, on the edge of saturation.
The dwelling time defined above quietly bundles two very different intervals together, and separating them matters.
The service time of a job is its net dwelling time: the time it spends actually being processed, with any queueing stripped out. The part stripped out is the waiting time — the time the job sits in a canal before a service unit takes it. Together the two make up the whole dwelling time,
The service time is what the dwelling time collapses to when there is nothing to wait for. Concretely, when the filling is — the job is the only one in the system — there is no queue to sit through, so the waiting time is and the dwelling time is exactly the service time, . Waiting appears only once jobs pile up and compete for a unit; it is a consequence of the crowd, not of the counter.
Little’s Law
One relation ties the flow quantities together so generally that it is the central law of traffic theory. It connects three of the parameters just defined — the filling, the throughput, and the dwelling time — and it holds with no assumption at all about how jobs arrive or in what order they are served. That generality is what makes it worth its name.
Little’s law relates the average filling of a system to its throughput and its average dwelling time. Over a time window in which the system starts and ends empty (), its operational (deterministic) form reads
so that the average filling equals the throughput times the average dwelling time. Replacing each quantity with its random-variable counterpart — capital letters , , for the stochastic versions of , , — and taking expected values gives the stochastic form
The relation is easiest to feel with a concrete rate. Picture a car plant that completes cars per hour, where each car spends hours on the line from first weld to drive-off. At any given moment the line must hold cars in progress — that is simply what it takes to keep finishing ten every hour when each one needs five hours inside. Speed the completions up, or make each car take longer, and the number simultaneously on the line rises in exact proportion.
The law sharpens into something even simpler for the smallest interesting case. Take a service unit on its own and make it simple (). Inside such a unit a job is always being processed rather than waiting — the queueing happens outside, in the canal — so its dwelling time is exactly its service time, . The stochastic law then collapses into a short chain:
Read it left to right. Substituting gives the first equality. A simple unit can finish jobs no faster than one per mean service time, so its maximum throughput is (a job needing six minutes caps the unit at ten per hour), which turns into . That last ratio is the expected utilization: is the stochastic counterpart of , so . For a simple unit, then, the average filling is nothing but its utilization.
An Intuitive Proof
The operational form is not an assumption but a piece of bookkeeping, and the picture that proves it is worth carrying. Track two running counts across the window: let be the number of jobs that have arrived by time , and the number that have departed — completed — by time . Both are step functions that jump up by one at each event; plotted together they trace two rising staircases, the arrival staircase always on or above the departure staircase . Because the system is empty at both ends of the window, the two staircases meet there: and .
At any instant the vertical gap between the staircases is the filling — the jobs that have arrived but not yet left:
The area trapped between the two staircases can be read two ways. Swept vertically, column by column, it is the filling accumulated over time. Swept horizontally, row by row, each job contributes a strip whose width is its own dwelling time . So the same area equals both the time-integral of the filling and the sum of every job’s dwelling time. Dividing that area by the elapsed time gives the average filling,
and dividing the very same area by the number of jobs that completed in the window gives the average dwelling time,
Because the two share that integral, dividing the first by the second cancels it outright:
The ratio that survives is the number of completions per unit time — exactly the throughput — and rearranging leaves Little’s law, .
One theme is worth carrying forward out of all this. Every case above is governed by a single number, the coefficient of variation . When is small the process is tame — the outside observer and the true average nearly agree, and intuition mostly works. When is large the same intuition breaks down: averages diverge, and the system becomes hard to reason about. Variation is the troublemaker. The smaller it is, the friendlier and more predictable the traffic; the larger it is, the more the randomness confounds our expectations. This is the first hint of a lesson that returns whenever stochasticity meets a system we are trying to run well.
Elementary Queuing Systems
A single service unit on its own is a dull object: jobs arrive, get served one after another, and there is little to optimize. The interesting behavior — queues forming, counters sitting idle, the tug-of-war between waiting time and idle time — appears only once a service unit is wired together with somewhere for jobs to wait. That assembly is a queuing system.
A queuing system (QS) is a system in which jobs cannot be lost. A job that arrives while its service unit is occupied is not turned away; instead it waits in a canal until the unit is free to take it. The canal holds the job, the service unit does the work, and no job leaves until it has been served.
The simplest such system worth studying has exactly one of each part.
An elementary queuing system (eIQS) is a queuing system built from just two functional units:
- one canal, acting as a waiting pool of capacity — the queue that holds jobs until a server is free;
- one service unit of capacity , which may equally be viewed as parallel simple service stations (SS), each of capacity .
The post office is the picture to keep: the waiting pool is the roped-off line, and the service unit is the bank of counters — of them, each serving one customer at a time.
One capacity or many stations — the same unit two ways. A service unit of capacity and four simple service stations bundled together describe identical behavior: four jobs in service at once. Which picture is handier depends on the question, and both notations for that count appear below — the unit capacity and the station count — for the same number.
To turn this into something we can analyze, two things have to be pinned down: how jobs come in, and how they are handled once inside. Both are governed by chance, so both are modeled as stochastic processes — the incoming stream specified from outside the system, the processing from within.
Arrival and Service Processes
The two processes describe the two halves of a job’s life: getting in, and getting served.
The arrival process models how jobs enter the system. Its defining parameter is the inter-arrival time — the time span that elapses between two consecutive arrival events. The standard assumption is that all inter-arrival times are independent and identically distributed, which makes the arrival process stationary.
The service process models how jobs are processed once a server takes them. Its defining parameter is the service time — the net time a job spends being worked on — taken to be independent of the arrival times.
For each process, the distribution of its governing time falls into one of three classes, and these three letters become the alphabet everything downstream is written in:
- D — deterministic: the time is constant, nothing is left to chance. Jobs arrive exactly every so many seconds, or every job takes exactly the same service time.
- M — Markovian: the time is negative-exponentially distributed, with distribution and expected value . This is the memoryless middle ground — general enough to be realistic, simple enough to compute with.
- G — general: the time follows an arbitrary distribution. Very powerful, but very complex to analyze.
The M class is the one to dwell on, and it comes with a named rate for each process.
The arrival rate is the parameter of a Markovian arrival process — the rate of the negative-exponential inter-arrival time, giving a mean inter-arrival time of . A larger means jobs arrive more often.
The service rate is the parameter of a Markovian service process — the rate of the negative-exponential service time, giving a mean service time of . A larger means each job is finished faster.
The Poisson Counting Process
The M class deserves a closer look, because its rate parameter is an old friend in disguise. Memorylessness already told us that a negative-exponential time has a constant hazard rate: at every instant, no matter how long we have already waited, the chance of the event landing in the next tiny slice of time is the same. That constant is exactly the rate just named.
The event rate of a Markovian process is its constant hazard rate,
where is either the inter-arrival time or the service time. In its two roles the event rate carries two names: the arrival rate for the arrival process, and the service rate for the service process.
So the answer to “why does the hazard rate reappear here as something else” is that it is not something else. The event rate, the arrival rate, the service rate, and the constant hazard rate of a memoryless time are one quantity seen from several angles. A Markovian process with rate is precisely a Poisson process, and the alternative name counting process says what it does: it counts events. The number of arrivals falling in an interval of fixed length is Poisson-distributed, which is where the whole family gets its name.
One consequence is worth stating plainly, because it draws the line between a system that copes and one that jams. As long as jobs arrive slower than the system can clear them — arrival rate below the maximum throughput, — the throughput simply matches the arrival rate:
Everything that comes in goes out at the same average pace, and nothing accumulates. Push past , though, and the picture breaks: the system can no longer keep up, a waiting line builds without bound, and the throughput stops tracking the arrival rate altogether — it saturates at the most the servers can manage while the queue behind them grows. Throughput reflects arrivals only while there is spare capacity to absorb them.
For all its convenience, M is a model, not reality, and it does not always fit. Its memorylessness is exactly what real traffic often violates: cars on a road travel in packs rather than independently, and at a hot-dog stand the feeling that “it’s been five minutes, someone is bound to come” is precisely the kind of memory a Poisson process forbids. M is popular because it is often realistic enough and easy to analyze — but the fit has to be checked, not assumed.
Classifying Elementary Queuing Systems
With arrival and service processes each sorted into D, M, or G, an entire elementary queuing system can be summarized in a compact shorthand.
Kendall notation describes an elementary queuing system with up to six parameters, separated by bars:
- arrival and service: the process classes, each D, M, or G;
- : the number of service stations, equivalently the capacity of the service unit;
- : the capacity of the waiting pool;
- : the overall number of jobs in the system;
- : the service strategy, the discipline by which the queue is emptied.
The last three parameters are optional and are dropped when unrestricted or unremarkable.
A few standard cases show the range, from the friendliest to the intractable:
- M | M | 1: negative-exponential arrivals and service, a single service unit. Simple and analytically treatable, though only of limited realism.
- M | G | 1: negative-exponential arrivals, arbitrarily distributed service, one service station. More complicated, but often quite realistic — a good model for a CPU.
- G | M | 1: general arrivals, Markovian service. Complicated, with less potential for applications.
- G | G | n: general arrivals and service, stations. Hardly treatable analytically at all.
The trade-off is clear: every step away from M toward G buys realism at the cost of tractability.
Sequence Planning: Service Strategy
The parameter hides a design question of its own — once several jobs are waiting, which one does a freed server take next? The rule that decides is the service strategy, and while we lean on just one of them throughout, it is worth seeing the menu.
A service strategy (or queue discipline) is the rule that picks which waiting job a freed server processes next. Strategies split into two families by whether a job already in service can be interrupted.
Non-preemptive strategies never interrupt a job once its service has begun — their advantage is that no work is ever thrown away:
- random: the next job is chosen at random;
- FCFS: first-come-first-served, the job that has waited longest goes next;
- LCFS: last-come-first-served, the newest job goes next;
- priority based: each job carries a priority and the highest goes next;
- SJN: shortest job next, the job with the smallest service time goes next.
Preemptive strategies may suspend a job in service to start another — their advantage is that they can react to service time without knowing in advance:
- round robin: jobs are served in turn, each for a fixed time slice;
- LCFS-preemptive: a newly arriving job is taken up immediately;
- priority first: an arriving job of higher priority displaces the one in service;
- SET: shortest elapsed time, the job served least so far wins;
- SERPT: shortest (expected) remaining processing time.
Preemption is powerful but costly to model: interrupting and resuming jobs makes the service and waiting times far harder to compute. To keep the analysis tractable we take the queue discipline to be FCFS unless stated otherwise — the plain, fair, first-come-first-served line of the post office.
First Results
A handful of quantities for the elementary queuing system now follow with almost no work, straight from Little’s law and the parameters already in hand. That none of them is surprising is itself the point — the machinery is consistent.
The formulas below reuse the letters introduced earlier, so a quick reminder before they arrive. Every capital is the stochastic (random-variable) counterpart of a lowercase operational quantity, and each subscript marks which part of the system it refers to:
- : service time, the net time in service; : waiting time spent in the canal; : dwelling time, the two together, .
- : throughput, jobs completed per unit time; : filling, jobs currently inside; : utilization, ; and : maximum throughput, the sustainable ceiling.
- Subscripts SS, SU, WP, QS: a single simple service station (capacity ), the service unit (its stations together), the waiting pool, and the whole queuing system.
Start with the maximum throughput. A single simple station finishes at most one job per mean service time, so its maximum throughput is ; a service unit of such stations working in parallel is times as fast:
The expected utilization of the service unit is then its throughput measured against that ceiling:
Little’s law applies just as well to the waiting pool on its own — its average filling equals throughput times average waiting time — and to the whole system, whose dwelling time is waiting plus service:
And because a job in the system is either waiting or being served, the two fillings simply add:
Useful as these are, the elementary queuing system is ultimately too small a model to carry the whole load. A real computing system is not one shop with one queue but many specialized shops wired together — a shopping center — with a job threading from one to the next, splitting and rejoining along the way. The next step is to connect elementary queuing systems into queuing networks: systems assembled from elementary queuing systems plus a model for how jobs are routed between them, which is where the theory starts to earn its keep.
Queuing Networks
The natural way to wire shops together is a graph. Each shop keeps its own counters and its own line, and the streets between them say where a customer may go next.
A queuing network is a system modeled as a graph whose vertices are elementary queuing systems and whose edges are the potential pathways a job may take between them. A job traverses a certain sequence of vertices serially, one after another, corresponding to the partial tasks being processed on its behalf; the network as a whole processes several jobs at the same time.
Whether jobs may enter and leave at all splits the networks into two families, and the distinction decides which analysis is available.
A closed queuing network has constant filling: no job enters from outside and none leaves, so the number of jobs circulating in the network never changes.
An open queuing network has a varying filling: jobs arrive from the environment and depart from it, so the number of jobs inside fluctuates over time.
A closed network is the shopping center with its doors locked and a fixed crowd inside, wandering from shop to shop forever. That sounds artificial, but it is the right idealization for a computing system running a fixed number of concurrent processes, and it is far easier to analyze — which is why the asymptotic analysis below is built on it.
There is one casualty of the move from one shop to many. In an elementary queuing system, “a job arrives” told us everything we needed; a job was an indivisible lump of work that a single server would eventually finish. In a network that description no longer suffices, because it leaves three questions open: does the job split into several subjobs? How many service stations does each subjob need, and for how long? And in what order must the subjobs run — which of them must wait for which?
Load and Task Streams
Answering those three questions is what the incoming stream has to specify. So the arrival description grows a second component: alongside when work shows up, it must now say what that work is.
A task stream is a pair consisting of an arrival process and a type of task. The arrival process fixes when jobs appear. The type of task is a set of subjobs together with the precedences (the ordering relations) among them, and a declaration of which resources each subjob needs — which elementary queuing system, for how long, and how often. Either component may be specified operationally, as a deterministic list, or stochastically, as a distribution.
The resource declaration gets its own compact notation. Writing
packs all three facts into one symbol: the subscript names the subjob and the service unit it claims, and the superscript gives the length of that claim. The letter itself names the task type the subjob belongs to.
Two task streams over the same network. Type jobs arrive at , and ; the arrivals of one type are typically serialized, meaning a type- job starts only once its predecessor has cleared. Its four subjobs run under the precedences
so subjob occupies service unit for three seconds, then subjobs and may run in parallel on units and , and only when both are finished may subjob take service unit for five seconds. Notice that unit is claimed twice by the same job — a single vertex can be revisited, which is exactly what the number of visits below will measure.
Type jobs arrive at and and are simpler, a plain chain of two subjobs:
Several task streams may feed the same network at once, and the whole demand it faces is their collection.
The load of a queuing network is the set of task streams offered to it. Writing each stream as its arrival-process class paired with its task type, the two streams above form the load
where marks each arrival process as Markovian.
Parameters in a Queuing Network
Take a network with elementary queuing systems as its vertices. Every parameter from the single-shop vocabulary survives, now carried by each vertex separately and tagged with its index: vertex has an arrival rate , a throughput , a maximum throughput , a utilization , a service time , and so on. These are the local quantities.
Alongside them stand the same quantities read off the network as a whole, the system quantities, marked by a subscript :
Only the arrival rate composes by plain summation — the rest are genuinely new, network-level quantities that the local ones do not simply add up to, as the counter-example at the end of this section makes uncomfortably clear. In a closed network nothing arrives from outside at all, so , and the filling is pinned to a constant, .
The one genuinely new parameter counts how often a single job passes through a given vertex.
The number of visits of a vertex is the ratio of the throughput at that vertex to the throughput of the whole network:
The name is easier to trust with the shopping center in view. If customers pass through the center while the restrooms record visits, then : on average every customer went twice. A vertex with is one that only a quarter of the customers ever enter. So is not restricted to whole numbers, and it may sit on either side of .
Because the ratio is fixed by the routing rather than by the load, raising the system throughput raises every local throughput in exact proportion, and with them every local utilization . Push more customers through the doors and every shop gets busier by the same factor. That observation is what makes the next definition inevitable.
The traffic bottleneck (TB) of a queuing network is the vertex with the maximum utilization . It need not be unique. Since raising the system throughput scales all utilizations together, the traffic bottleneck is the vertex that reaches the absolute maximum utilization first, and it is therefore what caps the whole network.
Push more customers into the center and every shop’s utilization climbs in lockstep. One shop hits before the others, and from that moment on it cannot serve anyone faster; the queue in front of it grows, and everything downstream starves. The network’s ceiling is set by its busiest vertex, not by its average one.
Locating the Traffic Bottleneck
The bottleneck can be pinned down in two ways, and they turn out to name the same vertex. The first reads straight off the definition, using :
The second comes at it from the network’s maximum throughput. A vertex visited times per job can support at most jobs per unit time through the network, and the network can only go as fast as its slowest such constraint:
That the maximizing vertex of the first expression is the minimizing vertex of the second is a one-line substitution. Replace each local throughput by and the system throughput, being the same for every vertex, factors straight out of the maximum:
What is left inside the maximum is , whose largest value picks out exactly the vertex whose is smallest. The most heavily utilized vertex and the one that caps the network are one and the same.
Asymptotic Analysis of Closed Networks
The popular way to get at a network’s behavior without solving it exactly is to watch what happens at the two ends of the filling range and connect them. Throughout this section the network is closed — a fixed crowd circulating inside — the model is stochastic, and every vertex is a simple elementary queuing system, so that its maximum throughput is the reciprocal of its mean service time,
Now run the thought experiment. Lock the doors of an empty shopping center and let customers in one at a time, watching the dwelling time and the throughput as the crowd grows.
A single job, . With nobody else inside there is nothing to queue behind, so wherever the customer goes they are served immediately. The dwelling time is exactly the service time, and the throughput is one job per that time:
Very little filling. With two or three or five customers in a whole shopping center, collisions are rare. The dwelling time is still essentially the service time — which, summed over the visits a job makes to each vertex, is
The throughput expression is just Little’s law with substituted in. Read it as the headline of this regime: dwelling time stays largely constant, and throughput grows linearly with the filling. Twice the customers, twice the business, and nobody waits any longer than before.
Gradually increasing filling. Now the shops start colliding with each other. Waiting time appears, so the dwelling time begins to climb above the service time, and for that same reason the throughput can only keep growing at a reduced rate. The linear regime is over.
Filling tending to infinity. Jam. Jobs pile up at the traffic bottleneck, whose utilization saturates at the absolute maximum while the rest of the network waits on it. The throughput can no longer grow at all; it settles on the network’s ceiling, and Little’s law then forces the dwelling time to grow linearly in its place:
Physically the last line is the whole story of a congested system. Pour more jobs in and the output does not react, so everything you add simply accumulates inside — the system gets fuller, and each job’s stay gets proportionally longer.
The Saturation Bend
Both diagrams that these limits describe have the same shape: a straight line out of the origin that eventually surrenders to a horizontal ceiling.
For the throughput, plotted against the filling , the small-filling limit is a straight line through the origin — every job you add contributes its full share of business. The large-filling limit is a horizontal line at the network’s maximum throughput. Both are asymptotes, which is worth saying plainly since only one of them looks like the asymptote of school geometry: the line is what the curve hugs as , the ceiling is what it hugs as . The real curve leaves the line, bends over, and flattens onto the ceiling.
For the dwelling time the picture is the mirror image. The desired behavior is the constant , a job’s stay being nothing but its own service; the actual curve departs from that floor and settles onto the rising straight line , whose slope is .
Two things are worth noticing about the bend that joins the asymptotes. First, without any stochasticity the curve would not bend at all: throughput would track the straight line exactly and then turn a sharp corner onto the ceiling at the instant the bottleneck saturated. The rounding of that corner is the randomness — with arrivals and service times scattered, some jobs collide and wait long before the network is on average full, so the curve starts losing ground to the ideal line well ahead of the corner. Second, the same softening is what makes the corner’s location a matter of convention rather than a fact you can point at.
The saturation number of jobs (also the saturation filling) is the filling at which the two throughput asymptotes intersect:
It marks the saturation bend: from there on the throughput no longer increases essentially, while the dwelling time increases almost linearly.
Setting the linear asymptote equal to the ceiling and solving for is all the formula is. Past there is no point admitting more jobs — the network finishes them no faster, and every newcomer merely lengthens everyone’s wait. Because the real curve is rounded rather than cornered, though, calling that particular filling the saturation point is in a certain sense an arbitrary determination of the intersection, and its usefulness depends on how sharp the bend actually is. It is sharpest when the service times are narrowly distributed and when the network has a single clear bottleneck; scattered service times, or several vertices competing for the bottleneck, blur the corner and the asymptotes approximate the truth less well.
Service Time and Maximum Throughput
One relation from the single-shop world does not survive the move to a network, and it is tempting enough to deserve a warning. For each simple service unit the reciprocal relation holds exactly,
and it is natural to expect the same at the system level. But the two system quantities are built by different operations. The mean service time sums the per-visit costs,
while the maximum throughput takes a minimum over the vertices,
A sum of reciprocals is not the reciprocal of a minimum, so
is false in general. The reason is structural: averages the work a job does everywhere in the network, while is dictated by the single worst vertex and ignores all the others.
Take a network of vertices with , , and maximum throughputs , . Each vertex is simple, so , and
The bottleneck is vertex , visited twice per job. Were the reciprocal relation true, the maximum throughput would come out as ; the correct value is . The two do not even agree to a first approximation.
What the Asymptotics Tell Us
The analysis leaves three findings worth carrying away, and they are about how a system responds to being asked to do more at once.
Moving from sequential processing — filling , one job in the network at a time — to simultaneous processing improves throughput and traffic while the filling is small. This is the linear regime, and it is where concurrency pays: the shops sit mostly idle, so an extra customer costs nobody anything and adds their full share of business.
With large filling those gains simply evaporate, because waiting prevails. Past the saturation bend the bottleneck is already running flat out; additional jobs contribute no throughput at all and only extend the queue in front of it. The benefit of concurrency is not a property of the system but of the regime it is operating in.
The most striking application of this is thrashing — the collapse of a computing system’s useful work when too many processes compete for too little memory, so that each of them spends its time fetching pages from disk rather than computing. The network model captures it exactly. As the degree of multiprogramming rises, the number of disk visits per job grows in proportion to the thrashing probability, which is to say climbs with the filling. Since the disk’s ceiling is , a growing drives that ratio down until the disk becomes the traffic bottleneck. Beyond a certain degree of multiprogramming it ruins the throughput of the whole system — not because the disk got slower, but because every job started visiting it more often.
Stochastic Processes Revisited
The queuing-network model already earns its keep: from a handful of stochastic ingredients it produces a genuinely useful description of data traffic, and it stays a sound starting point for performance analysis even of present-day systems. But it can be pushed further, especially toward open networks, and doing that pays to look harder at what a stochastic process actually is.
The push is motivated by three facts about computing systems. Their state space is discrete and finite — a machine sits in one of finitely many configurations, countable rather than smeared out. Every state is (perhaps only indirectly) reachable from every other, the rare exceptions being a deadlock or a crash that traps the system. And the probability of moving from one state to another depends only on the two states involved, not on how the system got there or on the clock. A process with all three traits is a homogeneous Markov process in continuous time, and building up to that object is the work of this section.
Processes, Time, and State
When stochastic processes first appeared, a process was a sequence of random variables indexed by position. The broader picture makes the index explicit as time: a stochastic process is a random variable whose value depends on time , so its distribution carries a time argument,
Two independent choices classify every such process — what values may take, and which instants are allowed:
- continuous process: the values are real, for all ;
- discrete process (or chain): the values are integers, or for all ;
- process in continuous time: ranges over ;
- process in discrete time: takes only countably many values.
Because the two axes are independent, they give combinations. The one that matters most here is the discrete process in continuous time — the filling of a service unit is exactly this, an integer count that jumps at random instants strung along a continuous clock.
The state space of a stochastic process is the set of all values its random variable can take — the collection of states the process can occupy.
Since the distribution now carries a time argument, so do the quantities read off it: the density and every moment — mean, variance, and the rest — are in general functions of .
That time dependence is exactly what a stationary process lacks. A process is stationary when its distribution does not change with time, which at the level of the mean reads
the expected value is the same whatever instant you sample it at. Rolling a die over and over is stationary, and so is the running sum of your last ten rolls — the recipe never changes. The average daily temperature across a year is not: a July reading and a January reading come from visibly different distributions, so the law drifts with the season. Even a process that starts out non-stationary often settles down, approaching a stationary limit process as — the way a damped oscillation forgets its initial swing and relaxes toward steady behavior.
From Independence to the Markov Property
The strongest way a process can be well-behaved is for its present to ignore its past entirely.
A stochastic process is independent when its future does not depend on any earlier result — conditioning on where it was tells you nothing:
Rolling a die is independent, and so is a lottery draw — yesterday’s numbers have no bearing on today’s. The running sum of the last hundred rolls is not: it plainly leans on what came before, and so does the filling of an M | M | 1 queue, where the number in the system a moment from now is the number now plus or minus one. (For processes in continuous time, independence is at best approximate, holding only when the two instants are far enough apart.)
Independence and stationarity are different demands, easy to run together. The running sum of recent rolls is stationary yet not independent: its distribution never changes, but each value still leans on the ones before it. Stationarity constrains the law; independence constrains the memory.
Between total independence and full history-dependence sits the compromise that makes the whole theory work: the process may remember, but only its most recent state.
A Markov process (MP), named after A. A. Markov (1856–1922), is a process whose future depends only on its present state, not on the path that led there. For any ordered instants ,
Conditioning on the entire history collapses to conditioning on the latest state alone.
The reading is practical: a new state depends only on the current one, and the quality of a forecast cannot be improved by looking further back than the present. Over a discrete state space a Markov process is drawn as a state graph, states as nodes and possible transitions as arrows. Every independent process is trivially Markov — if the future ignores even the present, it certainly ignores everything before it — so the Markov processes form the wider class. The running sum of the last ten dice rolls, by contrast, is not Markovian: to predict the next sum you need the ten most recent rolls, not just the current total, so a single state fails to summarize the past.
Homogeneity and Birth–Death
A Markov process pins down how much history matters. One more condition pins down whether the rules drift with time.
A homogeneous Markov process (HMP) is a Markov process whose transition probabilities are also independent of time, and therefore constant. The one-step conditional law carries no time argument at all,
it is the same probability no matter when the step is taken.
It is worth pausing on how a homogeneous Markov process differs from a stationary one, because the two “nothing changes with time” conditions govern different things. Stationarity holds the state distribution fixed — the odds of finding the process in a given state stay put. Homogeneity holds the transition law fixed — the odds of a given move stay put — while the state distribution is free to drift.
An M | M | 1 queue makes the gap concrete. Take one with inter-arrival times averaging and service times averaging , so the arrival rate is and the service rate is . Its filling is not stationary: jobs arrive faster than they can be served, the queue grows without bound, and the state distribution shifts steadily toward higher fillings. Yet the filling is a homogeneous Markov process, because the transition probabilities never change — from any occupied state the next event is an arrival with probability and a departure with probability , whatever the clock says. Homogeneous, not stationary.
Homogeneous Markov processes are everywhere in the modeling of computing systems, and elsewhere too — the running sum of dice rolls, or the jittering of a particle in Brownian motion. A special case earns its own name when the state can only inch up or down.
A homogeneous birth–death process (HBDP) is a homogeneous Markov chain whose state may change only by or at a time — never by a jump of two or more. The filling of a functional unit is the model case: jobs arrive and depart one at a time, and no two events land at exactly the same instant, so the count only ever ticks up or down by one.
The Family Tree
These conditions nest. Every birth–death process is a homogeneous Markov process (it just restricts the steps to ); every homogeneous Markov process is a Markov process (it just freezes the transition law); and the Markov processes sit inside the still-wider class of all stochastic processes. One more relative rounds out the picture: the random walk, a Markov process whose increments are independent, treated on its own further below.
Cutting across this hierarchy is the stationary / non-stationary divide, which is independent of it — a process at any level of the tree may or may not be stationary. A few examples locate the corners, each tagged by whether its time and its state are continuous () or discrete (), written time-then-state:
- a share price sampled once a day is a non-stationary Markov process, discrete in both time and state ();
- the filling of a functional unit is a stationary birth–death process, continuous in time but discrete in state ();
- the count of a Poisson process is a Markov process, again continuous in time and discrete in state ();
- Brownian motion is a homogeneous Markov process, continuous in both ().
The genuinely rare bird is a process that is stationary yet has time-dependent transition probabilities — stationary but not homogeneous — which almost never turns up in practice.
Markov Chains in Discrete Time
The cleanest setting for the machinery is a chain that both takes integer states and steps at integer times: a discrete homogeneous Markov process in discrete time, usually just a Markov chain. It has states (with the limit case allowed), and its dynamics are carried entirely by the probabilities of stepping from one state to another.
The transition probability of a Markov chain is the probability of moving to state at the next step given the chain is in state now,
Being the transition law of a homogeneous Markov process, it depends on neither the time nor the earlier history. The subscript order is source-then-destination: runs from to .
A chain is usually drawn as a state graph. Consider one with a start state , an intermediate state , and a final state : from the chain goes to with probability or straight to with probability ; from it either stays put with probability or moves on to with probability ; and , once reached, is never left. This chain is neither stationary nor a birth–death process, since has two possible successors and a single step is not confined to . It also shows that reachability need not be symmetric: can be reached from every state, but cannot be reached from at all. Classifying that reachability is the next task.
Classifying States by Reachability
A Markov chain is irreducible when every state is reachable from every other — not necessarily in a single step, but eventually. Such a chain cannot be split into pieces that fail to communicate.
A closed subset of states is one the chain can never leave: once inside, it is impossible to reach any state in the complementary set. A closed subset consisting of a single state is an absorbing state — the state above, which has no way out. An irreducible chain has no closed subset other than the whole state space, because by definition every state is still reachable.
The recurrence probability of a state is the probability that the chain, starting in , ever returns to at some later step,
The number of steps until that first return is the recurrence time, a random variable written .
A state is recurrent when its return is certain, : the chain is guaranteed to come back. When return is not certain, , the state is transient — visited only finitely many times before the chain wanders off for good.
A recurrent state is periodic when its returns are forced onto a fixed rhythm: the recurrence time is always a multiple of some , so . A state whose returns are bound to no such common divisor is aperiodic.
A recurrent state () is positively recurrent when its mean recurrence time is finite, — it comes back, and comes back reasonably soon. It is null recurrent when return is still certain but takes arbitrarily long on average, .
The days of the week make the vocabulary concrete. Stepping day to day, Wednesday is recurrent — it always comes back — and periodic with period , since it recurs exactly every seven steps. Christmas is recurrent too, but not periodic in the strict sense: leap years nudge the gap between successive Christmases off any single fixed multiple. And a state you might land on once but are not sure to see again is transient.
Long-Run Behavior
Irreducibility ties the fate of the states together, and two results follow at once. In an irreducible discrete-time HMP, all states are of the same kind — either all transient, or all positively recurrent, or all null recurrent; the classification is a property of the whole chain, not of individual states. Periodicity is shared too: if one state is periodic, every state is, and they all carry the same period length.
The quantity we actually want to track is the probability of finding the chain in each state at a given step.
The state probability is the probability that the chain occupies state at step . The state probabilities advance by one update per step and always sum to one,
The update reads as plain bookkeeping: to be in now, the chain had to be in some state last step and then take the transition, so sum over all sources . Collecting the transition probabilities into a transition matrix turns the whole update into a single matrix–vector product — which is the practical reason the two-index is worth the notation.
The deepest result is about what happens in the long run.
For an irreducible, aperiodic homogeneous Markov chain, the state probabilities always settle to fixed values as the steps run on — the stationary limit distribution,
These limiting values do not depend on the initial distribution : the chain forgets where it started. Transient and null-recurrent states end with , positively recurrent states with .
The days of the week as a chain
Model the weekday as a seven-state chain that steps deterministically — Monday to Tuesday to Wednesday and around again — so every transition probability is except the one leading to the next day, which is . Solving the linear system for the stationary distribution (or just reading off the symmetry) gives for each day: in the long run the chain is equally likely to sit on any given weekday. This chain is periodic with period , so strictly it has no limit distribution in the sense above — it never forgets its phase — but its long-run average occupancy is the uniform .
Markov Chains in Continuous Time
Letting the chain step in continuous rather than discrete time changes one thing fundamentally. When the intervals between possible transitions can shrink toward zero, a per-step probability is no longer the right currency — the chance of moving in a vanishing instant is itself vanishing. The fix is the one that turned a vanishing conditional probability into the hazard rate earlier: divide by the interval before taking the limit, converting a probability into a rate.
The transition rate of a continuous-time homogeneous Markov chain is the instantaneous rate of moving from state to state ,
For a homogeneous process these rates are constant in time. As with , the subscripts run source-then-destination.
In general the state probabilities of a continuous-time chain evolve by a system of ordinary differential equations rather than a step-by-step recursion. But the case we care about collapses to something simpler. When the chain is irreducible with all states positively recurrent, it has stationary state probabilities — independent of the initial state — fixed by a balance condition at every state:
Each equation states flow conservation. The left side is the total rate of leaving state — its outgoing rates summed and weighted by the probability of being there. The right side is the total rate of entering it from every other state. In the steady state the two must match, state by state, and together with the normalization they form a simple system of linear equations.
The distinction between the two time settings is the thing to carry away: a discrete-time chain is governed by transition probabilities, a continuous-time chain by transition rates.
The M | M | 1 Queue
The continuous-time machinery pays off on the queue that started all this — an M | M | 1 system in Kendall notation: Markovian arrivals, Markovian service, one server. It is a discrete homogeneous birth–death process in continuous time. Its states are the possible fillings (infinitely many), and the time the chain dwells in state before jumping is negative-exponentially distributed, with the total exit rate built from the individual transition rates.
Because it is a birth–death process, only two moves are possible from a state: up one, when a job arrives, at the arrival rate ; or down one, when a job completes, at the service rate . The state graph is a single ladder, each rung joined to by an upward rate and a downward rate .
The stationary probabilities follow from the balance condition, which for a birth–death ladder takes an especially clean form. Cut the ladder between state and state : in the steady state, traffic crossing that cut upward must equal traffic crossing it downward. Upward crossings happen at rate whenever the chain is in state , downward crossings at rate whenever it is in state , so
Written out from the bottom this reads , then , and so on — an infinite chain of equations. Each one multiplies in another factor of , so every state probability is pinned to :
The ratio is nothing new: it is the utilization , the arrival rate measured against the server’s ceiling. The last unknown, , is fixed by the requirement that the probabilities sum to one. Summing the geometric series, which converges precisely when (that is, ),
So : the probability that the queue is completely empty is one minus its utilization. For a single exclusive server this is also the probability that it is free to take a job the instant one arrives, .
Performance of the Queue
With the state probabilities in hand, the headline performance figures of the M | M | 1 queue fall out one after another. The average filling is the mean of the geometric distribution just found:
The average throughput is simply the arrival rate, , since below saturation everything that arrives is served; and the average service time is the reciprocal of the service rate, . Feeding these into Little’s law gives the average dwelling time,
and subtracting the part actually spent in service leaves the average waiting time,
That last formula carries a warning. The factor blows up as the utilization approaches one: run the server at and the average wait is , nine times the actual service time spent waiting. A system loaded near its ceiling does not slow down gracefully; the queue in front of it explodes. This is the quantitative face of the saturation seen for networks, and it points straight at the three levers for fighting waiting time: lower the arrival rate (turn customers away), lower the utilization by raising the server’s maximum throughput, or — the subtler one — cut the variation in service time.
That last lever needs a general-service queue to show itself, since a purely Markovian service time has its spread already fixed. Relax the service distribution from to — an M | G | 1 queue, with arbitrarily distributed service times — and the waiting time is given by a classic result.
The Pollaczek–Khinchine formula gives the average waiting time of an M | G | 1 queue in terms of the arrival rate, the utilization, and the second moment of the service time,
The service time enters through , which grows with the spread of : for a fixed mean service time, a more scattered service distribution means a larger second moment and a longer wait. Scattered service is bad. This is the lesson the coefficient of variation already taught for the outside observer — variation is the troublemaker — now showing up directly in the wait a job can expect.
Random Walk
One last birth–death process, this time in discrete time, rounds out the examples.
A random walk is a Markov process whose increments — the differences between successive positions — are independent: each step is taken afresh, blind to the ones before. The classic one-dimensional case takes steps of with equal probability.
Picture a drunk who leaves the pub at time and, at each step, staggers one pace forward or one pace back, the two directions equally likely. The states are the integer positions , and each step is a random variable taking the values with probability each. The position after steps is the running sum
which follows a linearly transformed binomial distribution. Its mean is — with no bias either way the drunk drifts nowhere on average — but its variance is , so the typical distance from the pub after steps is , growing with the square root of the number of steps. The continuous analog of this process is the Wiener process, the model underlying, among other things, the wandering of a stock price.
Random and Pseudo-Random Numbers
The whole model has been leaning on randomness from the start — synthetic input sampled from a distribution, stochastic service times, whole simulation runs whose output is itself random. All of it rests on a question raised early on and deferred until now: how does a computer, a machine built to be perfectly deterministic, produce random numbers at all? For the traffic model the pressing case is the negative-exponentially distributed inter-arrival times that an M | M | 1 input stream demands.
Start with the cleanest target, numbers drawn from a uniform (equal) distribution. There are ways to get genuinely perfect ones:
- roll a die — a uniform distribution on ;
- run a fast counter that cycles from up to some and back, quickly enough that stopping it by hand lands anywhere — a uniform distribution on ;
- read off the digits of , which, as far as they are known, show no period and no correlation.
Each is useless for simulation. A truly unbiased die is hard to come by; human action — rolling, stopping — is far too slow when a run may need millions of values; and even a computer-driven counter cannot keep up. The obvious workaround, pre-computing a huge table of random numbers and storing it, runs aground too: it is never clear in advance how many will be needed, and storing the millions that some simulations consume becomes a memory problem.
Pseudo-Random Numbers
Since real random numbers are so awkward to produce, the practical answer is to fake them with an algorithm.
Pseudo-random numbers (also approximate random numbers) are numbers produced by a deterministic rule that computes each term from the previous one,
typically through some permutation-like function, chosen so the sequence is simple and fast to compute yet behaves in statistical tests like a genuinely random one.
That the rule is deterministic is the whole catch, and it fixes the bar a generator must clear. Three questions decide whether one is good enough:
- distribution: how faithfully does the sequence reproduce the target distribution — its mean, its standard deviation, and the rest?
- independence: can neighboring numbers be treated as independent? Strictly they cannot, since each is a fixed function of the last, so the real question is whether the dependence is weak enough to ignore.
- patterns: do patterns, short periods, or other correlations creep in and spoil the sequence?
The idea dates to the 1950s, and the necessary standard has stayed the same since: a pseudo-random sequence must behave, under statistical testing, comparably to a real random one.
Everything is built on a single distribution. The uniform distribution on the unit interval — a random variable with density for and elsewhere — is the mother of all the others: once you can generate numbers uniform on , you can derive numbers for any other distribution from them, the Poisson, the negative-exponential, and the Gaussian (normal) among them. So the work splits in two, and this section takes only the first half: strategies for generating uniform numbers on . Turning those into the “derived” distributions — the negative-exponential inter-arrival times chief among them — is taken up further on.
The Linear Congruential Generator
The workhorse for the uniform part is a piece of modular arithmetic from 1948.
The linear congruential generator (LCG; also linear congruential pseudo-random number generator, LCPNG), due to D. Lehmer in 1948, produces a sequence of integers by the recurrence
Each integer lands in , and dividing by the modulus rescales it to a value . Four non-negative integer parameters fix the generator: the seed (the starting value), the multiplier , the increment , and the modulus , all with .
The generator comes in two flavors, split by whether the increment vanishes: a multiplicative LCG has , and a mixed LCG has .
Choosing the Parameters
The recurrence is trivial; the art is entirely in the choice of , , and , because a careless choice yields a sequence that repeats after only a few terms or shows glaring patterns. One rule of thumb holds across both flavors: the modulus should be large. Since the integers are confined to , a bigger buys both a finer grid of possible values on and room for a longer period before the sequence inevitably repeats.
For a mixed generator there is a clean recipe for the best possible outcome — a full period of length , in which all integers appear exactly once before the sequence repeats. Knuth showed in 1969 that this maximum period is reached precisely when three conditions hold together:
- the modulus is large, for instance for a machine word length — at the choice is a prime number;
- the increment is coprime to the modulus, ;
- the multiplier satisfies for every prime factor of , and additionally whenever divides .
Two mixed generators on similar parameters, one bad and one good.
A poor choice: , , , seed . Here , so the coprimality condition already fails. The recurrence produces
which repeats after only four terms — far short of the modulus, and it never even visits half the possible values.
A good choice: , , , seed . The modulus is prime and every condition holds, so gives
the full period of length : every value in appears exactly once before it repeats, so each is equally likely.
Beyond simply maximizing the period, the parameters can be tuned to minimize the correlation between neighboring numbers. For the mixed case that correlation is smallest near
and Kobayashi and Knuth collect a set of practical suggestions: take so the modulo reduces to bit shifts, with , then , , no obvious pattern in the binary representation of , and .
The multiplicative case () has its own recipe, again from Kobayashi: a modulus or for an odd prime , a multiplier with and , and for minimal correlation. Choosing instead makes the arithmetic fast — the modulo is again just a shift — at the cost of a reduced period, around . A few classical generators became standard fixtures: with , the IBM 360’s with , and the VAX’s with . One practical warning outlives all the parameter tuning: rescaling the integers to by dividing by invites round-off error, which has to be watched.
Functions of Random Variables
The linear congruential generator delivers the raw material — numbers uniform on . The rest of the job is turning that stream into whatever distribution the model actually calls for, the negative-exponential inter-arrival times above all. The tool for this is a change of variables: push a random variable through a function and ask what distribution comes out the other side.
Take random variables with joint density and a set of differentiable, invertible functions . They define derived random variables whose joint density rescales by the absolute Jacobian determinant of the inverse map,
In the one-variable case this is simply , equivalently .
The formula is the density bookkeeping behind a plain fact: the transform must carry into each region exactly the probability of the region it came from. So where the map stretches the axis it thins the density out, and where it compresses the axis it piles the density up. That stretching factor is precisely the Jacobian — the same change of variables that rescales integrals.
The Logarithmic Transform
The first use is the one the traffic model needs: turning uniform numbers into negative-exponential ones. Let be uniform on , so on and elsewhere, and define the transform
which maps onto . Its derivative is , so the transformed density works out to
using , which the transform rearranges to. This is exactly the negative-exponential density: has distribution and mean . So the transform turns uniform random numbers into negative-exponential ones . Why this particular logarithm is the right choice is left hanging for the moment; the inverse-transform method below explains it.
The Sine–Cosine Transform
A second transform takes two uniform inputs to two Gaussian outputs. Let be two realizations of a variable uniform on the unit interval. Two functions () produce two new random variables,
and the change-of-variables formula, worked through, gives their joint density as a clean product,
which is a product of two independent standard normals.
The normal distribution (or Gaussian distribution) , with mean and standard deviation , has the bell-shaped density
The standard normal , with mean and standard deviation , is the special case .
So the two outputs are independent variates. A shift by a mean and a rescale by a standard deviation turns each into a general variate,
and likewise for with the sine. The whole construction is known as the Box–Muller transform.
Inverse Transform Sampling
The logarithmic transform worked, but it dropped from the sky — nothing so far said why is the right function. The answer is a single method that handles almost any target distribution at once and reduces the whole problem back to generating uniform numbers.
Inverse transform sampling generates numbers from an almost arbitrary target distribution out of uniform ones. Let be uniform on , and let the target random variable have distribution function and density . Applying the transform
— the inverse function of , not its reciprocal — produces a random variable whose density is exactly . This follows from the change-of-variables formula,
The caution “inverse function, not reciprocal” earns its place: undoes , it is not . The principle is completely general — feed uniform numbers through the inverse of any distribution function and out come numbers following that distribution — so it reduces the generation of arbitrarily distributed numbers to the one thing already in hand.
Generating Negative-Exponential Numbers
Applied to the negative-exponential distribution, the method finally motivates the logarithmic transform. With , solving for gives
exactly the transform used earlier, now with a reason behind it. One simplification is worth taking: if is uniform on , then so is , so may be replaced by throughout, leaving the tidier
With this in hand, the input data for the arrival process of an M | M | 1 queue can be produced directly.
Tabulated Inversion
Inversion needs the inverse function in hand, and sometimes there is none to be had in closed form, or computing it is too costly. Then the distribution is stored as a table and inverted by lookup. Generate a uniform number , find the two table entries that bracket it,
and linearly interpolate between the corresponding values:
The result approximates closely whenever the table is fine enough.
Tailored Methods for Special Distributions
Some distributions carry special structure that a purpose-built generator can exploit, at times more cheaply than a general inversion. Two matter here — the Gaussian and the Poisson — and both reuse machinery already assembled. (How to test whether a generator’s output is good enough is taken up further on; here the concern is only the algorithms.)
The Gaussian via the Central Limit Theorem
The Box–Muller transform is not the only route to Gaussians. A second one falls straight out of a cornerstone result.
Let be independent, identically distributed random variables with mean and standard deviation . Then the normalized sum
tends toward a standard normal as . Independence is what the result truly requires; identical distribution can even be relaxed.
The theorem hands over a generator for free. Choose the to be the uniform numbers already on hand — uniform on , with mean and variance . Substituting these into collapses it to a sample-mean form,
so averaging uniform numbers and rescaling produces an approximately standard-normal one. The count is a compromise: too large wastes uniform numbers, too small leaves the approximation coarse and the numbers small. (A popular choice is , which makes the factor collapse and reduces the whole recipe to the sum of twelve uniform numbers minus six.)
Poisson Numbers by Counting
The Poisson generator reuses the exponential one. A Poisson-distributed count , with , is — as established earlier — the number of arrivals a negative-exponential inter-arrival process drops into a fixed window. So counting is the method. Generate a sequence of negative-exponential inter-arrival times (through the logarithmic transform above), and add them up until the running total first exceeds a window of length :
The number of inter-arrival gaps that fit inside the unit window is the number of arrivals in it — a Poisson random number with parameter .
Testing Random Numbers
Generating pseudo-random numbers is only half the battle; the other half is checking that a given generator’s output is actually good enough to trust. That check is a matter of statistics, and the concern here is the application of the standard tests, not the theory behind them, which lives in the literature and in precomputed tables. Because every other distribution is built from uniform numbers, it is enough to test the uniform ones, so the tests below all target the uniform distribution on .
Two properties matter. Uniformity asks whether the numbers really look drawn from a uniform distribution; independence asks whether successive numbers are unrelated to one another. An established simulation language or a well-documented generator needs no testing; a new or undocumented one should be checked on both counts.
Each test is a hypothesis test, and the vocabulary is worth pinning down once.
A statistical test starts from a null hypothesis , the assumption to be checked. For uniformity it is , the numbers are uniform on the unit interval; for independence it is : the are independent. A test can go wrong two ways: a type-1 error rejects a true , with probability , and a type-2 error accepts a false one. The significance level is the bound placed on these error probabilities.
One caution comes with running many tests: the chance of committing at least one type-1 error, rejecting a good generator purely by bad luck, grows with the number of tests performed.
Frequency Tests: Uniformity
The tests for uniformity are the frequency tests, and there are two of them. Both measure how well the distribution of the samples agrees with the uniform distribution; they differ in how they measure it.
The Kolmogorov–Smirnov test (K–S test) compares the empirical distribution function of the samples against the theoretical one and takes the largest gap between them as its statistic. For uniform samples the theoretical distribution is and the empirical one counts the fraction of samples at or below ,
In practice the samples are sorted, , and the largest deviation is found from above and below each step,
The generator passes at significance if , the critical value read from a K–S table for the given and sample size .
Five samples, , sorted and tabulated against with :
The largest entries in the last two rows give and , so . The table provides , and since the samples pass.
The chi-square test ( test) subdivides into equal classes and compares how many samples land in each class against how many should. Its statistic sums the squared discrepancies, each scaled by the expected count,
where is the observed number of samples in class and the expected number for equally sized classes. For genuinely random numbers follows a chi-square distribution with degrees of freedom (the number of freely varying classes), and the generator passes if stays at or below the critical value from a chi-square table for significance and degrees of freedom. Two rules of thumb keep the approximation honest: at least samples overall, and expected per class.
samples sorted into classes of width , so per class. Counting the observed occupancies and accumulating the per-class contributions:
| class | |||||
|---|---|---|---|---|---|
| 1 | 8 | 10 | 4 | 0.4 | |
| 2 | 8 | 10 | 4 | 0.4 | |
| 3 | 10 | 10 | 0 | 0 | 0.0 |
| 4 | 9 | 10 | 1 | 0.1 | |
| 5 | 12 | 10 | 2 | 4 | 0.4 |
| 6 | 8 | 10 | 4 | 0.4 | |
| 7 | 10 | 10 | 0 | 0 | 0.0 |
| 8 | 14 | 10 | 4 | 16 | 1.6 |
| 9 | 10 | 10 | 0 | 0 | 0.0 |
| 10 | 11 | 10 | 1 | 1 | 0.1 |
| sum | 100 | 100 | 0 | 3.4 |
The test statistic is . The table gives the critical value , and since the samples pass comfortably.
Of the two, the Kolmogorov–Smirnov test is the more powerful, and it stays usable for smaller sample sizes where the chi-square approximation would be unreliable.
Runs Tests: Independence
Passing a frequency test settles only uniformity — the numbers could still march in a suspicious order. Picture a sequence whose every stretch climbs steadily upward: it can be perfectly uniform overall yet obviously not independent. The runs tests catch exactly this.
A runs test measures independence by counting runs, the maximal monotonic stretches of the sequence. An up run is a subsequence of increasing numbers, a down run one of decreasing numbers. For a genuinely random sequence of numbers the number of runs is a random variable with
and for it is approximately normally distributed, , so independence can be checked with a standard test on the mean of a normal distribution.
The extremes show why the count is the right thing to watch: a sequence with only one run (a single unbroken climb) and one with runs (up, down, up, down at every step) are both wildly unrealistic for random data.
Up and down are not the only kind of run. One can equally count runs above and runs below the mean — a block of twenty numbers above the mean followed by twenty below passes the up/down test but plainly is not independent — and test the number of such subsequences the same way, against a normal mean. A further refinement tests the length of the runs: if every run comes out the same length, the sequence is again unconvincing, and comparing the observed run-length distribution to the expected one leads to a chi-square test. In practice a generator is checked against all the main run characteristics at once.
Other Tests of Independence
Three more tests round out the independence checks, each catching a pattern the others miss.
Autocorrelation tests hunt for regularities like “every fifth number exceeds ” — a clear sign of dependence that slips straight past the tests above.
The gap test looks at the intervals between successive recurrences of the same digit. Recording these gaps for all ten digits, the theoretical distribution of a gap length is
and sorting the observed and expected gap lengths into classes, a Kolmogorov–Smirnov test decides the fit.
The poker test borrows from cards: it groups the leading digits of each number into a short hand and classifies the hand by how many distinct digits it holds. For three-digit hands the probabilities are
and a chi-square test compares the observed hand types against these. A telling failure: eight numbers whose three-digit hands all hold exactly two distinct digits, far more pairs than the expected , betray a generator that is not independent however uniform it looked.
With generators built and validated, the discrete toolkit is complete — a stochastic model of data traffic, the queuing networks and Markov chains that analyze it, and the tested pseudo-random machinery that drives its simulation. The continuous counterpart, where traffic is a flowing density rather than a stream of countable jobs, is taken up in Traffic Flow Models.