МатВектор

Command Palette

Search for a command to run...

🔀 Дискретка30 минСложность 3/5+65 XP

Сложность алгоритмов: O-нотация и классы скорости роста

Почему программа на маленьких данных летает, а на больших умирает: определение O-большого, таблица классов роста, подсчёт операций и честные примеры.

4 интерактива3 квизаУрок 17 из 20Обновлено 01.06.2025Обычный

Программа, которая мгновенно обрабатывает сто строк, на миллионе виснет намертво. Погони за железом здесь обычно нет: умирает не машина, а алгоритм, который делает слишком много работы. Умение заранее оценить, сколько шагов сделает алгоритм на входе размера , отделяет инженера от человека, копирующего код со Stack Overflow. Оценка эта записывается специальным языком — O-нотацией, и после этого урока вы будете читать записи вроде как открытую книгу.

Время работы и размер входа#

Секунды — плохая мера работы алгоритма: они зависят от процессора, языка и настроения компилятора. Вместо этого считают базовые операции — сравнения, присваивания, обращения к памяти — и смотрят, как их число растёт вместе с размером входа : длиной массива, числом вершин графа, количеством строк. Обычно интересуются худшим случаем: гарантия «не дольше, чем столько-то» полезнее средних обещаний, когда на кону отзывчивость системы. Функцию — число операций в худшем случае — и описывает O-нотация.

O-большое: сверху T(n) зажата константой C, умноженной на g(n), начиная с некоторого n₀

Читаем определение по-человечески: растёт не быстрее, чем , если константный множитель и участок малых не считать. Запись верна: при сумма не превышает , константа найдена. Рядом живут два соседа: — оценка снизу («растёт не медленнее»), — точная зажимающая оценка с обеих сторон. На практике чаще всего пишут , имея в виду разумную верхнюю границу порядка роста.

Правила работы с O-нотацией#

Четыре правила снимают почти все вопросы упрощения. Первое — константы не важны: , потому что множитель зажат в из определения. Второе — младшие слагаемые отбрасываются: при больших член высшего порядка съедает все остальные, что и показал график выше. Третье — сумма порядков равна максимуму: , медленная часть прячется за быстрой. Четвёртое — основание логарифма не важно: , а изменение основания — тоже константный множитель, поэтому пишут просто . Произведения же остаются как есть: — вложенные циклы умножают работы.

Класс$n = 10$$n = 100$$n = 1000$Типичный источник
111доступ к элементу массива по индексу
≈3,3≈6,6≈10бинарный поиск
101001000один проход по массиву
≈33≈664≈10 000сортировка слиянием
10010 00010⁶вложенные циклы
1024≈1,3·10³⁰—перебор всех подмножеств
3 628 800≈9,3·10¹⁵⁷—перебор маршрутов коммивояжёра в лоб
Классы роста: посмотрите на колонку n = 1000 и поймёте, почему сортировки не бывают квадратичными в больших системах

Таблица стоит любого десятка определений. При квадратичный алгоритм делает миллион операций — компьютеру смешно. При это уже операций: на машине со операций в секунду — семнадцать минут против долей секунды у линейного прохода и около операций у . Рост порядка — это не «чуть медленнее», это смена категории: от мгновения к минутам, от минут к годам.

Оговорка о честности: сложность бывает не только худшей. Быстрая сортировка в среднем делает операций, но на неудачных опорных элементах деградирует до — поэтому в библиотеках её страхуют переключением на другой алгоритм или случайным выбором опоры. Когда в задаче не сказано, о каком случае речь, спрашивайте сами: худший даёт гарантию, средний — ожидание, и это разные обещания.

Учимся считать: разбор типовых алгоритмов#

Оценка сложности — это подсчёт операций, а не гадание. Бинарный поиск в отсортированном массиве: на каждом шаге половина выбрасывается, размер задачи идёт — вопрос «за сколько шагов останется один элемент?» равен вопросу «сколько раз делится на 2», ответ . Для миллиона элементов — около шагов: . Логарифмическая сложность — фирменный знак «делим задачу пополам»; тот же счёт ведёт к глубине сбалансированных деревьев в структурах данных.

