Two parameters

Two parameters, one bound, no order

With one size parameter the candidate classes are ordered — n beats n log n beats n², always, and comparing two bounds is reading them. With two, E log V and V² have no order at all, and which one is smaller is a property of the graph. Sweeping V at fixed degree and at fixed density are different experiments, and the same algorithm fits different classes in the two.

O(ElogV)O(E \log V) and O(V2)O(V^2). Which is smaller?

The question has no answer, and that is not a quibble about constants. On a graph of two thousand vertices with average degree six, the first is about sixty-six thousand and the second is four million. On a graph of two thousand vertices at half density, the first is about eleven million and the second is still four million. The two bounds cross, they cross somewhere that depends entirely on the graph, and nothing in either expression says where.

This is the ordinary situation in graph algorithms and it is almost never stated. A textbook table gives Dijkstra as O(ElogV)O(E \log V) and the array-queue version as O(V2)O(V^2), and a reader takes away that the first is better. It is better on some graphs.

The same algorithms, two density regimes, two answersLeft: V swept at a fixed average degree, so E grows like V. Right: V swept at fixed density, so E grows like V². The label beside each line is the class the fit grants over that sweep, and it is not the same class on the two sides. How much separates the two algorithms also changes by more than an order of magnitude — a factor of 37.2 between them on the left and 1.61 on the right. Neither published bound is wrong; neither is a comparison until the regime is named.sparse, fixed average degree10010³10³10⁴10⁵10⁶VworkDijkstra, binary heap — E log EDijkstra, all V queued — V^2dense, fixed density10010⁴10⁵VworkDijkstra, binary heap — V + EDijkstra, all V queued — Ethe class is a property of the sweep as much as of the algorithmwork = scans + visits + relaxations + queue comparisons
Fig. 1 The same two algorithms swept two ways. On the left, V grows with the average degree held at six, so E is proportional to V. On the right, V grows at fixed density, so E is proportional to V². The class the fit grants is different on the two sides for both algorithms, and the gap between the two collapses from a factor of thirty-seven to under two. Nothing changed except which family of graphs was measured.

What a sweep can and cannot separate

The fitting machinery this site runs on takes measured counts, divides them by a candidate class, and grants the class if the ratio stays flat across the range. With one size parameter that procedure discriminates well: count ÷ n and count ÷ n log n behave visibly differently as nn moves over three orders of magnitude.

With two parameters it can fail silently, and the failure is worth understanding because it is a property of the experiment rather than of the algorithm.

Sweep VV with the average degree held fixed. Then E=3VE = 3V throughout, so:

  • VV, EE and V+EV + E are the same curve, differing by a constant.
  • V2V^2 and VEVE are the same curve.
  • ElogVE \log V and VlogVV \log V are the same curve.

Breadth-first search on that sweep fits VV, EE and V+EV + E at a spread of 1.00 apiece. All three “fit”. None of them is evidence, because the experiment cannot tell them apart.

Now sweep VV at fixed density. Then E12dV2E \approx \tfrac{1}{2}dV^2, and a different set of pairs collapses:

  • EE, V+EV + E and V2V^2 become the same curve.
  • VV separates cleanly from all of them.

Breadth-first search on that sweep fits V+EV + E at 1.03 and VV alone at 9.50. Now the claim has content, and the content came from the second experiment rather than from the first.

So the honest procedure needs both sweeps, and the site’s two-variable fit takes an extra argument for exactly this: a claim is granted only when the sweep can also be shown to distinguish it from a named alternative. A fit that cannot fail is not a test, and on a single-regime sweep half of these fits cannot fail.

The full table, which is the argument

Here is every candidate class against every algorithm, on the sparse sweep. The number is the spread of work ÷ class across VV from 64 to 2,048; 1.00 is a perfect fit and the tolerance is 1.6.

algorithm V E V+E V log V E log V VE
breadth-first 1.00 1.00 1.00 1.83 1.83 32.00 32.00
Dijkstra, heap 1.52 1.52 1.52 1.21 1.21 21.06 21.06
Dijkstra, all V queued 24.34 24.34 24.34 13.28 13.28 1.31 1.31
Bellman–Ford, all passes 32.45 32.45 32.45 17.70 17.70 1.01 1.01
Prim 1.59 1.59 1.59 1.15 1.15 20.09 20.09

