You're viewing the readable version of this site. The interactive extras (search, diagrams, read-aloud) need JavaScript and a current browser. Enable JavaScript; if it is already enabled, update your browser.

Learn · The Art of Not Looking

budding

Two Lists, One Walk

Sorted postings turn conjunction into a walk whose cost follows the gaps in this instance, not merely the lengths of its lists.

search, information-retrieval, intersection, adaptive-algorithms, postings, algorithms, data-structures, learn

“We develop the idea of a proof that a given set is indeed the correct answer.”

— Erik D. Demaine, Alejandro López-Ortiz, and J. Ian Munro, “Adaptive Set Intersections, Unions, and Differences

This is the fourth chapter in a book about search from first principles. The previous chapter made indexing and querying agree on what a term means. Now a query contains two agreed-upon terms, each naming a sorted postings list. You will derive the linear merge and prove its invariant, replace repeated binary searches with stateful galloping, derive the O(mlog(1+n/m))O(m\log(1+n/m)) comparison bound for a short list of length mm and a long list of length nn, and see why a shortest-list-first plan is a bound rather than a superstition. You will also derive the square-root skip heuristic and learn what an adaptive certificate says that worst-case notation cannot. The next chapter asks the question Boolean intersection cannot answer: did the matching words occur together?

Two guest lists at one door

A conference has an invited list and a checked-in list. Both are sorted by badge number:

invited    3   8  14  21  34  55
checked    1   8  13  21  22  34  89

The people who may enter are on both lists: {8,21,34}\{8,21,34\}. One method starts again at the top of checked for every invited badge. Another puts one finger on each list.

At 3 and 1, badge 1 is too small. Because the invited list is sorted, 1 cannot reappear later there. Advance only the checked-in finger. At 3 and 8, the same argument discards 3. Equality emits an answer and advances both.

One comparison has ruled out an entire future possibility. Order is doing the work.

Posting — a record saying that a term occurs in a document. A document-only postings list stores sorted document identifiers; richer postings may also store counts or positions. Learn more.

For two query terms aa and bb, Boolean AND asks for the set intersection

P(a)P(b), P(a) \cap P(b),

where P(t)P(t) is the set of documents containing term tt. OR is union and a AND NOT b is relative complement. The index has turned words into ordinary set algebra over integers.

Prediction — choose the work from the shape of the instance. Commit before reading the trace.

The reference walk

Let A[1:m]A[1:m] and B[1:n]B[1:n] be strictly increasing sequences. The reference algorithm carries two forward-only indices.

Linear intersection of two sorted sets

MERGE-INTERSECT(A, B)
Input:  strictly increasing sequences A[1:m] and B[1:n]
Output: the strictly increasing sequence A intersect B

i  1
j  1
R  empty sequence
while i  m and j  n
    if A[i] = B[j]
        append A[i] to R
        i  i + 1
        j  j + 1
    else if A[i] < B[j]
        i  i + 1
    else
        j  j + 1
return R

The loop invariant is the whole proof:

Before each comparison, R is exactly the intersection of the consumed prefixes, and no consumed value can match an unconsumed value.

Initially both prefixes are empty. On equality, the shared value belongs in the answer and strict increase means it occurs nowhere else. If A[i]<B[j]A[i] < B[j], then every unconsumed value in BB is at least B[j]B[j], so A[i]A[i] can never match; advancing ii is safe. The other inequality is symmetric. When either list ends, no further pair can match. Thus R is exactly ABA\cap B.

Every iteration advances at least one index and neither retreats. The time is O(m+n)O(m+n) comparisons and the working space is O(1)O(1) beyond the output. If the consumer accepts a stream, the result need not be retained at all.

The bound is tight for this algorithm, but not for every instance. Consider

A = [47]
B = [4, 6, 10, 12, 14, 16, 18, 20, 22, 32, 47, 81, ...]

A merge walks ten losing values before reaching 47. Sorted random access lets the short list ask a sharper question: where could 47 occur in the remaining suffix of BB?

Gallop, then close the bracket

