What is taught wrongly

What the approximation gives up

Compared at every one of the 3,736 decisions a search could ask about, the published shift rules and the exact ones agree at all of them on a set of 128 patterns. At two patterns they differ at five of 84, by up to four positions — and the run reads 6.3% more characters.

The published shift rules are a safe approximation to a quantity with an exact definition. Two questions follow and neither is answered by the phrase: how safe, and how approximate.

The first is a proof and it is checked: at every pair of a trie node and a mismatching character, on every pattern set measured here, the published shift is at most the exact one. The second is a measurement, and the answer is that the approximation gives up nothing at all past a set size a plate can show.

Every shift decision the two rules could ever make, 2 patternsThe published rule and the exact rule compared at every one of the 84 (trie node, mismatching character) pairs a search could ask about — the whole domain of the function rather than the part one text happens to visit. The published rule is never larger than the exact one, which is what makes it safe, and it is smaller at 5 of them, which is what makes it an approximation. The largest deficit is 4 positions against a shortest pattern of 10.short by 133.6% of pairsshort by 211.2% of pairsshort by 411.2% of pairsthe same shift7994.0% of pairspatterns of 10 over four symbolsthe published rule is never the larger of the twoshift decisions84 pairs · 21 nodes
Fig. 1 Every shift decision two rules could make on a two-pattern set. Five of the 84 differ, and the published rule is the smaller in all five.

Comparing over the domain, not over a run

A run is a sample. It visits the trie nodes its text happens to lead it to, mismatches on the characters that text happens to contain, and reports a number that mixes the rules’ difference with the text’s shape.

So the comparison here is over the whole domain of the function: every trie node, every character of the alphabet, whether or not any text would produce that pair. At 128 patterns of ten characters that is 3,736 pairs; at two patterns it is 84.

That distinction matters because the run-level numbers and the decision-level numbers say different things, and only the second is a property of the rules. It is the same move the ceiling the shortest pattern sets makes when it asserts the shift ceiling over the whole shift table rather than over the shifts a run happened to take.

The numbers

Across pattern sets of one, two, four, eight, sixteen, thirty-two, sixty-four and 128 patterns of ten characters over four symbols:

At one pattern, 2 of 44 pairs differ, with deficits of one and seven. At two, 5 of 84, with a worst of four. At four, 3 of 152, all of them one. At eight, 6 of 288, all of them one. At sixteen, 2 of 552, both one.

At thirty-two, sixty-four and 128 patterns: zero. Not a small share — none. The two functions agree at every one of the 1,060, 2,016 and 3,736 pairs in their domains.

So the approximation is an approximation on small sets and is the exact rule on large ones, which is the opposite of what “linear-time approximation” suggests and is exactly what the mechanism predicts once it is looked at.

Every shift decision the two rules could ever make, 32 patternsThe published rule and the exact rule compared at every one of the 1,060 (trie node, mismatching character) pairs a search could ask about — the whole domain of the function rather than the part one text happens to visit. The published rule is never larger than the exact one, which is what makes it safe, and at this set size it is not smaller at a single one of them — the approximation and the definition are the same function over the whole domain, which is why the two rules read identically here. They are distinct functions: at two patterns they differ.the same shift1,060100.0% of pairspatterns of 10 over four symbolsthe published rule is never the larger of the twoshift decisions1,060 pairs · 265 nodes
Fig. 2 Thirty-two patterns, and every decision the same. The plate has to establish that the two rules are distinct functions somewhere else, which it does at two patterns.

Why the gap closes as the set grows

The published shift is min(max(d1,char(c)j1),d2)\min(\max(d_1, \mathrm{char}(c) - j - 1), d_2) and the exact shift is the largest step no pattern can end inside. The difference between them lives in the slack between d1d_1 and the exact good-suffix condition, and in the unconditional min\min with d2d_2.

Both of those quantities collapse as the trie fills up. With 128 patterns of ten characters over four symbols, nearly every node has a failure child one level down, so d1d_1 is one nearly everywhere; nearly every node has a pattern end within one or two levels of its suffix set, so d2d_2 is small everywhere as well.

