What a bound is

Where a crossing moved to

The prediction was that a succinct range minimum would move the document listing's crossing "to a handful". It moves it from 32 occurrences per document to 11 — a factor of three, not an order of magnitude — because a constant-time query is ten lookups rather than one.

The cost that is the size of the answer ended with a prediction, and predictions on this site are made to be checked.

The situation it left was this. A document listing that is proportional to the answer — twice the number of documents holding the pattern, plus one — costs 258 range-minimum queries’ worth of node visits, flat, whatever the range holds. A scan that is proportional to the occurrences costs one visit each. They cross at about 250 occurrences, which on eight documents is 32 per document, and below that the output-sensitive method loses.

The essay then said that a succinct range minimum “would move the document listing’s crossing from thirty-one occurrences per document to a handful”. This is what it does.

Where the two methods meet, with each range structure8 documents of 2,048 characters, the pattern planted at rising density in 3 of them, so the answer stays at 8 documents and only the occurrence count moves. The scan costs one read per occurrence and rises along the diagonal. The chain costs 282 reads with a segment tree and 92 with the succinct structure, near enough flat in both cases — so changing the range structure moves the crossing from 33 occurrences per document to 9. The strand that named this structure predicted "a handful"; it is a factor of 3.56x, because a constant-time query is a dozen lookups rather than one.10010100occurrences of the patternreadsthe scanchain, treechain, succinct8 documents · answer 8crossing 267 → 75
Fig. 1 Both range structures on the same sweep. The scan rises along the diagonal; each chain is flat, and each crossing is where its line meets the diagonal.

The answer

The crossing moves from 257 occurrences to 86 — from 32.2 per document to 10.8, a factor of 2.98.

That is a real improvement and it is not a handful. Below eleven occurrences per document the scan still wins, and eleven occurrences of a phrase in a document is not a high bar to fail: it is what a moderately common word does in a page of text, and what a rare one does in a chapter.

The reason is in the previous essay and can be stated in one line: a constant-time range-minimum query is nine or ten memory accesses, not one. Ten or eleven queries at ten accesses each is about a hundred, and a hundred accesses cross a scan at a hundred occurrences.

It is worth being concrete about what that means for the method’s own claim. “Output-sensitive” says the cost follows the size of the answer rather than the size of the range, and that is true of both structures: at 790 occurrences the succinct chain spends 98 accesses and at 30 it spends 96. The claim is not weakened by the constant. What the constant decides is whether the other method — the one whose cost does follow the range — has got out of the way yet, and at eight documents that takes eighty-six occurrences.

So the honest summary of the method is two sentences rather than one. Its cost is proportional to the answer. Its constant is about a hundred accesses, and below a hundred occurrences a scan is proportional to something smaller than that.

The 30 rows for one pattern, and the 7 documents behind themEvery row of the suffix array whose suffix begins with " was t", in order, with the document each row belongs to written under it. A row is filled when it is the FIRST row of its document inside this range, which is exactly the condition that the previous row holding that document falls outside the range — the chain the listing is built on. There are 7 filled cells and 30 rows, and the whole of document listing is finding the first without visiting the second.2·0·1·20101206·7·3·25·012012201012102rowsdocumentfirstpattern " was t" · 30 occurrences · 7 documentsA filled row is the first of its document. Reporting one costs a range minimum;reading every row costs one visit per occurrence.English-like · 8 documents30 rows · 7 documents
Fig. 2 The rows one pattern occupies and which of them is the first of its document. The chain turns that into a range minimum; how many range minima is fixed, and what each costs is what this strand changed.

Where the prediction went wrong

Nothing was miscalculated. The prediction was made from an asymptotic reading, and an asymptotic reading has no place to put a constant of ten.

“Constant time” and “logarithmic” differ by a factor that grows without bound, so a structure that replaces the second with the first sounds like it removes the cost. What it removes is the growth of the cost. At the sizes here the segment tree’s logarithm is about 25 accesses a query and the succinct structure’s constant is about 10, and a factor of 2.5 is what a factor of 2.5 looks like.

