In interpolation search, what breaks when the range's endpoint values a[lo] and a[hi] are equal?
answer
- Look hard at the denominator
- What if every value in the range matches
- The loop also runs with lo equal to hi
- Zero divisor before any probe happens
- Guard resolves the plateau without dividing
basics
~20 sThe probe formula divides by a[hi] - a[lo], so equal endpoint values make the denominator zero and the probe computation fault or produce nonsense. It happens on any plateau of duplicates and, in the textbook loop, on every single-element range too.
solid answer
~50 sThe probe is `lo + (key - a[lo]) * (hi - lo) / (a[hi] - a[lo])`, and that denominator is zero whenever the two endpoint values coincide. Two ordinary situations produce it. First, a run of duplicate values — a stretch of identical readings — can occupy the whole surviving range. Second, and more insidiously, `lo == hi`: the loop legitimately narrows to one element, both endpoints are the same element, and the difference is zero even when every value in the array is distinct. So the bug fires on clean data. The guard goes before the division: if `a[lo] == a[hi]`, then either the key equals that value, in which case return `lo`, or it is absent from this range — no interpolation needed. Falling back to a plain midpoint probe also works and keeps the loop uniform.
code
pseudocode · 12 lineslo = 0
hi = length(a) - 1
while lo <= hi and key >= a[lo] and key <= a[hi]:
// MISSING: if a[hi] == a[lo] handle without dividing
pos = lo + ((key - a[lo]) * (hi - lo)) / (a[hi] - a[lo])
if a[pos] == key:
return pos
if a[pos] < key:
lo = pos + 1
else:
hi = pos - 1
return NOT_FOUNDgo deeper
Look at what the formula divides by. If both endpoint values are the same number, that denominator is zero, and the probe cannot be computed at all — that is the answer in one sentence.
Name both triggers: a run of duplicate values covering the range, and the final iteration where lo equals hi even on distinct data. Then state a guard that resolves the range without dividing.
Treat it as a code-review reflex. Point at the denominator, the multiplication that can overflow, and the missing clamp in one pass, and note that a zero divisor may silently produce a garbage index rather than fault.
Frame it as why hand-rolled variants of a textbook search carry real risk: the fast path is memorable, the guards are not, and a rarely-executed edge in bespoke search code is expensive to discover in production.
## The fragment and the fault The canonical loop looks harmless: ``` while lo <= hi and key >= a[lo] and key <= a[hi]: pos = lo + ((key - a[lo]) * (hi - lo)) / (a[hi] - a[lo]) ... ``` The entry condition checks three things: the range is non-empty, and the key lies between the endpoint values. What it never checks is that the endpoint values *differ*. The formula divides by `a[hi] - a[lo]`, so when they are equal the division is by zero. ## Two ways to reach it, one of them on clean data **The plateau.** A run of identical values can swallow the whole surviving range. In a buffer of quantised readings where a sensor reported the same value for a long stretch, `a[lo]` and `a[hi]` can easily coincide while `hi - lo` is still large. The loop condition is satisfied — the key equals both endpoints — and the division blows up. **The single element.** This is the one people miss. The loop is written with `lo <= hi`, so it legitimately runs one final time with `lo == hi`. Then `a[lo]` and `a[hi]` are literally the same element, and the difference is zero *no matter how distinct the array's values are*. So an implementation of this fragment can fault on a perfectly uniform, duplicate-free array — precisely on the successful-lookup path, when the range narrows to the element holding the key. The bug is not exotic; it is on the happy path. A subtlety worth noticing: whether you get a crash, a silently wrong value, or something else depends entirely on how the surrounding arithmetic behaves on a zero divisor. Integer division by zero typically raises; floating-point division by zero typically yields an infinity or a not-a-number, which then converts to some arbitrary index and probes out of bounds or loops. "Sometimes it works" is the worst possible failure mode, and it is a realistic one here. ## The guards Two correct fixes, both placed *before* the division. **Resolve the range directly.** ``` if a[hi] == a[lo]: if key == a[lo]: return lo return NOT_FOUND ``` This is exact. If the endpoints are equal, every value between them is equal too (the range is sorted), so the range is a constant plateau: either the key is that constant — and any index in the range is a valid answer — or it is not present in the range at all. No probing required. **Fall back to a midpoint.** ``` if a[hi] == a[lo]: pos = lo + (hi - lo) / 2 else: pos = lo + ((key - a[lo]) * (hi - lo)) / (a[hi] - a[lo]) ``` This keeps the loop shape uniform and degrades gracefully into binary search's rule exactly where interpolation has nothing to say. It is slightly less direct on a plateau but composes well with a general hybrid that also caps consecutive bad estimates. ## The neighbouring boundary hazards While you are looking at this line, three more deserve a check. **Overflow.** `(key - a[lo]) * (hi - lo)` multiplies a value difference by an index difference. On a large array of large values that product can exceed a fixed-width integer's range, producing a negative or wrapped result and an out-of-bounds probe. Reorder, widen the accumulator, or compute the fraction in floating point before scaling. **Clamping.** Even without overflow, arithmetic truncation and rounding should not be trusted to keep `pos` inside `[lo, hi]`. A defensive clamp costs two comparisons and removes an entire class of out-of-bounds reads. **Progress.** Because the probe is not guaranteed to be strictly inside the range the way a midpoint is, an implementation must ensure each iteration actually moves `lo` or `hi`. Writing the updates as `lo = pos + 1` and `hi = pos - 1` — never `lo = pos` — is what makes termination safe. ## Why interviewers like this It separates people who have recited the formula from people who have run it. The formula is memorable and the guard is not, so the plateau question is a compact way to ask "did you ever implement this?" The strong answer names both triggers — the duplicate run and the single-element range — states the guard, and adds that a zero-denominator fault is a correctness bug, not the performance degradation that skewed data causes. Those are two independent failure modes of the same algorithm, and mixing them up is the giveaway.
- Does the equal-endpoints fault only occur on arrays containing duplicates?No, and that is the trap. The loop condition lo <= hi lets a final iteration run with lo == hi, where both endpoints index the same element and the difference is zero regardless of duplicates. A duplicate-free, perfectly uniform array reaches it on the ordinary successful-lookup path.
- How is this failure different from the O(n) degradation on skewed data?Entirely different kinds of defect. The zero denominator is a correctness and crash bug — the search faults or computes a garbage index before any comparison happens. Skew is a performance problem: every answer is still correct, it just takes far more probes. Fixing one does nothing for the other.
- What other arithmetic hazard hides in the same expression?The product of the value difference and the index difference can overflow a fixed-width integer on a large array of large keys, wrapping to a negative or absurd index. Compute the fraction in a wider or floating type before scaling, and clamp the result into the current range as a cheap final safeguard.
saying these in an interview costs you the question
- Says the fault needs duplicate values to occur
- Assumes the loop guard already prevents equal endpoints
- Confuses this crash with the skewed-data slowdown
- Clamps the probe after dividing, too late to help
- Never considers that lo can equal hi