And the exact rule collapses too, for the same reason: with that many patterns, the segment already matched can line up with something at almost every offset, so the exact shift is small as well.

Two functions that are both pinned at their floor agree. The gap between them is largest where both have room to move, which is on small sets — where the exact tables are cheap to compute anyway.

What each rule reads, as the pattern set growsCharacters examined in 20,000 characters of four symbols, by pattern set size. The bad-character rule alone stops skipping almost at once; the published rules and the exact rules run together, apart at the smallest sets — 1.06x at 2 patterns — and identical from 32 upward. Both axes are logarithmic.101001,00010⁴10⁵characters of pattern in the setcharacters examinedno shift (step one)bad character onlythe 1979 shift functionsboth rules, exactlyfour symbols · patterns of 10n = 20,000
Fig. 3 The run-level consequence: two lines that separate at the left of the sweep and lie on top of each other from the middle onward.

What it costs in a run

The decision-level numbers are the property of the rules; the run-level numbers are what a user of them pays.

On twenty thousand characters of four-symbol text: at one pattern both rules read 7,197 characters. At two, the published rules read 13,084 against 12,306, which is 6.3 per cent more. At four, 12,211 against 12,069 — 1.2 per cent. At eight, 23,551 against 22,551 — 4.4 per cent. At sixteen, 24,372 against 24,299 — 0.3 per cent. At thirty-two and above, identical to the character.

The largest penalty in the sweep is 6.3 per cent, it occurs at two patterns, and it is not monotone: 6.3, then 1.2, then 4.4, then 0.3, then nothing. That wobble is the run sampling the decisions rather than the rules changing, which is exactly why the domain-level comparison is the one to believe.

What each rule reads, as the pattern set growsCharacters examined in 20,000 characters of twenty-six symbols, by pattern set size. The bad-character rule alone stops skipping almost at once; the published rules and the exact rules run together, apart at the smallest sets — 1.00x at 2 patterns — and identical from the middle of the sweep upward. Both axes are logarithmic.10010⁴characters of pattern in the setcharacters examinedno shift (step one)bad character onlythe 1979 shift functionsboth rules, exactlytwenty-six symbols · patterns of 16n = 20,000
Fig. 4 The same comparison on a twenty-six-symbol alphabet with longer patterns, where every rule skips further and the two still run together.

Both counts move differently, again

The run-level comparison has two numbers in it and this strand’s convention requires both, because they do not move together.

Characters examined is what the matcher spends, and it counts a position once for every window that reads it. At 128 patterns the published rules examine 41,580 characters in a text of twenty thousand — more than twice the text, because windows overlap.

Distinct positions read is the complement of the skip. At the same point it is 17,405 of 20,000, so 13 per cent of the text was never looked at.

The two rules agree on both at large sets and differ on both at small ones — at two patterns, 13,084 examined against 12,306 and 10,254 touched against 9,870. The second gap is 3.9 per cent where the first is 6.3, which is the same run reported two ways.

That convention came out of the phase that first measured a skipping matcher, where the two counts cross the one-pass matcher’s at different set sizes: examined characters overtake at eight patterns and distinct positions never do. The shift a set of patterns allows is where that was established, and it is why no plate here reports one of them alone.

Safety is the half that is not negotiable

An unsafe shift is a wrong answer, and the gate treats it as one.

Every run in this strand is checked against exhaustive search on the occurrence list, and the domain-level comparison additionally requires the published shift to be at most the exact one at every pair. A rule that stepped one position too far would pass a count comparison whenever the missed occurrence happened not to exist, so both checks are needed.

The rejection test is the constructive half: raise d1d_1 to the ceiling at every node — a change that looks like a strict improvement, since larger shifts mean fewer windows — and require the exhaustive comparison to catch it. Measured: two occurrences reported where there are eight.

The bug this comparison found

The comparison over the whole domain found something, and it was in the reference rather than in the approximation.

