# конспект · шаг 1 из 1
Конспект раздела: разделяй и властвуй
Понятия
Разделяй и властвуй - три шага: разделить вход на части, решить части тем же алгоритмом,
объединить ответы. Цена записывается рекуррентностью T(n) = a·T(n/b) + f(n).
Три режима этой формулы: лист тяжелее (ответ определяют рекурсивные вызовы), уровни равны
(добавляется множитель log n), корень тяжелее (всё решает объединение).
Полоса - зона вокруг разреза, где могут лежать пары, разрезанные границей.
Корень из единицы - число ω с ω^n = 1; их набор самоподобен: квадраты n корней дают n/2
корней меньшего порядка.
Свёртка - c[k] = Σ a[i]·b[k−i]; умножение многочленов - её частный случай.
Что унести
Весь раздел отвечает на один вопрос: сколько стоит объединение по сравнению с числом и
размером частей: f(n) сравнивают с n^(log_b a), и это сравнение решает, даст ли деление выигрыш.
T(n) = a·T(n/b) + f(n), сравниваем f(n) с n^(log_b a)
f(n) МЕНЬШЕ → Θ(n^(log_b a)) работу делают листья
f(n) РАВНО → Θ(n^(log_b a)·log n) все уровни весят одинаково
f(n) БОЛЬШЕ* → Θ(f(n)) работу делает верхний вызов
* полиномиально и регулярно: 2T(n/2) + n·log n - уже Θ(n·log² n)
| алгоритм | рекуррентность | ответ |
|---|---|---|
| сортировка слиянием | 2·T(n/2) + n |
Θ(n·log n) |
| двоичный поиск | T(n/2) + 1 |
Θ(log n) |
| умножение в лоб | 4·T(n/2) + n |
Θ(n²) - выигрыша нет |
| Карацуба | 3·T(n/2) + n |
Θ(n^1.585) |
| Штрассен | 7·T(n/2) + n² |
Θ(n^2.807) |
| ближайшая пара | 2·T(n/2) + n |
Θ(n·log n) |
| БПФ | 2·T(n/2) + n |
Θ(n·log n) |
Видно главное: рост берётся не из числа частей и не из их размера по отдельности, а из ОТНОШЕНИЯ
log_b a к стоимости объединения. Убрать одно умножение из восьми у Штрассена - значит сдвинуть
показатель с 3 до 2.807; убрать одно из четырёх у Карацубы - с 2 до 1.585.
Три способа сэкономить на объединении:
1. экономия умножений - одно произведение вместо двух за счёт сложений
(Карацуба: 3 из 4; Штрассен: 7 из 8; Тоом-Кук: 5 из 9)
2. геометрическое отсечение - объединять не всё, а только окрестность границы
(ближайшая пара: полоса шириной 2d, семь соседей по y)
3. смена представления - перейти туда, где операция дешёвая
(БПФ: в значениях умножение поточечное)
Что проверять в своей задаче. Части независимы? Ответ либо целиком внутри части, либо задевает границу? Пограничный случай считается дешевле, чем задача целиком? Если на любой вопрос «нет» - схема не подойдёт, и надо смотреть в сторону динамического программирования (когда части пересекаются) или жадности (когда шаг можно выбрать локально).
Порог выгоды всегда замеряют. У всех алгоритмов раздела большая константа, и на малых входах простой алгоритм быстрее: сортировка слиянием переключается на вставки около 10-30 элементов, Штрассен окупается на сотнях, БПФ - на сотнях-тысячах. Точный порог зависит от машины, и его измеряют, а не выводят.
Тихие ошибки раздела: считать, что деление пополам ускоряет что угодно (при тяжёлом объединении выигрыша нет вовсе); забыть про случаи, задевающие границу (ответ молча завышается); брать в БПФ длину без запаса на весь результат (циклическая свёртка вместо обычной); переносить NTT с большим модулем в арифметику с 53 битами точности; ждать целых чисел от комплексного БПФ.
Куда это ведёт
Дальше - жадные алгоритмы: там задача не делится, а решается по шагам, и каждый шаг выбирается локально. Главная работа там не в коде, а в доказательстве, что локальный выбор не портит будущее. Чуть позже, в разделе про динамическое программирование, вернётся идея этого раздела - но для случая, когда части ПЕРЕСЕКАЮТСЯ, и решать их заново становится дорого.
Комментарии
Пока нет комментариев. Будь первым!
Оставить комментарий