One cost follows the array and the other does notReads a query, averaged over 80 random ranges at each size. One act is one memory access the structure chose to make: a node visited in the segment tree, a table or array entry read in the succinct one. The tree goes from 23.1 to 41.9 across a 64x growth in the array, which is its two logarithms; the succinct structure goes from 15.4 to 16.9, which is flat, because a query is a fixed number of lookups whatever the array holds. The two answered every range identically.0102030401e+42e+43e+4values in the arrayreads a querythe segment treesuccinct80 queries a size2.49x apart
Fig. 3 The two costs a query, against the array’s size. The tree’s rises by 1.81 across a sixty-fourfold growth and the succinct structure’s by 1.09 — which is the whole of what constant time buys, drawn.

This is a limit is not a prediction applied to a structure rather than to an algorithm, and it is the third time in this collection that the same reading has produced the same size of error in the same direction. The constant the notation drops is the general case; this is a worked one, with the prediction on record before the measurement.

It is also the shape of the error that the operations a candidate count leaves out records for a grid, and the two are close enough to be worth putting side by side. There, a filter’s candidate count fell by a factor of 132 and the operations those candidates cost fell by 1.8, because a candidate on one side was an array read and on the other was a point lifted through a dozen levels. Here, a query’s asymptotic class improved without bound and its cost fell by 2.5, because the class was never what was being paid.

Both are the same lesson: the unit that changes is not always the unit that is billed, and the only defence is to measure in something a reader could be charged for.

The sweep, line by line

Eight documents of two thousand characters, the pattern planted at rising density in three of them, so the answer stays at eight documents throughout and only the occurrence count moves. That is the separation the plate needs: one cost follows the axis and the other does not.

occurrences scan chain, tree chain, succinct
30 30 256 96
75 75 272 100
219 219 266 98
406 406 276 100
790 790 288 98

Both chains are flat, and the succinct one is flatter — 96 to 100 across a twenty-sixfold growth in the occurrence count, against 256 to 288. The drift in the tree’s figure is the logarithm of the range the queries are asked about growing; the succinct structure has no such term, so what drift it has is which block the endpoints happen to land in — the block structure the table that fits inside a block builds, showing through in the third decimal place.

The reads one listing spends, across the sweepThe same 6 points, as bars rather than curves. The chain's cost is nearly flat in the occurrence count — 179 to 282 reads across a 71x growth — because it performs 10 range-minimum queries whatever the range holds, and only the size of the range they are asked about grows. The succinct structure answers the same queries in 92, which is what a fixed number of lookups per query looks like beside a descent.11 occurrences17920 occurrences22256 occurrences250200 occurrences259392 occurrences282776 occurrences282reads · upper bar the tree, lower the succinct structure8 documents · answer 83.07x apart
Fig. 4 The same five points as bars, with both range structures. The query count is identical in every row — the two structures answer the same questions — and only what a query costs differs.

What did not change, and why that matters

The number of queries is the same in every row: ten or eleven, which is 2s + 1 for an answer of eight documents give or take a branch that terminates early. Changing the range structure cannot change it, because that count is a property of Muthukrishnan’s recursion and not of what answers its queries.

That is worth stating because it is the part of the method that is genuinely output-sensitive, and it survives everything. A list of documents is not a list of occurrences is where the recursion comes from and where its query count is derived. What the range structure decides is the constant multiplying it, and this strand has now measured two values of that constant. There is no third one available without a different recursion.

The other half: what it costs to hold

The crossing is one of two numbers the earlier essay left. The other was that the listing apparatus — the document array, the chain of previous occurrences, and the range minimum over the chain — came to 2.3 times the index it sits on, with 62% of that being the range structure alone.

With the succinct structure it comes to 1.47 times, and the range minimum falls to about a third of the apparatus rather than nearly two thirds of it.

What answering “which documents” costs, with each range structureThe parts of the index over 8 documents of 2,048 characters. Everything above the line is the price of answering *where*; everything below is the price of answering *which*. With a segment tree the listing apparatus is 2.40x the index it sits on, and 62.5% of that is the range structure alone. With the succinct one the apparatus is 1.47x, which is the difference between a structure you would think twice about and one you would simply build.suffix array and text327,820document array49,173previous-occurrence chain245,865range minimum, tree491,730range minimum, succinct186,077bitsapparatus 2.40x the index, or 1.47x8 documents · 2,048 characters each1.64x apart
Fig. 5 The parts of the index, with both range structures drawn. Everything above the line answers “where”; everything below answers “which”.