The helper that reports “the shift this node and this character allow” took an argument named depth, and it used that argument twice: once to index the good-suffix table, which is indexed by node, and once to index the bad-character table, which is indexed by depth. One argument cannot be both.

It had been correct at every existing call site by accident, which is the shape the check must reject is about: a condition nobody asks about is a condition nobody is checking. The two callers were an assertion about the shift ceiling — every shift is at most the shortest pattern, which holds whichever node’s entry is read — and a rejection test that inflates a shift on purpose and requires the matcher to break.

It became visible the moment two rules were compared decision by decision: the published shift came out larger than the exact one at a node, which is a safety violation, and it was an indexing error in the thing the comparison was against.

The lesson is one this collection keeps relearning. A helper is only as checked as the questions asked of it, and a helper whose two existing callers both ask about a maximum can be wrong about every individual entry and right about the maximum.

The two tables, on the trie of 3 reversed patternsEach node is a string read backwards from the window's right-hand end; a filled node ends a pattern. The pair under each node is d₁ and d₂ — the least depth increase to a node whose word has this one as a proper suffix, and the same restricted to nodes that end a pattern. Both are the failure-link relation of the trie, so both are one pass; the shift is min(max(d₁, char(c) − depth − 1), d₂), and the min with d₂ is the guard that keeps a window from stepping past a place a pattern could end. The tables are checked against the quadratic definition node for node on the plate.·1, 4b1, 3b1, 2b4, 2a4, 2a2, 2a4, 2b4, 2b4, 2b4, 2patterns abbb, baab, bbab · shortest 4d₁, d₂ under each node · a filled node ends a patternthe reversed-pattern trie10 nodes
Fig. 5 The two tables the comparison reads. One is indexed by node and one is indexed by depth, and the helper that reads both used the same argument for each.

What the deficits look like when there are any

The distribution of deficits is short and it is worth reporting rather than summarising, because a mean over five values is not a summary.

At two patterns: three decisions short by one, one short by two, one short by four, out of 84. At one pattern: one short by one and one short by seven, out of 44. At four, eight and sixteen patterns: every deficit is exactly one. That is a shift falling one position short of the largest safe step, which costs one extra window and the characters that window reads — a quantity one run, two counts would have this collection report in both of its units.

The worst case in the sweep is seven positions against a shortest pattern of ten, and it occurs on a single pattern, where the published rule is at its crudest: with one word in the trie there are no failure children at all, so d1d_1 is the pattern’s length everywhere and d2d_2 is the length as well, and the shift is whatever the character table says.

That is Horspool’s matcher, near enough, which is what a single-pattern skipping matcher usually is — and the shift the pattern already knows is where this collection measured the exact good-suffix rule against it and found the same shape of gap.

Every shift decision the two rules could ever make, 1 patternsThe published rule and the exact rule compared at every one of the 44 (trie node, mismatching character) pairs a search could ask about — the whole domain of the function rather than the part one text happens to visit. The published rule is never larger than the exact one, which is what makes it safe, and it is smaller at 2 of them, which is what makes it an approximation. The largest deficit is 7 positions against a shortest pattern of 10.short by 112.3% of pairsshort by 712.3% of pairsthe same shift4295.5% of pairspatterns of 10 over four symbolsthe published rule is never the larger of the twoshift decisions44 pairs · 11 nodes
Fig. 6 The single-pattern case, where the published rule has the least information to work with and the largest deficit in the sweep.

The one place the deficit is worth something

Six point three per cent of reads is small and it is not nothing, and there is a regime where it would be the whole decision: a pattern set that is built once and used on an enormous amount of text.

There, the construction cost is amortised to nothing and the only thing that matters is the scan. The exact rules read 12,306 characters per twenty thousand at two patterns against the published rules’ 13,084, so on a terabyte the difference is six per cent of the whole run and the tables cost 594 comparisons to build.

That is the crossing the rule that pays on a long enough text measures, and it exists only at the set sizes where the two rules differ at all. At thirty-two patterns and above there is no crossing to find, because the scan is identical and the construction is 13.8 times dearer — so the exact rules are dominated at every text length.

