1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6

# теория · шаг 2 из 6

Два разных «в среднем»: по входам и по монетке

Коротко

Слово «в среднем» скрывает два разных обещания. Средний случай - ожидание по РАСПРЕДЕЛЕНИЮ входов: держится, только если входы и правда такие, как предположили, и злонамеренный вход может каждый раз бить в худший случай. Рандомизированное среднее - ожидание по МОНЕТКЕ самого алгоритма: держится на ЛЮБОМ входе, плохого входа не существует, бывает лишь неудачный бросок. Одно и то же «n·log n в среднем» - две разные гарантии.

Если понимаешь, почему рандомизированный quicksort быстр на любом входе, а обычный - нет, - листай до «Теперь сам».

Как думаешь, где подвох

Быстрая сортировка (quicksort) устроена так: выбирают один элемент - опорный, раскладывают остальные на «меньше опорного» и «больше», а потом так же сортируют каждую из двух частей. Если опорный делит список примерно пополам, получается log n уровней по n работы - Θ(n·log n) (это как деление пополам из главы про суммы). Разбор точного среднего - в разделе про сортировки; здесь нам хватит этой картинки.

Обычный quicksort берёт опорным первый элемент. На случайном входе он в среднем Θ(n·log n). Но подай ему уже отсортированный список - и он выродится в Θ(n²): первый элемент отсортированного списка - наименьший, значит в часть «меньше» не попадает никто, а в «больше» - все n-1; деления пополам нет, уровней n, на каждом работа до n. А ведь отсортированный вход - не экзотика, он встречается сплошь и рядом. Как же тогда им пользуются? Ответ: опорный выбирают СЛУЧАЙНО - и n² исчезает для всех входов сразу. Почему одна случайная строчка меняет гарантию - об этом урок.

На что это похоже

Первое «в среднем» - ставка на обычную погоду: в среднем по году тепло, но конкретный день может оказаться бураном, и если ты каждый день выходишь в одном и том же месте, буран тебя найдёт. Второе - ты сам тасуешь колоду перед раздачей: какую бы колоду тебе ни подсунули, твоя карта случайна, и подстроить её нельзя. Случайность либо снаружи (во входах, и тогда её кто-то контролирует), либо внутри алгоритма (в его монетке, и тогда её не контролирует никто).

Механизм: над чем берут ожидание

Средний случай (по входам). Алгоритм фиксирован, детерминирован. Берём ожидание времени по входам, взятым из некоторого РАСПРЕДЕЛЕНИЯ - обычно предполагают, что все входы размера n равновероятны. Вывод зависит от этого предположения: если в жизни входы не такие (часто уже почти отсортированы, например), «средний» не выполняется. Хуже того: противник, знающий твой алгоритм, подберёт худший вход и будет подавать его каждый раз - никакой случайности во входе нет, она вся в твоём (неверном) предположении.

Рандомизированное среднее (по монетке). Алгоритм сам делает случайный выбор - например, берёт опорный элемент случайно. Ожидание берут по ЭТИМ случайным выборам, для ОДНОГО фиксированного входа. Плохого входа больше не существует: поведение зависит не только от входа, но и от монетки, а её противник не видит и не контролирует. Худший случай Θ(n²) формально остаётся, но случается лишь при редкой череде неудачных бросков, и ни один вход его не вызывает - только невезение.

Правило

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

Разбор: отсортированный вход и две сортировки

Дай обеим сортировкам уже отсортированный список. Детерминированный quicksort с опорным-первым проваливается в Θ(n²): на нём его «средний по входам» не работает, потому что этот вход - как раз худший, а он вовсе не «в среднем». Рандомизированный quicksort на том же входе - Θ(n·log n) ожидаемо: он выбирает опорный случайно, и отсортированность входа ему безразлична. Один и тот же вход: у первого он вскрывает худший случай, у второго - рядовой.

Разбор: у рандомизированного тоже есть худший случай - но его не подстроить

Рандомизированный алгоритм не отменяет Θ(n²) - он делает его недостижимым по заказу. Чтобы получить n², монетка должна раз за разом выбирать почти крайний опорный, а вероятность такого исчезающе мала и не зависит от того, какой список подали. Разница с детерминированным - не в том, что худшего нет, а в том, что к нему нельзя ПРИНУДИТЬ входом: нет строки данных, которая гарантированно роняет алгоритм. Для защиты от злонамеренных входов это и есть всё.

Насколько мала вероятность? Чтобы рандомизированный quicksort выродился в n², монетка должна раз за разом выбирать почти крайний опорный - вероятность такого исчезающе мала и быстро падает с ростом n: на списке в тысячу элементов словить квадрат практически невозможно. И, главное, эта вероятность ОДИНАКОВА для любого входа: ни отсортированный список, ни собранный врагом не повышают её ни на столько. Врагу нечего подать - монетка не в его руках.

Что ломается без этого

Главная ошибка - положиться на «средний случай O(n·log n)», когда входы не случайны. В проде данные часто уже отсортированы, сгруппированы или присланы тем, кто хочет тебя уронить, - и детерминированный алгоритм со «средним по входам» получает свой худший случай стабильно, а не изредка. Это называется атакой на сложность: злоумышленник шлёт ровно те данные, что вырождают твой алгоритм. Лечит её именно перенос случайности ВНУТРЬ алгоритма.

Зачем это в работе

Поэтому стандартные библиотеки сортируют рандомизированным quicksort (или его защищённым вариантом). То же и с хеш-таблицами - структурами, которые ищут за O(1), раскладывая ключи по ячейкам по значению хеш-функции (подробно в разделе про хеширование): к хеш-функции подмешивают случайное зерно, потому что без него противник подберёт ключи, которые все лягут в одну ячейку, и O(1) выродится в O(n) - тот же приём «случайность внутрь алгоритма». Выбор «средний случай или рандомизация» - это выбор, кто контролирует случайность: если входы могут быть враждебными, её уводят внутрь алгоритма, где до неё не дотянуться.

Теперь сам

Какое из двух «в среднем» требует предположения о том, как распределены входы, а какое защищает от злонамеренно подобранного входа?

Ответ. Предположения о распределении входов требует СРЕДНИЙ СЛУЧАЙ: он берёт ожидание по входам, и если реальные входы не такие (или подобраны врагом), оценка не держится. От злонамеренного входа защищает РАНДОМИЗИРОВАННОЕ среднее: случайность внутри алгоритма, поведение не определяется одним лишь входом, поэтому подобрать гарантированно плохой вход нельзя - плохого входа не существует.

Главное

«В среднем» бывает двух видов. Средний случай - ожидание по распределению входов: держится при предположении о входах и имеет плохие входы, которые противник может подать. Рандомизированное среднее - ожидание по монетке алгоритма: держится на любом входе, плохого входа нет, есть лишь редкое невезение. Отсортированный вход роняет детерминированный quicksort в Θ(n²), но не рандомизированный. Если входы бывают враждебными, случайность уводят внутрь алгоритма.

Комментарии

Пока нет комментариев. Будь первым!

Оставить комментарий

Комментарий появится после проверки. Email не публикуется. Войти, чтобы не вводить имя каждый раз.