Every column comes in pairs. That is the sparse regime’s signature and it means the sparse regime, alone, cannot distinguish a bound in VV from a bound in EE — which is precisely the distinction the whole notation exists to make.

The same table on the dense sweep, VV from 64 to 512 at density 0.5:

algorithm V E V+E V log V E log V VE
breadth-first 9.50 1.03 1.03 6.11 1.60 1.06 10.35
Dijkstra, heap 8.35 1.17 1.11 5.37 1.82 1.21 11.77
Dijkstra, all V queued 9.72 1.01 1.05 6.25 1.56 1.04 10.11
Bellman–Ford, all passes 99.67 10.22 10.77 64.08 6.57 9.89 1.01
Prim 8.16 1.19 1.13 5.25 1.86 1.23 12.04

Different pairs collapse. EE, V+EV+E and V2V^2 are now indistinguishable, and VV has separated from everything.

Put the two tables together and every class is separable from every other by one sweep or the other. Neither sweep alone is enough, and this is the reason both are run.

The withdrawn claim

Dijkstra with a binary heap declared O(ElogV)O(E \log V) in all three regimes, because that is what the bound is. On sparse graphs the fit grants it at a spread of 1.21 and on the grid at 1.36. On dense graphs it is refused at 1.82, and what fits instead is V+EV + E at 1.11.

The reason is not subtle once the work is broken apart. At V=512V = 512 and half density, Dijkstra with a heap performs:

primitive count
adjacency scans 132,400
relaxations 66,200
queue comparisons 5,575
visits 512

The queue — the only part the logV\log V describes — is 2.7% of the work. Everything else is proportional to EE, and EE at fixed density is proportional to V2V^2, so the total is proportional to EE and the logarithm never shows up.

The bound is not wrong. ElogVE \log V is a correct upper bound on the queue work and therefore on the whole, and an upper bound that overstates is still a true statement. What it is not, on this family of graphs, is a description. Somebody choosing a queue on its authority is choosing on the strength of a term that is two percent of the cost.

Why the heap wins anyway, and why the folklore says otherwise

There is a widely repeated piece of advice that follows from the crossing: use a simple array queue on dense graphs, because V2V^2 beats ElogVE \log V when EE is near V2V^2.

Measured here, the array queue never wins. At V=512V = 512 and half density the heap costs 204,687 units and the array queue costs 330,440 — the heap is ahead by a factor of 1.61. On sparse graphs it is ahead by a factor of 37.

The advice is not nonsense; it is about a different cost model, and identifying which is more instructive than either agreeing or disagreeing with it. The V2V^2 analysis assumes the graph is an adjacency matrix, so scanning a vertex’s neighbours costs VV rather than its degree, and the scanning term is V2V^2 for both queues. Under that model the queue is the only difference and the array wins on a dense graph. Under the model here — adjacency lists, scanning charged per edge that exists — the scanning term is 2E2E for both queues, it dominates both, and the heap’s smaller queue is a free improvement.

So the answer to “which queue on a dense graph” depends on the representation, which is a third thing the bound does not mention. What survives is the shape of the claim rather than its conclusion: the heap’s advantage collapses from thirty-seven-fold to negligible as the graph fills up, and an implementation choosing on the sparse-graph number is choosing on a number that does not apply.

Every graph algorithm's class, tested on dense, fixed density graphsFor each algorithm: the class it declares for this regime, and how flat the ratio work ÷ class stays across V from 64 to 512. A spread of 1.00 is a perfect fit and the tolerance is 1.6. 4 of the 11 rows declare no class in this regime — the fit refused the one the textbooks give, and each refusal is an essay rather than a rounding.spread of work ÷ the declared class (1.00 is exact)Breadth-first1.03V + EDepth-first, explicit stack1.03V + ETopological sort1.04V + EDepth-first, recursive1.03V + EDijkstra, array queue1.02V^2Dijkstra, all V queued1.03V^2Dijkstra, binary heapno class fits — flattest is V + E at 1.10Bellman–Fordno class fits — flattest is V + E at 1.49Bellman–Ford, all passes1.01V EPrimno class fits — flattest is V + E at 1.13Kruskalno class fits — flattest is V^2 at 1.02V from 64 to 512, dense, fixed densitywork = scans + visits + relaxations + queue comparisons
Fig. 2 The whole field audited on dense graphs. Two rows that carry a class on the sparse sweep have none here — Dijkstra with a heap and Prim — and in both cases the class refused is the one the algorithm is famous for. The refusals are the useful part of this figure.