That is the more decisive of the two results. A structure that triples the size of the index it is bolted to is one a reader weighs; a structure that adds half again is one they build. And the two remaining terms — the document array and the chain — are each n⌈log₂ n⌉ bits and are untouched by anything in this strand, which is the same position one separator, or one for each left the collection’s own text in, so they are now the larger half of the apparatus and the obvious place for the next idea.

Why the crossing does not move further

It is tempting to read the two results together and expect the crossing to have moved by the same factor the apparatus did. It did not: the apparatus fell by 1.63 and the crossing by 2.98, and neither number causes the other.

The crossing is set by the query cost and the apparatus by the size, and the succinct structure is better at both by different amounts — 2.5 times on accesses and 4 times on the range structure’s own bits. Nothing requires those to agree, and a structure that traded more space for fewer accesses would move one and not the other.

That is worth carrying because a reader deciding whether to build this has two questions and this strand answers them separately. If queries are rare and the index has to fit, the size result decides it. If the collection is queried constantly, the crossing does.

2n bits, and everything that makes them answerableA range minimum over 16,384 values. The parentheses are 32,768 bits — the 2n the structure is quoted at — and everything below them is what turns a sequence of bits into a structure that answers a query: the support that finds the k-th opening, and the two layers of block minima. Together they come to 152,963 bits, which is 4.67x the payload. The segment tree this replaces is 491,520 bits, so the whole structure is 2.65x smaller — a real saving, and not the hundredfold one "2n + o(n)" invites. The in-block lookup table is a further 32,768 bits and is not counted here, because it belongs to the block length rather than to this structure.parentheses32,76817.6%select support28,41615.3%superblock minima4,6882.5%superblock table19,21510.3%block minima37,45620.2%block tables63,18834.0%the partsbitstotal 185,731 · the segment tree 491,520shared lookup table 32,768, not counted16,384 values · openings in index order, found by select2.65x smaller
Fig. 6 The size half of the answer, at the size a collection of this shape produces. The payload is 2n and everything below it is what makes the payload answer.

The crossing on a smaller collection

There is a regime where the chain never wins at all, and the earlier essay drew it: a collection small enough that the whole range fits in fewer occurrences than a chain’s fixed cost.

Eight documents of five hundred characters, swept the same way, never reaches 86 occurrences, so the succinct chain loses at every point of it — as the tree’s does, further out. That is not a defect of either structure; it is the flat cost of any output-sensitive method meeting a problem too small to have an output worth being sensitive to.

What a real corpus does to this

Eleven occurrences per document is a number about a planted pattern, and a planted pattern is a parameter rather than a measurement. Whether real queries reach it is a different question, and it is one this collection could not answer until it had a corpus it did not generate.

The short answer, measured on twelve essays of a hundred and forty-five thousand characters: with a segment tree the chain beats the scan on two of twelve patterns drawn from the corpus’s own vocabulary, and with the succinct structure on seven of twelve. What the generated collection was right about is where that measurement sits, and it is the clearest case in this collection of one strand’s structure deciding another strand’s conclusion.

What was checked

The two structures are required to answer identically, not equivalently: the same position, on four hundred random ranges of a three-thousand-element array, at three combinations of encoding and blocking. A tie makes “the same minimum” and “the same position” different claims, and a listing that walks left and right of the reported position notices the difference.

The listing itself is required to return the same documents by both routes on every collection measured, checked against exhaustion — the set of documents obtained by looking at every occurrence. And the crossing is computed by interpolation between measured points rather than read off a plate, so that the number quoted is not a member of the list of densities that happened to be swept.

The two numbers this strand produced are a query cost and a size, and the section after next is about which of the method’s parameters the first of them is really a function of.

What a reader should take from a structure with two numbers

There is a habit this collection keeps returning to, and this strand is a clean instance of it: a structure has a size and a query cost, and quoting either one alone is a way of choosing the answer.

The published description of a succinct range minimum leads with the size — 2n + o(n) bits — because that is the surprising half and the half the name is about. Read only that, and the structure is a seventeenfold saving over a segment tree. Measure it, and it is 2.85 times, because o(n) at sixty-five thousand values is five times the payload.

The published description leads on the query with “constant time”, because that is the strong statement. Read only that, and a query costs nothing. Measure it, and it is ten memory accesses, which crosses a scan at a hundred occurrences.