An approximation that is exact past a threshold has no crossing above that threshold. That sounds obvious written down and it is the reason the third curve this strand was asked for turns out to have two regimes rather than a shape.

Precomputation plus scan, with 32 patternsBoth terms are character comparisons: the ones performed before the text is touched and the ones performed on it. With 32 patterns of 10 characters, the exact rules cost 68,760 comparisons to build against the published rules' 1,539, and buy a scan that reads 1.01x less. The extra skipping never pays for the tables inside this range — at n = 32,000 the exact rules are still 2.39x the total. Both axes are logarithmic.1,00010,00010⁴10⁵characters of textcomparisons, built and scannedboth rules, exactlythe 1979 tables32 patterns of 10 · four symbolsno crossing in range
Fig. 7 The two totals at a set size where the rules read identically. The gap is the construction and it never closes, whatever the text length.

What this settles about the third curve

The direction after the phase that built the exact rules asked where the published functions sit between “no rules” and “the exact rules”. The answer is a surprise in one direction and not the other.

They do not sit in the middle. On randomly drawn sets of two or more patterns they are within 6.3 per cent of the exact rules in reads and are identical from thirty-two patterns up, while the bad-character rule alone has stopped skipping entirely by eight patterns.

So the scale is not a scale: there are two useful rules and one useless one, and the difference between the two useful ones is a construction cost rather than a skip. The table that walks every pair is that construction cost, and it is a factor of 137 at the top of this sweep — for the construction measured there. The table the links already knew builds the same tables from the trie’s links at 1.6 times the published rule’s cost, which is what the difference between the two rules is actually worth.

What each set of tables cost to buildCharacter comparisons performed before the text is touched, against the total length of the pattern set. The published tables are flat per character of pattern — 4.4 at 128 patterns against 5.1 at 1 — because they are computed from the trie's own failure links. The exact rules quantify over every pattern at every offset for every node, and at 128 patterns they cost 137.35x as much, against 4.06x at 1: 769,724 against 5,604. Both axes are logarithmic.101001,00010010³10⁴10⁵characters of pattern in the setcomparisons before the scanboth rules, exactlybad character onlythe 1979 shift functionsfour symbols · patterns of 10137.35x at 128 patterns
Fig. 8 The axis the two rules actually differ on. What the previous plates show as two lines on top of each other, this one shows as two orders of magnitude.

Where an adversary would put the difference

The measurement is on pattern sets drawn uniformly at random with no member a substring of another, and it establishes what happens on such sets. It does not establish what happens on a set built to separate the two rules.

The mechanism says where to look. The published d1d_1 asks whether the matched segment can reappear deeper in the trie; the exact rule asks whether it can line up with a pattern at a given offset. A set where those differ systematically is one with long shared suffixes arranged so that the trie’s failure links are short while the actual alignments are not.

Nothing here constructs such a set, and the honest reading of this essay’s numbers is therefore: on random sets over a small alphabet, at one pattern length, the approximation costs between nothing and 6.3 per cent. A worst case exists in principle and is not measured.

Why this is in the wrong-answers field

The sentence this essay is against is one this collection wrote when it built the exact rules: that the published shift functions skip less.

They do, in principle, and on the pattern sets a real matcher is given they do not — at eight patterns, six decisions of 288 differ and every one of them by a single position. A reader taking “skips less, costs nothing to build” as a trade would conclude that using the exact rules buys something, and on these sets it buys 0.3 to 6.3 per cent of reads for between four and 137 times the construction as measured there — or 1.5 to 2.4 times, once the exact tables are built from the trie’s links rather than from their definition.

The correction is not that the published rules are better. It is that the two halves of the trade have completely different sizes, and the sentence gives no way to tell. That is the same complaint the constant the notation drops makes about complexity classes, arriving at a pair of algorithms that are not distinguished by a class at all.

