2.2.3.5 — между линейным поиском и бинарным, когда шаг назад дороже сравнения2.2.3.5 — between linear and binary search, when stepping back costs more than a comparison

Поиск ПрыжкамиJump Search

Бинарный поиск (2.2.3.1) минимизирует число сравнений, но постоянно "прыгает" по массиву в разных направлениях — на структуре, где движение вперёд дёшево, а произвольный переход дорог (лента, диск с последовательным чтением), это может быть не идеально. Jump search движется только вперёд блоками фиксированного размера √n, находит блок, где может быть ответ, и только внутри него ищет линейно.Binary search (2.2.3.1) minimizes comparison count, but keeps "jumping" around the array in different directions — on a structure where moving forward is cheap but an arbitrary jump is expensive (tape, disk with sequential reads), this may not be ideal. Jump search moves only forward, in fixed-size √n blocks, finds the block that could contain the answer, and only searches linearly within it.

O(√n) in-place только движение вперёдforward-only movement

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

Выбирается фиксированный размер блока step (оптимальное значение — √n, обоснование ниже). Указатель прыгает по массиву с этим шагом (0, step, 2·step, ...), пока не встретит блок, чей последний элемент ≥ x, или не дойдёт до конца массива. На этом этапе известно: если x есть в массиве, он лежит в диапазоне (предыдущая позиция прыжка, текущая позиция] — потому что все более ранние блоки целиком меньше x (иначе прыжок остановился бы раньше). Второй этап — обычный линейный поиск внутри этого диапазона размера не больше step.A fixed block size step is chosen (the optimal value is √n, justified below). A pointer jumps through the array with this stride (0, step, 2·step, ...) until it hits a block whose last element is ≥ x, or reaches the array's end. At this point it's known: if x is in the array, it lies within (previous jump position, current position] — because every earlier block was entirely less than x (otherwise the jump would have stopped sooner). The second stage is ordinary linear search within that range of size at most step.

n=16, step=4, x=11 block 0: [1,3,5,7] block 1: [8,9,10,12] block 2: [14,16,18,20] a[7]=12 ≥ 11 — останавливаемся на block 1, x должен быть здесь линейный поиск внутри [8,9,10,12] — x=11 отсутствует 3 прыжка + до 4 сравнений вместо 4 сравнений бинарного поиска — но каждый шаг только вперёд

O(√n) операций вместо O(log n) — хуже по числу сравнений, но лучше по паттерну доступа к памяти/дискуO(√n) operations instead of O(log n) — worse in comparison count, better in memory/disk access pattern

int jump_search(const std::vector<int>& a, int x) {
    int n = (int)a.size();
    if (n == 0) return -1;
    int step = std::max(1, (int)std::sqrt(n));

    int prev = 0, curr = step;
    while (curr < n && a[curr - 1] < x) {  // прыгаем, пока блок целиком меньше x// jump while the block is entirely less than x
        prev = curr;
        curr += step;
    }
    int hi = std::min(curr, n);
    for (int i = prev; i < hi; ++i)          // линейный поиск внутри найденного блока// linear search inside the found block
        if (a[i] == x) return i;
    return -1;
}

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

Столбики сгруппированы в блоки размера step = √n (жёлтая рамка обозначает границы блоков). Оранжевая подсветка — текущий проверяемый последний элемент блока во время прыжков; зелёная — линейный поиск внутри найденного блока.Bars are grouped into blocks of size step = √n (a yellow outline marks block boundaries). Orange highlights the block's last element being checked during the jump phase; green marks linear search inside the found block.

step: -   сравнений:comparisons: 0

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

Почему оптимальный шаг — именно √nWhy the optimal step is exactly √n

Число прыжков в худшем случае — n/step (нужно пройти весь массив блоками), число сравнений линейного поиска в найденном блоке — до step. Суммарная стоимость f(step) = n/step + step. Чтобы её минимизировать, берём производную по step и приравниваем к нулю: f'(step) = -n/step² + 1 = 0 → step² = n → step = √n. При этом выборе f(√n) = √n + √n = 2√n — то есть общее число операций в худшем случае Θ(√n), что и является доказанной, а не эмпирически подобранной, оптимальной точкой компромисса между "много мелких прыжков" и "один большой линейный поиск".The worst-case number of jumps is n/step (the whole array must be covered in blocks), and linear-search comparisons inside the found block go up to step. The total cost is f(step) = n/step + step. To minimize it, take the derivative with respect to step and set it to zero: f'(step) = -n/step² + 1 = 0 → step² = n → step = √n. With this choice, f(√n) = √n + √n = 2√n — so the total worst-case operation count is Θ(√n), a proven (not empirically tuned) optimal trade-off point between "many small jumps" and "one large linear search."

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

01

Поиск на магнитной ленте и других устройствах с последовательным доступомSearching magnetic tape and other sequential-access media

На ленте перемотка назад значительно дороже перемотки вперёд — бинарный поиск с его постоянными переходами туда-обратно неэффективен, а jump search, двигаясь только вперёд, соответствует физическим ограничениям устройства.On tape, rewinding is significantly more expensive than winding forward — binary search's constant back-and-forth jumps are inefficient, while jump search, moving only forward, matches the device's physical constraints.

02

Индексы с блочной структурой хранения (B-tree листья, страницы БД)Block-structured storage indexes (B-tree leaves, DB pages)

Когда данные физически хранятся блоками фиксированного размера (страницы диска, блоки индекса), пропуск целых блоков без их чтения (по значению на границе блока) — идея, родственная jump search, экономящая операции ввода-вывода, а не только сравнения.When data is physically stored in fixed-size blocks (disk pages, index blocks), skipping whole blocks without reading them (via the value at the block boundary) is an idea related to jump search, saving I/O operations rather than just comparisons.