Перейти к основному содержимому

Четыре простых правила

Чтобы превратить «сколько шагов делает этот код» в класс Big O, нужны четыре правила:

1. Оставляем худший случай — считаем наибольшее число шагов, которое код может потребовать.
2. Отбрасываем константы — O(2n) и O(n/2) — это оба O(n).
3. Отбрасываем младшие слагаемые — O(n + n²) — это O(n²).
4. Разным входным данным — разные буквы — два разных массива дают O(a · b), а не O(n²).

Для каждого правила ниже есть один пример.


Правило 1: оставляем худший случай

Линейному поиску может повезти: если значение стоит в начале, достаточно одного шага. Big O не учитывает везение и описывает худший случай для массива — значение стоит последним или его нет вовсе, и проверяются все n элементов.

Поэтому линейный поиск — это O(n), хотя часто он заканчивается раньше. Худший случай — то обещание, которое алгоритм может выполнить всегда.

Лучший и худший случай поиска

Поиск первого слова занимает 1 шаг. Поиск отсутствующего слова занимает все 5. Big O оставляет 5: линейный поиск — это O(n).

Prompt
Напиши PHP-скрипт с функцией поиска, которая ищет слово в массиве one, two, three, four, five, считая шаги цикла и возвращая их количество. Вызови её один раз для первого слова (лучший случай) и один раз для отсутствующего слова (худший случай) и выведи оба количества шагов с подписями.
Вывод: PHP-скрипт
Best case and worst case of a search

Правило 2: отбрасываем константы

Цикл, который посещает каждый второй элемент, делает n/2 шагов. Работы вдвое меньше — но форма роста не меняется: удвоив вход, удваиваем и число шагов.

Big O оставляет только форму, поэтому O(n/2), O(n) и O(2n) — это один и тот же класс: линейный.

Вдвое меньше шагов — всё равно O(n)

Цикл шагает через 2 и посещает 4 из 8 элементов. Если массив станет вдвое больше, число шагов тоже удвоится — значит n/2 шагов — это по-прежнему линейный рост, O(n).

Prompt
Напиши PHP-скрипт, который проходит по массиву 3, 1, 4, 1, 5, 9, 2, 6, посещая каждый второй элемент (индекс увеличивается на 2), выводит посещённые значения и считает шаги. В конце выведи количество элементов и количество шагов.
Вывод: PHP-скрипт
Half the steps is still O(n)

Правило 3: отбрасываем младшие слагаемые

Код часто смешивает части с разной стоимостью: простой цикл (n шагов), за которым идут вложенные циклы (n² шагов). Всего получается n + n².

С ростом n часть n² поглощает остальное — при n = 1000 это миллион шагов против тысячи. Big O оставляет только самое быстро растущее слагаемое: O(n + n²) превращается в O(n²).

n + n² шагов

Для 6 элементов простой цикл добавляет 6 шагов, а вложенные циклы — 36; всего 42. Квадратичное слагаемое уже доминирует, и его перевес только растёт вместе с n.

Prompt
Напиши PHP-скрипт для массива из 6 чисел, который сначала выполняет один простой цикл, считая n шагов, а затем два вложенных цикла, считая n умножить на n шагов, — всё в один общий счётчик. Выведи n и общее количество шагов, чтобы я увидел n + n*n.
Вывод: PHP-скрипт
n + n² steps

Правило 4: разные входные данные — разные буквы

Когда вложенные циклы проходят по двум разным входным данным, число шагов равно a · b — произведению двух длин. Называть это O(n²) было бы неверно: размеры входных данных могут сильно различаться.

Честный класс — O(a · b). Он становится O(n²) только в особом случае, когда оба входа — это один и тот же набор данных.

Два разных массива

3 цвета, умноженные на 2 животных, дают 6 шагов. Сложность — O(a · b): если массив животных растёт, а массив цветов нет, важно только b.

Prompt
Напиши PHP-скрипт с массивом из 3 цветов и массивом из 2 животных. Используй вложенные циклы по двум разным массивам, считая шаги. Выведи две длины и общее количество шагов, чтобы показать форму «a умножить на b».
Вывод: PHP-скрипт
Two different arrays

Ограничения Big O

Big O намеренно является грубым инструментом, и стоит знать, что он скрывает.

Он видит только худший случай. Алгоритм с пугающим худшим случаем может быть отличным на практике, если этот случай редок. Иногда важен средний случай.

Он игнорирует константы. На бумаге 1000 · n и n — это один класс. Для небольших входных данных «медленный» алгоритм O(n²) с крошечными шагами может обогнать «быстрый» O(n) с тяжёлыми шагами.

Используйте Big O, чтобы сравнивать, как алгоритмы масштабируются. Для настоящей скорости на настоящих данных — измеряйте.