2.2.3.6 — первый алгоритм этого раздела, выходящий за пределы чистой модели сравнения2.2.3.6 — the first algorithm in this section stepping outside the pure comparison model

Интерполяционный ПоискInterpolation Search

Бинарный поиск всегда берёт mid ровно посередине диапазона по индексу, игнорируя значения. Но если открыть телефонную книгу в поисках фамилии на "Я", разумно открыть её ближе к концу, а не ровно посередине — потому что известно приблизительное распределение значений. Interpolation search формализует эту интуицию: позиция следующей проверки оценивается линейной интерполяцией по значениям на границах текущего диапазона, а не просто как середина индексов.Binary search always picks mid exactly at the range's index midpoint, ignoring values. But when opening a phone book looking for a surname starting with "Z," it's sensible to open it closer to the end, not exactly in the middle — because the approximate distribution of values is known. Interpolation search formalizes this intuition: the position of the next check is estimated via linear interpolation of the values at the current range's boundaries, not simply as the index midpoint.

avg O(log log n) worst O(n) требует равномерное распределениеneeds a uniform distribution

Идея и инвариантIdea & invariant

Инвариант диапазона [lo, hi] тот же, что и в бинарном поиске (2.2.3.1) — ответ, если он есть, лежит внутри. Отличие в вычислении следующей точки проверки: вместо pos = lo + (hi-lo)/2 берётся pos = lo + (hi-lo) · (x - a[lo]) / (a[hi] - a[lo]) — линейная интерполяция, предполагающая, что значения между a[lo] и a[hi] распределены примерно равномерно, и оценивающая, на какой относительной доле пути от lo до hi должно находиться значение x. Если предположение верно, pos оказывается очень близко к истинной позиции x с первой попытки — в этом источник ускорения до O(log log n) в среднем на равномерных данных.The [lo, hi] range invariant is the same as in binary search (2.2.3.1) — the answer, if present, lies inside. The difference is in computing the next check point: instead of pos = lo + (hi-lo)/2, it's pos = lo + (hi-lo) · (x - a[lo]) / (a[hi] - a[lo]) — a linear interpolation assuming the values between a[lo] and a[hi] are roughly uniformly distributed, estimating what relative fraction of the way from lo to hi x's value should be at. If the assumption holds, pos ends up very close to x's true position on the first try — this is the source of the speedup to O(log log n) average on uniform data.

массив [10, 20, ..., 990, 1000] (равномерный шаг), x = 850 a[lo]=10 a[hi]=1000 середина индекса (бинарный поиск) интерполяция: (850-10)/(1000-10) ≈ 0.85 пути → почти сразу попадание бинарный поиск проверил бы середину (≈505), затем ещё несколько раз сузил бы диапазон

на равномерных данных интерполяция "прицеливается" в область ответа сразу, а не идёт туда постепенноon uniform data, interpolation "aims" at the answer's region immediately instead of walking there gradually

int interpolation_search(const std::vector<int>& a, int x) {
    int lo = 0, hi = (int)a.size() - 1;
    while (lo <= hi && x >= a[lo] && x <= a[hi]) {
        if (a[hi] == a[lo]) {                     // защита от деления на ноль при повторах// guard against division by zero on duplicates
            return (a[lo] == x) ? lo : -1;
        }
        int pos = lo + (int)((long long)(hi - lo) * (x - a[lo]) / (a[hi] - a[lo]));
        if (a[pos] == x) return pos;
        else if (a[pos] < x) lo = pos + 1;
        else hi = pos - 1;
    }
    return -1;
}

Демонстрация вживуюLive demonstration

Переключатель распределения показывает контраст: на равномерном массиве интерполяция почти всегда быстрее бинарного поиска (счётчики сравниваются справа), на неравномерном (перекошенном) — вырождается, иногда до почти линейного числа шагов.The distribution switch shows the contrast: on a uniform array, interpolation is almost always faster than binary search (counters compared on the right); on a non-uniform (skewed) one, it degrades, sometimes to a near-linear number of steps.

шагов:steps: 0

нажмите «Вперёд» или «Играть», чтобы начатьpress "Forward" or "Play" to begin

Почему O(log log n) в среднем и O(n) в худшем — не противоречиеWhy O(log log n) average and O(n) worst case aren't a contradiction

Доказательство O(log log n) (Перл, 1957) опирается на предположение, что значения массива — независимые случайные величины из равномерного распределения; при этом условии интерполяция статистически "почти всегда" оценивает позицию с точностью до O(√(текущий размер диапазона)), что даёт двойной логарифм после суммирования по всем шагам. Если распределение не равномерно (степенной закон, много повторов, кластеризация), формула интерполяции может каждый раз ошибаться в одну и ту же сторону на предсказуемую величину — например, для геометрически растущей последовательности [1, 2, 4, 8, ..., 2^n] интерполяционный поиск вырождается до Θ(n), потому что вычисленная позиция почти всегда близка к hi, независимо от истинного положения x.The O(log log n) proof (Perl, 1957) relies on assuming the array's values are independent random variables from a uniform distribution; under that assumption, interpolation "almost always" estimates position to within O(√(current range size)), which yields a double logarithm after summing over all steps. If the distribution isn't uniform (a power law, many duplicates, clustering), the interpolation formula can be wrong in the same direction every time by a predictable amount — for instance, for a geometrically growing sequence [1, 2, 4, 8, ..., 2^n], interpolation search degenerates to Θ(n), because the computed position is almost always close to hi, regardless of x's true position.

Это делает interpolation search рискованным выбором без предварительного знания о распределении данных — в отличие от бинарного поиска, чья гарантия O(log n) держится вообще без предположений о значениях, только о порядке.This makes interpolation search a risky choice without prior knowledge of the data's distribution — unlike binary search, whose O(log n) guarantee holds with no assumptions about values at all, only about order.

Где встречаетсяWhere it shows up

01

Поиск по числовым ключам с известным равномерным распределением (ID, временные метки)Searching numeric keys with a known uniform distribution (IDs, timestamps)

Таблицы с автоинкрементным ID или равномерно распределёнными по времени записями (логи, поступающие с постоянной частотой) — хороший кандидат для интерполяционного поиска, потому что распределение значений известно заранее и действительно близко к равномерному.Tables with auto-incrementing IDs or records uniformly distributed over time (logs arriving at a constant rate) are good interpolation-search candidates, because the value distribution is known in advance and genuinely close to uniform.

02

Учебный пример компромисса "среднее vs. худшее"A teaching example of the "average vs. worst case" trade-off

Interpolation search часто приводят как контрпример к интуиции "более умная эвристика всегда лучше" — она действительно быстрее в среднем на подходящих данных, но именно потому, что использует предположение, которое, если оно неверно, делает её хуже линейного поиска по константе.Interpolation search is often cited as a counterexample to the intuition "a smarter heuristic is always better" — it's genuinely faster on average on suitable data, but precisely because it relies on an assumption that, if wrong, makes it worse than linear search by a constant factor.