2.2.4.4 — последняя статья раздела: не про произвольный k, а про точный подсчёт сравнений для двух конкретных значений2.2.4.4 — the section's last article: not about an arbitrary k, but an exact comparison count for two specific values

Min и Max за 1.5n СравненийMin & Max in 1.5n Comparisons

Наивный подход — найти минимум за n-1 сравнений, затем максимум ещё за n-1 — даёт 2n-2 сравнений и не использует общую информацию между двумя поисками. Обрабатывая элементы попарно и сравнивая победителя пары с текущим максимумом, а проигравшего — с текущим минимумом, оба экстремума находятся всего за 3⌊n/2⌋ сравнений — примерно на четверть меньше, и это доказанный оптимум, а не эвристика.The naive approach — find the minimum in n-1 comparisons, then the maximum in another n-1 — gives 2n-2 comparisons and doesn't exploit shared information between the two searches. By processing elements in pairs and comparing the pair's winner against the current maximum and its loser against the current minimum, both extremes are found in just 3⌊n/2⌋ comparisons — about a quarter fewer, and this is a proven optimum, not a heuristic.

1.5n сравненийcomparisons in-place доказанный оптимумproven optimum

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

Элементы обрабатываются парами (a[i], a[i+1]). Внутри пары одно сравнение определяет, какой элемент "больше" (кандидат на максимум) и какой "меньше" (кандидат на минимум) — эта информация не пропадает зря, в отличие от наивного подхода, где каждый элемент сравнивается и с текущим минимумом, и с текущим максимумом по отдельности. "Победитель" пары сравнивается только с текущим глобальным максимумом (одно сравнение), "проигравший" — только с текущим глобальным минимумом (ещё одно сравнение). Итого на пару элементов уходит 3 сравнения вместо 4 (по 2 на элемент при наивном подходе) — экономия ровно потому, что внутрипарное сравнение выполняет двойную работу: оно же решает, кого дальше сравнивать с максимумом, а кого — с минимумом.Elements are processed in pairs (a[i], a[i+1]). Within a pair, one comparison determines which element is "larger" (a max candidate) and which is "smaller" (a min candidate) — this information isn't wasted, unlike the naive approach where every element is compared against both the current min and the current max separately. The pair's "winner" is compared only against the current global max (one comparison), the "loser" only against the current global min (another comparison). So each pair of elements costs 3 comparisons instead of 4 (2 per element in the naive approach) — the saving comes precisely from the within-pair comparison doing double duty: it's also what decides who gets compared against the max and who against the min.

пара (7, 3): сравнение 1 → 7 больше, 3 меньше 7 3 ← 1 сравнение внутри пары сравнение 2: 7 (победитель) vs. текущий максимум сравнение 3: 3 (проигравший) vs. текущий минимум 3 сравнения на пару (2 элемента) вместо 4 (по 2 на элемент при наивном подходе) → 1.5n вместо 2n

внутрипарное сравнение "бесплатно" сортирует пару на кандидата в максимум и кандидата в минимумthe within-pair comparison "freely" sorts the pair into a max candidate and a min candidate

std::pair<int,int> min_max(const std::vector<int>& a) {
    int n = (int)a.size();
    int mn, mx, i;
    if (n % 2 == 0) {
        if (a[0] < a[1]) { mn = a[0]; mx = a[1]; }  // сравнение №1 (инициализация)// comparison #1 (initialization)
        else { mn = a[1]; mx = a[0]; }
        i = 2;
    } else {
        mn = mx = a[0];    // нечётный n — первый элемент без сравнений// odd n — the first element costs no comparisons
        i = 1;
    }
    for (; i < n - 1; i += 2) {
        int smaller, larger;
        if (a[i] < a[i+1]) { smaller = a[i]; larger = a[i+1]; }  // сравнение №1 в паре// comparison #1 in the pair
        else { smaller = a[i+1]; larger = a[i]; }
        if (smaller < mn) mn = smaller;   // сравнение №2// comparison #2
        if (larger > mx) mx = larger;     // сравнение №3// comparison #3
    }
    return {mn, mx};
}

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

Столбики обрабатываются парами (жёлтая рамка). Оранжевый — текущий известный минимум, синий — текущий известный максимум. Счётчик сравнений сравнивается с наивным подходом (2n-2) справа.Bars are processed in pairs (yellow outline). Orange is the current known minimum, blue is the current known maximum. The comparison counter is shown against the naive approach's count (2n-2) on the right.

сравнений:comparisons: 0 / наивно:naive: 0

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

Почему 1.5n — это доказанный нижний предел, а не просто хороший результатWhy 1.5n is a proven lower bound, not just a good result

Точная формула — 3⌊n/2⌋ сравнений (плюс поправка на 1, если n нечётно), что действительно приближается к 1.5n при больших n. Доказательство оптимальности (нижней границы) использует "состязательный" аргумент: элемент, ни разу не проигравший сравнение, — кандидат на максимум, ни разу не выигравший — кандидат на минимум; можно показать, что любой алгоритм должен потратить минимум ⌈3n/2⌉ - 2 сравнений, чтобы однозначно определить оба множества-кандидата размером 1 (собственно min и max). Это означает, что представленный алгоритм не просто "хорошая эвристика", а асимптотически точное решение задачи с доказанной нижней границей, аналогично тому, как Ω(n log n) в 2.2.1 — не эмпирическое наблюдение, а теорема.The exact formula is 3⌊n/2⌋ comparisons (plus a correction of 1 if n is odd), which indeed approaches 1.5n for large n. The optimality (lower bound) proof uses an "adversary" argument: an element that has never lost a comparison is a max candidate, one that has never won is a min candidate; it can be shown that any algorithm must spend at least ⌈3n/2⌉ - 2 comparisons to uniquely narrow both candidate sets down to size 1 (the actual min and max). This means the algorithm presented here isn't just a "good heuristic" but an asymptotically exact solution with a proven lower bound, much like Ω(n log n) in 2.2.1 isn't an empirical observation but a theorem.

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

01

Нормализация данных (min-max scaling) в предобработке для машинного обученияData normalization (min-max scaling) in machine-learning preprocessing

Приведение признаков к диапазону [0, 1] требует одновременного знания min и max каждого столбца — на больших датасетах экономия в 25% сравнений при однократном проходе по данным не теоретическая мелочь, а заметный вклад в общее время предобработки.Scaling features to a [0, 1] range requires simultaneous knowledge of each column's min and max — on large datasets, a 25% comparison saving from a single pass over the data isn't a theoretical footnote but a noticeable contribution to overall preprocessing time.

02

Учебный пример "count comparisons exactly" в анализе алгоритмовA teaching example of "count comparisons exactly" in algorithm analysis

В отличие от большинства алгоритмов этого раздела, где интересна асимптотика (O(n), O(n log n)), здесь интересна точная константа перед n — классический пример того, что "оптимальность" в анализе алгоритмов может означать не только класс сложности, но и точное число элементарных операций.Unlike most algorithms in this section, where the asymptotics (O(n), O(n log n)) are the point of interest, here the exact constant in front of n matters — a classic example that "optimality" in algorithm analysis can mean not just the complexity class but the exact number of elementary operations.