Precomputation plus scan, and where the exact rules start payingBoth terms are character comparisons: the ones performed before the text is touched and the ones performed on it. With 2 patterns of 10 characters, the exact rules cost 596 comparisons to build against the published rules' 106, and buy a scan that reads 1.02x less. So the better rule is a function of the text length, and the crossing is at n = 32,000: below it the cheapest tables win, above it the extra skipping pays for them. Both axes are logarithmic.1,00010,00010³10⁴characters of textcomparisons, built and scannedcrossing at n = 32,000both rules, exactlythe 1979 tables2 patterns of 10 · four symbolscrossing n = 32,000
Fig. 9 The trade with both halves on one axis, at the set size where the exact rules genuinely skip further. Which one wins depends on how much text the tables are used on.

Folding positions into reads, which the run already does

The last of those limits — that the deficits are in positions and the costs are in reads — is one the sweep can close, because the run reports both counts and the ratio between their gaps is the exchange rate.

At two patterns the published rules read 778 more characters than the exact ones, 13,084 against 12,306, and touch 384 more distinct positions, 10,254 against 9,870. The second number is the shift actually given up; the first is what giving it up cost.

Divide: 2.03 characters read per position of shift lost. That is the missing conversion, and it is not a modelled quantity — it is the same run reported in its two units, so it already carries whatever the trie walk really costs on this set and this text.

It also explains the pair of percentages the essay reports without connecting: 6.3 per cent in reads against 3.9 per cent in positions. The ratio of those is 1.6, and the ratio of the two totals is 13,084/10,254=1.2813{,}084/10{,}254 = 1.28 — the same overlap appearing once in the gap and once in the base. Two counts that move differently move by a factor that is measured rather than mysterious.

With a rate, the deficit distribution can be priced. At two patterns the five differing decisions give up eight positions between them — three of one, one of two, one of four — so a firing of the worst of them costs about eight character reads and a firing of the commonest costs two. The seven-position deficit at one pattern is worth about fourteen.

Which makes the single-pattern row the most instructive line in the sweep, and it needs saying plainly. Two of its 44 decisions differ, one of them by seven positions, and both rules read 7,197 characters — identically. The text never put either pair to the matcher, so the largest deficit anywhere in this essay cost exactly nothing on the run that could have paid for it.

That is the distinction the whole comparison rests on, arriving as a number instead of a methodological remark. A deficit is a property of the rules; a cost is a property of the rules and a text, and the two need not be related at all. A decision that differs by seven positions and never fires is free; a decision that differs by one and fires four hundred times is 800 reads. Nothing about the domain-level comparison predicts which, and nothing about the run-level comparison distinguishes a rule that is nearly exact from a rule that is badly wrong in a corner the text avoided.

Both are needed, and the honest summary of what each is for is this. The domain comparison bounds the worst a text could extract — five decisions, eight positions, about sixteen reads per full sweep of the differing pairs. The run says what one text did extract, which at two patterns was 6.3 per cent and at one pattern was nothing. The gap between those two statements is the text, and it is the largest term in the whole comparison.

That is one run, two counts’s convention doing more than book-keeping. Reporting characters alone would have hidden the conversion; reporting positions alone would have hidden that a position is worth two reads on this set and would be worth more on a deeper trie, which is the direction the shift a set of patterns allows says the number moves as the set grows.

The honest limit

Everything above is one pattern length — ten characters — on two alphabets, with sets drawn uniformly at random. The mechanism that closes the gap is the trie filling up, which happens sooner on a small alphabet and with short patterns, so a set of long patterns over a large alphabet would keep the two rules apart for longer.

The domain-level comparison covers every (node, character) pair, which is the whole function; it does not cover every pattern set, and the sets here are eight sizes at one seed each.

And the deficits are in positions rather than in reads. A shift short by one costs one extra window, and a window costs however many characters the trie walk reads before mismatching — which is a number this collection measures separately and does not fold in.

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

The objects this essay names

Each one links to every other essay that touches it.

Alphabet sizeApproximationBad-character ruleCommentz-walterFailure linkFalsificationGood-suffix ruleMeasurementMulti patternPattern matchingPreprocessingShift ruleString matchingTrie