Binary search from the beginning of BB for every member of AA costs O(mlogn)O(m\log n). It throws away information: after searching for A[i]A[i], the next target A[i+1]A[i+1] is larger, so it cannot lie before the position just reached.

A finger search retains that position. A galloping search probes offsets 1,2,4,8,1,2,4,8,\ldots until it passes the target, then binary-searches only the bracket just found.

Galloping search — exponential search from a known lower bound, followed by binary search inside the first power-of-two bracket containing the target. Its cost is logarithmic in the distance traveled, not in the whole sequence. Learn more.

Lower-bound search from a forward-only finger

GALLOP-LOWER-BOUND(B, start, target)
Input:  strictly increasing B[1:n], valid start, target not before B[start]
Output: least j at or after start with B[j] ≥ target, or n + 1

if start > n or B[start]  target
    return start
step  1
while start + step  n and B[start + step] < target
    step  2 × step
low  start + step / 2 + 1
high  min(start + step, n)
while low  high
    middle  low + floor((high - low) / 2)
    if B[middle] < target
        low  middle + 1
    else
        high  middle - 1
return low

Galloping intersection, short list against long list

GALLOP-INTERSECT(A, B)
Input:  strictly increasing A[1:m] and B[1:n], with m ≤ n
Output: the strictly increasing sequence A intersect B

j  1
R  empty sequence
for each value x in A
    j  GALLOP-LOWER-BOUND(B, j, x)
    if j > n
        return R
    if B[j] = x
        append x to R
        j  j + 1
return R

The finger never moves backward, so the intervals searched for successive targets are disjoint. Suppose their traversed lengths are g1,g2,,gmg_1,g_2,\ldots,g_m. Galloping and closing a bracket of length gig_i takes O(1+log(gi+1))O(1+\log(g_i+1)) comparisons. Because the finger crosses at most nn positions,

i=1mgin. \sum_{i=1}^{m} g_i \le n.

The logarithm is concave. Jensen's inequality therefore gives

i=1m(1+log(gi+1))m+mlog(1+igim). \sum_{i=1}^{m}\left(1+\log(g_i+1)\right) \le m + m\log\left(1+\frac{\sum_i g_i}{m}\right).

Because igin\sum_i g_i\le n, this is at most

m+mlog(1+nm). m + m\log\left(1+\frac{n}{m}\right).

Thus the intersection costs

O(mlog(1+nm)). O\!\left(m\log\left(1+\frac{n}{m}\right)\right).

When mnm\ll n, this beats touching all nn entries and improves on mm unrelated O(logn)O(\log n) searches. When mm and nn are similar, the bound becomes O(m)O(m), but the plain merge often wins in elapsed time: it has a predictable sequential access pattern and less branch machinery.

Use a merge when both lists deserve a finger. Use galloping when one list can serve as a small sequence of questions into the other.

More than two terms

For a conjunctive query with postings P1,,PqP_1,\ldots,P_q, intersection is associative:

(P1P2)P3=P1(P2P3). (P_1\cap P_2)\cap P_3 = P_1\cap(P_2\cap P_3).

The answer does not depend on parenthesization. The work does. A simple small-versus-small plan sorts the lists by increasing document frequency and repeatedly intersects the current result with the next list.

Shortest-list-first conjunctive evaluation

SVS-INTERSECT(L)
Input:  finite sequence L of sorted postings lists
Output: the intersection of every list in L

order L by nondecreasing length
R  first list in L
for each remaining list P in L
    R  GALLOP-INTERSECT(shorter of R and P, longer of R and P)
    if R is empty
        return R
return R

After any step, |RP||R||R\cap P|\le |R|. Starting with the shortest list gives the smallest available upper bound on every later intermediate result. This does not prove that length order wins on every concrete set: contents, compression, cache residence, and correlations can reverse two plans. It proves the worst-case cardinality fact the planner is using.

The two main evaluation views now have names:

  • term-at-a-time combines one whole term's evidence with an accumulator, then moves to the next term;
  • document-at-a-time advances several postings cursors in document order and finishes one candidate document before the next.