The grid, where a third thing goes wrong

Two regimes are enough to make the point about fitting. A third family makes a point about the graph that neither of the others can.

A square grid has VV vertices, about 2V2V edges, and — unlike a random sparse graph — a diameter of V\sqrt{V} rather than logV\log V. It is planar, every vertex has degree four, and it is much closer to the shape of a road network, a mesh or an image than either of the other two families.

On the grid, Dijkstra with a lazily-filled array queue does not fit V2V^2. The spread is 8.24 and the flattest candidate is V3/2V^{3/2} at 1.45.

The reason is the frontier. An array queue costs one comparison per element it holds, per pop, and this implementation only holds vertices that have been reached. On a random graph the reached-but-not-settled set is a constant fraction of VV, so VV pops of a VV-sized queue give V2V^2. On a grid the frontier is a ring of about V\sqrt V vertices, so VV pops of a V\sqrt V-sized queue give V3/2V^{3/2}.

The bound quoted for this algorithm is Θ(V2)\Theta(V^2) and it is correct — for the version that puts every vertex in the queue at the start with distance infinity, which is how the algorithm is usually written and is a line of code no discussion of it ever mentions. Both versions are implemented here. On the grid, the eager one costs 2,157,702 units and the lazy one costs 112,926: a factor of nineteen, from one initialisation loop, in an algorithm whose complexity is regarded as settled.

The two sweeps the regimes are read from

The regime plate is a summary of two sweeps, and the sweeps are worth having on the page because what they show is which distinctions each regime erases.

Counted work against V, dense, fixed densityEvery counted operation, summed, on logarithmic axes. The lines fan out because the algorithms differ in class rather than only in constant — at V = 64 the spread between best and worst is 122.4× and at V = 512 it is 1018×. On this sweep E is proportional to V², so E, V + E and V² coincide instead.10010⁴10⁵10⁶10⁷10⁸Vcounted workBreadth-firstDijkstra, binary heapDijkstra, all V queuedBellman–Ford, all passesV from 64 to 512, dense, fixed densitywork = scans + visits + relaxations + queue comparisons
Fig. 3 The four algorithms on dense graphs at fixed density, where EE is proportional to V2V^2 and EE, V+EV+E and V2V^2 coincide. The spread between best and worst is 122× at V = 64 and 1,018× at V = 512.
Counted work against V, sparse, fixed average degreeEvery counted operation, summed, on logarithmic axes. The lines fan out because the algorithms differ in class rather than only in constant — at V = 64 the spread between best and worst is 2.3× and at V = 2048 it is 37×. On this sweep E is proportional to V, so V, E and V + E are the same line and the picture cannot tell them apart.10010³10³10⁴10⁵10⁶Vcounted workDijkstra, binary heapDijkstra, all V queuedV from 64 to 2048, sparse, fixed average degreework = scans + visits + relaxations + queue comparisons
Fig. 4 The two Dijkstras alone on sparse graphs, where the vertical scale is not being set by anything else: 2.3× at V = 64 and 37× at V = 2,048. Two implementations of one algorithm, separated by a class.

The third narrows differently again: three algorithms rather than two or four, on the regime where the two parameters of the bound stop being independent.

Counted work against V, dense, fixed densityEvery counted operation, summed, on logarithmic axes. The lines fan out because the algorithms differ in class rather than only in constant — at V = 64 the spread between best and worst is 122.4× and at V = 512 it is 1018×. On this sweep E is proportional to V², so E, V + E and V² coincide instead.10010⁴10⁵10⁶10⁷10⁸Vcounted workBreadth-firstDijkstra, binary heapBellman–Ford, all passesV from 64 to 512, dense, fixed densitywork = scans + visits + relaxations + queue comparisons
Fig. 5 And three of them dense, which is the comparison an implementer is actually making: 70.3× at V = 64 and 661× at V = 512. Every number on all three plates is counted rather than fitted, which is why the ratios can be quoted at all.

