skip to content

Why is exponential search O(log i) in the target's position rather than O(log n)?

level: middleimportance: should knowfreq 36%

answer

  1. name the position of the answer first
  2. how many doublings to pass position i
  3. the bracket is no wider than i
  4. two logarithms added together
  5. cost stated in the answer, not the input

basics

~20 s

Both phases are bounded by where the answer sits, not by how much data exists: doubling stops at the first power of two past position i, and the binary search then covers an interval no wider than i.

solid answer

~50 s

Let `i` be the position of the first entry at-or-past the target. The doubling phase halts at the smallest power of two that is `>= i`, so it makes about `log2(i)` probes — it never looks beyond roughly `2i`, no matter how much data lies further on. The bracket it produces, `[i_prev, bound]`, has length at most `i`, so the binary search inside costs another `log2(i)` probes. Total is `2 log2(i) + O(1)`, i.e. `O(log i)`. That is an **output-sensitive** bound: cost is stated in where the answer is, not in the collection size `n`, which never enters the analysis. The practical consequence is that a hit near the head of an enormous sorted log is nearly free, while a hit near the far end costs about twice a plain binary search over a known size.

go deeper

for a junior

Know that the work depends on how far in the answer is, not on how much data there is, and that the total is roughly two logarithms — one for the doubling, one for the search.

for a middle

Be able to derive it: doubling halts at the first power of two past position i, giving log i probes, and the surviving bracket is no wider than i, giving log i more.

for a senior

Show you can pick the right lens. Argue from where queries actually land — head-clustered reads make the position-based bound the honest one; uniformly spread reads make the factor-of-two penalty real.

for a principal

Frame it as a cost model choice for a read path: an output-sensitive bound is a promise you can defend to a latency budget only if you also know the distribution of query positions.

## Naming the quantity Write `i` for the position of the answer — the index of the first entry whose key is at-or-past the target `T`. Write `n` for the total number of entries, which by assumption we cannot cheaply learn. The claim is that exponential search costs `O(log i)` probes, and that `n` appears nowhere. ## Counting the doubling phase The probes are at indexes 1, 2, 4, 8, ..., 2^k. The loop continues while the probed entry exists and its key is below `T`, so it stops at the first `k` with `2^k >= i`. That is `k = ceil(log2 i)`. So the doubling phase makes about `log2(i)` probes and never reads an index beyond `2^k < 2i`. The temptation is to say "doubling is exponential, so it must be expensive". The opposite is true: the *step size* grows exponentially, which is precisely why the *number of steps* is logarithmic. Compare with fixed-step probing (every 1000th index): reaching position `i` then takes `i/1000` probes — still linear in `i`, just with a nicer constant. ## Counting the binary search The surviving bracket is `[2^(k-1), 2^k]` (with `[0, 1]` in the degenerate first-probe case). Its width is `2^(k-1) <= i`, so a binary search over it takes at most `log2(i)` more probes. Add the two phases: `log2(i) + log2(i) = 2 log2(i)` probes, so `O(log i)`. ## Why this is not `O(log n)` `n` never appears because the algorithm never touches an index near `n` unless the answer is near `n`. Some cost lenses and what they say here: | Situation | Plain binary search over known size | Exponential search | |---|---|---| | Answer near the head (`i` tiny) | `log2 n` probes | about `2 log2 i` — a handful | | Answer in the middle | `log2 n` | about `2 log2 n - 2` | | Answer near the end (`i` close to `n`) | `log2 n` | about `2 log2 n` | | Size unknown | not applicable | works unchanged | The headline: exponential search buys independence from `n` and pays for it with a factor of about 2 when the answer is far out. For a log tailing workload — "give me everything since timestamp T", where T is usually recent and the answer is therefore near the head of the unread region — this trade is overwhelmingly favourable. ## Output-sensitive complexity as a lens An output-sensitive bound expresses cost in terms of the answer rather than the input. It is the honest way to describe algorithms whose work scales with what they find. Here it is what makes the technique interesting: an early hit in a petabyte-scale sorted log costs almost nothing, and the algorithm cannot be made to pay for the petabytes it never looks at. Be precise about the direction of the claim, though. `O(log i)` is an upper bound on probes, and it says nothing about the cost of a single probe — if one probe pulls a cold page across a network, twenty probes may still be slow in wall-clock terms. It also promises nothing about `i` itself: an adversarial or simply unlucky query whose answer sits at the far end pays the full `2 log2 n`. ## Where else the same doubling appears The same "jump ahead in doubling steps, then binary search the overshoot" idea shows up inside merge routines. When merging two sorted runs and one run keeps winning comparisons, comparing one element at a time wastes a comparison per element; galloping ahead in doubling steps finds the crossover point in logarithmic probes and copies the skipped stretch in one move. Several mainstream ecosystems ship merge-based library sorts built on exactly this — the list sorts in Python and in Java both gallop when a run pulls ahead — which is a good illustration that the technique is a general "find the boundary cheaply when it is probably near" tool, not a niche trick for unbounded arrays. ## The comparison to state in an interview If the size is known and cheap and queries land anywhere, use plain binary search: `log n` beats `2 log i` on average. If the size is unknown, expensive, or stale, or if answers cluster near the front, exponential search is the better bound and the simpler dependency graph.

  • When does plain binary search over a known size beat exponential search?
    When the size is cheap to obtain and queries land anywhere in the range. Plain binary search costs about `log2 n` probes; exponential search costs about `2 log2 i`, which approaches `2 log2 n` for answers near the end. The crossover is roughly `i > sqrt(n)`: below that the doubling version wins, above it the plain one does.
  • Why doesn't the doubling phase's exponential step size make the algorithm expensive?
    Because the step size and the step count are inverses here. Steps that double cover position `i` in `log2 i` probes; steps of fixed width `w` need `i / w` probes. Growth in the step size is exactly what collapses the number of steps to a logarithm.
  • Where else does this doubling-then-binary-search shape appear?
    Inside merge routines. When one sorted run keeps winning against the other, comparing one element at a time costs a comparison per element; galloping ahead in doubling steps locates the crossover in logarithmic probes and lets the whole skipped stretch move at once. Same two-phase shape, different setting.

saying these in an interview costs you the question

  • Says the cost is O(log n) because it ends in a binary search
  • Calls the doubling phase exponential time
  • Claims the bound holds regardless of where the target sits
  • Forgets the binary search phase when counting probes
  • Treats O(log i) as a promise that any single probe is fast

context