Boolean pairwise intersection looks term-at-a-time because it materializes an intermediate set. The forward cursors inside it already foreshadow document-at-a-time ranked retrieval: later chapters will keep several cursors aligned and use score bounds to decide which document can be skipped.

A skip is a stored promise

Sequentially encoded postings may not support constant-time random access. A skip pointer stores a shortcut from one decoded position to a later one. It may be followed only when its destination is still below the opposing target; that comparison proves every posting under the jump cannot match.

How far apart should skips be? Let a list contain nn postings and place a skip every ss postings. A deliberately simple model charges about n/sn/s skip checks to cross the list and at most ss ordinary advances after the last useful jump:

T(s)ns+s. T(s) \approx \frac{n}{s}+s.

By the arithmetic–geometric mean inequality,

ns+s2n, \frac{n}{s}+s \ge 2\sqrt{n},

with equality at s=ns=\sqrt n. This derives the familiar square-root spacing heuristic: about n\sqrt n skips, each spanning about n\sqrt n postings.

It is a model, not a universal optimum. It ignores the query distribution, compressed-block boundaries, cache lines, update costs, and the possibility that checking a skip costs more than advancing one decoded integer. The Stanford information-retrieval text presents square-root spacing as a practical heuristic and explicitly notes those limitations.

Reveal — a shortcut is useful only when its destination remains below the opposing cursor.

The input carries its own difficulty

Worst-case analysis asks for one bound covering every pair of lengths m,nm,n. Adaptive analysis asks what this particular arrangement forced an algorithm to learn.

Certificate — a set of observed comparison outcomes sufficient to prove the reported answer. A shortest certificate is a lower-bound witness for that instance: any comparison-based algorithm must obtain enough information to distinguish the answer from a different legal input. Learn more.

Suppose the two lists occupy distant, cleanly separated ranges. A few boundary comparisons can certify that their intersection is empty. Suppose instead that their values alternate and nearly coincide. Many local comparisons may be unavoidable. The lengths are identical in both cases; the proof burden is not.

In 2000, Erik Demaine, Alejandro López-Ortiz, and J. Ian Munro formalized this view for intersection, union, and difference of sorted sets. They characterized proofs of an answer and gave comparison algorithms within a constant factor of an instance-sensitive difficulty measure. The theorem is stronger than “it ran quickly on sparse lists”: it names the information any correct comparison algorithm must expose on that input.

Text retrieval supplied the pressure. Boolean queries over an inverted file look like ordinary set operations, yet repositories produce posting lists with wildly different lengths and arrangements. A worst-case bound treats a clean million-element gap and a million ambiguous near-matches alike. Demaine, López-Ortiz, and Munro changed the question from “how large are the sets?” to “how short can a proof of this answer be?” The resulting adaptive framework made the instance—not an average distribution guessed in advance—the unit of difficulty.

The full instance-optimal result needs a careful certificate definition and comparison-model proof; it is cited here rather than disguised as the short galloping derivation above. Galloping is one concrete lesson from the same stance: retain what earlier comparisons proved, and let long gaps be cheap.

The machine underneath the notation

Asymptotics decide which growth curves are possible. Hardware decides which curve wins on a given machine.

J. Shane Culpepper and Alistair Moffat compared intersection methods for inverted indexes and separated two representation regimes: sorted arrays with constant-time indexed access, and sequential or compressed lists where reaching the $d$th integer has its own cost. That distinction changes which “skip” is physically available.

For flat resident arrays:

  • merge streams contiguous memory and gives prefetchers an easy job;
  • galloping performs fewer comparisons on imbalanced inputs but probes less predictably;
  • branch mispredictions can cost more than several integer comparisons;
  • vectorized block comparisons can move the crossover point.

For compressed postings:

  • a logical jump may first require finding and decoding a block;
  • auxiliary samples consume bytes but can bound the decode distance;
  • the best skip spacing follows blocks and query frequencies, not merely n\sqrt n;
  • dynamic updates can invalidate a layout whose performance assumed a static list.

Measure at least comparisons, decoded integers, bytes touched, and elapsed time. Reporting only the last number hides why the winner changed. Reporting only comparisons pretends the memory hierarchy does not exist.