What the field does about it

Three procedural consequences, all of them small and all of them load-bearing.

Every graph claim names its regime. The declaration in the algorithm table is not a class, it is a map from regime to class, and the same algorithm carries different entries under different regimes and no entry at all where the fit refused one. Four algorithms in this field have a missing entry somewhere.

Every figure prints the regime and the range. A miss count without a line size is not a measurement, and by exactly the same argument a graph bound without a density is not a claim. The caption strip carries both.

The fit is asked to discriminate. Granting a class on a sweep where an alternative fits equally well is recorded as no evidence at all, and the assertion that enforces this has a name and rejects a real case: it refuses to accept V+EV + E from the sparse sweep alone, because on that sweep VV alone fits identically.

None of that is deep. It is the same discipline the sorting field already had — name the input, name the range, let the claim be refused — with one more thing to name. What is new is how much it catches: adding one parameter took four declared classes off the table, and every one of them is a class that appears in textbooks without a qualifier.

Counted work against V, dense, fixed densityEvery counted operation, summed, on logarithmic axes. The lines fan out because the algorithms differ in class rather than only in constant — at V = 64 the spread between best and worst is 122.4× and at V = 512 it is 1018×. On this sweep E is proportional to V², so E, V + E and V² coincide instead.10010⁴10⁵10⁶10⁷10⁸Vcounted workBreadth-firstDijkstra, binary heapDijkstra, all V queuedBellman–Ford, all passesV from 64 to 512, dense, fixed densitywork = scans + visits + relaxations + queue comparisons
Fig. 6 Counted work on dense graphs, where E is proportional to V². The curves are much closer together than on the sparse sweep — the adjacency scanning that all of these share has grown to dominate all of them — which is the same conclusion as the component figure, reached from the other direction.
Counted work against V, square gridEvery counted operation, summed, on logarithmic axes. The lines fan out because the algorithms differ in class rather than only in constant — at V = 64 the spread between best and worst is 98.2× and at V = 2048 it is 3296×. On this sweep E is proportional to V², so E, V + E and V² coincide instead.10010³10³10⁴10⁵10⁶10⁷Vcounted workBreadth-firstDijkstra, binary heapDijkstra, all V queuedBellman–Ford, all passesV from 64 to 2048, square gridwork = scans + visits + relaxations + queue comparisons
Fig. 7 A third family, and a third set of collapses. A grid has degree four by construction, so E is proportional to V here as it is on the sparse sweep — but the diameter is √V rather than log V, and that is enough to change which class two of these algorithms fit.

Two is not the maximum

Graphs have two size parameters and that is already enough to break the ordering of the candidate classes. Nothing stops at two, and the problems with more are not obscure ones.

String matching has a pattern length mm and a text length nn. Naive matching is O(mn)O(mn), Knuth–Morris–Pratt is O(m+n)O(m + n), and Boyer–Moore is O(n/m)O(n/m) in the good case — which is sublinear in the text, and which no ordering of the three expressions captures without knowing the ratio.

Sparse matrix multiplication has rows, columns and non-zeros, and the standard bounds mention all three. Which of O(nnnz)O(n \cdot \mathrm{nnz}) and O(nnz1.5)O(\mathrm{nnz}^{1.5}) is smaller depends on how the non-zeros are distributed, not merely on how many there are.

A hash table has a capacity mm and an occupancy kk, and every interesting bound is in the ratio rather than in either. That case is instructive because the field has solved the problem by convention: everybody writes the load factor α=k/m\alpha = k/m and quotes bounds in it, so the two parameters were collapsed into one derived quantity by agreement, once, a long time ago.

Graph algorithms have no such convention. There is no agreed name for E/VE/V, no standard practice of quoting bounds in it, and no convention that a stated bound comes with a stated regime — even though “average degree” is exactly the derived quantity that would do for graphs what the load factor does for hash tables.

Writing the bounds that way is illuminating. At average degree dd, so E=dV/2E = dV/2:

bound in terms of V and d
V+EV + E V(1+d/2)V(1 + d/2)
ElogVE \log V d2VlogV\tfrac{d}{2} V \log V
V2V^2 V2V^2
VEVE d2V2\tfrac{d}{2}V^2