Вложенные циклы умножают. Внешний цикл по от 1 до , внутренний — по от 1 до : на -м витке внутренний делает операций, всего . Половинка — константа, а сумма старших членов — снова : ответ . Сортировка слиянием делит массив пополам и сливает: рекуррента разворачивается в уровней по операций, итого — развёртка показана в уроке о рекуррентных соотношениях. А перебор всех маршрутов коммивояжёра делает вариантов — факториал, против которого бессильна любая техника; прогресс здесь возможен только сменой постановки задачи.

  1. Внешний цикл выполняется раз; на витке с номером внутренний цикл делает операций.
  2. Суммируем: .
  3. Арифметическая прогрессия: .
  4. Упрощаем по правилам O-нотации: — константа, слагаемое — младший порядок. Остаётся .
  5. Ответ: . Контрольная проверка: при получаем операций — между и , ближе к квадрату.

P, NP и честная граница мечты#

Классы сложности делят задачи не по алгоритмам, а по самим задачам. P — задачи, решаемые за полиномиальное время, вроде сортировки () или кратчайших путей у Дейкстры из соответствующего урока. NP — задачи, чей ответ можно быстро проверить: показать готовый маршрут коммивояжёра длиной меньше K — минутное дело, а найти такой — неизвестно как. Вопрос «совпадают ли P и NP» — открытая проблема с миллионным призом; практический итог знают все: для NP-трудных задач заменяют точные алгоритмы приближенными и эвристиками.

Куда двигаться дальше? Сложность неразрывна с рекуррентами: любое время рекурсии — рекуррентное соотношение, и уметь его разворачивать — половина дела. Другая половина — практика: считайте операции в собственном коде, найдите двойной цикл и спросите себя, не обойтись одним проходом. Заодно посмотрите потоки в сетях — там алгоритм Форда-Фалкерсона оценивается числом итераций, и это живой пример того, как анализ сложности выбирает между вариантами реализации.

Финальная проверка наблюдательности: в таблице выше для записан как — посчитано через формулу Стирлинга, а само число шагов алгоритма коммивояжёра, , для уже превысит сорок миллиардов. Числа такого масштаба — лучшая мотивация выучить факториал не как значок калькулятора, а как реальную категорию медлительности. Ну а считать перестановки и сочетания быстро — отдельный навык, его качает тренажёр по комбинаторике.

Проверь себя+15 XP

Упростите: 3n² + 100n + 7. Чему это равно в O-нотации?

Проверь себя+15 XP

Сколько сравнений в худшем случае сделает бинарный поиск по отсортированному массиву из 1 000 000 элементов?

Проверь себя+15 XP

Что растёт быстрее при неограниченном росте n?

Частые вопросы

Чем O-большое отличается от тета Θ?

O — верхняя граница: 3n² + n = O(n²) и одновременно O(n³). Θ — точная оценка порядка с двух сторон: 3n² + n = Θ(n²). В разговорной практике O часто используют там, где строго было бы Θ, — это общепринятая вольность, но на экзамене различие могут проверять.

Почему константы вообще можно отбрасывать?

Потому что O-нотация описывает масштабирование, а не абсолютное время. Константа не зависит от n, и при сравнении алгоритмов разных порядков рано или поздно старший порядок побеждает. Внутри одного порядка константы, наоборот, важны — но это уже уровень оптимизации кода, а не выбора алгоритма.

Что такое сложность по памяти и зачем она отдельно?

Это объём дополнительной памяти как функция от n, измеренный той же O-нотацией. Сортировка слиянием тратит O(n) на буфер, быстрая сортировка — O(log n) на стек рекурсии, а хеш-таблица меняет время поиска на O(1) ценой памяти. Время и память часто обмениваются друг на друга, и обе оценки смотрят вместе.

Как оценивать сложность своего кода на практике?

Идите по циклам: подряд — складывайте, вложенные — умножайте. Каждое деление задачи пополам приносит логарифм, каждый вызов рекурсии — рекурренту. Найдите самый внутренний участок и спросите, сколько раз он выполняется; это и есть доминанта. Достаточно уметь оценивать порядок — точный подсчёт не нужен.

Готовитесь к контрольной?

Чеклист тем по «Дискретка»: что вы уже умеете, что повторить и в каком порядке.

Открыть чеклист предмета →

Проверьте себя в бою

Босс-экзамен по «Дискретка»: квизы всех уроков плюс бесконечный поток сгенерированных задач. Каждая попытка — новый расклад.

Начать босс-экзамен →