Neither reading is wrong about the mathematics and both are wrong about the structure, in the same direction, by about the same factor. What O-notation does not say is the essay this collection opens with and it is the one this strand keeps rediscovering: a class is a statement about a family of inputs growing without bound, and every decision anybody makes is about one input of a fixed size.

Two numbers that did not exist before this strand

Both of the results above are comparisons, and comparisons need both sides to have been built. That is the reason this strand exists at all rather than being a paragraph in the essay that named it.

The earlier essay could say that the crossing was at thirty-one occurrences per document and that a succinct structure would move it. It could not say where to, because the number depends on a constant that no account of the structure states — nine or ten accesses is not in any description of the method, and it is not derivable from “constant time” by any amount of reading.

The same is true of the apparatus. That the range minimum was 62% of the listing’s overhead was measurable from the segment tree; that it becomes about a third was not measurable from anything until the alternative was built and weighed the same way.

A structure that is named and not built is a structure whose constants are unknown, and this collection’s habit of building the alternative rather than citing it is what turns two hopeful sentences into two numbers. The hopeful sentences were, in this case, optimistic by a factor of about three.

Where the two methods meet, with each range structure12 documents of 1,024 characters, the pattern planted at rising density in 4 of them, so the answer stays at 12 documents and only the occurrence count moves. The scan costs one read per occurrence and rises along the diagonal. The chain costs 219 reads with a segment tree and 65 with the succinct structure, near enough flat in both cases — so changing the range structure moves the crossing from 27 occurrences per document to 9. The strand that named this structure predicted "a handful"; it is a factor of 2.91x, because a constant-time query is a dozen lookups rather than one.100100occurrences of the patternreadsthe scanchain, treechain, succinct12 documents · answer 12crossing 329 → 113
Fig. 7 The same measurement on a collection of twelve shorter documents, where the answer is larger and the crossing accordingly moves. The shape holds; the numbers are not universal.

Which parameter the crossing is actually about

The crossing is quoted per document — 10.8 with the succinct structure against 32.2 with the tree — and the per-document form hides which parameter it depends on, which is worth extracting because it decides where the method is worth using.

The chain’s cost is 2s+12s+1 queries at about ten accesses each, where ss is the number of documents holding the pattern. So the crossing, in total occurrences, is about (2s+1)c(2s+1)c and it does not mention the size of the collection at all. Dividing by the number of documents to get a per-document figure introduces a dd that was never in the cost.

Which means the honest statement is: the chain wins when the pattern occurs more than about (2s+1)c(2s+1)c times in total, and whether that is easy or hard depends entirely on how ss behaves as the collection grows.

A rare pattern is the good case. If a phrase appears in three documents whatever the collection’s size, then ss is fixed, the crossing stays near ninety occurrences, and the per-document figure falls like 1/d1/d — so on a collection of a thousand documents the method wins on a pattern occurring ninety times anywhere in it, which is a very low bar.

A common pattern is the bad case. If the pattern is in a constant fraction of the documents then ss grows with dd, the crossing grows with it, and the per-document figure is constant — the method never gets any easier however large the collection becomes.

That is the reverse of the reading the per-document number invites, and it explains the real-corpus result without any further measurement. Seven of twelve patterns beat the scan there because patterns drawn from a corpus’s own vocabulary are mostly rare — most words are in a few documents rather than in most of them — and it is exactly the rare ones the method is built for.

So an output-sensitive listing is worth building for a collection whose queries are specific, and worth nothing for one whose queries are common words. Both halves of that were in the arithmetic before either structure was built, and neither is visible in a crossing quoted per document.

What this strand did not do

It did not touch the chain of previous occurrences, which is n⌈log₂ n⌉ bits and is now the largest part of the listing apparatus. It did not touch the document array. And it did not change the number of queries, which is the part of the method that is genuinely output-sensitive.

What it did was replace one structure with the structure the method is published with, and measure what that is worth: a factor of three on the crossing and 1.6 on the apparatus, from a structure whose own accounting turned out to be five times its payload and whose most expensive design decision was which order to write its parentheses in.

The strand’s own deferral is what it leaves: an output-sensitive listing whose apparatus is now dominated by two plain arrays, and no idea here for making either of them smaller.

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 10 that link here.

The objects this essay names

Each one links to every other essay that touches it.

Constant factorCrossoverDocument listingIndex sizeLower boundMeasurementOutput-sensitiveRange minimumSegment treeSpace overheadSuccinctTrade off