Now they are all in one variable and they are ordered, for a fixed dd. The first is linear, the second linearithmic, the last two quadratic — and the density has moved out of the class and into the constant, which is where a parameter that does not grow with the problem belongs.

That is the honest reading of the whole field. A density regime is a decision to treat one of the two parameters as a constant. Once it is made, everything the one-variable machinery does works again: the classes are ordered, the fit discriminates, and the constant carries the density. Refusing to make it is what leaves two bounds incomparable, and the refusal is usually not a decision at all but an omission.

What the density convention would not fix

The load-factor analogy is illuminating and it is not sufficient, and this essay’s own tables are what show it.

A hash table’s bounds really are functions of α=k/m\alpha = k/m. Two tables with the same load factor behave the same way whatever their sizes, which is what makes the collapse honest: the two parameters genuinely enter only through their ratio, so naming the ratio loses nothing.

Graph bounds are not functions of d=2E/Vd = 2E/V, and the grid is the counterexample sitting three sections above. A random sparse graph at average degree six and a square grid at average degree four have nearly the same density — both have EE proportional to VV, both are at the sparse end, and a bound quoted in VV and dd would give them the same expression. The lazily-queued Dijkstra fits V2V^2 on one and V3/2V^{3/2} on the other.

So the second parameter was not the last one. What separates the two families is the size of the search frontier, which is a consequence of the diameter: a random sparse graph has diameter about logV\log V and a reached-but-unsettled set that is a constant fraction of the graph, while a grid has diameter V\sqrt V and a frontier that is a ring of about V\sqrt V vertices. The V3/2V^{3/2} is VV pops of a V\sqrt V-sized queue, and the V\sqrt V is the diameter showing up in a cost.

That is a third parameter, it is not a size, and it is not derivable from the first two. It is also not new to this collection: the number of passes Bellman–Ford takes is exactly the source’s hop-eccentricity, and the measured pass counts are the diameters of the three families rather than anything about VV or EE.

Two families of graphs at the same density, and two different classes for the same code. A convention that named only the density would make those two rows look comparable and they are not.

Which does not make the convention worthless

The right conclusion is narrower than abandoning the idea, and it is worth stating because the same move is available in every field with more than one parameter.

A derived ratio is a legitimate collapse exactly when the cost is genuinely a function of it. For hash tables that is true and the collapse is total. For graphs the density collapses the VV-against-EE ambiguity — which is the ambiguity this essay opened with, and which is real — and leaves the shape ambiguity untouched. That is still most of the way, because VV against EE is where the incomparable bounds are and the shape effect shows up in one algorithm of five.

So the practice the field should adopt is not one number but a stated regime, which is what this collection’s plates already carry: the family, the density and the range. Three graphs of the same density from three families are three experiments, and every table above is printed per family for exactly that reason.

And the general form is the useful thing to take away, because it applies wherever a bound has more parameters than a reader can hold. Ask which parameters the cost is actually a function of, then quote the bound in those and treat everything else as a regime to be named. For sorting it is one and the convention is invisible. For hash tables it is one derived from two, agreed long ago. For graphs it is at least three, no convention exists, and the tables in this essay are what a field looks like before it has one.

The general form

The trouble here is not really about graphs. It is the missing argument again, with a second axis, and the second axis makes it much harder to ignore.

With one parameter, an unstated input distribution can make a bound misleading by a factor. Insertion sort is Θ(n)\Theta(n) or Θ(n2)\Theta(n^2) depending on the input, and both are true statements about the same code. That is bad enough, and at least the two classes are ordered: whichever input arrives, the quadratic case is worse than the linear one.

With two parameters, the classes are not ordered, so the missing argument does not merely change how bad the answer is. It changes which answer is better. Two implementers can each measure carefully, each get a correct result, and reach opposite conclusions about the same pair of algorithms — because they measured different graphs and neither wrote the density down.

What this makes readable

Essays that name this one as a prerequisite.

Named alongside this one

Essays reaching for the same objects. Nobody chose these; they are what the concept index makes visible.

What links here

The 8 essays that link to this one and share the most of its objects, of 13 that link here.

The objects this essay names

Each one links to every other essay that touches it.

AdjacencyCurve fittingDensityDijkstra's algorithmHeapRegimeSparse graphTwo parameter bound