What not to do

Several plausible approaches fail for different reasons.

Approach Why it is tempting Exact failure Reversal condition
scan every document no index machinery work follows corpus size, not evidence tiny or one-shot corpora
restart a scan for each short-list value simplest membership test repeats already disproved prefixes opposing list is itself tiny
independent binary searches logarithmic lookup discards the previous lower bound parallel immutable probes or no ordered query sequence
gallop every pair adaptive-looking default extra probes and branches lose on balanced lists lists are strongly imbalanced
add skips everywhere visible shortcuts metadata and checks dominate short lists long sequential encodings with repeated conjunctions
always trust shortest-list-first cheap cardinality heuristic length omits correlation and physical cost no better statistics are available

The honest production rule is a dispatch, not a champion: compare list lengths and representation capabilities, choose a sequential merge for balanced resident lists, choose finger or galloping search for strong imbalance, and measure the threshold on the actual encoding and machine. Preserve a simple merge as the differential oracle.

The unresolved match

Intersection has now proved that every returned document contains every query term. It has not proved what the reader probably meant.

A document containing “new evidence arrived yesterday” in its first paragraph and “York hosted the meeting” in its last contains both new and york. It survives the Boolean intersection for “New York.” The lists have met; the words have not.

To answer that question, a posting must remember where a term occurred. The next chapter turns a phrase into another intersection—this time shifted by an offset—and makes us pay explicitly for storing position.

Lessons

  • A comparison between sorted cursors proves which smaller value cannot match later; this is why linear merge is correct.
  • Linear merge costs O(m+n)O(m+n) time and O(1)O(1) working space beyond its output.
  • A forward-only galloping intersection costs O(mlog(1+n/m))O(m\log(1+n/m)) for mnm\le n because it pays logarithmically for disjoint gaps.
  • Shortest-list-first minimizes a worst-case bound on intermediate cardinality; it does not know correlations or physical access costs.
  • The square-root skip rule follows from the model n/s+sn/s+s and remains a heuristic, not a law of storage.
  • A certificate measures the comparison evidence this instance needs, which worst-case length bounds deliberately erase.
  • Fewer comparisons need not mean less time; layout, decoding, branches, and cache traffic choose the practical crossover.

Practice

Retrieval — reconstruct the merge invariant before checking the choices.
Discrimination — distinguish the asymptotic winner from the likely constant-factor winner.
Transfer — choose the evidence a benchmark must expose.
  1. Trace MERGE-INTERSECT on [3, 8, 14, 21, 34, 55] and [1, 8, 13, 21, 22, 34, 89]. Record every comparison and restate the invariant after each emitted match.
  2. For m=8m=8 and n=1,048,576n=1,048,576, compare the upper bounds for merge, independent binary searches, and forward galloping. State what each bound omits about memory access.
  3. Derive the minimizer of n/s+csn/s+cs for a skip check that costs cc ordinary advances. What happens to the spacing as skip checks become more expensive?
  4. Construct two pairs of lists with the same lengths: one whose empty intersection has a short comparison certificate, and one whose answer requires comparisons throughout the range.
  5. Design a dispatcher using a measured imbalance threshold. State the reference oracle and the observations a differential test must preserve.

References

  1. Erik D. Demaine, Alejandro López-Ortiz, and J. Ian Munro. “Adaptive Set Intersections, Unions, and Differences.” Proceedings of the Eleventh Annual ACM–SIAM Symposium on Discrete Algorithms, 2000.
  2. J. Shane Culpepper and Alistair Moffat. “Efficient Set Intersection for Inverted Indexing.” ACM Transactions on Information Systems 29, no. 1, 2010.
  3. F. K. Hwang and Shen Lin. “A Simple Algorithm for Merging Two Disjoint Linearly Ordered Sets.” SIAM Journal on Computing 1, no. 1, 1972.
  4. Christopher D. Manning, Prabhakar Raghavan, and Hinrich Schütze. “Faster Postings List Intersection via Skip Pointers.” In Introduction to Information Retrieval. Cambridge University Press, 2008.