0% нашли этот документ полезным (0 голосов)
339 просмотров1 296 страниц

Untitled

Авторское право
© © All Rights Reserved
Мы серьезно относимся к защите прав на контент. Если вы подозреваете, что это ваш контент, заявите об этом здесь.
Доступные форматы
Скачать в формате PDF, TXT или читать онлайн в Scribd
0% нашли этот документ полезным (0 голосов)
339 просмотров1 296 страниц

Untitled

Авторское право
© © All Rights Reserved
Мы серьезно относимся к защите прав на контент. Если вы подозреваете, что это ваш контент, заявите об этом здесь.
Доступные форматы
Скачать в формате PDF, TXT или читать онлайн в Scribd

АЛГОРИТМЫ

ПОСТРОЕНИЕ И АНАЛИЗ
ВТОРОЕ ИЗДАНИЕ

Стр. 1
Thomas H. Cormen
Charles E. Leiserson
Ronald L. Rivest
Clifford Stein

INTRODUCTION TO

ALGORITHMS
SECOND EDITION

The MIT Press


Cambridge, Massachusetts London, England

McGrawHill Book Company


Boston Burr Ridge, IL Dubuque, IA Madison, WI
New York San Francisco St. Louis Montrйal Toronto

Стр. 2
Томас Кормен
Чарльз Лейзерсон
Рональд Ривест
Клиффорд Штайн

АЛГОРИТМЫ
ПОСТРОЕНИЕ И АНАЛИЗ
ВТОР ОЕ ИЗДАНИЕ

Москва • СанктПетербург • Киев

Стр. 3
ББК 32.973.26-018.2.75
В24
УДК 681.3.07

Издательский дом “Вильямс”


Зав. редакцией С.Н. Тригуб
Перевод с английского канд. техн. наук И.В. Красикова, Н.А. Ореховой, В.Н. Романова
Под редакцией канд. техн. наук И.В. Красикова
По общим вопросам обращайтесь в Издательский дом “Вильямс” по адресу:
[email protected], http://www.williamspublishing.com

Кормен, Томас Х., Лейзерсон, Чарльз И., Ривест, Рональд Л., Штайн,
Клиффорд.
В24 Алгоритмы: построение и анализ, 2-е издание. : Пер. с англ. — М. :
Издательский дом “Вильямс”, 2011. — 1296 с. : ил. — Парал. тит. англ.
ISBN 978-5–8459–0857–5 (рус.)
Фундаментальный труд известных специалистов в области кибернетики досто-
ин занять место на полке любого человека, чья деятельность так или иначе связана
с информатикой и алгоритмами. Для профессионала эта книга может служить на-
стольным справочником, для преподавателя — пособием для подготовки к лекциям
и источником интересных нетривиальных задач, для студентов и аспирантов —
отличным учебником. Каждый может найти в ней именно тот материал, который
касается интересующей его темы, и изложенный именно с тем уровнем сложности
и строгости, который требуется читателю.
Описание алгоритмов на естественном языке дополняется псевдокодом, который
позволяет любому имеющему хотя бы начальные знания и опыт программирова-
ния, реализовать алгоритм на используемом им языке программирования. Строгий
математический анализ и обилие теорем сопровождаются большим количеством
иллюстраций, элементарными рассуждениями и простыми приближенными оцен-
ками. Широта охвата материала и степень строгости его изложения дают основания
считать эту книгу одной из лучших книг, посвященных разработке и анализу алго-
ритмов.
ББК 32.973.26–018.2.75
Все названия программных продуктов являются зарегистрированными торговыми марками
соответствующих фирм.
Никакая часть настоящего издания ни в каких целях не может быть воспроизведена в какой
бы то ни было форме и какими бы то ни было средствами, будь то электронные или механиче-
ские, включая фотокопирование и запись на магнитный носитель, если на это нет письменного
разрешения издательства MIT Press.

ISBN 978-5–8459–0857–5 (рус.) 


c Издательский дом “Вильямс”, 201 1
ISBN 0–07–013151–1 (англ.) 
c MIT Press, 2002

Стр. 4
ОГЛАВЛЕНИЕ

Введение 30
Часть I. Основы 43
Глава 1. Роль алгоритмов в вычислениях 46
Глава 2. Приступаем к изучению 57
Глава 3. Рост функций 87
Глава 4. Рекуррентные соотношения 109
Глава 5. Вероятностный анализ и рандомизированные алгоритмы 140
Часть II. Сортировка и порядковая статистика 173
Глава 6. Пирамидальная сортировка 178
Глава 7. Быстрая сортировка 198
Глава 8. Сортировка за линейное время 220
Глава 9. Медианы и порядковые статистики 240
Часть III. Структуры данных 255
Глава 10. Элементарные структуры данных 260
Глава 11. Хеш-таблицы 282
Глава 12. Бинарные деревья поиска 316
Глава 13. Красно-черные деревья 336
Глава 14. Расширение структур данных 365
Часть IV. Усовершенствованные методы разработки и анализа 383
Глава 15. Динамическое программирование 386
Глава 16. Жадные алгоритмы 442
Глава 17. Амортизационный анализ 482
Часть V. Сложные структуры данных 511
Глава 18. B-деревья 515
Глава 19. Биномиальные пирамиды 537

Стр. 5
6 Оглавление

Глава 20. Фибоначчиевы пирамиды 558


Глава 21. Структуры данных для непересекающихся множеств 581
Часть VI. Алгоритмы для работы с графами 607
Глава 22. Элементарные алгоритмы для работы с графами 609
Глава 23. Минимальные остовные деревья 644
Глава 24. Кратчайшие пути из одной вершины 663
Глава 25. Кратчайшие пути между всеми парами вершин 708
Глава 26. Задача о максимальном потоке 734
Часть VII. Избранные темы 795
Глава 27. Сортирующие сети 799
Глава 28. Работа с матрицами 823
Глава 29. Линейное программирование 869
Глава 30. Полиномы и быстрое преобразование Фурье 926
Глава 31. Теоретико-числовые алгоритмы 954
Глава 32. Поиск подстрок 1017
Глава 33. Вычислительная геометрия 1047
Глава 34. NP-полнота 1085
Глава 35. Приближенные алгоритмы 1151
Часть VIII. Приложения: математические основы 1189
Приложение А. Ряды 1191
Приложение Б. Множества и прочие художества 1202
Приложение В. Комбинаторика и теория вероятности 1226
Библиография 1257
Предметный указатель 1277

Стр. 6
СОДЕРЖАНИЕ

Введение 30

Часть I. Основы 43
Введение 44
Глава 1. Роль алгоритмов в вычислениях 46
1.1 Алгоритмы 46
Какие задачи решаются с помощью алгоритмов? 47
Структуры данных 50
Методические указания 50
Сложные задачи 51
Упражнения 52
1.2 Алгоритмы как технология 52
Эффективность 52
Алгоритмы и другие технологии 54
Упражнения 55
Задачи 55
Заключительные замечания 56
Глава 2. Приступаем к изучению 57
2.1 Сортировка вставкой 57
Инварианты цикла и корректность сортировки вставкой 59
Соглашения, принятые при составлении псевдокода 61
Упражнения 63
2.2 Анализ алгоритмов 64
Анализ алгоритма, работающего по методу вставок 66

Стр. 7
8 Содержание

Наихудшее и среднее время работы 69


Порядок возрастания 70
Упражнения 71
2.3 Разработка алгоритмов 71
2.3.1 Метод декомпозиции 72
2.3.2 Анализ алгоритмов, основанных на принципе
“разделяй и властвуй” 78
Анализ алгоритма сортировки слиянием 78
Упражнения 81
Задачи 83
Заключительные замечания 86
Глава 3. Рост функций 87
3.1 Асимптотические обозначения 88
Θ-обозначения 88
O-обозначения 91
Ω-обозначения 92
Асимптотические обозначения в уравнениях
и неравенствах 93
o-обозначения 94
ω-обозначения 95
Сравнение функций 96
Упражнения 97
3.2 Стандартные обозначения и часто встречающиеся функции 98
Монотонность 98
Округление в большую и меньшую сторону 98
Модульная арифметика 98
Полиномы 99
Показательные функции 99
Логарифмы 100
Факториалы 102
Функциональная итерация 102
Итерированная логарифмическая функция 103
Числа Фибоначчи 103
Упражнения 104
Задачи 105
Заключительные замечания 108
Глава 4. Рекуррентные соотношения 109
Технические детали 110
4.1 Метод подстановки 111
Как угадать решение 112

Стр. 8
Содержание 9

Тонкие нюансы 113


Остерегайтесь ошибок 114
Замена переменных 114
Упражнения 115
4.2 Метод деревьев рекурсии 115
Упражнения 120
4.3 Основной метод 121
Основная теорема 121
Использование основного метода 122
Упражнения 123
 4.4 Доказательство основной теоремы 124
4.4.1 Доказательство теоремы для точных степеней 125
4.4.2 Учет округления чисел 130
Упражнения 133
Задачи 133
Заключительные замечания 138
Глава 5. Вероятностный анализ и рандомизированные алгоритмы 140
5.1 Задача о найме сотрудника 140
Анализ наихудшего случая 142
Вероятностный анализ 142
Рандомизированные алгоритмы 143
Упражнения 144
5.2 Индикаторная случайная величина 144
Анализ задачи о найме сотрудника с помощью
индикаторных случайных величин 146
Упражнения 148
5.3 Рандомизированные алгоритмы 149
Массивы, полученные в результате случайной
перестановки 151
Упражнения 155
 5.4 Вероятностный анализ и дальнейшее применение
индикаторных случайных величин 156
5.4.1 Парадокс дней рождения 157
Анализ с помощью индикаторных случайных величин 158
5.4.2 Шары и урны 160
5.4.3 Последовательности выпадения орлов 161
5.4.4 Задача о найме сотрудника в оперативном режиме 165
Упражнения 167
Задачи 168
Заключительные замечания 171

Стр. 9
10 Содержание

Часть II. Сортировка и порядковая статистика 173


Введение 174
Глава 6. Пирамидальная сортировка 178
6.1 Пирамиды 179
Упражнения 181
6.2 Поддержка свойства пирамиды 182
Упражнения 184
6.3 Создание пирамиды 184
Упражнения 187
6.4 Алгоритм пирамидальной сортировки 187
Упражнения 188
6.5 Очереди с приоритетами 190
Упражнения 193
Задачи 194
Заключительные замечания 196
Глава 7. Быстрая сортировка 198
7.1 Описание быстрой сортировки 199
Разбиение массива 199
Упражнения 203
7.2 Производительность быстрой сортировки 203
Наихудшее разбиение 203
Наилучшее разбиение 204
Сбалансированное разбиение 204
Интуитивные рассуждения для среднего случая 205
Упражнения 207
7.3 Рандомизированная версия быстрой сортировки 208
Упражнения 209
7.4 Анализ быстрой сортировки 209
7.4.1 Анализ в наихудшем случае 209
7.4.2 Математическое ожидание времени работы 210
Время работы и сравнения 210
Упражнения 213
Задачи 214
Заключительные замечания 219
Глава 8. Сортировка за линейное время 220
8.1 Нижние оценки алгоритмов сортировки 221
Модель дерева решений 221
Нижняя оценка для наихудшего случая 222
Упражнения 223

Стр. 10
Содержание 11

8.2 Сортировка подсчетом 224


Упражнения 226
8.3 Поразрядная сортировка 226
Упражнения 230
8.4 Карманная сортировка 230
Упражнения 234
Задачи 234
Заключительные замечания 238
Глава 9. Медианы и порядковые статистики 240
9.1 Минимум и максимум 241
Одновременный поиск минимума и максимума 241
Упражнения 242
9.2 Выбор в течение линейного ожидаемого времени 243
Упражнения 247
9.3 Алгоритм выбора с линейным временем работы в наихудшем
случае 247
Упражнения 250
Задачи 252
Заключительные замечания 254

Часть III. Структуры данных 255


Введение 256
Глава 10. Элементарные структуры данных 260
10.1 Стеки и очереди 260
Стеки 260
Очереди 262
Упражнения 263
10.2 Связанные списки 264
Поиск в связанном списке 265
Вставка в связанный список 265
Удаление из связанного списка 266
Ограничители 266
Упражнения 268
10.3 Реализация указателей и объектов 269
Представление объектов с помощью нескольких массивов 269
Представление объектов с помощью одного массива 270
Выделение и освобождение памяти 271
Упражнения 273
10.4 Представление корневых деревьев 274
Бинарные деревья 274

Стр. 11
12 Содержание

Корневые деревья с произвольным ветвлением 275


Другие представления деревьев 276
Упражнения 276
Задачи 277
Заключительные замечания 280
Глава 11. Хеш-таблицы 282
11.1 Таблицы с прямой адресацией 283
Упражнения 284
11.2 Хеш-таблицы 285
Разрешение коллизий при помощи цепочек 286
Анализ хеширования с цепочками 288
Упражнения 290
11.3 Хеш-функции 291
Чем определяется качество хеш-функции 291
Интерпретация ключей как целых неотрицательных чисел 292
11.3.1 Метод деления 292
11.3.2 Метод умножения 293
 11.3.3 Универсальное хеширование 294
Построение универсального множества хеш-функций 297
Упражнения 298
11.4 Открытая адресация 300
Линейное исследование 302
Квадратичное исследование 303
Двойное хеширование 303
Анализ хеширования с открытой адресацией 305
Упражнения 307
 11.5 Идеальное хеширование 308
Упражнения 312
Задачи 313
Заключительные замечания 315
Глава 12. Бинарные деревья поиска 316
12.1 Что такое бинарное дерево поиска 317
Упражнения 319
12.2 Работа с бинарным деревом поиска 319
Поиск 320
Поиск минимума и максимума 321
Предшествующий и последующий элементы 321
Упражнения 323
12.3 Вставка и удаление 324
Вставка 324

Стр. 12
Содержание 13

Удаление 325
Упражнения 327
 12.4 Случайное построение бинарных деревьев поиска 328
Упражнения 331
Задачи 332
Заключительные замечания 335
Глава 13. Красно-черные деревья 336
13.1 Свойства красно-черных деревьев 336
Упражнения 339
13.2 Повороты 340
Упражнения 341
13.3 Вставка 342
Анализ 350
Упражнения 350
13.4 Удаление 351
Анализ 356
Упражнения 356
Задачи 357
Заключительные замечания 364
Глава 14. Расширение структур данных 365
14.1 Динамические порядковые статистики 366
Выборка элемента с заданным рангом 367
Определение ранга элемента 368
Поддержка размера поддеревьев 369
Упражнения 371
14.2 Расширение структур данных 372
Расширение красно-черных деревьев 373
Упражнения 374
14.3 Деревья отрезков 375
Упражнения 380
Задачи 381
Заключительные замечания 382

Часть IV. Усовершенствованные методы разработки и анализа 383


Введение 384
Глава 15. Динамическое программирование 386
15.1 Расписание работы конвейера 387
Первый этап: структура самой быстрой сборки 389
Второй этап: рекурсивное решение 391

Стр. 13
14 Содержание

Третий этап: вычисление минимальных промежутков


времени 393
Четвертый этап: построение самого быстрого пути 394
Упражнения 395
15.2 Перемножение цепочки матриц 395
Подсчет количества способов расстановки скобок 397
Первый этап: структура оптимальной расстановки скобок 398
Второй этап: рекурсивное решение 399
Третий этап: вычисление оптимальной стоимости 400
Четвертый этап: конструирование оптимального решения 403
Упражнения 404
15.3 Элементы динамического программирования 404
Оптимальная подструктура 405
Перекрытие вспомогательных задач 411
Построение оптимального решения 414
Запоминание 414
Упражнения 417
15.4 Самая длинная общая подпоследовательность 418
Этап 1: характеристика самой длинной общей
подпоследовательности 419
Этап 2: рекурсивное решение 421
Этап 3: вычисление длины самой длинной общей
подпоследовательности 422
Этап 4: построение самой длинной общей
подпоследовательности 423
Улучшение кода 424
Упражнения 425
15.5 Оптимальные бинарные деревья поиска 425
Этап 1: структура оптимального бинарного дерева поиска 429
Этап 2: рекурсивное решение 430
Этап 3: вычисление математического ожидания
стоимости поиска в оптимальном бинарном дереве поиска 431
Упражнения 433
Задачи 434
Заключительные замечания 440
Глава 16. Жадные алгоритмы 442
16.1 Задача о выборе процессов 443
Оптимальная подструктура задачи о выборе процессов 444
Рекурсивное решение 446

Стр. 14
Содержание 15

Преобразование решения динамического


программирования в жадное решение 446
Рекурсивный жадный алгоритм 449
Итерационный жадный алгоритм 451
Упражнения 452
16.2 Элементы жадной стратегии 453
Свойство жадного выбора 454
Оптимальная подструктура 455
Сравнение жадных алгоритмов и динамического
программирования 456
Упражнения 458
16.3 Коды Хаффмана 459
Префиксные коды 460
Построение кода Хаффмана 462
Корректность алгоритма Хаффмана 464
Упражнения 466
 16.4 Теоретические основы жадных методов 467
Матроиды 467
Жадные алгоритмы на взвешенном матроиде 470
Упражнения 474
 16.5 Планирование заданий 474
Упражнения 478
Задачи 478
Заключительные замечания 481
Глава 17. Амортизационный анализ 482
17.1 Групповой анализ 483
Стековые операции 483
Приращение показаний бинарного счетчика 485
Упражнения 487
17.2 Метод бухгалтерского учета 487
Стековые операции 489
Приращение показаний бинарного счетчика 490
Упражнения 490
17.3 Метод потенциалов 491
Стековые операции 492
Увеличение показаний бинарного счетчика 493
Упражнения 494
17.4 Динамические таблицы 495
17.4.1 Расширение таблицы 496
17.4.2 Расширение и сжатие таблицы 499

Стр. 15
16 Содержание

Упражнения 504
Задачи 505
Заключительные замечания 510

Часть V. Сложные структуры данных 511


Введение 512
Глава 18. B-деревья 515
Структуры данных во вторичной памяти 516
18.1 Определение B-деревьев 519
Высота B-дерева 521
Упражнения 522
18.2 Основные операции с B-деревьями 522
Поиск в B-дереве 522
Создание пустого B-дерева 523
Вставка ключа в B-дерево 524
Упражнения 528
18.3 Удаление ключа из B-дерева 530
Упражнения 533
Задачи 533
Заключительные замечания 536
Глава 19. Биномиальные пирамиды 537
19.1 Биномиальные деревья и биномиальные пирамиды 539
19.1.1 Биномиальные деревья 539
19.1.2 Биномиальные пирамиды 541
Упражнения 543
19.2 Операции над биномиальными пирамидами 544
Создание новой биномиальной пирамиды 544
Поиск минимального ключа 544
Слияние двух биномиальных пирамид 545
Вставка узла 550
Извлечение вершины с минимальным ключом 551
Уменьшение ключа 552
Удаление ключа 554
Упражнения 554
Задачи 555
Заключительные замечания 557

Стр. 16
Содержание 17

Глава 20. Фибоначчиевы пирамиды 558


20.1 Структура фибоначчиевых пирамид 559
Потенциальная функция 561
Максимальная степень 562
20.2 Операции над сливаемыми пирамидами 562
Создание новой фибоначчиевой пирамиды 563
Вставка узла 563
Поиск минимального узла 564
Объединение двух фибоначчиевых пирамид 564
Извлечение минимального узла 565
Упражнения 571
20.3 Уменьшение ключа и удаление узла 571
Уменьшение ключа 571
Удаление узла 575
Упражнения 575
20.4 Оценка максимальной степени 575
Упражнения 578
Задачи 578
Заключительные замечания 579
Глава 21. Структуры данных для непересекающихся множеств 581
21.1 Операции над непересекающимися множествами 582
Приложение структур данных для непересекающихся
множеств 583
Упражнения 584
21.2 Представление непересекающихся множеств с помощью
связанных списков 585
Простая реализация объединения 586
Весовая эвристика 587
Упражнения 588
21.3 Лес непересекающихся множеств 589
Эвристики для повышения эффективности 589
Псевдокоды 590
Влияние эвристик на время работы 592
Упражнения 592
 21.4 Анализ объединения по рангу со сжатием пути 592
Очень быстро и очень медленно растущая функция 593
Свойства рангов 594
Доказательство границы времени работы 595
Потенциальная функция 596

Стр. 17
18 Содержание

Изменения потенциала и амортизированная стоимость


операций 598
Упражнения 601
Задачи 601
Заключительные замечания 605

Часть VI. Алгоритмы для работы с графами 607


Введение 608
Глава 22. Элементарные алгоритмы для работы с графами 609
22.1 Представление графов 609
Упражнения 612
22.2 Поиск в ширину 613
Анализ 616
Кратчайшие пути 617
Деревья поиска в ширину 620
Упражнения 621
22.3 Поиск в глубину 622
Свойства поиска в глубину 626
Классификация ребер 628
Упражнения 630
22.4 Топологическая сортировка 632
Упражнения 634
22.5 Сильно связные компоненты 635
Упражнения 640
Задачи 641
Заключительные замечания 643
Глава 23. Минимальные остовные деревья 644
23.1 Построение минимального остовного дерева 645
Упражнения 649
23.2 Алгоритмы Крускала и Прима 651
Алгоритм Крускала 651
Алгоритм Прима 653
Упражнения 656
Задачи 658
Заключительные замечания 661
Глава 24. Кратчайшие пути из одной вершины 663
Варианты 664
Оптимальная структура задачи о кратчайшем пути 665
Ребра с отрицательным весом 666

Стр. 18
Содержание 19

Циклы 667
Представление кратчайших путей 668
Ослабление 669
Свойства кратчайших путей и ослабления 671
Краткое содержание главы 672
24.1 Алгоритм Беллмана-Форда 672
Упражнения 676
24.2 Кратчайшие пути из одной вершины в ориентированных
ациклических графах 677
Упражнения 679
24.3 Алгоритм Дейкстры 680
Анализ 684
Упражнения 686
24.4 Разностные ограничения и кратчайшие пути 687
Линейное программирование 687
Системы разностных ограничений 688
Графы ограничений 690
Решение систем разностных ограничений 692
Упражнения 692
24.5 Доказательства свойств кратчайших путей 694
Неравенство треугольника 694
Влияние ослабления на оценки кратчайшего пути 695
Ослабление и деревья кратчайших путей 697
Упражнения 700
Задачи 702
Заключительные замечания 706
Глава 25. Кратчайшие пути между всеми парами вершин 708
Краткое содержание главы 710
25.1 Задача о кратчайших путях и умножение матриц 711
Структура кратчайшего пути 711
Рекурсивное решение задачи о кратчайших путях
между всеми парами вершин 712
Вычисление весов кратчайших путей в восходящем
порядке 712
Улучшение времени работы 714
Упражнения 716
25.2 Алгоритм Флойда-Варшалла 718
Структура кратчайшего пути 718
Рекурсивное решение задачи о кратчайших путях
между всеми парами вершин 719

Стр. 19
20 Содержание

Вычисление весов кратчайших путей в восходящем


порядке 720
Построение кратчайшего пути 720
Транзитивное замыкание ориентированного графа 722
Упражнения 724
25.3 Алгоритм Джонсона для разреженных графов 726
Сохранение кратчайших путей 726
Генерация неотрицательных весов путем их изменения 728
Вычисление кратчайших путей между всеми парами
вершин 728
Упражнения 730
Задачи 731
Заключительные замечания 732
Глава 26. Задача о максимальном потоке 734
26.1 Транспортные сети 735
Транспортные сети и потоки 735
Пример потока 737
Сети с несколькими источниками и стоками 739
Как работать с потоками 740
Упражнения 741
26.2 Метод Форда-Фалкерсона 742
Остаточные сети 743
Увеличивающие пути 745
Разрезы транспортных сетей 746
Базовый алгоритм Форда-Фалкерсона 749
Анализ метода Форда-Фалкерсона 750
Алгоритм Эдмондса-Карпа 752
Упражнения 755
26.3 Максимальное паросочетание 756
Задача поиска максимального паросочетания
в двудольном графе 757
Поиск максимального паросочетания в двудольном графе 758
Упражнения 761
 26.4 Алгоритмы проталкивания предпотока 761
Интуитивные соображения 762
Основные операции 764
Операция проталкивания 764
Операция подъема 766
Универсальный алгоритм 766
Корректность метода проталкивания предпотока 768

Стр. 20
Содержание 21

Анализ метода проталкивания предпотока 770


Упражнения 773
 26.5 Алгоритм “поднять-в-начало” 774
Допустимые ребра и сети 775
Списки соседей 777
Разгрузка переполненной вершины 777
Алгоритм “поднять-в-начало” 780
Анализ 783
Упражнения 785
Задачи 786
Заключительные замечания 793

Часть VII. Избранные темы 795


Введение 796
Глава 27. Сортирующие сети 799
27.1 Сравнивающие сети 800
Упражнения 803
27.2 Нуль-единичный принцип 805
Упражнения 807
27.3 Битоническая сортирующая сеть 808
Полуфильтр 809
Битонический сортировщик 810
Упражнения 812
27.4 Объединяющая сеть 813
Упражнения 815
27.5 Сортирующая сеть 816
Упражнения 818
Задачи 819
Заключительные замечания 822
Глава 28. Работа с матрицами 823
28.1 Свойства матриц 824
Матрицы и векторы 824
Операции над матрицами 827
Обратные матрицы, ранги и детерминанты 828
Положительно определенные матрицы 831
Упражнения 831
28.2 Алгоритм умножения матриц Штрассена 833
Обзор алгоритма 833
Определение произведений подматриц 834
Обсуждение метода 838

Стр. 21
22 Содержание

Упражнения 839
28.3 Решение систем линейных уравнений 839
Обзор LUP-разложения 841
Прямая и обратная подстановки 842
Вычисление LU-разложения 845
Вычисление LUP-разложения 848
Упражнения 852
28.4 Обращение матриц 853
Вычисление обратной матрицы из LUP-разложения 853
Умножение матриц и обращение матрицы 854
Упражнения 857
28.5 Симметричные положительно определенные матрицы
и метод наименьших квадратов 858
Метод наименьших квадратов 861
Упражнения 865
Задачи 865
Заключительные замечания 867
Глава 29. Линейное программирование 869
Политическая задача 869
Общий вид задач линейного программирования 872
Краткий обзор задач линейного программирования 872
Приложения линейного программирования 876
Алгоритмы решения задач линейного программирования 877
29.1 Стандартная и каноническая формы задач линейного
программирования 877
Стандартная форма 878
Преобразование задач линейного программирования
в стандартную форму 879
Преобразование задач линейного программирования
в каноническую форму 882
Упражнения 885
29.2 Формулирование задач в виде задач линейного
программирования 886
Кратчайшие пути 887
Максимальный поток 887
Поиск потока с минимальными затратами 888
Многопродуктовый поток 889
Упражнения 891
29.3 Симплекс-алгоритм 892
Пример симплекс-алгоритма 893

Стр. 22
Содержание 23

Замещение 897
Формальный симплекс-алгоритм 899
Завершение 905
Упражнения 907
29.4 Двойственность 908
Упражнения 914
29.5 Начальное базисное допустимое решение 914
Поиск начального решения 914
Основная теорема линейного программирования 920
Упражнения 921
Задачи 922
Заключительные замечания 924
Глава 30. Полиномы и быстрое преобразование Фурье 926
Полиномы 926
Краткое содержание главы 928
30.1 Представление полиномов 928
Представление, основанное на коэффициентах 929
Представление, основанное на значениях в точках 929
Быстрое умножение полиномов, заданных
в коэффициентной форме 932
Упражнения 934
30.2 ДПФ и БПФ 935
Комплексные корни из единицы 935
Дискретное преобразование Фурье 938
Быстрое преобразование Фурье 938
Интерполяция в точках, являющихся комплексными
корнями из единицы 941
Упражнения 942
30.3 Эффективные реализации БПФ 943
Итеративная реализация БПФ 944
Параллельная схема БПФ 947
Упражнения 949
Задачи 949
Заключительные замечания 953
Глава 31. Теоретико-числовые алгоритмы 954
Размер входных наборов данных и стоимость
арифметических вычислений 955
31.1 Элементарные обозначения, принятые в теории чисел 956
Делимость и делители 956
Простые и составные числа 956

Стр. 23
24 Содержание

Теорема о делении, остатки и равенство по модулю 957


Общие делители и наибольшие общие делители 958
Взаимно простые целые числа 960
Единственность разложения на множители 960
Упражнения 961
31.2 Наибольший общий делитель 962
Алгоритм Евклида 963
Время работы алгоритма Евклида 964
Развернутая форма алгоритма Евклида 965
Упражнения 967
31.3 Модульная арифметика 968
Конечные группы 968
Группы, образованные сложением и умножением
по модулю 969
Подгруппы 972
Подгруппы, сгенерированные элементом группы 973
Упражнения 975
31.4 Решение модульных линейных уравнений 975
Упражнения 979
31.5 Китайская теорема об остатках 979
Упражнения 982
31.6 Степени элемента 983
Возведение в степень путем последовательного
возведения в квадрат 985
Упражнения 987
31.7 Криптосистема с открытым ключом RSA 987
Криптографические системы с открытым ключом 988
Криптографическая система RSA 991
Упражнения 995
 31.8 Проверка простоты 995
Плотность распределения простых чисел 996
Проверка псевдопростых чисел 997
Рандомизированный тест простоты Миллера-Рабина 999
Частота ошибок в тесте Миллера-Рабина 1002
Упражнения 1006
 31.9 Целочисленное разложение 1006
Эвристический ρ-метод Полларда 1007
Упражнения 1012
Задачи 1013
Заключительные замечания 1015

Стр. 24
Содержание 25

Глава 32. Поиск подстрок 1017


Обозначения и терминология 1019
32.1 Простейший алгоритм поиска подстрок 1020
Упражнения 1021
32.2 Алгоритм Рабина-Карпа 1022
Упражнения 1028
32.3 Поиск подстрок с помощью конечных автоматов 1028
Конечные автоматы 1029
Автоматы поиска подстрок 1030
Вычисление функции переходов 1035
Упражнения 1036
 32.4 Алгоритм Кнута-Морриса-Пратта 1036
Префиксная функция для образца 1037
Анализ времени работы 1040
Корректность вычисления префиксной функции 1041
Корректность алгоритма Кнута-Морриса-Пратта 1043
Упражнения 1044
Задачи 1045
Заключительные замечания 1046
Глава 33. Вычислительная геометрия 1047
33.1 Свойства отрезков 1048
Векторное произведение 1049
Поворот последовательных отрезков 1050
Определение того, пересекаются ли два отрезка 1051
Другие применения векторного произведения 1053
Упражнения 1053
33.2 Определение наличия пересекающихся отрезков 1055
Упорядочение отрезков 1056
Перемещение выметающей прямой 1057
Псевдокод, выявляющий пересечение отрезков 1058
Корректность 1060
Время работы 1061
Упражнения 1062
33.3 Построение выпуклой оболочки 1063
Сканирование по Грэхему 1065
Обход по Джарвису 1071
Упражнения 1073
33.4 Поиск пары ближайших точек 1074
Алгоритм декомпозиции 1075
Корректность 1077

Стр. 25
26 Содержание

Реализация и время работы алгоритма 1078


Упражнения 1079
Задачи 1080
Заключительные замечания 1083
Глава 34. NP-полнота 1085
NP-полнота и классы P и NP 1087
Как показать, что задача является NP-полной 1088
Краткое содержание главы 1091
34.1 Полиномиальное время 1091
Абстрактные задачи 1092
Кодирование 1093
Структура формальных языков 1096
Упражнения 1100
34.2 Проверка за полиномиальное время 1100
Гамильтоновы циклы 1101
Алгоритмы верификации 1102
Класс сложности NP 1103
Упражнения 1105
34.3 NP-полнота и приводимость 1106
Приводимость 1106
NP-полнота 1108
Выполнимость схем 1110
Упражнения 1117
34.4 Доказательство NP-полноты 1118
Выполнимость формулы 1119
3-CNF выполнимость 1122
Упражнения 1126
34.5 NP-полные задачи 1127
34.5.1 Задача о клике 1128
34.5.2 Задача о вершинном покрытии 1131
34.5.3 Задача о гамильтоновых циклах 1133
34.5.4 Задача о коммивояжере 1138
34.5.5 Задача о сумме подмножества 1140
Упражнения 1144
Задачи 1145
Заключительные замечания 1149
Глава 35. Приближенные алгоритмы 1151
Оценка качества приближенных алгоритмов 1151
Краткое содержание главы 1153
35.1 Задача о вершинном покрытии 1154

Стр. 26
Содержание 27

Упражнения 1157
35.2 Задача о коммивояжере 1157
35.2.1 Задача о коммивояжере с неравенством треугольника 1158
35.2.2 Общая задача о коммивояжере 1161
Упражнения 1163
35.3 Задача о покрытии множества 1164
Жадный приближенный алгоритм 1166
Анализ 1166
Упражнения 1169
35.4 Рандомизация и линейное программирование 1170
Рандомизированный приближенный алгоритм для
задачи о MAX-3-CNF выполнимости 1170
Аппроксимация взвешенного вершинного покрытия
с помощью линейного программирования 1172
Упражнения 1175
35.5 Задача о сумме подмножества 1176
Точный алгоритм с экспоненциальным временем работы 1176
Схема аппроксимации с полностью полиномиальным
временем работы 1178
Упражнения 1182
Задачи 1182
Заключительные замечания 1186

Часть VIII. Приложения: математические основы 1189


Введение 1190
Приложение А. Ряды 1191
А.1 Суммы и их свойства 1192
Линейность 1192
Арифметическая прогрессия 1193
Суммы квадратов и кубов 1193
Геометрическая прогрессия 1193
Гармонический ряд 1194
Интегрирование и дифференцирование рядов 1194
Суммы разностей 1194
Произведения 1195
Упражнения 1195
А.2 Оценки сумм 1195
Математическая индукция 1196
Почленное сравнение 1196
Разбиение рядов 1198

Стр. 27
28 Содержание

Приближение интегралами 1199


Упражнения 1201
Задачи 1201
Заключительные замечания 1201
Приложение Б. Множества и прочие художества 1202
Б.1 Множества 1202
Упражнения 1207
Б.2 Отношения 1207
Упражнения 1209
Б.3 Функции 1210
Упражнения 1212
Б.4 Графы 1213
Упражнения 1217
Б.5 Деревья 1218
Б.5.1 Свободные деревья 1218
Б.5.2 Деревья с корнем и упорядоченные деревья 1220
Б.5.3 Бинарные и позиционные деревья 1221
Упражнения 1223
Задачи 1224
Заключительные замечания 1225
Приложение В. Комбинаторика и теория вероятности 1226
В.1 Основы комбинаторики 1226
Правила суммы и произведения 1227
Строки 1227
Перестановки 1227
Сочетания 1228
Биномиальные коэффициенты 1229
Оценки биномиальных коэффициентов 1229
Упражнения 1230
В.2 Вероятность 1232
Аксиомы вероятности 1232
Дискретные распределения вероятностей 1233
Непрерывное равномерное распределение вероятности 1234
Условная вероятность и независимость 1234
Теорема Байеса 1236
Упражнения 1237
В.3 Дискретные случайные величины 1238
Математическое ожидание случайной величины 1239
Дисперсия и стандартное отклонение 1242
Упражнения 1243

Стр. 28
Содержание 29

В.4 Геометрическое и биномиальное распределения 1243


Геометрическое распределение 1244
Биномиальное распределение 1245
Упражнения 1248
 В.5 Хвосты биномиального распределения 1249
Упражнения 1254
Задачи 1255
Заключительные замечания 1256
Библиография 1257

Предметный указатель 1277

Стр. 29
Введение

Эта книга служит исчерпывающим вводным курсом по современным компью-


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

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

Стр. 30
Введение 31

для обычного курса, рассчитанного на один семестр, можно выбрать только тот
материал, который лучше всего соответствует курсу, который вы собираетесь пре-
подавать.
Курсы удобно разрабатывать на основе отдельных глав. Книга написана так,
что ее главы сравнительно независимы одна от другой. В каждой главе материал
излагается по мере увеличения его сложности и разбит на разделы. В студенческом
курсе можно использовать только более легкие разделы, а в аспирантском — всю
главу в полном объеме.
В книгу вошли более 920 упражнений и свыше 140 задач. Упражнения даются
в конце каждого раздела, а задачи — в конце каждой главы. Упражнения пред-
ставлены в виде кратких вопросов для проверки степени освоения материала.
Некоторые из них простые и предназначены для самоконтроля, в то время как
другие — посложнее и могут быть рекомендованы в качестве домашних заданий.
Решение задач требует больших усилий, и с их помощью часто вводится новый
материал. Обычно задачи сформулированы так, что в них содержатся наводящие
вопросы, помогающие найти верное решение.
Разделы и упражнения, которые больше подходят для аспирантов, чем для
студентов, обозначены звездочкой (). Они не обязательно более сложные, чем
те, возле которых звездочка отсутствует; просто для их понимания может пона-
добиться владение более сложным математическим аппаратом. Для того чтобы
справиться с упражнениями со звездочкой, может понадобиться более основа-
тельная подготовка или неординарная сообразительность.

Студенту
Надеемся, что этот учебник станет хорошим введением в теорию алгоритмов.
Авторы попытались изложить каждый алгоритм в доступной и увлекательной
форме. Чтобы облегчить освоение незнакомых или сложных алгоритмов, каждый
из них описывается поэтапно. В книге также приводится подробное объясне-
ние математических вопросов, необходимых для того, чтобы понять проводимый
анализ алгоритмов. Для тех читателей, которые уже в некоторой мере знакомы
с какой-то темой, материал глав организован таким образом, чтобы эти читатели
могли опустить вводные разделы и перейти непосредственно к более сложному
материалу.
Книга получилась довольно большой, поэтому не исключено, что в курсе лек-
ций будет представлена лишь часть изложенного в ней материала. Однако авторы
попытались сделать ее такой, чтобы она стала полезной как сейчас в качестве
учебника, способствующего усвоению курса лекций, так и позже, в профессио-
нальной деятельности, в качестве настольного справочного пособия для матема-
тиков или инженеров.

Стр. 31
32 Введение

Ниже перечислены необходимые предпосылки, позволяющие освоить матери-


ал этой книги.
• Читатель должен обладать некоторым опытом в программировании. В част-
ности, он должен иметь представление о рекурсивных процедурах и про-
стых структурах данных, таких как массивы и связанные списки.
• Читатель должен обладать определенными навыками доказательства теорем
методом математической индукции. Для понимания некоторых вопросов, из-
ложенных в этой книге, потребуется умение выполнять некоторые простые
математические преобразования. Помимо этого, в частях I и VIII этой книги
рассказывается обо всех используемых математических методах.

Профессионалу
Широкий круг вопросов, которые излагаются в этой книге, позволяет говорить
о том, что она станет прекрасным учебником по теории алгоритмов. Поскольку
каждая глава является относительно самостоятельной, читатель сможет сосредо-
точить внимание на вопросах, интересующих его больше других.
Основная часть обсуждаемых здесь алгоритмов обладает большой практи-
ческой ценностью. Поэтому не обойдены вниманием особенности реализации
алгоритмов и другие инженерные вопросы. Часто предлагаются реальные альтер-
нативы алгоритмам, представляющим преимущественно теоретический интерес.
Если вам понадобится реализовать любой из приведенных алгоритмов, будет
достаточно легко преобразовать приведенный псевдокод в код на вашем любимом
языке программирования. Псевдокод разработан таким образом, чтобы каждый ал-
горитм был представлен ясно и лаконично. Вследствие этого не рассматриваются
обработка ошибок и другие связанные с разработкой программного обеспечения
вопросы, требующие определенных предположений, касающихся конкретной сре-
ды программирования. Авторы попытались представить каждый алгоритм просто
и непосредственно, не используя индивидуальные особенности того или иного
языка программирования, что могло бы усложнить понимание сути алгоритма.

Коллегам
В книге приведена обширная библиография и представлены указания на совре-
менную литературу. В конце каждой главы даются “заключительные замечания”,
содержащие исторические подробности и ссылки. Однако эти замечания не могут
служить исчерпывающим руководством в области алгоритмов. Возможно, в это
будет сложно поверить, но даже в такую объемную книгу не удалось включить
многие интересные алгоритмы из-за недостатка места.
Несмотря на огромное количество писем от студентов с просьбами предоста-
вить решения задач и упражнений, политика авторов — не приводить ссылки на

Стр. 32
Введение 33

источники, из которых эти задачи и упражнения были позаимствованы. Это сде-


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

Изменения во втором издании


Какие изменения произошли во втором издании книги? В зависимости от
точки зрения, можно сказать, что их достаточно мало или довольно много.
Бо́льшая часть глав первого издания содержится и во втором издании. Авторы
убрали две главы и некоторые разделы, но добавили три новые главы и (кроме
них) четыре новых раздела. Если судить об изменениях по оглавлению, то, скорее
всего, можно прийти к выводу, что их объем достаточно скромный.
Однако эти изменения далеко выходят за рамки оглавления. Ниже без опреде-
ленного порядка приводится обзор наиболее значительных изменений во втором
издании.
• К коллективу соавторов присоединился Клифф Штайн (Cliff Stein).
• Были исправлены ошибки. Сколько их было допущено? Ну, скажем, не-
сколько.
• Появились три новые главы:
• в главе 1 обсуждается роль алгоритмов в вычислениях;
• в главе 5 излагается вероятностный анализ и рандомизированные ал-
горитмы; в первом издании эта тема упоминается в разных местах
книги;
• глава 29 посвящена линейному программированию.
• Что касается глав, которые присутствовали в первом издании, в них добав-
лены новые разделы по таким вопросам:
• идеальное хеширование (раздел 11.5);
• два приложения динамического программирования (разделы 15.1
и 15.5);
• алгоритмы аппроксимации, в которых применяется рандомизация и ли-
нейное программирование (раздел 35.4).
• Чтобы поместить в начальную часть книги больше алгоритмов, три главы
по основным разделам математики перенесены из части I в приложение
(часть VIII).
• Добавлено более 40 новых задач и 185 новых упражнений.
• В явном виде были использованы инварианты цикла для доказательства
корректности алгоритмов. Первый инвариант цикла используется в главе 2,
и в дальнейшем в книге они применяются еще несколько десятков раз.

Стр. 33
34 Введение

• Переделаны многие места, где проводится вероятностный анализ. В част-


ности, более десяти раз используется метод “индикаторных случайных ве-
личин”, упрощающих вероятностный анализ, особенно при наличии зави-
симости между случайными величинами.
• Дополнены и обновлены заключительные замечания к главам и библиогра-
фия. Библиография увеличилась более чем на 50%. Кроме того, упоминают-
ся многие новые результаты, которые были достигнуты в теории алгоритмов
после выхода первого издания.
Также внесены перечисленные ниже изменения.
• В главе, посвященной решению рекуррентных соотношений, больше не со-
держится информация об итеративном методе. Вместо этого в разделе 4.2
на первый план выступают деревья рекурсии, сами по себе представля-
ющие определенный метод. Авторы пришли к выводу, что использование
деревьев рекурсии в меньшей мере связано с риском допустить ошибку. Од-
нако следует заметить, что деревья лучше всего использовать для генерации
предположений, которые затем проверяются методом подстановок.
• Несколько по-иному излагается метод разбиения, который применялся для
быстрой сортировки (раздел 7.1), и алгоритм порядковой статистики, мате-
матическое ожидание времени работы которого выражается линейной функ-
цией (раздел 9.2). В данном издании используется метод, разработанный
Ломуто (Lomuto), который наряду с индикаторными случайными величина-
ми позволяет несколько упростить анализ. Предложенный Хоаром (Hoare)
метод, который содержался в первом издании, включен в качестве задачи
в главе 7.
• Внесены модификации в обсуждение универсального хеширования в разде-
ле 11.3.3, после чего оно интегрируется в представление идеального хеши-
рования.
• Значительно упрощен анализ высоты бинарного дерева поиска, построен-
ного в разделе 12.4 случайным образом.
• Существенно расширено обсуждение элементов динамического программи-
рования (раздел 15.3) и жадных алгоритмов (раздел 16.2). Исследование
задачи о выборе задания, которым начинается глава о жадных алгоритмах,
помогает прояснить взаимосвязь между динамическим программированием
и жадными алгоритмами.
• Доказательство времени работы для объединения непересекающихся мно-
жеств заменено в разделе 21.4 доказательством, в котором точные границы
определяются с помощью метода потенциалов.
• Значительно упрощено доказательство корректности алгоритма из разде-
ла 22.5, предназначенного для сильно связных компонентов.

Стр. 34
Введение 35

• Реорганизована глава 24, посвященная задаче о кратчайших путях из одной


вершины: доказательства существенных свойств в ней вынесены в отдель-
ный раздел.
• В разделе 34.5 расширен обзор NP-полноты, приводятся новые доказатель-
ства NP-полноты задачи поиска гамильтонова цикла и задачи о сумме под-
множества.
Наконец, почти каждый раздел подредактирован, в результате чего в них вне-
сены исправления, упрощения, а также даны более четкие объяснения и доказа-
тельства.

Web-узел
Еще одно изменение по сравнению с первым изданием данной книги заклю-
чается в том, что теперь книга имеет свой Web-узел: http://mitpress.mit.
edu/algorithms/. С помощью этого Web-узла можно сообщить об ошибках,
получить список известных ошибок или внести предложения; авторы будут рады
узнать мнение читателей. Особенно приветствуются идеи по поводу новых задач
и упражнений, однако их следует предлагать вместе с решениями.
Авторы выражают сожаление, что не могут лично ответить на все замечания.

Благодарности к первому изданию


Большой вклад в эту книгу внесли многие друзья и коллеги, что способство-
вало повышению ее качества. Выражаем всем благодарность за помощь и кон-
структивные замечания.
Лаборатория вычислительной техники Массачуссетского технологического ин-
ститута предоставила идеальные рабочие условия. Особенно большую поддержку
оказали наши коллеги из группы Теории вычислений, входящей в состав лабора-
тории. Особую благодарность мы выражаем Баруху Авербаху (Baruch Awerbuch),
Шафи Гольдвассеру (Shafi Goldwasser), Лео Гибасу (Leo Guibas), Тому Лайтону
(Tom Leighton), Альберту Мейеру (Albert Meyer), Дэвиду Шмойсу (David Shmoys)
и Эве Тардош (Eva Tardos). Благодарим Вильяма Энга (William Ang), Салли Бемус
(Sally Bemus), Рея Хиршфелда (Ray Hirschfeld) и Марка Рейнгольда (Mark Rein-
hold) за поддержку в рабочем состоянии наших машин (DEC Microvaxes, Apple
Macintoshes и Sun Sparcstations), а также за перекомпилирование текста в фор-
мате TEX каждый раз, когда мы превышали наш лимит времени, выделенного на
компиляцию. Компания Machines Corporation предоставила частичную поддерж-
ку Чарльзу Лейзерсону (Charles Leiserson), позволившую ему работать над этой
книгой во время отпуска в Массачуссетском технологическом институте.
Многие наши коллеги опробовали черновые варианты этого учебника, читая
по ним курсы лекций на других факультетах. Они предложили многочисленные

Стр. 35
36 Введение

исправления и изменения. Особую благодарность хотелось бы выразить Ричар-


ду Биглю (Richard Beigel), Эндрю Гольдбергу (Andrew Goldberg), Джоан Лукас
(Joan Lucas), Марку Овермарсу (Mark Overmars), Алану Шерману (Alan Sherman)
и Дайан Сувейн (Diane Souvaine).
Многие ассистенты преподавателей, читающих курсы лекций по учебнику,
внесли значительный вклад в разработку материала. Особенно авторы благодар-
ны Алану Баратцу (Alan Baratz), Бонни Бергер (Bonnie Berger), Эдити Дагат (Aditi
Dhagat), Берту Калиски (Burt Kaliski), Артуру Ленту (Arthur Lent), Эндрю Мо-
ултону (Andrew Moulton), Мариосу Папаефсимиу (Marios Papaefthymiou), Синди
Филлипс (Cindy Phillips), Марку Рейнгольду (Mark Reinhold), Филу Рогевей (Phil
Rogaway), Флавио Роузу (Flavio Rose), Эйри Рудичу (Arie Rudich), Алану Шер-
ману (Alan Sherman), Клиффу Штайну (Cliff Stein), Сасмите Сюр (Susmita Sur),
Грегори Трокселу (Gregory Troxel) и Маргарет Таттл (Margaret Tuttle).
Ценную техническую помощь оказали многие частные лица. Дениз Серджент
(Denise Sergent) провела много часов в лабораториях Массачуссетского техноло-
гического института, исследуя библиографические ссылки. Мария Синсейл (Maria
Sensale), библиотекарь из нашего читального зала, была всегда приветливой и го-
товой оказать помощь. Много времени, отведенного для работы над литературой
при подготовке заключительных замечаний к главам, удалось сэкономить бла-
годаря предоставлению доступа к личной библиотеке Альберта Мейера (Albert
Meyer). Шломо Кипнис (Shlomo Kipnis), Билл Нихаус (Bill Niehaus) и Дэвид Вил-
сон (David Wilson) откорректировали старые упражнения, разработали новые и на-
писали примечания к их решениям. Мариос Папаефсимиу (Marios Papaefthymiou)
и Грегори Троксель (Gregory Troxel) внесли вклад в составление предметного
указателя. Неоценимую поддержку этого проекта в течение многих лет оказыва-
ли наши секретари Инна Радзиховски (Inna Radzihovsky), Дениз Серджент (Denise
Sergent), Гейли Шерман (Gayle Sherman) и особенно Би Блекбурн (Be Blackburn),
за что авторы им благодарны.
Многие ошибки в ранних черновых вариантах книги были обнаружены сту-
дентами. Особую благодарность за внимательное прочтение мы хотели бы вы-
разить Бобби Блюмоуфу (Bobby Blumofe), Бонни Эйзенберг (Bonnie Eisenberg),
Раймонду Джонсону (Raymond Johnson), Джону Кину (John Keen), Ричарду Ле-
тину (Richard Lethin), Марку Лиллибриджу (Mark Lillibridge), Джону Пезарису
(John Pezaris), Стиву Понзио (Steve Ponzio) и Маргарет Таттл (Margaret Tuttle).
Авторы благодарны коллегам, прорецензировавшим отдельные главы или пре-
доставившим информацию по отдельным алгоритмам. Особая благодарность вы-
ражается Биллу Айелло (Bill Aiello), Алоку Аггарвалю (Alok Aggarwal), Эрику
Баху (Eric Bach), Вашеку Чваталу (Vasek Chvatal), Ричарду Коулу (Richard Cole),
Джоан Хастад (Johan Hastad), Алексу Исии (Alex Ishii), Дэвиду Джонсону (David
Johnson), Джо Килиану (Joe Kilian), Дайне Кравец (Dina Kravets), Брюсу Маггсу
(Bruce Maggs), Джиму Орлину (Jim Orlin), Джеймсу Парку (James Park), Тейну

Стр. 36
Введение 37

Пламбеку (Thane Plambeck), Гершелю Сейферу (Hershel Safer), Джеффу Шаллиту


(Jeff Shallit), Клиффу Штайну (Cliff Stein), Гилу Стренгу (Gil Strang), Бобу Тар-
жану (Bob Tarjan) и Паулю Вонгу (Paul Wang). Несколько наших коллег также
любезно предоставили нам задачи; особенно мы благодарны Эндрю Гольдбергу
(Andrew Goldberg), Дэнни Слитору (Danny Sleator) и Умешу Вазирани (Umesh
Vazirani).
При написании учебника авторам было приятно работать с издательствами
The MIT Press и McGraw-Hill. Особую благодарность за моральную поддержку,
помощь и терпение они выражают сотрудникам издательства The MIT Press Фрэн-
ку Сэтлоу (Frank Satlow), Терри Элингу (Terry Ehling), Лари Кохену (Larry Cohen)
и Лори Лиджину (Lorrie Lejeune), а также Дэвиду Шапиро (David Shapiro) из из-
дательства McGraw-Hil. Наша особая благодарность — Лари Кохену (Larry Cohen)
за выдающуюся работу по корректированию.

Благодарности ко второму изданию


Когда мы предложили Джули Сассман (Julie Sussman, P.P.A.) быть техниче-
ским редактором второго издания, то даже не представляли себе, насколько удач-
ный договор заключаем. В дополнение к техническому редактированию, Джули
с энтузиазмом взялась за редактирование нашего текста. Стыдно вспомнить, как
много ошибок Джули обнаружила в ранних черновиках, хотя это не удивитель-
но с учетом того, сколько их она нашла в первом издании (к сожалению, после
его публикации). Более того, Джули пожертвовала своим собственным рабочим
графиком, чтобы согласовать его с нашим, — она даже взяла с собой главы из
нашей книги в поездку на Виргинские острова! Джули, мы никогда не сможем
отблагодарить вас в достаточной мере.
Во время работы над вторым изданием книги авторы работали в отделе вы-
числительной техники в Дартмутском колледже и лаборатории вычислительной
техники в Массачуссетском технологическом институте. Оба учебных заведения
послужили стимулирующей рабочей средой, и авторы выражают благодарность
своим коллегам.
Друзья и коллеги со всего мира вносили предложения и высказывали свое мне-
ние, что помогло направить усилия в нужное русло. Большое спасибо хотелось
бы сказать Сандживу Ароре (Sanjeev Arora), Джавиду Асламу (Javed Aslam), Гаю
Блеллоку (Guy Blelloch), Авриму Блюму (Avrim Blum), Скоту Дрисдейлу (Scot
Drysdale), Хэйни Фариду (Hany Farid), Халу Габову (Hal Gabow), Эндрю Гольд-
бергу (Andrew Goldberg), Дэвиду Джонсону (David Johnson), Янлинь Лю (Yanlin
Liu), Николасу Шабанелю (Nicolas Schabanel), Александеру Шрайверу (Alexan-
der Schrijver), Саше Шену (Sasha Shen), Дэвиду Шмойсу (David Shmoys), Дену
Шпильману (Dan Spielman), Джеральду Джею Сассману (Gerald Jay Sussman), Бо-

Стр. 37
38 Введение

бу Таржану (Bob Tarjan), Миккелю Торупу (Mikkel Thorup) и Виджею Вазирани


(Vijay Vazirani).
Многие преподаватели и коллеги охотно делились с нами своими знаниями по
теории алгоритмов. Особую признательность авторы выражают Джону Л. Бентли
(Jon L. Bentley), Бобу Флойду (Bob Floyd), Дону Кнуту (Don Knuth), Гарольду Куну
(Harold Kuhn), Кунгу (H. T. Kung), Ричарду Липтону (Richard Lipton), Арнольду
Россу (Arnold Ross), Ларри Снайдеру (Larry Snyder), Майклу Шамосу (Michael I.
Shamos), Дэвиду Шмойсу (David Shmoys), Кену Штайглицу (Ken Steiglitz), Тому
Шимански (Tom Szymanski), Эве Тардош (Eva Tardos), Бобу Таржану (Bob Tarjan)
и Джеффри Ульману (Jeffrey Ullman).
Авторы признательны за работу многим ассистентам, читающим курсы по
теории алгоритмов в Массачуссетском технологическом институте и колледже
г. Дартмут, включая Джозефа Адлера (Joseph Adler), Крэйга Баррака (Craig Bar-
rack), Бобби Блюмоуфа (Bobby Blumofe), Роберто де Приско (Roberto De Prisco),
Маттео Фриго (Matteo Frigo), Игала Гальперина (Igal Galperin), Дэвида Гупту
(David Gupta), Рея Д. Айера (Raj D. Iyer), Небила Каэйла (Nabil Kahale), Сарфраза
Хуршида (Sarfraz Khurshid), Ставроса Коллиопулоса (Stavros Kolliopoulos), Алена
Лебланка (Alain Leblanc), Юана Ма (Yuan Ma), Марию Минкофф (Maria Minkoff),
Димитриса Митсураса (Dimitris Mitsouras), Алина Попеску (Alin Popescu), Гераль-
да Прокопа (Harald Prokop), Судипту Сенгупту (Sudipta Sengupta), Донну Слоним
(Donna Slonim), Джошуа А. Таубера (Joshua A. Tauber), Сивана Толедо (Sivan Tole-
do), Элишеву Вернер-Рейсс (Elisheva Werner-Reiss), Ли Витти (Lea Wittie), Кван
Ву (Qiang Wu) и Майкла Женга (Michael Zhang).
Компьютерная поддержка была предоставлена Вильямом Энгом (William Ang),
Скоттом Бломквистом (Scott Blomquist) и Грэгом Шомо (Greg Shomo) из Массачус-
сетского технологического института, а также Вейном Криппсом (Wayne Cripps),
Джоном Конклом (John Konkle) и Тимом Трегубовым (Tim Tregubov) из Дартмута.
Авторы также благодарят Би Блекбурна (Be Blackburn), Дона Дейли (Don Dailey),
Ли Дикона (Leigh Deacon), Ирен Сибида (Irene Sebeda) и Шерил Паттон Ву (Cheryl
Patton Wu) из Массачуссетского технологического института и Филлис Беллмор
(Phyllis Bellmore), Келли Кларк (Kelly Clark), Делию Мауцели (Delia Mauceli),
Семми Тревиса (Sammie Travis), Деба Уитинга (Deb Whiting) и Бет Юнг (Beth
Young) из Дартмута за административную поддержку. Регулярную помощь так-
же оказывали Майкл Фромбергер (Michael Fromberger), Брайан Кемпбелл (Brian
Campbell), Аманда Эвбенкс (Amanda Eubanks), Санг Хун Ким (Sung Hoon Kim)
и Неха Нарула (Neha Narula) из Дартмута.
Многие читатели любезно сообщили об ошибках в первом издании. Авто-
ры благодарят перечисленных ниже людей, каждый из которых первым сообщил
о той или иной ошибке: Лен Адлеман (Len Adleman), Селим Экл (Selim Akl),
Ричард Андерсон (Richard Anderson), Жуан Андрейд-Цетто (Juan Andrade-Cetto),
Грегори Бачелис (Gregory Bachelis), Дэвид Баррингтон (David Barrington), Пол Бим

Стр. 38
Введение 39

(Paul Beame), Ричард Бигль (Richard Beigel), Маргрит Бетке (Margrit Betke), Алекс
Блейкмор (Alex Blakemore), Бобби Блюмоуф (Bobby Blumofe), Александр Браун
(Alexander Brown), Ксавье Кэйзин (Xavier Cazin), Джек Чен (Jack Chan), Ричард
Ченг (Richard Chang), Чинхуа Чен (Chienhua Chen), Йен Ченг (Ien Cheng), Хун
Чой (Hoon Choi), Дрю Коулс (Drue Coles), Кристиан Коллберг (Christian Collberg),
Джордж Коллинз (George Collins), Эрик Конрад (Eric Conrad), Питер Цазар (Peter
Csaszar), Пол Дитц (Paul Dietz), Мартин Дитцфельбингер (Martin Dietzfelbinger),
Скот Дрисдейл (Scot Drysdale), Патриция Илай (Patricia Ealy), Яков Эйзенберг
(Yaakov Eisenberg), Майкл Эрнст (Michael Ernst), Майкл Форменн (Michael For-
mann), Недим Фреско (Nedim Fresko), Хал Габов (Hal Gabow), Марек Галецки
(Marek Galecki), Игал Гальперин (Igal Galperin), Луиза Гаргано (Luisa Gargano),
Джон Гейтли (John Gately), Розарио Дженарио (Rosario Genario), Майэли Гереб
(Mihaly Gereb), Рональд Гринберг (Ronald Greenberg), Джерри Гроссман (Jerry
Grossman), Стефен Гуттери (Stephen Guattery), Александр Хартемик (Alexander
Hartemik), Энтони Хилл (Anthony Hill), Томас Гофмейстер (Thomas Hofmeister),
Мэтью Хостеттер (Mathew Hostetter), Юй-Чун Ху (Yih-Chun Hu), Дик Джонсон-
баух (Dick Johnsonbaugh), Марцин Юрдзинки (Marcin Jurdzinki), Набил Каэйл
(Nabil Kahale), Фумияки Камайя (Fumiaki Kamiya), Ананд Канагала (Anand Kana-
gala), Марк Кентровиц (Mark Kantrowitz), Скотт Карлин (Scott Karlin), Дин Келли
(Dean Kelley), Сэнджей Канна (Sanjay Khanna), Халук Конук (Haluk Konuk), Дайна
Кравец (Dina Kravets), Джон Кроугер (Jon Kroger), Бредли Казмаул (Bradley Kusz-
maul), Тим Ламберт (Tim Lambert), Хенг Лау (Hang Lau), Томас Ленгауэр (Thomas
Lengauer), Джордж Мадрид (George Madrid), Брюс Маггс (Bruce Maggs), Вик-
тор Миллер (Victor Miller), Джозеф Мускат (Joseph Muskat), Танг Нгуйен (Tung
Nguyen), Михаил Орлов (Michael Orlov), Джеймс Парк (James Park), Сионбин
Парк (Seongbin Park), Иоаннис Паскалидис (Ioannis Paschalidis), Боз Патт-Шамир
(Boaz Patt-Shamir), Леонид Пешкин (Leonid Peshkin), Патрицио Поблит (Patricio
Poblete), Ира Пол (Ira Pohl), Стивен Понцио (Stephen Ponzio), Кьелл Пост (Kjell
Post), Тодд Пойнор (Todd Poynor), Колин Препскиус (Colin Prepscius), Шолом Ро-
зен (Sholom Rosen), Дейл Рассел (Dale Russell), Гершель Сейфер (Hershel Safer),
Карен Зайдель (Karen Seidel), Джоэль Сейфирес (Joel Seiferas), Эрик Селиджман
(Erik Seligman), Стэнли Селков (Stanley Selkow), Джеффри Шаллит (Jeffrey Shal-
lit), Грэг Шеннон (Greg Shannon), Мика Шарир (Micha Sharir), Саша Шен (Sasha
Shen), Норман Шульман (Norman Shulman), Эндрю Зингер (Andrew Singer), Дэни-
ел Слитор (Daniel Sleator), Боб Слоан (Bob Sloan), Майкл Софка (Michael Sofka),
Фолькер Струмпен (Volker Strumpen), Лон Саншайн (Lon Sunshine), Джули Сас-
сман (Julie Sussman), Астерио Танака (Asterio Tanaka), Кларк Томборсон (Clark
Thomborson), Нильс Томмесен (Nils Thommesen), Гомер Тильтон (Homer Tilton),
Мартин Томпа (Martin Tompa), Андрей Тум (Andrei Toom), Фельзер Торстен (Felzer
Torsten), Хайенду Вэйшнав (Hirendu Vaishnav), М. Вельдхорст (M. Veldhorst), Люка
Венути (Luca Venuti), Джайн Вонг (Jian Wang), Майкл Веллман (Michael Wellman),

Стр. 39
40 Введение

Джерри Винер (Gerry Wiener), Рональд Вильямс (Ronald Williams), Дэвид Вольф
(David Wolfe), Джефф Вонг (Jeff Wong), Ричард Ваунди (Richard Woundy), Нил
Юнг (Neal Young), Гайан Ю (Huaiyuan Yu), Тайан Юксинг (Tian Yuxing), Джо
Зачари (Joe Zachary), Стив Жанг (Steve Zhang), Флориан Щоук (Florian Zschoke)
и Ури Цвик (Uri Zwick).
Многие наши коллеги написали подробные обзоры или заполнили длинные
бланки опросов. Авторы благодарят Нэнси Амато (Nancy Amato), Джима Аспне-
са (Jim Aspnes), Кевина Комптона (Kevin Compton), Вильяма Иванса (William
Evans), Питера Гекса (Peter Gacs), Майкла Гольдвассера (Michael Goldwasser),
Андржея Проскуровски (Andrzej Proskurowski), Виджея Рамачандрана (Vijaya Ra-
machandran) и Джона Райфа (John Reif). Мы также благодарим тех, кто участвовал
в опросе и вернул его результаты. Это Джеймс Абелло (James Abello), Джош Би-
нело (Josh Benaloh), Брайан Бересфорд-Смит (Bryan Beresford-Smith), Кеннет Бла
(Kenneth Blaha), Ганс Бодлаендер (Hans Bodlaender), Ричард Бори (Richard Borie),
Тед Браун (Ted Brown), Доменико Кантоун (Domenico Cantone), М. Чен (M. Chen),
Роберт Цимиковски (Robert Cimikowski), Вильям Клоксин (William Clocksin), Па-
уль Калл (Paul Cull), Рик Декер (Rick Decker), Мэтью Дикерсон (Matthew Dick-
erson), Роберт Дуглас (Robert Douglas), Маргарет Флек (Margaret Fleck), Майкл
Гудрич (Michael Goodrich), Сюзанн Гамбруш (Susanne Hambrusch), Дин Гендрикс
(Dean Hendrix), Ричард Джонсонбау (Richard Johnsonbaugh), Кирякос Калоркоти
(Kyriakos Kalorkoti), Шринивас Канканахалли (Srinivas Kankanahalli), Хикио Кох
(Hikyoo Koh), Стивен Линделль (Steven Lindell), Еррол Ллойд (Errol Lloyd), Энди
Лопес (Andy Lopez), Дайан Рей Лопес (Dian Rae Lopez), Джордж Лакер (George
Lucker), Дэвид Мейер (David Maier), Чарльз Мартель (Charles Martel), Ксайнонг
Менг (Xiannong Meng), Дэвид Маунт (David Mount), Альберто Поликрити (Al-
berto Policriti), Анджей Проскуровски (Andrzej Proskurowski), Кирк Прус (Kirk
Pruhs), Ив Робер (Yves Robert), Гуна Сизараман (Guna Seetharaman), Стэнли Сел-
ков (Stanley Selkow), Роберт Слоан (Robert Sloan), Чарльз Стил (Charles Steele),
Джерад Тель (Gerard Tel), Мурали Варанаси (Murali Varanasi), Бернд Уолтер (Bernd
Walter) и Альдин Райт (Alden Wright). Авторы хотели бы учесть все внесенные
предложения. Единственная проблема заключается в том, что если бы это было
сделано, объем второго издания мог бы превысить 3000 страниц!
Второе издание готовилось к публикации в LATEX 2ε . Майкл Даунс (Michael
Downes) преобразовал LATEX-сценарии из “классической” версии LATEX в LATEX 2ε
и изменил текстовые файлы таким образом, чтобы в них использовались эти сце-
нарии. Помощь в составлении текста в формате LATEX также оказал Дэвид Джонс
(David Jones). Рисунки ко второму изданию созданы авторами в программе Mac-
Draw Pro. Как и в первом издании, предметный указатель составлен с помощью
Windex, — программы на C, написанной авторами, а библиография подготовлена
с помощью BIBTEX. Айоркор Миллз-Титти (Ayorkor Mills-Tettey) и Роб Лизерн

Стр. 40
Введение 41

(Rob Leathern) помогли преобразовать рисунки в MacDraw Pro, а Айоркор также


проверил библиографию.
Как и во время работы над первым изданием, сотрудничество с издательствами
MIT Press и McGraw-Hill принесло авторам большое удовольствие. Редакторы Боб
Прайор (Bob Prior) из MIT Press и Бетси Джонс (Betsy Jones) были терпеливы
к нашим причудам и помогли преодолеть все препятствия.
Наконец, мы выражаем благодарность нашим женам Николь Кормен (Nicole
Cormen), Гейл Ривест (Gail Rivest) и Ребекке Иври (Rebecca Ivry), нашим де-
тям Рикки (Ricky), Вильяму (William) и Дебби Лейзерсон (Debby Leiserson),
Алексу (Alex) и Кристоферу Ривест (Christopher Rivest), а также Молли (Molly),
Ноа (Noah) и Бенджамену Штайн (Benjamin Stein), нашим родителям Рени (Re-
nee) и Перри (Perry) Корменам, Джин (Jean) и Марку (Mark) Лейзерсон, Ширли
(Shirley) и Ллойду (Lloyd) Ривестам, а также Ирен (Irene) и Ире Штайн (Ira Stein)
за любовь и поддержку во время написания этой книги. Этот проект стал воз-
можным благодаря поддержке и поощрению членов наших семей. С любовью
посвящаем им эту книгу.

ТОМАС КОРМЕН (THOMAS H. CORMEN), Ганновер, Нью-Гемпшир


ЧАРЛЬЗ ЛЕЙЗЕРСОН (CHARLES E. LEISERSON), Кембридж, Массачуссетс
РОНАЛЬД РИВЕСТ (RONALD L. RIVEST), Кембридж, Массачуссетс
КЛИФФОРД ШТАЙН (CLIFFORD STEIN), Ганновер, Нью-Гемпшир

Стр. 41
От издательства

Вы, читатель этой книги, и есть главный ее критик и комментатор. Мы ценим


ваше мнение и хотим знать, что было сделано нами правильно, что можно было
сделать лучше и что еще вы хотели бы увидеть изданным нами. Нам интересно
услышать и любые другие замечания, которые вам хотелось бы высказать в наш
адрес.
Мы ждем ваших комментариев и надеемся на них. Вы можете прислать нам
бумажное или электронное письмо, либо просто посетить наш Web-сервер и оста-
вить свои замечания там. Одним словом, любым удобным для вас способом дайте
нам знать, нравится или нет вам эта книга, а также выскажите свое мнение о том,
как сделать наши книги более интересными для вас.
Посылая письмо или сообщение, не забудьте указать название книги и ее авто-
ров, а также ваш обратный адрес. Мы внимательно ознакомимся с вашим мнением
и обязательно учтем его при отборе и подготовке к изданию последующих книг.
Наши координаты:
E-mail: [email protected]
WWW: http://www.williamspublishing.com
Информация для писем
из России:
из Украины: 03150, Киев, а/я 152

Стр. 42
ЧАСТЬ I
Основы

Стр. 43
Введение
Эта часть книги заставит вас задуматься о вопросах, связанных с разработкой
и анализом алгоритмов. Она была запланирована как вводный курс, в котором
рассматриваются способы определения алгоритмов, некоторые стратегии их раз-
работки, использующиеся в этой книге, а также применяемые при анализе алго-
ритмов различные основополагающие идеи. Кратко рассмотрим содержание глав
первой части.
В главе 1 рассматривается понятие алгоритма и его роль в современных
вычислительных системах. В ней приводится определение алгоритма и даются
некоторые примеры. Здесь также обосновывается положение о том, что алгорит-
мы — это такой же технологический продукт, как, например, аппаратное обеспече-
ние, графические интерфейсы пользователя, объектно-ориентированные системы
или сети.
В главе 2 читатель получит возможность ознакомиться с алгоритмами, с помо-
щью которых решается задача о сортировке последовательности из n чисел. Эти
алгоритмы сформулированы в виде псевдокода. Несмотря на то, что используе-
мый псевдокод напрямую не преобразуется ни в один из общепринятых языков
программирования, он вполне адекватно передает структуру алгоритма, поэтому
достаточно компетентный программист легко сможет реализовать его на любом
языке программирования. Для изучения выбраны алгоритм сортировки вставкой,
в котором используется инкрементный подход, и алгоритм сортировки слиянием,
который характеризуется применением рекурсивного метода, известного также
как метод “разделяй и властвуй” (метод разбиения). В обоих алгоритмах вре-
мя выполнения возрастает с ростом количества сортируемых элементов, однако
скорость этого роста зависит от выбранного алгоритма. В этой главе будет опре-
делено время работы изучаемых алгоритмов; кроме того, вы познакомитесь со
специальными обозначениями для выражения времени работы алгоритмов.
В главе 3 дается точное определение обозначений, введенных в главе 2 (ко-
торые называются асимптотическими обозначениями). В начале главы 3 опре-
деляется несколько асимптотических обозначений для оценки времени работы
алгоритма сверху и/или снизу. Остальные разделы главы в основном посвяще-
ны описанию математических обозначений. Их предназначение не столько в том,
чтобы ознакомить читателя с новыми математическими концепциями, сколько
в том, чтобы он смог убедиться, что используемые им обозначения совпадают
с принятыми в данной книге.
В главе 4 представлено дальнейшее развитие метода “разделяй и властвуй”,
введенного в главе 2. В частности, в главе 4 представлены методы решения рекур-
рентных соотношений, с помощью которых описывается время работы рекурсив-
ных алгоритмов. Бо́льшая часть главы посвящена обоснованию “метода контроля”
(master method), который используется для решения рекуррентных соотношений,

Стр. 44
Часть I. Основы 45

возникающих в алгоритмах разбиения. Заметим, что изучение доказательств мож-


но опустить без ущерба для понимания материала книги.
Глава 5 служит введением в анализ вероятностей и рандомизированные алго-
ритмы (т.е. такие, которые основаны на использовании случайных чисел). Анализ
вероятностей обычно применяется для определения времени работы алгоритма
в тех случаях, когда оно может изменяться для различных наборов входных па-
раметров, несмотря на то, что эти наборы содержат одно и то же количество
параметров. В некоторых случаях можно предположить, что распределение вход-
ных величин описывается некоторым известным законом распределения вероят-
ностей, а значит, время работы алгоритма можно усреднить по всем возможным
наборам входных параметров. В других случаях распределение возникает не из-за
входных значений, а в результате случайного выбора, который делается во время
работы алгоритма. Алгоритм, поведение которого определяется не только входны-
ми значениями, но и величинами, полученными с помощью генератора случайных
чисел, называется рандомизированным алгоритмом.
В приложениях А–В содержится дополнительный математический материал,
который окажется полезным в процессе чтения книги. Скорее всего, вы уже зна-
комы с основной частью материала, содержащегося в приложениях (хотя неко-
торые из встречавшихся вам ранее обозначений иногда могут отличаться от тех,
что приняты в данной книге). Поэтому к приложениям следует относиться как
к справочному материалу. С другой стороны, не исключено, что вы еще незнако-
мы с большинством вопросов, рассматриваемых в части I.

Стр. 45
ГЛАВА 1
Роль алгоритмов в вычислениях

Что такое алгоритмы? Стоит ли тратить время на их изучение? Какова роль


алгоритмов и как они соотносятся с другими компьютерными технологиями? Эта
глава дает ответы на поставленные вопросы.

1.1 Алгоритмы
Говоря неформально, алгоритм (algorithm) — это любая корректно опреде-
ленная вычислительная процедура, на вход (input) которой подается некоторая
величина или набор величин, и результатом выполнения которой является выход-
ная (output) величина или набор значений. Таким образом, алгоритм представляет
собой последовательность вычислительных шагов, преобразующих входные ве-
личины в выходные.
Алгоритм также можно рассматривать как инструмент, предназначенный для
решения корректно поставленной вычислительной задачи (computational prob-
lem). В постановке задачи в общих чертах задаются отношения между входом
и выходом. В алгоритме описывается конкретная вычислительная процедура, с по-
мощью которой удается добиться выполнения указанных отношений.
Например, может понадобиться выполнить сортировку последовательности
чисел в неубывающем порядке. Эта задача часто возникает на практике и служит
благодатной почвой для ознакомления на ее примере со многими стандартными
методами разработки и анализа алгоритмов. Задача сортировки (sorting problem)
формально определяется следующим образом.

Стр. 46
Глава 1. Роль алгоритмов в вычислениях 47

Вход: последовательность из n чисел a1 , a2 , . . . , an .


Выход: перестановка (изменение порядка) a1 , a2 , . . . , an  входной последова-
тельности таким образом, что для ее членов выполняется соотношение
a1  a2  · · ·  an .
Например, если на вход подается последовательность 31, 41, 59, 26, 41, 58, то
вывод алгоритма сортировки должен быть таким: 26, 31, 41, 41, 58, 59. Подобная
входная последовательность называется экземпляром (instance) задачи сортиров-
ки. Вообще говоря, экземпляр задачи состоит из ввода (удовлетворяющего всем
ограничениям, наложенным при постановке задачи), необходимого для решения
задачи.
В информатике сортировка является основополагающей операцией (во многих
программах она используется в качестве промежуточного шага), в результате чего
появилось большое количество хороших алгоритмов сортировки. Выбор наиболее
подходящего алгоритма зависит от многих факторов, в том числе от количества
сортируемых элементов, от их порядка во входной последовательности, от воз-
можных ограничений, накладываемых на члены последовательности, а также от
того, какое устройство используется для хранения последовательности: основная
память, магнитные диски или накопители на магнитных лентах.
Говорят, что алгоритм корректен (correct), если для каждого ввода резуль-
татом его работы является корректный вывод. Мы говорим, что корректный ал-
горитм решает данную вычислительную задачу. Если алгоритм некорректный,
то для некоторых вводов он может вообще не завершить свою работу или вы-
дать ответ, отличный от ожидаемого. Правда, некорректные алгоритмы иногда
могут оказаться полезными, если в них есть возможность контролировать частоту
возникновения ошибок. Такой пример рассматривается в главе 31, в которой изу-
чаются алгоритмы определения простых чисел, намного превышающих единицу.
Тем не менее, обычно мы заинтересованы только в корректных алгоритмах.
Алгоритм может быть задан на естественном языке, в виде компьютерной про-
граммы или даже воплощен в аппаратном обеспечении. Единственное требова-
ние — его спецификация должна предоставлять точное описание вычислительной
процедуры, которую требуется выполнить.

Какие задачи решаются с помощью алгоритмов?


Вычислительные задачи, для которых разработаны алгоритмы, отнюдь не огра-
ничиваются сортировкой. (Возможно, об их разнообразии можно судить по объему
данной книги.) Практическое применение алгоритмов чрезвычайно широко, о чем
свидетельствуют приведенные ниже примеры.
• Целью проекта по расшифровке генома человека является идентификация
всех 100 000 генов, входящих в состав ДНК человека, определение последо-

Стр. 47
48 Часть I. Основы

вательностей, образуемых 3 миллиардами базовых пар, из которых состоит


ДНК, сортировка этой информации в базах данных и разработка инстру-
ментов для ее анализа. Для реализации всех перечисленных этапов нужны
сложные алгоритмы. Решение разнообразных задач, являющихся составны-
ми частями данного проекта, выходит за рамки настоящей книги, однако
идеи, описанные во многих ее главах, используются для решения упомяну-
тых биологических проблем. Это позволяет ученым достигать поставленных
целей, эффективно используя вычислительные ресурсы. При этом эконо-
мится время (как машинное, так и затрачиваемое сотрудниками) и деньги,
а также повышается эффективность использования лабораторного оборудо-
вания.
• Internet позволяет пользователям в любой точке мира быстро получать до-
ступ к информации и извлекать ее в больших объемах. Управление и манипу-
ляция этими данными осуществляется с помощью хитроумных алгоритмов.
В число задач, которые необходимо решить, входит определение оптималь-
ных маршрутов, по которым перемещаются данные (методы для решения
этой задачи описываются в главе 24), и быстрый поиск страниц, на кото-
рых находится та или иная информация, с помощью специализированных
поисковых машин (соответствующие методы приводятся в главах 11 и 32).
• Электронная коммерция позволяет заключать сделки и предоставлять това-
ры и услуги с помощью электронных технических средств. Для того чтобы
она получила широкое распространение, важно иметь возможность защи-
щать такую информацию, как номера кредитных карт, пароли и банковские
счета. В число базовых технологий в этой области входят криптография
с открытым ключом и цифровые подписи (они описываются в главе 31),
основанные на численных алгоритмах и теории чисел.
• В производстве и коммерции очень важно распорядиться ограниченными
ресурсами так, чтобы получить максимальную выгоду. Нефтяной компании
может понадобиться информация о том, где пробурить скважины, чтобы по-
лучить от них как можно более высокую прибыль. Кандидат в президенты
может задаться вопросом, как потратить деньги, чтобы максимально повы-
сить свои шансы победить на выборах. Авиакомпаниям важно знать, какую
минимальную цену можно назначить за билеты на тот или иной рейс, чтобы
уменьшить количество свободных мест и не нарушить при этом законы, ре-
гулирующие авиаперевозку пассажиров. Поставщик услуг Internet должен
уметь так размещать дополнительные ресурсы, чтобы повышался уровень
обслуживания клиентов. Все эти задачи можно решить с помощью линей-
ного программирования, к изучению которого мы приступим в главе 29.
Несмотря на то, что некоторые детали представленных примеров выходят за
рамки настоящей книги, в ней приводятся основные методы, применяющиеся для

Стр. 48
Глава 1. Роль алгоритмов в вычислениях 49

их решения. В книге также показано, как решить многие конкретные задачи, в том
числе те, что перечислены ниже.
• Пусть имеется карта дорог, на которой обозначены расстояния между каж-
дой парой соседних перекрестков. Наша цель — определить кратчайший
путь от одного перекрестка к другому. Количество возможных маршрутов
может быть огромным, даже если исключить те из них, которые содержат
самопересечения. Как найти наиболее короткий из всех возможных марш-
рутов? При решении этой задачи карта дорог (которая сама по себе является
моделью настоящих дорог) моделируется в виде графа (мы подробнее по-
знакомимся с графами в главе 10 и приложении Б). Задача будет заключаться
в определении кратчайшего пути от одной вершины графа к другой. Эффек-
тивное решение этой задачи представлено в главе 24.
• Пусть дана последовательность A1 , A2 , . . . , An , образованная n матрица-
ми, и нам нужно найти произведение этих матриц. Поскольку матричное
произведение обладает свойством ассоциативности, существует несколь-
ко корректных порядков умножения. Например, если n = 4, перемноже-
ние матриц можно выполнять любым из следующих способов (опреде-
ляемых скобками): (A1 (A2 (A3 A4 ))), (A1 ((A2 A3 ) A4 )), ((A1 A2 ) (A3 A4 )),
((A1 (A2 A3 )) A4 ) или (((A1 A2 ) A3 ) A4 ). Если все эти матрицы квадратные
(т.е. одинакового размера), порядок перемножения не влияет на продолжи-
тельность процедуры. Если же матрицы различаются по размеру (но при
этом их размеры соответствуют правилам матричного умножения), то по-
рядок их перемножения может очень сильно влиять на время выполнения
процедуры. Количество всевозможных вариантов, различающихся порядком
перемножения матриц, растет с увеличением количества матриц по экспо-
ненциальному закону, поэтому для перебора всех этих вариантов может
потребоваться довольно длительное время. Из главы 15 мы узнаем, как эта
задача намного эффективнее решается с помощью общего метода, извест-
ного как динамическое программирование.
• Пусть имеется уравнение ax ≡ b (modn), где a, b и n — целые числа, и нуж-
но найти все целые x по модулю n, удовлетворяющие этому уравнению.
Оно может не иметь решения, может иметь одно или несколько решений.
Можно попытаться применить метод, заключающийся в последовательной
подстановке в уравнение чисел x = 0, 1, . . . , n−1, но в главе 31 представлен
более эффективный метод.
• Пусть имеется n принадлежащих плоскости точек, и нужно найти выпуклую
оболочку этих точек. Выпуклой оболочкой точек называется минимальный
выпуклый многоугольник, содержащий эти точки. Для решения этой задачи
удобно воспользоваться такой наглядной картиной: если представить точки
в виде вбитых в доску и торчащих из нее гвоздей, то выпуклую оболочку

Стр. 49
50 Часть I. Основы

можно получить, намотав на них резинку таким образом, чтобы все гвоз-
ди вошли внутрь замкнутой линии, образуемой резинкой. Каждый гвоздь,
вокруг которого обвивается резинка, становится вершиной выпуклой обо-
лочки (рис. 33.6). В качестве набора вершин может выступать любое из
2n подмножеств множества точек. Однако недостаточно знать, какие точ-
ки являются вершинами выпуклой оболочки, нужно еще указать порядок
их обхода. Таким образом, чтобы найти выпуклую оболочку, приходится
перебрать множество вариантов. В главе 33 описаны два хороших метода
определения выпуклой оболочки.
Приведенные выше случаи применения алгоритмов отнюдь не являются ис-
черпывающими, однако на их примере выявляются две общие характеристики,
присущие многим интересным алгоритмам.
1. Есть множество вариантов-кандидатов, большинство из которых решения-
ми не являются. Поиск среди них нужной комбинации является довольно
трудоемким.
2. Задачи имеют практическое применение. Простейший пример среди пере-
численных задач — определение кратчайшего пути, соединяющего два пере-
крестка. Любая компания, занимающаяся авто- или железнодорожными пе-
ревозками, финансово заинтересована в том, чтобы определить кратчайший
маршрут. Это способствовало бы снижению затрат труда и расходов горю-
чего. Маршрутизатору Internet также нужно иметь возможность находить
кратчайшие пути в сети, чтобы как можно быстрее доставлять сообщения.

Структуры данных
В книге представлен ряд структур данных. Структура данных (data struc-
ture) — это способ хранения и организации данных, облегчающий доступ к этим
данным и их модификацию. Ни одна структура данных не является универсаль-
ной и не может подходить для всех целей, поэтому важно знать преимущества
и ограничения, присущие некоторым из них.

Методические указания
Данную книгу можно рассматривать как “сборник рецептов” для алгоритмов.
Правда, однажды вам встретится задача, для которой вы не сможете найти опуб-
ликованный алгоритм (например, таковы многие из приведенных в книге упраж-
нений и задач). Данная книга научит вас методам разработки алгоритмов и их
анализа. Это позволит вам разрабатывать корректные алгоритмы и оценивать их
эффективность самостоятельно.

Стр. 50
Глава 1. Роль алгоритмов в вычислениях 51

Сложные задачи
Большая часть этой книги посвящена эффективным алгоритмам. Обычной
мерой эффективности для нас является скорость и время, в течение которого
алгоритм выдает результат. Однако существуют задачи, для которых неизвестны
эффективные методы их решения. В главе 34 рассматривается интересный вопрос,
имеющий отношение к подобным задачам, известным как NP-полные.
Почему же NP-полные задачи представляют интерес? Во-первых, несмотря
на то, что до сих пор не найден эффективный алгоритм их решения, также не
доказано, что такого алгоритма не существует. Другими словами, неизвестно, су-
ществует ли эффективный алгоритм для NP-полных задач. Во-вторых, набор NP-
полных задач обладает замечательным свойством. Оно заключается в том, что
если эффективный алгоритм существует хотя бы для одной из этих задач, то его
можно сформулировать и для всех остальных. Эта взаимосвязь между NP-пол-
ными задачами и отсутствие методов их эффективного решения вызывает еще
бо́льший интерес к ним. В третьих, некоторые NP-полные задачи похожи (но не
идентичны) на задачи, для которых известны эффективные алгоритмы. Небольшое
изменение формулировки задачи может значительно ухудшить эффективность са-
мого лучшего из всех известных алгоритмов.
Полезно располагать знаниями о NP-полных задачах, поскольку в реальных
приложениях некоторые из них возникают неожиданно часто. Если перед вами
встанет задача найти эффективный алгоритм для NP-полной задачи, скорее всего,
вы потратите много времени на безрезультатные поиски. Если же вы покажете, что
данная задача принадлежит к разряду NP-полных, то можно будет вместо самого
лучшего из всех возможных решений попробовать найти достаточно эффективное.
Рассмотрим конкретный пример. Компания грузового автотранспорта имеет
один центральный склад. Каждый день грузовики загружаются на этом складе
и отправляются по определенному маршруту, доставляя груз в несколько мест.
В конце рабочего дня грузовик должен вернуться на склад, чтобы на следующее
утро его снова можно было загрузить. Чтобы сократить расходы, компании нужно
выбрать оптимальный порядок доставки груза в различные точки. При этом рас-
стояние, пройденное грузовиком, должно быть минимальным. Эта задача хорошо
известна как “задача о коммивояжере”, и она является NP-полной. Эффективный
алгоритм решения для нее неизвестен, однако при некоторых предположениях
можно указать такие алгоритмы, в результате выполнения которых полученное
расстояние будет ненамного превышать минимально возможное. Подобные “при-
ближенные алгоритмы” рассматриваются в главе 35.

Стр. 51
52 Часть I. Основы

Упражнения
1.1-1. Приведите реальные примеры задач, в которых возникает одна из таких
вычислительных задач: сортировка, определение оптимального порядка
перемножения матриц, поиск выпуклой оболочки.
1.1-2. Какими еще параметрами, кроме скорости, можно характеризовать алго-
ритм на практике?
1.1-3. Выберите одну из встречавшихся вам ранее структур данных и опишите
ее преимущества и ограничения.
1.1-4. Что общего между задачей об определении кратчайшего пути и задачей
о коммивояжере? Чем они различаются?
1.1-5. Сформулируйте задачу, в которой необходимо только наилучшее реше-
ние. Сформулируйте также задачу, в которой может быть приемлемым
решение, достаточно близкое к наилучшему.

1.2 Алгоритмы как технология


Предположим, быстродействие компьютера и объем его памяти можно уве-
личивать до бесконечности. Была бы в таком случае необходимость в изучении
алгоритмов? Была бы, но только для того, чтобы продемонстрировать, что метод
решения имеет конечное время работы и что он дает правильный ответ.
Если бы компьютеры были неограниченно быстрыми, подошел бы любой
корректный метод решения задачи. Возможно, вы бы предпочли, чтобы реализа-
ция решения была выдержана в хороших традициях программирования (т.е. ка-
чественно разработана и аккуратно занесена в документацию), но чаще всего
выбирался бы метод, который легче всего реализовать.
Конечно же, сегодня есть весьма производительные компьютеры, но их быст-
родействие не может быть бесконечно большим. Память тоже дешевеет, но она не
может быть бесплатной. Таким образом, время вычисления — это такой же огра-
ниченный ресурс, как и объем необходимой памяти. Этими ресурсами следует
распоряжаться разумно, чему и способствует применение алгоритмов, эффектив-
ных в плане расходов времени и памяти.

Эффективность
Алгоритмы, разработанные для решения одной и то же задачи, часто очень
сильно различаются по эффективности. Эти различия могут быть намного зна-
чительнее, чем те, что вызваны применением неодинакового аппаратного и про-
граммного обеспечения.

Стр. 52
Глава 1. Роль алгоритмов в вычислениях 53

В качестве примера можно привести два алгоритма сортировки, которые рас-


сматриваются в главе 2. Для выполнения первого из них, известного как сортиров-
ка вставкой, требуется время, которое оценивается как c1 n2 , где n — количество
сортируемых элементов, а c1 — константа, не зависящая от n. Таким образом, вре-
мя работы этого алгоритма приблизительно пропорционально n2 . Для выполнения
второго алгоритма, сортировки слиянием, требуется время, приблизительно рав-
ное c2 n lg n, где lg n — краткая запись log2 n, а c2 — некоторая другая константа,
не зависящая от n. Обычно константа метода вставок меньше константы метода
слияния, т.е. c1 < c2 . Мы убедимся, что постоянные множители намного меньше
влияют на время работы алгоритма, чем множители, зависящие от n. Для двух
приведенных методов последние относятся как lg n к n. Для небольшого количе-
ства сортируемых элементов сортировка включением обычно работает быстрее,
однако когда n становится достаточно большим, все заметнее проявляется пре-
имущество сортировки слиянием, возникающее благодаря тому, что для больших
n незначительная величина lg n по сравнению с n полностью компенсирует разни-
цу величин постоянных множителей. Не имеет значения, во сколько раз константа
c1 меньше, чем c2 . С ростом количества сортируемых элементов обязательно бу-
дет достигнут переломный момент, когда сортировка слиянием окажется более
производительной.
В качестве примера рассмотрим два компьютера — А и Б. Компьютер А бо-
лее быстрый, и на нем работает алгоритм сортировки вставкой, а компьютер Б
более медленный, и на нем работает алгоритм сортировки методом слияния. Оба
компьютера должны выполнить сортировку множества, состоящего из миллиона
чисел. Предположим, что компьютер А выполняет миллиард инструкций в се-
кунду, а компьютер Б — лишь десять миллионов. Таким образом, компьютер А
работает в 100 раз быстрее, чем компьютер Б. Чтобы различие стало еще бо́льшим,
предположим, что код для метода вставок (т.е. для компьютера А) написан самым
лучшим в мире программистом с использованием команд процессора, и для сор-
тировки n чисел надо выполнить 2n2 команд (т.е. c1 = 2). Сортировка же методом
слияния (на компьютере Б) реализована программистом-середнячком с помощью
языка высокого уровня. При этом компилятор оказался не слишком эффективным,
и в результате получился код, требующий выполнения 50n lg n команд (т.е. c2 =
= 50). Для сортировки миллиона чисел компьютеру А понадобится
 2
2 · 106 команд
= 2000 с,
109 команд / с
а компьютеру Б —
50 · 106 · lg 106 команд
≈ 100 c.
107 команд / с
Как видите, использование кода, время работы которого возрастает медленнее,
даже при плохом компиляторе на более медленном компьютере требует на по-

Стр. 53
54 Часть I. Основы

рядок меньше процессорного времени! Если же нужно выполнить сортировку


10 миллионов чисел, то преимущество метода слияния становится еще более оче-
видным: если для сортировки вставкой потребуется приблизительно 2.3 дня, то
для сортировки слиянием — меньше 20 минут. Общее правило таково: чем боль-
ше количество сортируемых элементов, тем заметнее преимущество сортировки
слиянием.

Алгоритмы и другие технологии


Приведенный выше пример демонстрирует, что алгоритмы, как и аппаратное
обеспечение компьютера, представляют собой технологию. Общая производи-
тельность системы настолько же зависит от эффективности алгоритма, как и от
мощности применяющегося аппаратного обеспечения. В области разработки ал-
горитмов происходит такое же быстрое развитие, как и в других компьютерных
технологиях.
Возникает вопрос, действительно ли так важны алгоритмы, работающие на
современных компьютерах, если и так достигнуты выдающиеся успехи в других
областях высоких технологий, таких как:
• аппаратное обеспечение с высокими тактовыми частотами, конвейерной об-
работкой и суперскалярной архитектурой;
• легкодоступные, интуитивно понятные графические интерфейсы (GUI);
• объектно-ориентированные системы;
• локальные и глобальные сети.
Ответ — да, безусловно. Несмотря на то, что иногда встречаются приложения, ко-
торые не требуют алгоритмического наполнения (например, некоторые простые
Web-приложения), для большинства приложений определенное алгоритмическое
наполнение необходимо. Например, рассмотрим Web-службу, определяющую, как
добраться из одного места в другое (во время написания книги существовало
несколько таких служб). В основе ее реализации лежит высокопроизводительное
аппаратное обеспечение, графический интерфейс пользователя, глобальная сеть
и, возможно, объектно-ориентированный подход. Кроме того, для определенных
операций, выполняемых данной Web-службой, необходимо использование алго-
ритмов — например, таких как вычисление квадратных корней (что может потре-
боваться для определения кратчайшего пути), визуализации карт и интерполяции
адресов.
Более того, даже приложение, не требующее алгоритмического наполнения
на высоком уровне, сильно зависит от алгоритмов. Ведь известно, что работа
приложения зависит от производительности аппаратного обеспечения, а при его
разработке применяются разнообразные алгоритмы. Все мы также знаем, что

Стр. 54
Глава 1. Роль алгоритмов в вычислениях 55

приложение тесно связано с графическим интерфейсом пользователя, а для раз-


работки любого графического интерфейса пользователя требуются алгоритмы.
Вспомним приложения, работающие в сети. Чтобы они могли функционировать,
необходимо осуществлять маршрутизацию, которая, как уже говорилось, основана
на ряде алгоритмов. Чаще всего приложения составляются на языке, отличном от
машинного. Их код обрабатывается компилятором или интерпретатором, которые
интенсивно используют различные алгоритмы. И таким примерам нет числа.
Кроме того, ввиду постоянного роста вычислительных возможностей ком-
пьютеров, они применяются для решения все более сложных задач. Как мы уже
убедились на примере сравнительного анализа двух методов сортировки, с ростом
сложности решаемой задачи различия в эффективности алгоритмов проявляются
все значительнее.
Знание основных алгоритмов и методов их разработки — одна из характери-
стик, отличающих умелого программиста от новичка. Располагая современными
компьютерными технологиями, некоторые задачи можно решить и без основа-
тельного знания алгоритмов, однако знания в этой области позволяют достичь
намного большего.

Упражнения
1.2-1. Приведите пример приложения, для которого необходимо алгоритмиче-
ское наполнение на уровне приложений, и дайте характеристику функ-
ций, входящих в состав этих алгоритмов.
1.2-2. Предположим, на одной и той же машине проводится сравнительный
анализ реализаций двух алгоритмов сортировки, работающих по методу
вставок и по методу слияния. Для сортировки n элементов методом вста-
вок необходимо 8n2 шагов, а для сортировки методом слияния — 64n lg n
шагов. При каком значении n время сортировки методом вставок превы-
сит время сортировки методом слияния?
1.2-3. При каком минимальном значении n алгоритм, время работы которого
определяется формулой 100n2 , работает быстрее, чем алгоритм, время
работы которого выражается как 2n , если оба алгоритма выполняются на
одной и той же машине?

Задачи
1-1. Сравнение времени работы алгоритмов
Ниже приведена таблица, строки которой соответствуют различным функ-
циям f (n), а столбцы — значениям времени t. Определите максималь-
ные значения n, для которых задача может быть решена за время t, если

Стр. 55
56 Часть I. Основы

предполагается, что время работы алгоритма, необходимое для решения


задачи, равно f (n) микросекунд.
1 секунда 1 минута 1 час 1 день 1 месяц 1 год 1 век
lg n

n
n
n lg n
n2
n3
2n
n!

Заключительные замечания
Имеется множество учебников, посвященных общим вопросам, которые име-
ют отношение к алгоритмам. К ним относятся книги Ахо (Aho), Хопкрофта
(Hopcroft) и Ульмана (Ullman) [5,6], Бейза (Baase) и Ван Гельдера (Van Gelder)
[26], Брассарда (Brassard) и Бретли (Bratley) [46,47], Гудрича (Goodrich) и Та-
мазии (Tamassia) [128], Горовица (Horowitz), Сани (Sahni) и Раджисекарана (Ra-
jasekaran) [158], Кингстона (Kingston) [179], Кнута (Knuth) [182,183,185], Козе-
на (Kozen) [193], Манбера (Manber) [210], Мельхорна (Mehlhorn) [217,218,219],
Пурдома (Purdom) и Брауна (Brown) [252], Рейнгольда (Reingold), Ньевергель-
та (Nievergelt) и Део (Deo) [257], Седжевика (Sedgewick) [269], Скьены (Skiena)
[280] и Вильфа (Wilf) [315]. Некоторые аспекты разработки алгоритмов, имеющие
большую практическую направленность, обсуждаются в книгах Бентли (Bentley)
[39,40] и Гоннета (Gonnet) [126]. Обзоры по алгоритмам можно также найти в кни-
гах Handbook of Theoretical Computer Science, Volume A [302] и CRC Handbook on
Algorithms and Theory of Computation [24]. Обзоры алгоритмов, применяющихся
в вычислительной биологии, можно найти в учебниках Гасфилда (Gusfield) [136],
Певзнера (Pevzner) [240], Сетубала (Setubal) и Мейдениса (Meidanis) [272], а также
Вотермена (Waterman) [309].

Стр. 56
ГЛАВА 2
Приступаем к изучению

В этой главе мы ознакомимся с основными понятиями, с помощью которых


на протяжении всей книги будет проводиться разработка и анализ алгоритмов.
В начале главы исследуется алгоритм сортировки вставками. Он предназначен
для решения задачи сортировки, поставленной в главе 1. Для формулирования
алгоритмов мы используем псевдокод (который должен быть понятен читателям,
имеющим опыт программирования). Для каждого алгоритма обосновывается его
корректность и анализируется время работы. В ходе анализа указанного алгоритма
вводятся обозначения, используемые для указания зависимости времени работы
алгоритма от количества сортируемых элементов. После обсуждения сортировки
вставками описывается метод декомпозиции, основанный на принципе “разделяй
и властвуй”. Этот подход используется для разработки различных алгоритмов;
в данном случае с его помощью будет сформулирован алгоритм, получивший
название сортировки слиянием. В конце главы анализируется время работы этого
алгоритма сортировки.

2.1 Сортировка вставкой


Наш первый алгоритм, алгоритм сортировки методом вставок, предназначен
для решения задачи сортировки (sorting problem), сформулированной в главе 1.
Приведем еще раз эту формулировку.
Вход: последовательность из n чисел a1 , a2 , . . . , an .
Выход: перестановка (изменение порядка) a1 , a2 , . . . , an  входной последова-
тельности таким образом, что для ее членов выполняется соотношение
a1  a2  · · ·  an .

Стр. 57
58 Часть I. Основы

♣♣♣
7


♣♣
5♣ ♣
4 ♣♣ ♣♣ ♣
10
2 ♣
♣ ♣♣ ♣ ♣

♣♣ ♣
7
♣ ♣♣ ♣


5♣4 2♣
♣♣♣
♣♣ ♣
10

Рис. 2.1. Сортировка карт методом вставок

Сортируемые числа также известны под названием ключи (keys).


В этой книге алгоритмы обычно описываются в виде псевдокода, который во
многих отношениях похож на языки программирования C, Pascal и Java. Тем, кто
знаком хотя бы с одним из этих языков, не потребуется много усилий на чтение
алгоритмов. От обычного кода псевдокод отличается тем, что он выразителен,
а также лаконично, точно и понятно описывает алгоритм. Иногда в качестве тако-
вого выступает литературный язык, поэтому не удивляйтесь, если в инструкции
“настоящего” кода встретите обычную фразу или предложение. Другое различие
между псевдокодом и обычным кодом заключается в том, что в псевдокоде, как
правило, не рассматриваются некоторые вопросы, которые приходится решать раз-
работчикам программного обеспечения. Такие вопросы, как абстракция данных,
модульность и обработка ошибок часто игнорируются, чтобы более выразительно
передать суть, заложенную в алгоритме.
Наше изучение алгоритмов начинается с рассмотрения сортировки вставкой
(insertion sort). Этот алгоритм эффективно работает при сортировке небольшого
количества элементов. Сортировка вставкой напоминает способ, к которому при-
бегают игроки для сортировки имеющихся на руках карт. Пусть вначале в левой
руке нет ни одной карты, и все они лежат на столе рубашкой вверх. Далее со стола
берется по одной карте, каждая из которых помещается в нужное место среди карт,
которые находятся в левой руке. Чтобы определить, куда нужно поместить оче-
редную карту, ее масть и достоинство сравнивается с мастью и достоинством карт
в руке. Допустим, сравнение проводится в направлении слева направо (рис. 2.1).
В любой момент времени карты в левой руке будут рассортированы, и это будут
те карты, которые первоначально лежали в стопке на столе.

Стр. 58
Глава 2. Приступаем к изучению 59

Псевдокод сортировки методом вставок представлен ниже под названием IN-


SERTION_SORT. На его вход подается массив A [1..n], содержащий последователь-
ность из n сортируемых чисел (количество элементов массива A обозначено в этом
коде как length [A].) Входные числа сортируются без использования дополни-
тельной памяти: их перестановка производится в пределах массива, и объем
используемой при этом дополнительной памяти не превышает некоторую посто-
янную величину. По окончании работы алгоритма INSERTION_SORT входной массив
содержит отсортированную последовательность:
INSERTION_SORT(A)
1 for j ← 2 to length[A]
2 do key ← A[j]
3  Вставка элемента A[j] в отсортированную
 последовательность A[1..j − 1]
4 i←j−1
5 while i > 0 и A[i] > key
6 do A[i + 1] ← A[i]
7 i←i−1
8 A[i + 1] ← key

Инварианты цикла и корректность сортировки вставкой


На рис. 2.2 продемонстрировано, как этот алгоритм работает для массива
A = 5, 2, 4, 6, 1, 3. Элементы массива обозначены квадратиками, над которы-
ми находятся индексы, а внутри — значения соответствующих элементов. Части
а–д этого рисунка соответствуют итерациям цикла for в строках 1–8 псевдокода.
В каждой итерации черный квадратик содержит значение ключа, которое сравни-
вается со значениями серых квадратиков, расположенных слева от него (строка
псевдокода 5). Серыми стрелками указаны те значения массива, которые сдви-
гаются на одну позицию вправо (строка 6), а черной стрелкой — перемещение
ключа (строка 8). В части е показано конечное состояние сортируемого массива.

1 2 3 4 5 6 1 2 3 4 5 6 1 2 3 4 5 6
а) 5 2 4 6 1 3 б) 2 5 4 6 1 3 в) 2 4 5 6 1 3

1 2 3 4 5 6 1 2 3 4 5 6 1 2 3 4 5 6
г) 2 4 5 6 1 3 д) 1 2 4 5 6 3 е) 1 2 3 4 5 6

Рис. 2.2. Выполнение алгоритма INSERTION_SORT над массивом A =


= 5, 2, 4, 6, 1, 3

Стр. 59
60 Часть I. Основы

Индекс j указывает “текущую карту”, которая помещается в руку. В начале каж-


дой итерации внешнего цикла for с индексом j массив A состоит из двух частей.
Элементы A [1..j − 1] соответствуют отсортированным картам в руке, а элемен-
ты A [j + 1..n] — стопке карт, которые пока что остались на столе. Заметим, что
элементы A [1..j − 1] изначально тоже находились на позициях от 1 до j − 1, но
в другом порядке, а теперь они отсортированы. Назовем это свойство элементов
A [1..j − 1] инвариантом цикла (loop invariant) и сформулируем его еще раз.
В начале каждой итерации цикла for из строк 1–8 подмассив A [1..j − 1]
содержит те же элементы, которые были в нем с самого начала, но
расположенные в отсортированном порядке.
Инварианты цикла позволяют понять, корректно ли работает алгоритм. Необ-
ходимо показать, что инварианты циклов обладают следующими тремя свой-
ствами.
Инициализация. Они справедливы перед первой инициализацией цикла.
Сохранение. Если они истинны перед очередной итерацией цикла, то остаются
истинны и после нее.
Завершение. По завершении цикла инварианты позволяют убедиться в правиль-
ности алгоритма.
Если выполняются первые два свойства, инварианты цикла остаются истинными
перед каждой очередной итерацией цикла. Обратите внимание на сходство с ма-
тематической индукцией, когда для доказательства определенного свойства для
всех элементов упорядоченной последовательности нужно доказать его справед-
ливость для начального элемента этой последовательности, а затем обосновать
шаг индукции. В данном случае первой части доказательства соответствует обос-
нование того, что инвариант цикла выполняется перед первой итерацией, а второй
части — доказательство того, что инвариант цикла выполняется после очередной
итерации (шаг индукции).
Для наших целей третье свойство самое важное, так как нам нужно с помощью
инварианта цикла продемонстрировать корректность алгоритма. Оно также отли-
чает рассматриваемый нами метод от обычной математической индукции, в кото-
рой шаг индукции используется в бесконечных последовательностях. В данном
случае по окончании цикла “индукция” завершается.
Рассмотрим, соблюдаются ли эти свойства для сортировки методом вклю-
чений.
Инициализация. Начнем с того, что покажем справедливость инварианта цикла
перед первой итерацией, т.е. при j = 21 . Таким образом, подмножество эле-
ментов A [1..j − 1] состоит только из одного элемента A [1], сохраняющего
1
Если рассматривается цикл for, момент времени, когда проверяется справедливость инварианта
цикла перед первой итерацией, наступает сразу после начального присваивания значения индексу

Стр. 60
Глава 2. Приступаем к изучению 61

исходное значение. Более того, в этом подмножестве элементы рассорти-


рованы (тривиальное утверждение). Все вышесказанное подтверждает, что
инвариант цикла соблюдается перед первой итерацией цикла.
Сохранение. Далее, обоснуем второе свойство: покажем, что инвариант цикла
сохраняется после каждой итерации. Выражаясь неформально, можно ска-
зать, что в теле внешнего цикла for происходит сдвиг элементов A [j − 1],
A [j − 2], A [j − 3], . . . на одну позицию вправо до тех пор, пока не осво-
бодится подходящее место для элемента A [j] (строки 4–7), куда он и поме-
щается (строка 8). При более формальном подходе к рассмотрению второго
свойства потребовалось бы сформулировать и обосновать инвариант для
внутреннего цикла while. Однако на данном этапе мы предпочитаем не вда-
ваться в такие формальные подробности, поэтому будем довольствоваться
неформальным анализом, чтобы показать, что во внешнем цикле соблюда-
ется второе свойство.
Завершение. Наконец, посмотрим, что происходит по завершении работы цикла.
При сортировке методом включений внешний цикл for завершается, когда
j превышает n, т.е. когда j = n + 1. Подставив в формулировку инвари-
анта цикла значение n + 1, получим такое утверждение: в подмножестве
элементов A [1..n] находятся те же элементы, которые были в нем до начала
работы алгоритма, но расположенные в отсортированном порядке. Однако
подмножество A [1..n] и есть сам массив A! Таким образом, весь массив
отсортирован, что и подтверждает корректность алгоритма.
Метод инвариантов циклов будет применяться далее в данной главе, а также
в последующих главах книги.

Соглашения, принятые при составлении псевдокода


При составлении псевдокода использовались следующие соглашения.
1. Структура блоков указывается с помощью отступов. Например, тело цикла
for, начало которого — в строке 1, образовано строками 2–8, а тело цикла
while, который начинается в строке 5, состоит из строк 6 и 7, а строка 8 в него
не входит. Стиль отступов имеет силу также в инструкциях if-then-else. Ис-
пользование отступов вместо общепринятых указателей блочной структуры,
таких как инструкции begin и end, значительно уменьшает громоздкость.
При этом псевдокод не становится менее понятным, а даже наоборот2 .
цикла, но перед первой проверкой в заголовочной инструкции цикла. В листинге INSERTION_SORT
это момент, когда переменной j присвоено значение 2, но еще не выполнена проверка неравенства
j  length [A] .
2
В реальных языках программирования, вообще говоря, не рекомендуется применять отступы
для того, чтобы подчеркнуть структуру блоков, поскольку, если код разбит на несколько страниц,
то сложно определить уровень вложенности блоков.

Стр. 61
62 Часть I. Основы

2. Инструкции циклов while, for и repeat, а также общепринятая конструк-


ция if-then-else интерпретируются аналогично одноименным инструкциям
языка Pascal3 . Что касается цикла for, то здесь есть одно тонкое различие:
в языке Pascal после выхода из цикла значение переменной, выступающей
в роли счетчика цикла, становится неопределенным, а в данной книге счет-
чик сохраняет свое значение после выхода из цикла. Таким образом, сразу
после завершения цикла for значение счетчика равно значению, на единицу
превышающему верхнюю границу цикла. Это свойство уже использова-
лось для доказательства корректности алгоритма сортировки, работающего
по методу вставок. Заголовок цикла for, заданный в строке 1, имеет вид
for j ← 2 to length [A], поэтому по завершении цикла j = length [A] + 1
(или, что то же самое, j = n + 1).
3. Символ “” указывают, что оставшаяся часть строки — это комментарий.
4. Множественное присваивание, имеющее вид i ← j ← e, обозначает, что
значение выражения e присваивается обеим переменным i и j; оно анало-
гично двум последовательным инструкциям присваивания: j ← e и i ← j.
5. Переменные (такие как i, j и key) являются локальными по отношению
к данной процедуре. Глобальные переменные не применяются, если это не
указано явным образом.
6. Доступ к элементам массива осуществляется путем указания имени масси-
ва, после которого в квадратных скобках следует индекс. Например, A [i] —
это обозначение i-го элемента массива A. С помощью обозначения “..” ука-
зывается интервал значений, которые принимает индекс массива. Таким об-
разом, обозначение A [1..j] свидетельствует о том, что данное подмножество
массива A состоит из j элементов A [1], A [2], . . . , A [j].
7. Сложные данные обычно представляются в виде объектов, состоящих из
атрибутов и полей. Доступ к определенному полю осуществляется с по-
мощью имени поля, после которого в квадратных скобках указывается имя
объекта. Например, массив трактуется как объект с атрибутом length, ука-
зывающим количество его элементов. Чтобы указать количество элементов
массива A, нужно написать length [A]. Несмотря на то, что квадратные скоб-
ки используются и для индексирования элементов массива, и в атрибутах
объекта, их интерпретация будет понятна из контекста.
Переменная, которая используется в качестве имени массива или объекта,
трактуется как указатель на данные, представляющие этот массив или объ-
ект. Для всех полей f объекта x присваивание y ← x приводит к тому, что
f [y] = f [x]. Более того, если теперь выполнить присваивание f [x] ← 3,
3
Инструкции, эквивалентные перечисленным, есть в большинстве языков программирования
с блочной структурой, однако их синтаксис может немного отличаться от синтаксиса языка Pascal.

Стр. 62
Глава 2. Приступаем к изучению 63

то впоследствии будет выполняться не только соотношение f [x] = 3, но


и соотношение f [y] = 3. Другими словами, после присваивания y ← x
переменные x и y указывают на один и тот же объект.
Иногда указатель вообще не ссылается ни на какой объект. В таком случае
он имеет специальное значение NIL.
8. Параметры передаются в процедуру по значению: в вызывающей проце-
дуре создается своя собственная копия параметров, и если в вызванной
процедуре какому-то параметру присваивается значение, то в вызывающей
процедуре не происходит никаких изменений. Если передаются объекты, то
происходит копирование указателя на данные, представляющие этот объект,
но поля объекта не копируются. Например, если x — параметр вызванной
процедуры, то присваивание x ← y, выполненное в этой процедуре, неви-
димо для вызывающей процедуры. Однако присваивание f [x] ← 3 будет
видимым.
9. Логические операторы “и” и “или” вычисляются сокращенно (short circuit-
ing). Это означает, что при вычислении выражения “x и y” сначала вычис-
ляется значение выражения x. Если оно ложно, то все выражение не может
быть истинным, и значение выражения y не вычисляется. Если же выраже-
ние x истинно, то для определения значения всего выражения необходимо
вычислить выражение y. Аналогично, в выражении “x или y” величина y
вычисляется только в том случае, если выражение x ложно. Сокращенные
операторы позволяют составлять такие логические выражения, как “x = NIL
и f [x] = y”, не беспокоясь о том, что произойдет при попытке вычислить
выражение f [x], если x = NIL.

Упражнения
2.1-1. Используя в качестве модели рис. 2.2, проиллюстрируйте работу алгорит-
ма INSERTION_SORT по упорядочению массива A = 31, 41, 59, 26, 41, 58.
2.1-2. Перепишите процедуру INSERTION_SORT так, чтобы она выполняла сор-
тировку не в невозрастающем, а в неубывающем порядке.
2.1-3. Рассмотрим задачу поиска.
Вход: последовательность n чисел A = a1 , a2 , . . . , an  и величина v.
Выход: индекс i, обладающий свойством v = A [i], или значение NIL,
если в массиве A отсутствует значение v.
Составьте псевдокод линейного поиска, при работе которого выполня-
ется сканирование последовательности в поиске значения v. Докажите
корректность алгоритма с помощью инварианта цикла. Убедитесь, что

Стр. 63
64 Часть I. Основы

выбранные инварианты цикла удовлетворяют трем необходимым усло-


виям.
2.1-4. Рассмотрим задачу сложения двух двоичных целых чисел длиной n битов
каждое, хранящихся в массивах A и B, которые состоят из n элементов.
Сумму этих двух чисел необходимо занести в двоичной форме в мас-
сив C, состоящий из n + 1 элемента. Приведите строгую формулировку
задачи и составьте псевдокод для сложения этих двух чисел.

2.2 Анализ алгоритмов


Анализ алгоритма заключается в том, чтобы предсказать требуемые для его
выполнения ресурсы. Иногда оценивается потребность в таких ресурсах, как па-
мять, пропускная способность сети или необходимое аппаратное обеспечение,
но чаще всего определяется время вычисления. Путем анализа нескольких алго-
ритмов, предназначенных для решения одной и той же задачи, можно без труда
выбрать наиболее эффективный. В процессе такого анализа может также ока-
заться, что несколько алгоритмов примерно равноценны, а все остальные нужно
отбросить.
Прежде чем мы научимся анализировать алгоритмы, необходимо разработать
технологию, которая будет для этого использоваться. В эту технологию нужно
будет включить модель ресурсов и величины их измерения. С учетом того, что
алгоритмы реализуются в виде компьютерных программ, в этой книге в большин-
стве случаев в качестве технологии реализации принята модель обобщенной од-
нопроцессорной машины с памятью с произвольным доступом (Random-Access
Machine — RAM). В этой модели команды процессора выполняются последова-
тельно; одновременно выполняемые операции отсутствуют.
Строго говоря, в модели RAM следует точно определить набор инструкций
и время их выполнения, однако это утомительно и мало способствует пониманию
принципов разработки и анализа алгоритмов. С другой стороны, нужно соблю-
дать осторожность, чтобы не исказить модель RAM. Например, что будет, если
в RAM встроена команда сортировки? В этом случае сортировку можно выпол-
нять с помощью всего одной команды процессора. Такая модель нереалистична,
поскольку настоящие компьютеры не имеют подобных встроенных команд, а мы
ориентируемся именно на их устройство. В рассматриваемую модель входят те ко-
манды, которые обычно можно найти в реальных компьютерах: арифметические
(сложение, вычитание, умножение, деление, вычисление остатка деления, прибли-
жение действительного числа ближайшим меньшим или ближайшим большим це-
лым), операции перемещения данных (загрузка, занесение в память, копирование)
и управляющие (условное и безусловное ветвление, вызов подпрограммы и воз-

Стр. 64
Глава 2. Приступаем к изучению 65

врат из нее). Для выполнения каждой такой инструкции требуется определенный


фиксированный промежуток времени.
В модели RAM есть целочисленный тип данных и тип чисел с плавающей
точкой. Несмотря на то, что обычно в этой книге точность не рассматривается,
в некоторых приложениях она играет важную роль. Также предполагается, что
существует верхний предел размера слова данных. Например, если обрабатыва-
ются входные данные с максимальным значением n, обычно предполагается, что
целые числа представлены c lg n битами, где c — некоторая константа, не мень-
шая единицы. Требование c  1 обусловлено тем, что в каждом слове должно
храниться одно из n значений, что позволит индексировать входные элементы.
Кроме того, предполагается, что c — это конечная константа, поэтому объем слова
не может увеличиваться до бесконечности. (Если бы это было возможно, в одном
слове можно было бы хранить данные огромных объемов и осуществлять над
ними операции в рамках одной элементарной команды, что нереально.)
В реальных компьютерах содержатся команды, не упомянутые выше, которые
представляют “серую область” модели RAM. Например, является ли возведение
в степень элементарной командой? В общем случае — нет; чтобы вычислить выра-
жение xy , в котором x и y — действительные числа, потребуется несколько команд.
Однако в некоторых случаях эту операцию можно представить в виде элементар-
ной команды. Во многих компьютерах имеется команда побитового сдвига влево,
которая в течение времени, требуемого для выполнения элементарной команды,
сдвигает биты целого числа на k позиций влево. В большинстве случаев такой
сдвиг целого числа на одну позицию эквивалентен его умножению на 2. Сдвиг
битов на k позиций влево эквивалентен его умножению на 2k. Таким образом, на
этих компьютерах 2k можно вычислить с помощью одной элементарной инструк-
ции, сдвинув целое число 1 на k позиций влево; при этом k не должно превышать
количество битов компьютерного слова. Мы попытаемся избегать таких “серых
областей” модели RAM, однако вычисление 2k будет рассматриваться как элемен-
тарная операция, если k — достаточно малое целое положительное число.
В исследуемой модели RAM не предпринимаются попытки смоделировать
иерархию запоминающих устройств, общепринятую на современных компьюте-
рах. Таким образом, кэш и виртуальная память (которая чаще всего реализуется
в соответствии с требованиями страничной организации памяти) не моделирует-
ся. В некоторых вычислительных моделях предпринимается попытка смоделиро-
вать эффекты, вызванные иерархией запоминающих устройств, которые иногда
важны в реальных программах, работающих на реальных машинах. В ряде рас-
смотренных в данной книге задач принимаются во внимание эти эффекты, но
в большинстве случаев они не учитываются. Модели, включающие в себя иерар-
хию запоминающих устройств, заметно сложнее модели RAM, поэтому они мо-
гут затруднить работу. Кроме того, анализ, основанный на модели RAM, обычно

Стр. 65
66 Часть I. Основы

замечательно предсказывает производительность алгоритмов, выполняющихся на


реальных машинах.
Анализ даже простого алгоритма в модели RAM может потребовать значи-
тельных усилий. В число необходимых математических инструментов может вой-
ти комбинаторика, теория вероятностей, навыки алгебраических преобразований
и способность идентифицировать наиболее важные слагаемые в формуле. По-
скольку поведение алгоритма может различаться для разных наборов входных
значений, потребуется методика учета, описывающая поведение алгоритма с по-
мощью простых и понятных формул.
Даже когда для анализа данного алгоритма выбирается всего одна модель ма-
шины, нам все еще предстоит выбрать средства для выражения анализа. Хотелось
бы выбрать простые обозначения, которые позволят легко с ними работать и выяв-
лять важные характеристики требований, предъявляемых алгоритмом к ресурсам,
а также избегать сложных деталей.

Анализ алгоритма, работающего по методу вставок


Время работы процедуры INSERTION_SORT зависит от набора входных значе-
ний: для сортировки тысячи чисел требуется больше времени, чем для сортировки
трех чисел. Кроме того, время сортировки с помощью этой процедуры может быть
разным для последовательностей, состоящих из одного и того же количества эле-
ментов, в зависимости от степени упорядоченности этих последовательностей
до начала сортировки. В общем случае время работы алгоритма увеличивается
с увеличением количества входных данных, поэтому общепринятая практика —
представлять время работы программы как функцию, зависящую от количества
входных элементов. Для этого понятия “время работы алгоритма” и “размер вход-
ных данных” нужно определить точнее.
Наиболее адекватное понятие размера входных данных (input size) зависит от
рассматриваемой задачи. Во многих задачах, таких как сортировка или дискрет-
ные преобразования Фурье, это количество входных элементов, например, размер
n сортируемого массива. Для многих других задач, таких как перемножение двух
целых чисел, наиболее подходящая мера для измерения размера ввода — общее
количество битов, необходимых для представления входных данных в обычных
двоичных обозначениях. Иногда размер ввода удобнее описывать с помощью не
одного, а двух чисел. Например, если на вход алгоритма подается граф, размер
ввода можно описывать, указывая количество вершин и ребер графа. Для каж-
дой рассматриваемой далее задачи будет указываться способ измерения размера
входных данных.
Время работы алгоритма для того или иного ввода измеряется в количе-
стве элементарных операций, или “шагов”, которые необходимо выполнить. Здесь
удобно ввести понятие шага, чтобы рассуждения были как можно более машинно-

Стр. 66
Глава 2. Приступаем к изучению 67

независимыми. На данном этапе мы будем исходить из точки зрения, согласно ко-


торой для выполнения каждой строки псеводкода требуется фиксированное время.
Время выполнения различных строк может отличаться, но мы предположим, что
одна и та же i-я строка выполняется за время ci , где ci — константа. Эта точ-
ка зрения согласуется с моделью RAM и отражает особенности практической
реализации псевдокода на реальных компьютерах4 .
В последующих рассуждениях формула для выражения времени работы алго-
ритма INSERTION_SORT, которая сначала будет сложным образом зависеть от всех
величин ci , значительно упростится благодаря более лаконичным обозначениям,
с которыми проще работать. Эти более простые обозначения позволят легче опре-
делить, какой из двух алгоритмов эффективнее.
Начнем с того, что введем для процедуры INSERTION_SORT время выполнения
каждой инструкции и количество их повторений. Для каждого j = 2, 3, . . . , n,
где n = length [A], обозначим через tj количество проверок условия в цикле
while (строка 5). При нормальном завершении циклов for или while (т.е. когда
перестает выполняться условие, заданное в заголовке цикла) условие проверяется
на один раз больше, чем выполняется тело цикла. Само собой разумеется, мы
считаем, что комментарии не являются исполняемыми инструкциями, поэтому
они не увеличивают время работы алгоритма:
INSERTION_SORT(A) время количество раз
1 for j ← 2 to length[A] c1 n
2 do key ← A[j] c2 n−1
3  Вставка элемента A[j] в отсортированную
последовательность A[1..j − 1]. 0 n−1
4 i←j−1 c4 n−1
n
5 while i > 0 and A[i] > key c5 tj
j=2
n
6 do A[i + 1] ← A[i] c6 (tj − 1)
j=2
n
7 i←i−1 c7 j=2 (tj − 1)
8 A[i + 1] ← key c8 n−1
Время работы алгоритма — это сумма промежутков времени, необходимых для
выполнения каждой входящей в его состав исполняемой инструкции. Если вы-
4
Здесь есть некоторые тонкие нюансы. Шаги вычислений, описанные на обычном языке, часто
представляют собой разновидности процедур, состоящих из нескольких элементарных инструкций.
Например, далее в этой книге может встретиться строка “сортировка точек по координате x”. Как вы
увидите, эта команда требует больше, чем постоянного количества времени работы. Заметим также,
что команда вызова подпрограммы выполняется в течение фиксированного времени, однако сколько
длится выполнение вызванной подпрограммы — это зависит от ее сложности. Таким образом,
процесс вызова подпрограммы (передача в нее параметров и т.п. действия) следует отличать от
выполнения этой подпрограммы.

Стр. 67
68 Часть I. Основы

полнение инструкции длится в течение времени ci и она повторяется в алгоритме


n раз, то ее вклад в полное время работы алгоритма равно ci n.5 Чтобы вычислить
время работы алгоритма INSERTION_SORT (обозначим его через T (n)), нужно про-
суммировать произведения значений, стоящих в столбцах время и количество раз,
в результате чего получим:


n 
n
T (n) = c1 n + c2 (n − 1) + c4 (n − 1) + c5 t j + c6 (tj − 1)+
j=2 j=2

n
+ c7 (tj − 1) + c8 (n − 1) .
j=2

Даже если размер входных данных является фиксированной величиной, время


работы алгоритма может зависеть от степени упорядоченности сортируемых ве-
личин, которой они обладали до ввода. Например, самый благоприятный случай
для алгоритма INSERTION_SORT — это когда все элементы массива уже отсортиро-
ваны. Тогда для каждого j = 2, 3, . . . , n получается, что A [i]  key в строке 5,
еще когда i равно своему начальному значению j − 1. Таким образом, при j =
= 2, 3, . . . , n tj = 1, и время работы алгоритма в самом благоприятном случае
вычисляется так:

T (n) = c1 n + c2 (n − 1) + c4 (n − 1) + c5 (n − 1) + c8 (n − 1) =
= (c1 + c2 + c4 + c5 + c8 ) n − (c2 + c4 + c5 + c8 ) .

Это время работы можно записать как an + b, где a и b — константы, зависящие


от величин ci ; т.е. это время является линейной функцией от n.
Если элементы массива отсортированы в порядке, обратном требуемому (в дан-
ном случае в порядке убывания), то это наихудший случай. Каждый элемент A [j]
необходимо сравнивать со всеми элементами уже отсортированного подмноже-
ства A [1..j − 1], так что для j = 2, 3, . . . , n значения tj = j. С учетом того, что


n
n (n + 1)
j= −1
2
j=2

и

n
n (n − 1)
(j − 1) =
2
j=2

5
Это правило не всегда применимо к такому ресурсу, как память. Если инструкция оперирует
с m словами памяти и выполняется n раз, то это еще не означает, что всего при этом потребляется
mn слов памяти.

Стр. 68
Глава 2. Приступаем к изучению 69

(как выполняется это суммирование, показано в приложении А), получаем, что


время работы алгоритма INSERTION_SORT в худшем случае определяется соотно-
шением
 
n (n + 1)
T (n) = c1 n + c2 (n − 1) + c4 (n − 1) + c5 −1 +
2
   
n (n − 1) n (n − 1)
+ c6 + c7 + c8 (n − 1) =
2 2
c c6 c7  2  c 5 c6 c7 
5
= + + n + c1 + c2 + c4 + − − + c8 n−
2 2 2 2 2 2
− (c2 + c4 + c5 + c8 ) .

Это время работы можно записать как an2 + bn + c, где константы a, b и c зависят
от ci . Таким образом, это квадратичная функция от n.
Обычно время работы алгоритма фиксировано для определенных входных
данных, как в случае сортировки вставкой, однако в последующих главах мы
ознакомимся с некоторыми интересными алгоритмами, поведение которых носит
вероятностный характер. Время их работы может меняться даже для одних и тех
же входных данных.

Наихудшее и среднее время работы


Анализируя алгоритм, работающий по методу вставок, мы рассматривали как
наилучший, так и наихудший случай, когда элементы массива были рассорти-
рованы в порядке, обратном требуемому. Далее в этой книге мы будем уделять
основное внимание определению только времени работы в наихудшем случае,
т.е. максимальном времени работы из всех входных данных размера n. Тому есть
три причины.
• Время работы алгоритма в наихудшем случае — это верхний предел этой ве-
личины для любых входных данных. Располагая этим значением, мы точно
знаем, что для выполнения алгоритма не потребуется большее количество
времени. Не нужно будет делать каких-то сложных предположений о време-
ни работы и надеяться, что на самом деле эта величина не будет превышена.
• В некоторых алгоритмах наихудший случай встречается достаточно часто.
Например, если в базе данных происходит поиск информации, то наихудше-
му случаю соответствует ситуация, когда нужная информация в базе данных
отсутствует. В некоторых поисковых приложениях поиск отсутствующей
информации может происходить довольно часто.
• Характер поведения “усредненного” времени работы часто ничем не луч-
ше поведения времени работы для наихудшего случая. Предположим, что
последовательность, к которой применяется сортировка методом вставок,

Стр. 69
70 Часть I. Основы

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


определить, в какое место подмассива A [1..j − 1] следует поместить эле-
мент A [j]? В среднем половина элементов подмассива A [1..j − 1] меньше,
чем A [j], а половина — больше его. Таким образом, в среднем нужно прове-
рить половину элементов подмассива A [1..j − 1], поэтому tj приблизитель-
но равно j/2. В результате получается, что среднее время работы алгоритма
является квадратичной функцией от количества входных элементов, т.е. ха-
рактер этой зависимости такой же, как и для времени работы в наихудшем
случае.
В некоторых частных случаях нас будет интересовать среднее время рабо-
ты алгоритма, или его математическое ожидание6 . В главе 5 будет продемон-
стрирован метод вероятностного анализа, позволяющий определить ожидаемое
время работы. Однако при анализе усредненного времени работы возникает од-
на проблема, которая заключается в том, что не всегда очевидно, какие входные
данные для данной задачи будут “усредненными”. Часто делается предположе-
ние, что все наборы входных параметров одного и того же объема встречаются
с одинаковой вероятностью. На практике это предположение может не соблюдать-
ся, однако иногда можно применять рандомизированные алгоритмы, в которых
используется случайный выбор, и это позволяет провести вероятностный анализ.

Порядок возрастания
Для облегчения анализа процедуры INSERTION_SORT были сделаны некоторые
упрощающие предположения. Во-первых, мы проигнорировали фактическое вре-
мя выполнения каждой инструкции, представив эту величину в виде некоторой
константы ci . Далее мы увидели, что учет всех этих констант дает излишнюю ин-
формацию: время работы алгоритма в наихудшем случае выражается формулой
an2 + bn + c, где a, b, и c — некоторые константы, зависящие от стоимостей ci .
Таким образом, мы игнорируем не только фактические стоимости команд, но и их
абстрактные стоимости ci .
Теперь введем еще одно абстрактное понятие, упрощающее анализ. Это ско-
рость роста (rate of growth), или порядок роста (order of growth), времени рабо-
ты, который и интересует нас на самом деле. Таким образом, во внимание будет
приниматься только главный член формулы (т.е. в нашем случае an2 ), поскольку
при больших n членами меньшего порядка можно пренебречь. Кроме того, по-
стоянные множители при главном члене также будут игнорироваться, так как для
оценки вычислительной эффективности алгоритма с входными данными большо-
го объема они менее важны, чем порядок роста. Таким образом, время работы
6
Далее в книге строгий термин “математическое ожидание” некоторой величины зачастую для
простоты изложения заменяется термином “ожидаемое значение”, например, “ожидаемое время
работы алгоритма” означает “математическое ожидание времени работы алгоритма”. — Прим. ред.

Стр. 70
Глава 2. Приступаем к изучению 71

 
алгоритма, работающего по методу вставок, в наихудшем случае равно Θ n2
(произносится “тета от n в квадрате”). В этой главе Θ-обозначения используются
неформально; их строгое определение приводится в главе 3.
Обычно один алгоритм считается эффективнее другого, если время его работы
в наихудшем случае имеет более низкий порядок роста. Из-за наличия постоян-
ных множителей и второстепенных членов эта оценка может быть ошибочной,
если входные данные невелики.Однако
 если объем входных данных значитель-
ный, то, например, алгоритм Θ n 2 в наихудшем случае работает быстрее, чем
 
алгоритм Θ n3 .

Упражнения
2.2-1. Выразите функцию n3 /1000 − 100n2 − 100n + 3 в Θ-обозначениях.
2.2-2. Рассмотрим сортировку элементов массива A, которая производится так.
Сначала определяется наименьший элемент массива A, который ставится
на место элемента A [1], затем производится поиск второго наименьше-
го элемента массива A, который ставится на место элемента A [2]. Этот
процесс продолжается для первых n − 1 элементов массива A. Запи-
шите псевдокод этого алгоритма, известного как сортировка выбором
(selection sort). Какой инвариант цикла сохраняется для этого алгоритма?
Почему его достаточно выполнить для первых n − 1 элементов, а не для
всех n элементов? Определите время работы алгоритма в наилучшем
и наихудшем случаях и запишите его в Θ-обозначениях.
2.2-3. Рассмотрим алгоритм линейного поиска (см. упражнение 2.1-3). Для
скольких элементов входной последовательности в среднем нужно про-
извести проверку, если предполагается, что все элементы массива с рав-
ной вероятностью могут иметь искомое значение? Что происходит в наи-
худшем случае? Чему равно время работы алгоритма линейного поис-
ка в среднем и в наихудшем случае (в Θ-обозначениях)? Обоснуйте
ваш ответ.
2.2-4. Каким образом можно модифицировать почти каждый алгоритм, чтобы
получить оптимальное время работы в наилучшем случае?

2.3 Разработка алгоритмов


Есть много методов разработки алгоритмов. В алгоритме, работающем по ме-
тоду вставок, применяется инкрементный подход: располагая отсортированным
подмассивом A [1..j − 1], мы помещаем очередной элемент A [j] туда, где он дол-
жен находиться, в результате чего получаем отсортированный подмассив A [1..j].

Стр. 71
72 Часть I. Основы

В данном разделе рассматривается альтернативный подход к разработке ал-


горитмов, известный как метод разбиения (“разделяй и властвуй”). Разработаем
с помощью этого подхода алгоритм сортировки, время работы которого в наихуд-
шем случае намного меньше времени работы алгоритма, работающего по методу
включений. Одно из преимуществ алгоритмов, разработанных методом разбие-
ния, заключается в том, что время их работы часто легко определяется с помощью
технологий, описанных в главе 4.

2.3.1 Метод декомпозиции


Многие полезные алгоритмы имеют рекурсивную структуру: для решения дан-
ной задачи они рекурсивно вызывают сами себя один или несколько раз, чтобы
решить вспомогательную задачу, имеющую непосредственное отношение к по-
ставленной задаче. Такие алгоритмы зачастую разрабатываются с помощью мето-
да декомпозиции, или разбиения: сложная задача разбивается на несколько более
простых, которые подобны исходной задаче, но имеют меньший объем; далее эти
вспомогательные задачи решаются рекурсивным методом, после чего полученные
решения комбинируются с целью получить решение исходной задачи.
Парадигма, лежащая в основе метода декомпозиции “разделяй и властвуй”, на
каждом уровне рекурсии включает в себя три этапа.
Разделение задачи на несколько подзадач.
Покорение — рекурсивное решение этих подзадач. Когда объем подзадачи доста-
точно мал, выделенные подзадачи решаются непосредственно.
Комбинирование решения исходной задачи из решений вспомогательных задач.
Алгоритм сортировки слиянием (merge sort) в большой степени соответствует
парадигме метода разбиения. На интуитивном уровне его работу можно описать
таким образом.
Разделение: сортируемая последовательность, состоящая из n элементов, разби-
вается на две меньшие последовательности, каждая из которых содержит
n/2 элементов.
Покорение: сортировка обеих вспомогательных последовательностей методом
слияния.
Комбинирование: слияние двух отсортированных последовательностей для по-
лучения окончательного результата.
Рекурсия достигает своего нижнего предела, когда длина сортируемой последова-
тельности становится равной 1. В этом случае вся работа уже сделана, поскольку
любую такую последовательность можно считать упорядоченной.

Стр. 72
Глава 2. Приступаем к изучению 73

Основная операция, которая производится в процессе сортировки по методу


слияний, — это объединение двух отсортированных последовательностей в хо-
де комбинирования (последний этап). Это делается с помощью вспомогательной
процедуры MERGE(A, p, q, r), где A — массив, а p, q и r — индексы, нумерую-
щие элементы массива, такие, что p  q < r. В этой процедуре предполагается,
что элементы подмассивов A [p..q] и A [q + 1..r] упорядочены. Она сливает эти
два подмассива в один отсортированный, элементы которого заменяют текущие
элементы подмассива A [p..r].
Для выполнения процедуры MERGE требуется время Θ (n), где n = r − p + 1 —
количество подлежащих слиянию элементов. Процедура работает следующим об-
разом. Возвращаясь к наглядному примеру сортировки карт, предположим, что на
столе лежат две стопки карт, обращенных лицевой стороной вниз. Карты в каждой
стопке отсортированы, причем наверху находится карта наименьшего достоин-
ства. Эти две стопки нужно объединить в одну выходную, в которой карты будут
рассортированы и также будут обращены рубашкой вверх. Основной шаг состоит
в том, чтобы из двух младших карт выбрать самую младшую, извлечь ее из соот-
ветствующей стопки (при этом в данной стопке верхней откажется новая карта)
и поместить в выходную стопку. Этот шаг повторяется до тех пор, пока в одной из
входных стопок не кончатся карты, после чего оставшиеся в другой стопке карты
нужно поместить в выходную стопку. С вычислительной точки зрения выполне-
ние каждого основного шага занимает одинаковые промежутки времени, так как
все сводится к сравнению достоинства двух верхних карт. Поскольку необходи-
мо выполнить, по крайней мере, n основных шагов, время работы процедуры
слияния равно Θ (n).
Описанная идея реализована в представленном ниже псевдокоде, однако в нем
также есть дополнительное ухищрение, благодаря которому в ходе каждого ос-
новного шага не приходится проверять, является ли каждая из двух стопок пустой.
Идея заключается в том, чтобы поместить в самый низ обеих объединяемых колод
так называемую сигнальную карту особого достоинства, что позволяет упростить
код. Для обозначения сигнальной карты используется символ ∞. Не существу-
ет карт, достоинство которых больше достоинства сигнальной карты. Процесс
продолжается до тех пор, пока проверяемые карты в обеих стопках не окажутся
сигнальными. Как только это произойдет, это будет означать, что все несигналь-
ные карты уже помещены в выходную стопку. Поскольку заранее известно, что
в выходной стопке должно содержаться ровно r − p + 1 карта, выполнив такое
количество основных шагов, можно остановиться:
MERGE(A, p, q, r)
1 n1 ← q − p + 1
2 n2 ← r − q
3 Создаем массивы L[1..n1 + 1] и R[1..n2 + 1]

Стр. 73
74 Часть I. Основы

4 for i ← 1 to n1
5 do L[i] ← A[p + i − 1]
6 for j ← 1 to n2
7 do R[j] ← A[q + j]
8 L[n1 + 1] ← ∞
9 R[n2 + 1] ← ∞
10 i←1
11 j←1
12 for k ← p to r
13 do if L[i]  R[j]
14 then A[k] ← L[i]
15 i←i+1
16 else A[k] ← R[j]
17 j ←j+1
Подробно опишем работу процедуры MERGE. В строке 1 вычисляется длина
n1 подмассива A [p..q], а в строке 2 — длина n2 подмассива A [q + 1..r]. Далее
в строке 3 создаются массивы L (“левый” — “left”) и R (“правый” — “right”),
длины которых равны n1 + 1 и n2 + 1 соответственно. В цикле for в строках 4
и 5 подмассив A [p..q] копируется в массив L [1..n1 ], а в цикле for в строках 6 и 7
подмассив A [q + 1..r] копируется в массив R [1..n2 ]. В строках 8 и 9 последним
элементам массивов L и R присваиваются сигнальные значения.
Как показано на рис. 2.3, в результате копирования и добавления сигнальных
карт получаем массив L с последовательностью чисел 2, 4, 5, 7, ∞ и массив R
с последовательностью чисел 1, 2, 3, 6, ∞. Светло-серые ячейки массива A со-
держат конечные значения, а светло-серые ячейки массивов L и R — значения,
которые еще только должны быть скопированы обратно в массив A. В светло-се-
рых ячейках находятся исходные значения из подмассива A [9..16] вместе с двумя
сигнальными картами. В темно-серых ячейках массива A содержатся значения,
которые будут заменены другими, а в темно-серых ячейках массивов L и R —
значения, уже скопированные обратно в массив A. В частях рисунка а–з показано
состояние массивов A, L и R, а также соответствующие индексы k, i и j перед
каждой итерацией цикла в строках 12–17. В части и показано состояние масси-
вов и индексов по завершении работы алгоритма. На данном этапе подмассив
A [9..16] отсортирован, а два сигнальных значения в массивах L и R — един-
ственные элементы, оставшиеся в этих массивах и не скопированные в массив A.
В строках 10–17, проиллюстрированных на рис. 2.3, выполняется r − p + 1 основ-
ных шагов, в ходе каждого из которых производятся манипуляции с инвариантом
цикла, описанным ниже.

Стр. 74
Глава 2. Приступаем к изучению 75

8 9 10 11 12 13 14 15 16 17 8 9 10 11 12 13 14 15 16 17
A ... 2 4 5 7 1 2 3 6 ... A ... 1 4 5 7 1 2 3 6 ...
k k
1 2 3 4 5 1 2 3 4 5 1 2 3 4 5 1 2 3 4 5
L 2 4 5 7 ∞ R 1 2 3 6 ∞ L 2 4 5 7 ∞ R 1 2 3 6 ∞
i j i j
а) б)

8 9 10 11 12 13 14 15 16 17 8 9 10 11 12 13 14 15 16 17
A ... 1 2 5 7 1 2 3 6 ... A ... 1 2 2 7 1 2 3 6 ...
k k
1 2 3 4 5 1 2 3 4 5 1 2 3 4 5 1 2 3 4 5
L 2 4 5 7 ∞ R 1 2 3 6 ∞ L 2 4 5 7 ∞ R 1 2 3 6 ∞
i j i j
в) г)

8 9 10 11 12 13 14 15 16 17 8 9 10 11 12 13 14 15 16 17
A ... 1 2 2 3 1 2 3 6 ... A ... 1 2 2 3 4 2 3 6 ...
k k
1 2 3 4 5 1 2 3 4 5 1 2 3 4 5 1 2 3 4 5
L 2 4 5 7 ∞ R 1 2 3 6 ∞ L 2 4 5 7 ∞ R 1 2 3 6 ∞
i j i j
д) е)

8 9 10 11 12 13 14 15 16 17 8 9 10 11 12 13 14 15 16 17
A ... 1 2 2 3 4 5 3 6 ... A ... 1 2 2 3 4 5 6 6 ...
k k
1 2 3 4 5 1 2 3 4 5 1 2 3 4 5 1 2 3 4 5
L 2 4 5 7 ∞ R 1 2 3 6 ∞ L 2 4 5 7 ∞ R 1 2 3 6 ∞
i j i j
ж) з)

8 9 10 11 12 13 14 15 16 17
A ... 1 2 2 3 4 5 6 7 ...
k
1 2 3 4 5 1 2 3 4 5
L 2 4 5 7 ∞ R 1 2 3 6 ∞
i j
и)

Рис. 2.3. Операции, выполняемые в строках 10–17 процедуры MERGE(A, 9, 12, 16),
когда в подмассиве A [9..16] содержится последовательность 2, 4, 5, 7, 1, 2, 3, 6

Перед каждой итерацией цикла for в строках 12–17, подмас-


сив A [p..k − 1] содержит k − p наименьших элементов массивов
L [1..n1 + 1] и R [1..n2 + 1] в отсортированном порядке. Кроме то-
го, элементы L [i] и R [i] являются наименьшими элементами мас-
сивов L и R, которые еще не скопированы в массив A.

Стр. 75
76 Часть I. Основы

Необходимо показать, что этот инвариант цикла соблюдается перед первой


итерацией рассматриваемого цикла for, что каждая итерация цикла не нарушает
его, и что с его помощью удается продемонстрировать корректность алгоритма,
когда цикл заканчивает свою работу.
Инициализация. Перед первой итерацией цикла k = p, поэтому подмассив
A [p..k − 1] пуст. Он содержит k − p = 0 наименьших элементов масси-
вов L и R. Поскольку i = j = 1, элементы L [i] и R [j] — наименьшие
элементы массивов L и R, не скопированные обратно в массив A.
Сохранение. Чтобы убедиться, что инвариант цикла сохраняется после каждой
итерации, сначала предположим, что L [i]  R [j]. Тогда L [i] — наименьший
элемент, не скопированный в массив A. Поскольку в подмассиве A [p..k − 1]
содержится k −p наименьших элементов, после выполнения строки 14, в ко-
торой значение элемента L [i] присваивается элементу A [k], в подмассиве
A [p..k] будет содержаться k − p + 1 наименьший элемент. В результате
увеличения параметра k цикла for и значения переменной i (строка 15),
инвариант цикла восстанавливается перед следующей итерацией. Если же
выполняется неравенство L [i] > R [j], то в строках 16 и 17 выполняются
соответствующие действия, в ходе которых также сохраняется инвариант
цикла.
Завершение. Алгоритм завершается, когда k = r + 1. В соответствии с инвари-
антом цикла, подмассив A [p..k − 1] (т.е. подмассив A [p..r]) содержит k −
− p = r − p + 1 наименьших элементов массивов L [1..n1 + 1] и R [1..n2 + 1]
в отсортированном порядке. Суммарное количество элементов в массивах
L и R равно n1 + n2 + 2 = r − p + 3. Все они, кроме двух самых больших,
скопированы обратно в массив A, а два оставшихся элемента являются сиг-
нальными.
Чтобы показать, что время работы процедуры MERGE равно Θ (n), где n = r −
− p + 1, заметим, что каждая из строк 1–3 и 8–11 выполняется в течение фикси-
рованного времени; длительность циклов for в строках 4–7 равна Θ (n1 + n2 ) =
= Θ (n),7 а в цикле for в строках 12–17 выполняется n итераций, на каждую из
которых затрачивается фиксированное время.
Теперь процедуру MERGE можно использовать в качестве подпрограммы в ал-
горитме сортировки слиянием. Процедура MERGE_SORT(A, p, r) выполняет сор-
тировку элементов в подмассиве A [p..r]. Если справедливо неравенство p  r,
то в этом подмассиве содержится не более одного элемента, и, таким образом,
он отсортирован. В противном случае производится разбиение, в ходе которого
7
В главе 3 будет показано, как формально интерпретируются уравнения с Θ-обозначениями.

Стр. 76
Глава 2. Приступаем к изучению 77

Отсортированная последовательность

1 2 2 3 4 5 6 7

Слияние

2 4 5 7 1 2 3 6

Слияние Слияние

2 5 4 7 1 3 2 6

Слияние Слияние Слияние Слияние

5 2 4 7 1 3 2 6

Исходная последовательность

Рис. 2.4. Процесс сортировки массива A = 5, 2, 4, 7, 1, 3, 2, 6 методом сли-


яния. Длины подлежащих объединению отсортированных подпоследователь-
ностей возрастают по мере работы алгоритма

вычисляется индекс q, разделяющий массив A [p..r] на два подмассива: A [p..q]


с n/2
элементами и A [q..r] с n/2 элементами8 .
MERGE_SORT(A, p, r)
1 if p < r
2 then q ← (p + r)/2
3 MERGE_SORT(A, p, q)
4 MERGE_SORT(A, q + 1, r)
5 MERGE(A, p, q, r)
Чтобы отсортировать последовательность A = A [1] , A [2] , . . . , A [n], вызы-
вается процедура MERGE_SORT(A, 1, length [A]), где length [A] = n. На рис. 2.4
проиллюстрирована работа этой процедуры в восходящем направлении, если
n — это степень двойки. В ходе работы алгоритма происходит попарное объ-
единение одноэлементных последовательностей в отсортированные последова-
тельности длины 2, затем — попарное объединение двухэлементных последова-
тельностей в отсортированные последовательности длины 4 и т.д., пока не будут
8
Выражение x обозначает наименьшее целое число, которое больше или равно x, а выражение
x — наибольшее целое число, которое меньше или равно x. Эти обозначения вводятся в главе 3.
Чтобы убедиться в том, что в результате присваивания переменной q значения (p + r)/2 дли-
ны подмассивов A [p..q] и A [q + 1..r] получаются равными n/2 и n/2, достаточно проверить
четыре возможных случая, в которых каждое из чисел p и r либо четное, либо нечетное.

Стр. 77
78 Часть I. Основы

получены две последовательности, состоящие из n/2 элементов, которые объеди-


няются в конечную отсортированную последовательность длины n.

2.3.2 Анализ алгоритмов, основанных на принципе


“разделяй и властвуй”
Если алгоритм рекурсивно обращается к самому себе, время его работы часто
описывается с помощью рекуррентного уравнения, или рекуррентного соотно-
шения, в котором полное время, требуемое для решения всей задачи с объемом
ввода n, выражается через время решения вспомогательных подзадач. Затем дан-
ное рекуррентное уравнение решается с помощью определенных математических
методов, и устанавливаются границы производительности алгоритма.
Получение рекуррентного соотношения для времени работы алгоритма, осно-
ванного на принципе “разделяй и властвуй”, базируется на трех этапах, соответ-
ствующих парадигме этого принципа. Обозначим через T (n) время решения зада-
чи, размер которой равен n. Если размер задачи достаточно мал, скажем, n  c, где
c — некоторая заранее известная константа, то задача решается непосредственно
в течение определенного фиксированного времени, которое мы обозначим через
Θ (1). Предположим, что наша задача делится на a подзадач, объем каждой из
которых равен 1/b от объема исходной задачи. (В алгоритме сортировки мето-
дом слияния числа a и b были равны 2, однако нам предстоит ознакомиться со
многими алгоритмами разбиения, в которых a = b.) Если разбиение задачи на
вспомогательные подзадачи происходит в течение времени D (n), а объединение
решений подзадач в решение исходной задачи — в течение времени C (n), то мы
получим такое рекуррентное соотношение:

Θ (1) при n  c,
T (n) =
aT (n/b) + D (n) + C (n) в противном случае.

В главе 4 будет показано, как решаются рекуррентные соотношения такого вида.

Анализ алгоритма сортировки слиянием


Псевдокод MERGE_SORT корректно работает для произвольного (в том числе
и нечетного) количества сортируемых элементов. Однако если количество элемен-
тов в исходной задаче равно степени двойки, то анализ рекуррентного уравнения
упрощается. В этом случае на каждом шаге деления будут получены две подпо-
следовательности, размер которых точно равен n/2. В главе 4 будет показано, что
это предположение не влияет на порядок роста, полученный в результате решения
рекуррентного уравнения.
Чтобы получить рекуррентное уравнение для верхней оценки времени рабо-
ты T (n) алгоритма, выполняющего сортировку n чисел методом слияния, будем

Стр. 78
Глава 2. Приступаем к изучению 79

рассуждать следующим образом. Сортировка одного элемента методом слияния


длится в течение фиксированного времени. Если n > 1, время работы распреде-
ляется таким образом.
Разбиение. В ходе разбиения определяется, где находится средина подмассива.
Эта операция длится фиксированное время, поэтому D (n) = Θ (1).
Покорение. Рекурсивно решаются две подзадачи, объем каждой из которых со-
ставляет n/2. Время решения этих подзадач равно 2T (n/2).
Комбинирование. Как уже упоминалось, процедура MERGE в n-элементном под-
массиве выполняется в течение времени Θ (n), поэтому C (n) = Θ (n).
Сложив функции D (n) и C (n), получим сумму величин Θ (n) и Θ (1), ко-
торая является линейной функцией от n, т.е. Θ (n). Прибавляя к этой величине
слагаемое 2T (n/2), соответствующее этапу “покорения”, получим рекуррентное
соотношение для времени работы T (n) алгоритма сортировки по методу слияния
в наихудшем случае:

Θ (1) при n = 1,
T (n) = (2.1)
2T (n/2) + Θ (n) при n > 1.
В главе 4 мы ознакомимся с теоремой, с помощью которой можно показать, что
величина T (n) представляет собой Θ (n lg n), где lg n обозначает lg2 n. Посколь-
ку логарифмическая функция растет медленнее, чем линейная, то для достаточно
большого количества входных элементов производительность алгоритма сорти-
ровки методом слияния, время работы которого равно Θ (n lg n), превзойдет про-
изводительность алгоритма сортировки
 2 методом вставок, время работы которого
в наихудшем случае равно Θ n .
Правда, можно и без упомянутой теоремы интуитивно понять, что решением
рекуррентного соотношения (2.1) является выражение T (n) = Θ (n lg n). Пере-
пишем уравнение (2.1) в таком виде:

c при n = 1,
T (n) = (2.2)
2T (n/2) + cn при n > 1,
где константа c обозначает время, которое требуется для решения задачи? раз-
мер который равен 1, а также удельное (приходящееся на один элемент) время,
требуемое для разделения и сочетания9 .
9
Маловероятно, чтобы одна и та же константа представляла и время, необходимое для реше-
ния задачи, размер который равен 1, и приходящееся на один элемент время, в течение которого
выполняются этапы разбиения и объединения. Чтобы обойти эту проблему, достаточно предполо-
жить, что c — максимальный из перечисленных промежутков времени. В таком случае мы получим
верхнюю границу времени работы алгоритма. Если же в качестве c выбрать наименьший из всех
перечисленных промежутков времени, то в результате решения рекуррентного соотношения полу-
чим нижнюю границу времени работы алгоритма. Принимая во внимание, что обе границы имеют
порядок n lg n, делаем вывод, что время работы алгоритма ведет себя, как Θ (n lg n) .

Стр. 79
80 Часть I. Основы

T(n) cn cn

T(n/2) T(n/2) cn /2 cn /2

T(n/4) T(n/4) T(n/4) T(n/4)

а) б) в)

cn cn

cn /2 cn /2 cn

lg n
cn /4 cn /4 cn /4 cn /4 cn

..
.

c c c c c ... c c cn

г) Всего: cn lg n + cn

Рис. 2.5. Построение дерева рекурсии для уравнения T (n) = 2T (n/2) + cn

Процесс решения рекуррентного соотношения (2.2) проиллюстрирован на


рис. 2.5. Для удобства предположим, что n равно степени двойки. В части а
упомянутого рисунка показано время T (n), представленное в части б в виде
эквивалентного дерева, которое представляет рекуррентное уравнение. Корнем
этого дерева является слагаемое cn (стоимость верхнего уровня рекурсии), а два

Стр. 80
Глава 2. Приступаем к изучению 81

поддерева, берущих начало от корня, представляют две меньшие рекуррентные


последовательности T (n/2). В части в показан очередной шаг рекурсии. Время
выполнения каждого из двух подузлов, находящихся на втором уровне рекурсии,
равно cn/2. Далее продолжается разложение каждого узла, входящего в состав
дерева, путем разбиения их на составные части, определенные в рекуррентной
последовательности. Так происходит до тех пор, пока размер задачи не становит-
ся равным 1, а время ее выполнения — константе c. Получившееся в результате
дерево показано в части г. Дерево состоит из lg n + 1 уровней (т.е. его высота рав-
на lg n), а каждый уровень дает вклад в полное время работы, равный cn. Таким
образом, полное время работы алгоритма равно cn lg n + cn, что соответствует
Θ (n lg n).
После того как дерево построено, длительности выполнения всех его узлов
суммируются по всем уровням. Полное время выполнения верхнего уровня равно
cn, следующий уровень дает вклад, равный c (n/2) + c (n/2) = cn. Ту же вели-
чину вклада дают и все последующие уровни. В общем случае уровень i (если
вести отсчет сверху) имеет 2i узлов,
 каждый
 из которых дает вклад в общее вре-
мя работы алгоритма, равный c n/2i , поэтому
  полное время выполнения всех
принадлежащих уровню узлов равно 2i c n/2i = cn. На нижнем уровне имеется
n узлов, каждый из которых дает вклад c, что в сумме дает время, равное cn.
Полное количество уровней дерева на рис. 2.5 равно lg n + 1. Это легко понять
из неформальных индуктивных рассуждений. В простейшем случае, когда n = 1,
имеется всего один уровень. Поскольку lg 1 = 0, выражение lg n + 1 дает правиль-
ное количество уровней. Теперь в качестве индуктивного допущения примем, что
количество уровней рекурсивного дерева с 2i узлами равно lg 2i +1 = i+1 (так как
для любого i выполняется соотношение lg 2i = i). Поскольку мы предположили,
что количество входных элементов равняется степени двойки, то теперь нужно
рассмотреть случай для 2i+1 элементов. Дерево с 2i+1 узлами имеет на один уро-
вень больше, чем дерево с 2i узлами, поэтому полное количество уровней равно
(i + 1) + 1 = lg 2i+1 + 1.
Чтобы найти полное время, являющееся решением рекуррентного соотноше-
ния (2.2), нужно просто сложить вклады от всех уровней. Всего имеется lg n + 1
уровней, каждый из которых выполняется в течение времени cn, так что пол-
ное время равно cn (lg n + 1) = cn lg n + cn. Пренебрегая членами более низких
порядков и константой c, в результате получаем Θ (n lg n).

Упражнения
2.3-1. Используя в качестве образца рис. 2.4, проиллюстрируйте работу алго-
ритма сортировки методом слияний для массива A = 3, 41, 52, 26, 38, 57,
9, 49.

Стр. 81
82 Часть I. Основы

2.3-2. Перепишите процедуру MERGE так, чтобы в ней не использовались сиг-


нальные значения. Сигналом к остановке должен служить тот факт, что
все элементы массива L или массива R скопированы обратно в массив A,
после чего в этот массив копируются элементы, оставшиеся в непустом
массиве.
2.3-3. Методом математической индукции докажите, что если n равно степени
двойки, то решением рекуррентного уравнения

2 при n = 2,
T (n) =
2T (n/2) + n при n = 2k , k > 1

является T (n) = n lg n.
2.3-4. Сортировку вставкой можно представить в виде рекурсивной последо-
вательности следующим образом. Чтобы отсортировать массив A [1..n],
сначала нужно выполнить сортировку массива A [1..n − 1], после чего
в этот отсортированный массив помещается элемент A [n]. Запишите
рекуррентное уравнение для времени работы этой рекурсивной версии
алгоритма сортировки вставкой.
2.3-5. Возвращаясь к задаче поиска (см. упражнение 2.1-3), нетрудно заметить,
что если последовательность A отсортирована, то значение среднего эле-
мента этой последовательности можно сравнить с искомым значением v
и сразу исключить половину последовательности из дальнейшего рас-
смотрения. Двоичный (бинарный) поиск (binary search) — это алгоритм,
в котором такая процедура повторяется неоднократно, что всякий раз
приводит к уменьшению оставшейся части последовательности в два ра-
за. Запишите псевдокод алгоритма бинарного поиска (в итерационном
или рекурсивном виде). Докажите, что время работы этого алгоритма
в наихудшем случае возрастает как Θ (lg n).
2.3-6. Обратите внимание, что в цикле while в строках 5–7 процедуры INSER-
TION_SORT в разделе 2.1, используется линейный поиск для просмотра
(в обратном порядке) отсортированного подмассива A [1..j − 1]. Можно
ли использовать бинарный поиск (см. упражнение 2.3-5) вместо линейно-
го, чтобы время работы этого алгоритма в наихудшем случае улучшилось
и стало равным Θ (n lg n)?
 2.3-7. Пусть имеется множество S, состоящее из n целых чисел, и отдельное
целое число x; необходимо определить, существуют ли во множестве S
два элемента, сумма которых равна x. Разработайте алгоритм решения
этой задачи, время работы которого возрастало бы с увеличением n как
Θ (n lg n).

Стр. 82
Глава 2. Приступаем к изучению 83

Задачи
2-1. Сортировка вставкой для небольших подмассивов, возникающих
при сортировке слиянием
Несмотря на то, что с увеличением количества сортируемых элемен-
тов время сортировки методом слияний в наихудшем случае
 растет как
Θ (n lg n), а время сортировки методом вставок — как Θ n2 , благода-
ря постоянным множителям для малых n сортировка методом вставок
выполняется быстрее. Таким образом, есть смысл использовать сорти-
ровку методом вставок в процессе сортировки методом слияний, когда
подзадачи становятся достаточно маленькими. Рассмотрите модифика-
цию алгоритма сортировки, работающего по методу слияний, когда n/k
подмассивов длины k сортируются методом вставок, после чего они объ-
единяются с помощью обычного механизма слияния. Величина k должна
быть найдена в процессе решения задачи.
а) Покажите, что n/k подмассивов, в каждом из которых находится
k элементов, в наихудшем случае можно отсортировать по методу
вставок за время Θ (nk).
б) Покажите, что в наихудшем случае время, требуемое для объедине-
ния этих подмассивов, равно Θ (n lg (n/k)).
в) Допустим, что время работы модифицированного алгоритма в наи-
худшем случае равно Θ (nk + n lg (n/k)). Определите максималь-
ное асимптотическое (в Θ-обозначениях) значение k как функцию
от n, для которого асимптотическое время работы модифицирован-
ного алгоритма равно времени работы стандартного алгоритма сор-
тировки методом слияния.
г) Как следует выбирать параметр k на практике?
2-2. Корректность пузырьковой сортировки
Пузырьковый метод — популярный алгоритм сортировки. В его осно-
ве лежит многократная перестановка соседних элементов, нарушающих
порядок сортировки:

BUBBLESORT(A)
1 for i ← 1 to length[A]
2 do for j ← length[A] downto i + 1
3 do if A[j] < A[j − 1]
4 then Поменять местами A[j] ↔ A[j − 1]
а) Пусть A — выходной массив, полученный в результате работы про-
цедуры BUBBLESORT(A). Чтобы доказать, что этот алгоритм работает

Стр. 83
84 Часть I. Основы

корректно, нужно доказать, что он выполняется в течение конечного


времени и что выполняются неравенства

A [1]  A [2]  · · ·  A [n] , (2.3)

где n = length [A]. Что еще необходимо доказать, чтобы стало оче-
видно, что в ходе работы алгоритма BUBBLESORT действительно вы-
полняется сортировка входного массива?
В следующих двух пунктах доказываются неравенства (2.3).
б) Дайте точную формулировку инварианта цикла for в строках 2–4,
и докажите, что он сохраняется. Доказательство должно иметь та-
кую же структуру доказательства инварианта цикла, которая исполь-
зовалась в аналогичных доказательствах в данной главе.
в) С помощью условия завершения инварианта цикла, доказанного
в пункте б, сформулируйте инвариант цикла for в строках 1–4, кото-
рый бы позволил доказать неравенства (2.3). Доказательство должно
иметь такую же структуру доказательства инварианта цикла, кото-
рая использовалась в аналогичных доказательствах в данной главе.
г) Определите время пузырьковой сортировки в наихудшем случае
и сравните его со временем сортировки методом вставок.
2-3. Корректность правила Горнера
В приведенном ниже фрагменте кода реализовано правило Горнера (Hor-
ner’s rule), позволяющее вычислить значение полинома


n
P (x) = ak xk =
k=0
= a0 + x (a1 + x (a2 + · · · + x (an−1 + xan ) . . .))

по заданным коэффициентам a0 , a1 , . . . , an и величине x:

1 y←0
2 i←n
3 while i ≥ 0
4 do y ← ai + x · y
5 i←i−1
а) Определите асимптотическое время работы приведенного выше
фрагмента кода.
б) Напишите псевдокод, реализующий алгоритм обычного вычисления
полинома, когда каждое слагаемое полинома вычисляется отдельно.

Стр. 84
Глава 2. Приступаем к изучению 85

Определите асимптотическое время работы этого алгоритма и срав-


ните его со временем работы алгоритма, основанного на правиле
Горнера.
в) Докажите, что ниже сформулирован инвариант цикла while в стро-
ках 3–5.
В начале каждой итерации цикла while в строках 3–5 вы-
полняется такое соотношение:


n−(j+1)
y= ak+i+1 xk .
k=0

Суммирование по нулевому количеству слагаемых интерпретирует-


ся как 0. Доказательство должно иметь такую же структуру доказа-
тельства инварианта цикла, которая использовалась в аналогичных
доказательствах в данной главе. В ходе доказательства необходи-
мо продемонстрировать,
 что по завершении алгоритма выполняется
соотношение y = nk=0 ak xk .
г) В заключение продемонстрируйте, что в приведенном фрагменте
кода правильно вычисляется значение полинома, который задается
коэффициентами a0 , a1 , . . . , an .
2-4. Инверсии
Предположим, что A [1..n] — это массив, состоящий из n различных чи-
сел. Если i < j и A [i] > A [j], то пара (i, j) называется инверсией
(inversion) в массиве A.
а) Перечислите пять инверсий, содержащихся в массиве 2, 3, 8, 6, 1.
б) Сконструируйте массив из элементов множества {1, 2, . . . , n}, со-
держащий максимальное количество инверсий. Сколько инверсий
в этом массиве?
в) Какая взаимосвязь между временем сортировки методом вставок
и количеством инверсий во входном массиве? Обоснуйте ваш ответ.
г) Сформулируйте алгоритм, определяющий количество инверсий, со-
держащихся в произвольной перестановке n элементов, время ра-
боты которого в наихудшем случае равно Θ (n lg n). (Указание: мо-
дифицируйте алгоритм сортировки слиянием.)

Стр. 85
86 Часть I. Основы

Заключительные замечания
В 1968 году Кнут (Knuth) опубликовал первый из трех томов, объединен-
ных названием The Art of Computer Programming (Искусство программирования)
[182,183,185]. Этот том стал введением в современные компьютерные алгоритмы
с акцентом на анализе времени их работы, а весь трехтомник до сих пор остается
интереснейшим и ценным пособием по многим темам, представленным в дан-
ной книге. Согласно Кнуту, слово “алгоритм” происходит от имени персидского
математика девятнадцатого века аль-Хорезми (al-Khowârizmı̂).
Ахо (Aho), Хопкрофт (Hopcroft) и Ульман (Ullman) [5] являются сторонника-
ми асимптотического анализа алгоритмов, являющегося средством сравнения их
относительной производительности. Они также популяризируют применение ре-
куррентных соотношений для описания времени работы рекурсивных алгоритмов.
Кнут [185] с энциклопедической полнотой рассмотрел многие алгоритмы сор-
тировки. Его сравнение алгоритмов сортировки включает в себя подробный ана-
лиз с точным подсчетом шагов, подобный тому, что был проведен в этой книге
для сортировки методом вставок. В ходе обсуждения Кнутом алгоритма сорти-
ровки вставкой приводится несколько вариаций этого алгоритма. Важнейшей из
них является сортировка по Шеллу (D.L. Shell), который использовал сортировку
методом вставок для упорядочения периодических последовательностей, чтобы
получить более производительный алгоритм сортировки.
В книге Кнута также описана сортировка слиянием. В книге упоминается,
что в 1938 году была создана механическая машина, способная за один проход
объединять две стопки перфокарт. По всей видимости, первым программу для сор-
тировки методом слияния (предназначенную для компьютера EDVAC) в 1945 году
разработал Джон фон Нейман (J. von Neumann), который был одним из первых
создателей теории вычислительных машин.
Ранние этапы развития в области доказательства корректности программ опи-
саны Гризом (Gries) [133], который считает, что первая статья по этой теме была
написана Науром (P. Naur). Авторство понятия инварианта цикла Гриз припи-
сывает Флойду (R.W. Floyd). В учебнике Митчелла (Mitchell) [222] описывается
прогресс, достигнутый в области доказательства корректности программ в насто-
ящее время.

Стр. 86
ГЛАВА 3
Рост функций

Определенный в главе 2 порядок роста, характеризующий время работы ал-


горитма, является наглядной характеристикой эффективности алгоритма, а также
позволяет сравнивать производительность различных алгоритмов. Если количе-
ство сортируемых элементов n становится достаточно большим, производитель-
ность алгоритма сортировки по методу слияний, время работы которого в наи-
худшем случае возрастает как Θ (n lg n), становится выше производительности
алгоритма сортировки
  вставкой, время работы которого в наихудшем случае воз-
растает как Θ n2 . Несмотря на то, что в некоторых случаях можно определить
точное время работы алгоритма, как это было сделано для алгоритма сортировки
методом вставок в главе 2, обычно не стоит выполнять оценку с большой точно-
стью. Для достаточно больших входных данных постоянные множители и слагае-
мые низшего порядка, фигурирующие в выражении для точного времени работы
алгоритма, подавляются эффектами, вызванными увеличением размера ввода.
Рассматривая входные данные достаточно больших размеров для оценки толь-
ко такой величины, как порядок роста времени работы алгоритма, мы тем самым
изучаем асимптотическую эффективность алгоритмов. Это означает, что нас
интересует только то, как время работы алгоритма растет с увеличением размера
входных данных в пределе, когда этот размер увеличивается до бесконечности.
Обычно алгоритм, более эффективный в асимптотическом смысле, будет более
производительным для всех входных данных, за исключением очень маленьких.
В этой главе приведено несколько стандартных методов, позволяющих упро-
стить асимптотический анализ алгоритмов. В начале следующего раздела вводит-
ся несколько видов “асимптотических обозначений”, одним из которых являются
уже известные нам Θ-обозначения. Далее представлены некоторые соглашения

Стр. 87
88 Часть I. Основы

по поводу обозначений, принятых в данной книге. Наконец, в завершающей ча-


сти главы рассматривается поведение функций, часто встречающихся при анализе
алгоритмов.

3.1 Асимптотические обозначения


Обозначения, используемые нами для описания асимптотического поведения
времени работы алгоритма, используют функции, область определения которых —
множество неотрицательных целых чисел N = {0, 1, 2, . . .}. Подобные обозначе-
ния удобны для описания времени работы T (n) в наихудшем случае, как функции,
определенной только для целых чисел, представляющих собой размер входных
данных. Однако иногда удобно изменить толкование асимптотических обозначе-
ний тем или иным образом. Например, эти обозначения легко обобщаются на
область действительных чисел или, наоборот, ограничиваются до области, явля-
ющейся подмножеством натуральных чисел. При этом важно понимать точный
смысл обозначений, чтобы изменение толкования не привело к неверному их ис-
пользованию. В данном разделе вводятся основные асимптотические обозначения,
а также описывается, как чаще всего изменяется их толкование.

Θ-обозначения
В главе 2 было показано, что время работы алгоритма сортировки 2 методом
вставок в наихудшем случае выражается функцией T (n) = Θ n . Давайте раз-
беремся в смысле данного обозначения. Для некоторой функции g (n) запись
Θ (g (n)) обозначает множество функций

f (n) : существуют положительные константы c1 , c2 и n0 1


Θ (g (n)) = .
такие что 0  c1 g (n)  f (n)  c2 g (n) для всех n  n0

Функция f (n) принадлежит множеству Θ (g (n)), если существуют положитель-


ные константы c1 и c2 , позволяющие заключить эту функцию в рамки между
функциями c1 g (n) и c2 g (n) для достаточно больших n. Поскольку Θ (g (n)) —
это множество, то можно написать “f (n) ∈ Θ (g (n))”. Это означает, что функция
f (n) принадлежит множеству Θ (g (n)) (другими словами, является его элемен-
том). Мы обычно будем использовать эквивалентную запись “f (n) = Θ (g (n))”.
Такое толкование знака равенства для обозначения принадлежности множеству
поначалу может сбить с толку, однако далее мы убедимся, что у нее есть свои
преимущества.
1
В теории множеств двоеточие следует читать как“такие, что” или “для которых выполняется
условие”.

Стр. 88
Глава 3. Рост функций 89

c2 g (n) cg (n)

f (n)
f (n)
cg (n)
c1 g (n)

n n n
n0 n0 n0
f (n) = Θ( g (n)) f (n) = O( g (n)) f (n) = Ω( g (n))
а) б) в)

Рис. 3.1. Графические примеры Θ, O и Ω обозначений; в каждой части рисунка в качестве


n0 используется минимально возможное значение, т.е. любое большее значение также
сможет выполнить роль n0

На рис. 3.1а показано интуитивное изображение функций f (n) и g (n), таких


что f (n) = Θ (g (n)). Для всех значений n, лежащих справа от n0 , функция f (n)
больше или равна функции c1 g (n), но не превосходит функцию c2 g (n). Другими
словами, для всех n  n0 функция f (n) равна функции g (n) с точностью до
постоянного множителя. Говорят, что функция g (n) является асимптотически
точной оценкой функции f (n).
Согласно определению множества Θ (g (n)), необходимо, чтобы каждый эле-
мент f (n) ∈ Θ (g (n)) этого множества был асимптотически неотрицателен.
Это означает, что при достаточно больших n функция f (n) является неотрица-
тельной. (Асимптотически положительной называется такая функция, кото-
рая является положительной при любых достаточно больших n). Следовательно,
функция g (n) должна быть асимптотически неотрицательной, потому что в про-
тивном случае множество Θ (g (n)) окажется пустым. Поэтому будем считать, что
все функции, используемые в Θ-обозначениях, асимптотически неотрицательные.
Это предположение также справедливо для других асимптотических обозначений,
определенных в данной главе.
В главе 2 Θ-обозначения вводятся неформально. При этом игнорируется ко-
эффициент при старшем слагаемом, а также отбрасываются слагаемые низшего
порядка. Закрепим интуитивные представления, рассмотрев небольшой пример,
 2  с помощью формального определения доказывается, что n /2 − 3n =
в котором 2

= Θ n . Для этого необходимо определить, чему равны положительные кон-


станты c1 , c2 и n0 , для которых выполняется соотношение
1
c1 n2  n2 − 3n  c2 n2
2

Стр. 89
90 Часть I. Основы

для всех n  n0 . Разделив приведенное выше двойное неравенство на n2 , получим:


1 3
c1  −  c2 .
2 n
Правое неравенство выполняется для всех n  1, если выбрать c2  1/2. Анало-
гично, левое неравенство выполняется для всех n  7, если выбрать c1  1/14.
 2  выбрав c1 = 1/14, c2 = 1/2 и n0 = 7, мы убеждаемся, что n /2 −
Таким образом, 2

− 3n = Θ n . Конечно же, константы можно выбрать по-другому, однако важно


не то, как их выбрать, а то, что такая возможность существует. Обратите внима-
ние, что эти константы зависят от вида
 рассматриваемой функции; чтобы доказать
2
принадлежность множеству Θ n другой функции, скорее всего, понадобились
бы другие константы.
С помощью
 формального определения можно также доказать, что 6n3 =
= Θ n2 . Сделаем это методом “от противного”. Сначала допустим существо-
вание таких c2 и n0 , что 6n3  c2 n2 для всех n  n0 . Однако тогда получается,
что n  c2 /6, а это неравенство не может выполняться для больших n, поскольку
c2 — константа.
Интуитивно понятно, что при асимптотически точной оценке асимптотически
положительных функций, слагаемыми низших порядков в них можно пренебречь,
поскольку при больших n они становятся несущественными. Даже небольшой
доли слагаемого самого высокого порядка достаточно для того, чтобы превзойти
слагаемые низших порядков. Таким образом, для выполнения неравенств, фи-
гурирующих в определении Θ-обозначений, достаточно в качестве c1 выбрать
значение, которое несколько меньше коэффициента при самом старшем слагае-
мом, а в качестве c2 — значение, которое несколько больше этого коэффициента.
Поэтому коэффициент при старшем слагаемом можно не учитывать, так как он
лишь изменяет указанные константы.
В качестве примера рассмотрим квадратичную функцию f (n) = an2 + bn + c,
где a, b и c — константы, причем a > 0. Отбросив
 2  слагаемые низших порядков и иг-
норируя коэффициент, получим f (n) = Θ n . Чтобы показать то же самое фор-

мально, выберем константы c1 = a/4, c2 = 7a/4 и n0 = 2 max((|b|/a) , (|c|/a) ).
Читатель может сам убедиться, что неравенство 0  c1 n2  an2 + bn + c  c2 n2
выполняется
d для всех n  n0 . Вообще говоря, для любого
 d  полинома p (n) =
i
= i=0 ai n , где ai — это константы и ad > 0, p (n) = Θ n (см. задачу 3-1).
Поскольку любая константа — это полином нулевой степени, то постоянную
функцию можно выразить как Θ n0 или Θ (1). Однако последнее обозначение
не совсем точное, поскольку непонятно, по отношению к какой переменной ис-
следуется асимптотика2 . Мы часто будем употреблять Θ (1) для обозначения либо
константы, либо постоянной функции от какой-нибудь переменной.
2
На самом деле проблема состоит в том, что в наших обычных обозначениях функций не
делается различия между функциями и обычными величинами. В λ-вычислениях четко указываются

Стр. 90
Глава 3. Рост функций 91

O-обозначения
В Θ-обозначениях функция асимптотически ограничивается сверху и снизу.
Если же достаточно определить только асимптотическую верхнюю границу,
используются O-обозначения. Для данной функции g (n) обозначение O (g (n))
(произносится “о большое от g от n” или просто “о от g от n”) означает множество
функций, таких что

f (n) : существуют положительные константы с и n0


O (g (n)) = .
такие что 0  f (n)  cg (n) для всех n  n0

O-обозначения применяются, когда нужно указать верхнюю границу функции


с точностью до постоянного множителя. Интуитивное представление об O-обо-
значениях позволяет получить рис. 3.1б. Для всех n, лежащих справа от n0 , зна-
чение функции f (n) не превышает значения функции cg (n).
Чтобы указать, что функция f (n) принадлежит множеству O (g (n)), пишут
f (n) = O (g (n)). Обратите внимание, что из f (n) = Θ (g (n)) следует f (n) =
= O (g (n)), поскольку Θ-обозначения более сильные, чем O-обозначения. В обо-
значениях теории множеств Θ (g (n)) ⊂ O (g (n)). Таким образом, доказательство
 
того, что функция an2 +bn+c, где a > 0, принадлежит множеству Θ n2 , одновре-
менно доказывает, что множеству O n2 принадлежит любая такая квадратичная
функция. Может показаться удивительным то, чтолюбая линейная функция an + b
при a > 0 также принадлежит множеству O n2 , что легко проверить, выбрав
c = a + |b| и n0 = max (1, −b/a).
Некоторым читателям, уже знакомым с O-обозначениями,
  может показаться
странным, например, такое соотношение n = O n2 . В литературе O-обозна-
чения иногда неформально используются для описания асимптотической точной
оценки, т.е. так, как мы определили Θ-обозначения. Однако в данной книге, ко-
гда мы пишем f (n) = O (g (n)), то при этом подразумевается, что произведение
некоторой константы на функцию g (n) является асимптотическим верхним пре-
делом функции f (n). При этом не играет роли, насколько близко функция f (n)
находится к этой верхней границе. В литературе, посвященной алгоритмам, стало
стандартом различать асимптотически точную оценку и верхнюю асимптотиче-
скую границу.
Чтобы записать время работы алгоритма в O-обозначениях, нередко достаточ-
но просто изучить его общую структуру. Например, наличие двойного вложенно-
го цикла в структуре алгоритма сортировки по методу вставок, представленному
в главе 2, свидетельствует о том, что верхний предел времени работы в наихудшем
параметры функций: функцию от n2 можно обозначить как λn.n2 или даже как λr.r2 . Однако если
принять более строгие обозначения, то алгебраические преобразования могут усложниться, поэтому
мы предпочли более нестрогие обозначения.

Стр. 91
92 Часть I. Основы

 
случае выражается как O n2 : “стоимость” каждой итерации во внутреннем цик-
ле ограничена сверху константой O (1), индексы i и j — числом n, а внутренний
цикл выполняется самое большее один раз для каждой из n2 пар значений i и j.
Поскольку O-обозначения описывают верхнюю границу, то в ходе их исполь-
зования для ограничения времени работы алгоритма в наихудшем случае мы по-
лучаем верхнюю границу
  этой величины для любых входных данных. Таким
образом, граница O n2 для времени работы алгоритма в наихудшем случае при-
менима для времени решения задачи с любыми входными
 2 данными, чего нель-
зя сказать о Θ-обозначениях. Например, оценка Θ n для времени сортировки
вставкой в наихудшем случае неприменима для произвольных входных данных.
Например, в главе 2 мы имели возможность убедиться, что если входные элемен-
ты уже отсортированы, время работы алгоритма сортировки вставкой оценивается
как Θ (n).
Если подходить формально, то неправильно говорить,
 что время, необходимое
для сортировки по методу вставок, равняется O n2 , так как для данного n факти-
ческое время работы алгоритма изменяется в зависимости
 2  от конкретных входных
  n ”, то подразумевается, что
данных. Когда говорят, что “время работы равно O
существует функция f (n), принадлежащая O n2 и такая, что при любом вводе
размера n время решения задачи с данным вводом ограничено сверху значени-
ем функции
 f (n). Это равнозначно тому, что в наихудшем случае время работы
равно O n2 .

Ω-обозначения
Аналогично тому, как в O-обозначениях дается асимптотическая верхняя гра-
ница функции, в Ω-обозначениях дается ее асимптотическая нижняя граница.
Для данной функции g (n) выражение Ω (g (n)) (произносится “омега большое от
g от n” или просто “омега от g от n”) обозначает множество функций, таких что

f (n) : существуют положительные константы с и n0


Ω (g (n)) = .
такие что 0  cg (n)  f (n) для всех n  n0
Интуитивное представление о Ω-обозначениях позволяет получить рис. 3.1в. Для
всех n, лежащих справа от n0 , значения функции f (n) больше или равны значе-
ниям cg (n).
Пользуясь введенными определениями асимптотических обозначений, легко
доказать сформулированную ниже теорему (см. упражнение 3.1-5).
Теорема 3.1. Для любых двух функций f (n) и g(n) соотношение f (n) = Θ(g(n))
выполняется тогда и только тогда, когда f (n) = O (g (n)) и f (n) = Ω (g (n)). 

В качестве примера
  применения этой теоремы отметим, что из соотношения
an2 + bn + c = Θ n2 для произвольных констант a, b и c, где a > 0, непосред-

Стр. 92
Глава 3. Рост функций 93

   
ственно следует, что an2 + bn + c = O n2 и an2 + bn + c = Ω n2 . На практике
терема 3.1 применяется не для получения асимптотических верхней и нижней
границ, как это сделано выше, а наоборот — для определения асимптотически
точной оценки с помощью асимптотических верхней и нижней границ.
Поскольку Ω-обозначения используются для определения нижней границы
времени работы алгоритма в наилучшем случае, они также дают нижнюю границу
времени работы алгоритма для произвольных входных данных.
Итак, время работы
 2 алгоритма сортировки вставкой находится в пределах
между Ω (n) и O n , т.е. между линейной и квадратичной функциями от n. Бо-
лее того, эти границы охватывают асимптотику настолько плотно, насколько это
возможно: например, нижняя оценка  для времени работы алгоритма сортировки
вставкой не может быть равной Ω n2 , потому что существуют входные данные,
для которых эта сортировка выполняется за время Θ (n) (когда входные элементы
уже отсортированы), что не противоречит утверждению о том,  что
 время работы
алгоритма сортировки вставкой в наихудшем случае равно Ω n2 , поскольку су-
ществуют
  входные данные, для которых этот алгоритм работает в течение времени
Ω n2 . Когда говорят, что время работы (без каких-либо уточнений) алгоритма
равно Ω (g (n)), при этом подразумевается, что независимо от того, какие входные
данные выбраны для данного размера n, при достаточно больших n время работы
алгоритма представляет собой как минимум константу, умноженную на g (n).

Асимптотические обозначения в уравнениях


и неравенствах
Мы уже видели, как асимптотические обозначения используются в матема-
тических
  формулах. Например, при введении O-обозначения мы писали “n =
= O n2 ”. Можно также написать 2n2 + 3n + 1 = 2n2 + Θ (n). Как же интерпре-
тируются подобные формулы?
Если в правой части уравнения (или неравенства) находится
 2 только асимп-
тотическое обозначение, как в случае уравнения n = O n , то  знак
 равенства
используется для указания принадлежности множеству: n ∈ O n2 . Однако ес-
ли асимптотические обозначения встречаются в формуле в другой ситуации, они
рассматриваются как подставляемые взамен некоторой неизвестной функции, имя
которой не имеет значения. Например, формула 2n2 + 3n + 1 = 2n2 + Θ (n) озна-
чает, что 2n2 + 3n + 1 = 2n2 + f (n), где f (n) — некоторая функция из множества
Θ (n). В данном случае функция f (n) = 3n+1, и она действительно принадлежит
множеству Θ (n).
Подобное использование асимптотических обозначений позволяет избежать
несущественных деталей и неразберихи в уравнениях. Например, в главе 2 вре-
мя работы алгоритма сортировки методом слияния в наихудшем случае было

Стр. 93
94 Часть I. Основы

выражено в виде рекуррентного уравнения

T (n) = 2T (n/2) + Θ (n) .

Если нас интересует только асимптотическое поведение T (n), то нет смысла


точно выписывать все слагаемые низших порядков; подразумевается, что все они
включены в безымянную функцию, обозначенную как Θ (n).
Предполагается, что таких функций в выражении столько, сколько раз в нем
встречаются асимптотические обозначения. Например, в выражении ni=1 O (i)
имеется только одна функция без имени (аргументом которой является i). Таким
образом, это выражение — не одно и то же, что и O (1) + O (2) + · · · + O (n),
которое действительно не имеет однозначной интерпретации.
Иногда асимптотические обозначения появляются в левой части уравнения,
как, например, в таком случае:
 
2n2 + Θ (n) = Θ n2 .

Подобные уравнения интерпретируются в соответствии с таким правилом: при лю-


бом выборе безымянных функций, подставляемых вместо асимптотических обо-
значений в левую часть уравнения, можно выбрать и подставить в правую часть
такие безымянные функции, что уравнение будет правильным. Таким образом,
смысл приведенного выше уравнения в том,что для любой функции f (n) ∈ Θ (n)
существует некоторая функция g (n) ∈ Θ n2 , такая что 2n2 + f (n) = g (n)
для всех n. Другими словами, правая часть уравнения предоставляет меньший
уровень детализации, чем левая.
Несколько таких соотношений могут быть объединены в цепочку, например:
 
2n2 + 3n + 1 = 2n2 + Θ (n) = Θ n2 .

При этом каждое уравнение можно интерпретировать в соответствии со сфор-


мулированным выше правилом. Согласно первому уравнению, существует неко-
торая функция f (n) ∈ Θ (n), такая что для всех n выполняется соотношение
2n2 + 3n + 1 = 2n2 + f (n). Согласно второму уравнению,
 для любой функции
g (n) ∈ Θ (n) существует некоторая функция h (n) ∈ Θ n2 , такая что для всех
n выполняется соотношение 2n2 + g (n) = h (n). Заметим, что такая
 интерпре-
тация подразумевает выполнение соотношения 2n2 + 3n + 1 = Θ n2 , которое
согласуется с нашими интуитивными представлениями о цепочке уравнений.

o-обозначения
Верхняя асимптотическая граница, предоставляемая O-обозначениями, мо-
жет описывать
 асимптотическое
 поведение функции с разной точностью. Грани-
ца 2n2 = O n2 дает правильное представление об асимптотическом поведении

Стр. 94
Глава 3. Рост функций 95

 
функции, а граница 2n = O n2 его не обеспечивает. Для обозначения того, что
верхняя граница не является асимптотически точной оценкой функции, приме-
няются o-обозначения. Приведем формальное определение множества o (g (n))
(произносится как “о малое от g от n”):

f (n) : для любой положительной константы с существует


o (g (n)) = .
n0 > 0, такое что 0  f (n) < cg (n) для всех n  n0
   
Например: 2n = o n2 , но 2n2 = o n2 .
Определения O-обозначений и o-обозначений похожи друг на друга. Основ-
ное отличие в том, что определение f (n) = O (g (n)) ограничивает функцию f (n)
неравенством 0  f (n)  cg (n) лишь для некоторой константы c > 0, а опре-
деление f (n) = o (g (n)) ограничивает ее неравенством 0  f (n) < cg (n) для
всех констант c > 0. Интуитивно понятно, что в o-обозначениях функция f (n)
пренебрежимо мала по сравнению с функцией g (n), если n стремится к беско-
нечности, т.е.
f (n)
lim =0 (3.1)
n→∞ g (n)

Некоторые авторы используют этот предел в качестве определения o-обозначений.


Добавим, что определение, приведенное в этой книге, накладывает на безымян-
ную функцию ограничение, согласно которому она должна быть асимптотически
неотрицательной.

ω-обозначения
По аналогии, ω-обозначения соотносятся с Ω-обозначениями так же, как o-
обозначения с O-обозначениями. С помощью ω-обозначений указывается нижний
предел, не являющийся асимптотически точной оценкой. Один из возможных
способов определения ω-обозначения следующий:

f (n) ∈ ω (g (n)) тогда и только тогда, когда g (n) ∈ o (f (n)) .

Формально же ω (g (n)) (произносится как “омега малое от g от n”) определяется


как множество

f (n) : для любой положительной константы с существует


o (g (n)) = .
n0 > 0, такое что 0  cg (n) < f (n) для всех n  n0
 
Например, n2 /2 = ω (n), но n2 /2 = ω n2 . Соотношение f (n) = ω (g (n)) под-
разумевает, что lim fg(n)
(n)
= ∞, если этот предел существует. Таким образом,
n→∞
функция f (n) становится сколь угодно большой по сравнению с функцией g (n),
если n стремится к бесконечности.

Стр. 95
96 Часть I. Основы

Сравнение функций
Асимптотические сравнения обладают некоторыми свойствами отношений
обычных действительных чисел, показанными далее. Предполагается, что функ-
ции f (n) и g (n) асимптотически положительны.
Транзитивность
Из f (n) = Θ (g (n)) и g (n) = Θ (h (n)) следует f (n) = Θ (h (n)) .
Из f (n) = O (g (n)) и g (n) = O (h (n)) следует f (n) = O (h (n)) .
Из f (n) = Ω (g (n)) и g (n) = Ω (h (n)) следует f (n) = Ω (h (n)) .
Из f (n) = o (g (n)) и g (n) = o (h (n)) следует f (n) = o (h (n)) .
Из f (n) = ω (g (n)) и g (n) = ω (h (n)) следует f (n) = ω (h (n)) .

Рефлексивность
f (n) = Θ (f (n)) ,
f (n) = O (f (n)) ,
f (n) = Ω (f (n)) .

Симметричность
f (n) = Θ (g (n)) справедливо тогда и только тогда, когда g (n) = Θ (f (n)) .

Перестановочная симметрия
f (n) = O (g (n)) справедливо тогда и только тогда, когда g (n) = Ω (f (n)) ,
f (n) = o (g (n)) справедливо тогда и только тогда, когда g (n) = ω (f (n)) .

Поскольку эти свойства выполняются для асимптотических обозначений, мож-


но провести аналогию между асимптотическим сравнением двух функций f и g
и сравнением двух действительных чисел a и b:

f (n) = O (g (n)) ≈ a  b,
f (n) = Ω (g (n)) ≈ a  b,
f (n) = Θ (g (n)) ≈ a = b,
f (n) = o (g (n)) ≈ a < b,
f (n) = ω (g (n)) ≈ a > b.

Говорят, что функция f (n) асимптотически меньше функции g (n), если f (n) =
= o (g (n)), и асимптотически больше функции g (n), если f (n) = ω (g (n)).
Однако одно из свойств действительных чисел в асимптотических обозначе-
ниях не выполняется.

Стр. 96
Глава 3. Рост функций 97

Трихотомия: для любых действительных чисел a и b должно выполняться только


одно из соотношений a < b, a = b или a > b.
Хотя любые два действительных числа можно однозначно сравнить, в отно-
шении асимптотического сравнения функций это утверждение не является спра-
ведливым. Для двух функций f (n) и g (n) может не выполняться ни отношение
f (n) = O (g (n)), ни f (n) = Ω (g (n)). Например, функции n и n1+sin n нель-
зя асимптотически сравнивать, поскольку показатель степени в функции n1+sin n
колеблется между значениями 0 и 2 (принимая все значения в этом интервале).

Упражнения
3.1-1. Пусть f (n) и g (n) — асимптотически неотрицательные функции. Дока-
жите с помощью базового определения Θ-обозначений, что max(f (n),
g(n)) = Θ (f (n) + g (n)).
3.1-2. Покажите, что для любых действительных констант a и b, где b > 0,
справедливо соотношение
 
(n + a)b = Θ nb . (3.2)

3.1-3. Объясните, почему


  выражение “время работы алгоритма A равно, как
минимум, O n2 ” не имеет смысла.
3.1-4. Справедливы ли соотношения 2n+1 = O (2n ) и 22n = O (2n )?
3.1-5. Докажите теорему 3.1.
3.1-6. Докажите, что время работы алгоритма равно Θ (g (n)) тогда и только
тогда, когда время работы алгоритма в наихудшем случае равно O (g (n)),
а время работы алгоритма в наилучшем случае равно Ω (g (n)).
3.1-7. Докажите, что множество o (g (n)) ∩ ω (g (n)) пустое.
3.1-8. Наши обозначения можно обобщить для случая двух параметров n и m,
которые могут возрастать до бесконечности с разной скоростью. Для
данной функции g (n, m) выражение O (g (n, m)) обозначает множество
функций, такое что
 

 f (n, m) : существуют положительные константы 

O (g (n, m)) = с, n0 и m0 , такие что 0  f (n, m)  cg (n, m) .

 

для всех n  n0 или m  m0

Дайте аналогичные определения обозначений Ω (g (n, m)) и Θ (g (n, m)).

Стр. 97
98 Часть I. Основы

3.2 Стандартные обозначения и часто


встречающиеся функции
В этом разделе рассматриваются некоторые стандартные математические функ-
ции и обозначения, а также исследуются взаимоотношения между ними. В нем
также иллюстрируется применение асимптотических обозначений.

Монотонность
Функция f (n) является монотонно неубывающей (monotonically increasing),
если из неравенства m  n следует неравенство f (m)  f (n). Аналогично,
функция f (n) является монотонно невозрастающей (monotonically decreasing),
если из неравенства m  n следует неравенство f (m)  f (n). Функция f (n)
монотонно возрастающая (strictly increasing), если из неравенства m < n следу-
ет неравенство f (m) < f (n) и монотонно убывающая (strictly decreasing), если
из неравенства m < n следует, что f (m) > f (n).

Округление в большую и меньшую сторону


Для любого действительного числа x существует наибольшее целое число,
меньшее или равное x, которое мы будем обозначать как x (читается как “пол
(floor) x”), и наименьшее целое число, большее или равное x, которое мы будем
обозначать как x
(читается как “потолок (ceil) x”). Для всех действительных
чисел
x − 1  x  x  x
< x + 1 (3.3)
Для любого целого числа n n/2
+ n/2 = n, а для любого действительного
числа n  0 и натуральных чисел a и b справедливы следующие соотношения:

n/a
/b
= n/ab
, (3.4)
n/a /b = n/ab , (3.5)
a/b
 (a + (b − 1)) /b, (3.6)
a/b  (a − (b − 1)) /b. (3.7)

Функция f (x) = x является монотонно неубывающей, как и функция f (x) =


= x
.

Модульная арифметика
Для любого целого числа a и любого натурального n величина a mod n пред-
ставляет собой остаток от деления a на n:

a mod n = a − a/n n (3.8)

Стр. 98
Глава 3. Рост функций 99

Располагая подобным определением, удобно ввести специальные обозначения для


указания того, что два целых числа имеют одинаковые остатки при делении на
какое-то натуральное число. Тот факт, что (a mod n) = (b mod n), записывается
как a ≡ b (modn); при этом говорят, что число a эквивалентно, или равно числу
b по модулю n (или что числа a и b сравнимы по модулю n). Другими словами,
a ≡ b (modn), если числа a и b дают одинаковые остатки при делении на n.
Это эквивалентно утверждению, что a ≡ b (modn) тогда и только тогда, когда n
является делителем числа b − a. Запись a  ≡ b (modn) означает, что число a не
эквивалентно числу b по модулю n.

Полиномы
Полиномом степени d от аргумента n называется функция p (n) следующе-
го вида:

d
p (n) = ai ni ,
i=0

где константы a0 , a1 , . . . , ad — коэффициенты полинома, и ad = 0. Полином явля-


ется асимптотически положительной функцией тогда и только тогда, когда ad > 0.
Для асимптотически положительных
  полиномов p (n) степени d справедливо со-
отношение p (n) = Θ nd . Для любой действительной константы a  0 функция
na монотонно неубывающая, а для a  0 эта функция монотонно невозрастаю-
щая. Говорят, что функция f (n)полиномиально
 ограничена, если существует
такая константа k, что f (n) = O nk .

Показательные функции
Для всех действительных чисел a > 0, m и n справедливы следующие тожде-
ства:

a0 = 1,
a1 = a,
a−1 = 1/a,
(am )n = amn ,
(am )n = (an )m ,
am an = am+n

Для любого n и a  1 функция an является монотонно неубывающей функцией


аргумента n. Для удобства будем считать, что 00 = 1.
Соотношение между скоростями роста полиномов и показательных функций
можно определить исходя из того факта, что для любых действительных констант

Стр. 99
100 Часть I. Основы

a и b, таких что a > 1,


nb
lim =0 (3.9)
n→∞ an

откуда можно заключить, что nb = o (an ).


Таким образом, любая показательная функция, основание a которой строго
больше единицы, возрастает быстрее любой полиномиальной функции.
Обозначив через e основание натурального логарифма (приблизительно рав-
ное 2.718281828. . . ), можем записать следующее соотношение, справедливое для
любого действительного x:
 xi ∞
x2 x3
x
e =1+x+ + + ··· = (3.10)
2! 3! i!
i=0

где символ “!” обозначает факториал, определенный ниже в этом разделе. Для
всех действительных x справедливо следующее неравенство:

ex  1 + x (3.11)

Равенство соблюдается только в случае, если x = 0. При |x|  1 можно исполь-


зовать такое приближение:

1 + x  ex  1 + x + x2 (3.12)

При x → 0 приближение значения


  функции ex величиной 1 + x вполне удовлетво-
рительно: ex = 1 + x + Θ x2 . (В этом уравнении асимптотические обозначения
используются для описания предельного поведения при x → 0, а не при x → ∞.)
Для произвольного x справедливо следующее равенство:
 x n
lim 1 + = ex (3.13)
n→∞ n
Логарифмы
Мы будем использовать следующие обозначения:

lg n = log2 n (двоичный логарифм);


ln n = loge n (натуральный логарифм);
lgk n = (lg n)k (возведение в степень);
lg lg n = lg (lg n) (композиция).

Важное соглашение, которое мы приняли в книге, заключается в том, что


логарифмические функции применяются только к ближайшему члену выражения.

Стр. 100
Глава 3. Рост функций 101

Например, lg n + k означает (lg n) + k, а не lg (n + k). Если основание логарифма


b > 1, то при n > 0 функция logb n монотонно возрастает.
Для всех действительных a > 0, b > 0, c > 0 и n выполняются следующие
соотношения:
a = blogb a ,
logc (ab) = logc a + logc b,
logb an = n logb a,
logc a
logb a = , (3.14)
logc b
logb (1/a) = − logb a,
1
logb a = ,
loga b
alogb c = clogb a (3.15)
где в каждом из приведенных выше уравнений основание логарифма отлично от 1.
Согласно уравнению (3.14), изменение основы логарифма приводит к умно-
жению значения этого логарифма на постоянный множитель, поэтому мы часто
будем использовать обозначение “lg n”, не заботясь о постоянном множителе, как
это делается в O-обозначениях. Специалисты в области вычислительной техники
считают наиболее естественной основой логарифма число 2, так как во многих
алгоритмах и структурах данных производится разбиение задачи на две части.
При |x| < 1 для функции ln (1 + x) можно написать простое разложение в ряд:
x2 x3 x4 x5
ln (1 + x) = x −+ − + − ....
2 3 4 5
Кроме того, при x > −1 выполняются следующие неравенства:
x
 ln (1 + x)  x, (3.16)
1+x
причем знак равенства имеет место только при x = 0.
Говорят, что функция f (n) полилогарифмически
  ограничена, если существу-
ет такая константа k, что f (n) = O lgk n . Соотношение между скоростью роста
полиномов и полилогарифмов можно найти, подставив в уравнение (3.9) lg n вме-
сто n и 2a вместо a, в результате чего получим:
lgb n lgb n
lim = lim = 0.
n→∞ (2a )lg n n→∞ na

Из приведенного выше соотношения можно заключить, что для произвольной


константы a > 0 lgb n = o (na ). Таким образом, любая положительная полиноми-
альная функция возрастает быстрее, чем любая полилогарифмическая функция.

Стр. 101
102 Часть I. Основы

Факториалы
Обозначение n! (читается “n факториал”) определяется для целых чисел n  0
следующим образом:

1 при n = 0,
n! =
n · (n − 1)! при n > 0,

т.е. n! = 1 · 2 · 3 · . . . · n.
Верхнюю границу факториала можно записать в виде n!  nn , поскольку все
множители, которые определяют значение факториала, не превышают n. Однако
эта оценка является грубой. Более точное приближение верхней (а также нижней)
границы дает формула Стирлинга:
√  n n   
1
n! = 2πn 1+Θ (3.17)
e n
где e — основание натурального логарифма. Можно доказать (см. упражнение 3.2-3),
что

n! = o (nn ) ,
n! = ω (2n ) ,
lg (n!) = Θ (n lg n) (3.18)

причем при доказательстве уравнения (3.18) удобно использовать формулу Стир-


линга. Кроме того, для n  1 справедливо следующее равенство:
√  n n
n! = 2πn eαn (3.19)
e
где
1 1
< αn < . (3.20)
12n + 1 12n

Функциональная итерация
Чтобы указать, что функция f (n) последовательно i раз применяется к аргу-
менту n, мы будем использовать обозначение f (i) (n). Дадим формальное опре-
деление. Пусть функция f (n) задана на множестве действительных чисел. Для
неотрицательных целых i можно дать такое рекурсивное определение:

n при i = 0,
(i)
f (n) =  (i−1)  .
f f (n) при i > 0.

Например, если f (n) = 2n, то f (i) (n) = 2i n.

Стр. 102
Глава 3. Рост функций 103

Итерированная логарифмическая функция


Обозначение lg∗ n (читается как “логарифм со звездочкой от n”) будет при-
меняться для указания повторно применяемого логарифма, который определяется
следующим образом. Пусть lg(i) n представляет собой итерацию функции f (n) =
= lg n. Поскольку логарифм от неотрицательных чисел не определен, то функция
lg(i) n определена, только если lg(i−1) n > 0. Не перепутайте обозначения lg(i) n
(логарифм, примененный последовательно i раз к аргументу n) и lgi n (логарифм
n, возведенный в i-ую степень). Итерированный логарифм определяется следую-
щим образом:  
lg∗ n = min i  0 : lg(i) n  1 .

(Другими словами, lg∗ n — это минимальное число логарифмирований n, необхо-


димое для получения значения, не превосходящего 1.)
Итерированный логарифм возрастает очень медленно:

lg∗ 2 = 1,
lg∗ 4 = 2,
lg∗ 16 = 3,
lg∗ 65536 = 4,
 
lg∗ 265536 = 5.

Поскольку количество атомов в наблюдаемой части Вселенной оценивается при-


мерно в 1080 , что намного меньше, чем 265536 , то можно с уверенностью сказать,
что входные данные такого размера n, что lg∗ n > 5, встречаются крайне редко.

Числа Фибоначчи
Числа Фибоначчи определяются с помощью следующего рекуррентного со-
отношения:
F0 = 0,
F1 = 1, (3.21)
Fi = Fi−1 + Fi−2 , i  2.
Таким образом, числа Фибоначчи образуют последовательность, в которой каждое
очередное число представляет собой сумму двух предыдущих:

0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, . . . .

Стр. 103
104 Часть I. Основы

Числа Фибоначчи тесно связаны с золотым сечением φ и сопряженным значе-


 которые задаются следующими формулами:
нием φ,

1+ 5
φ= = 1.61803 . . .
2√ (3.22)
 1− 5
φ= = −0.61803 . . .
2
В частности, справедливо такое соотношение:

φi − φi
Fi = √ , (3.23)
5
которое
  можно доказать методом индукции
  √ (см. упражнение 3.2-6). Поскольку
   i  √
φ < 1, то выполняются неравенства φ / 5 < 1/ 5 < 1/2. Таким образом, i-е

число Фибоначчи Fi равно φi / 5, округленному до ближайшего целого числа.
Следовательно, числа Фибоначчи возрастают как показательная функция.

Упражнения
3.2-1. Покажите, что если функции f (n) и g (n) — монотонно неубывающие,
то функции f (n) + g (n) и f (g (n)) также монотонно неубывающие.
Кроме того, если к тому же функции f (n) и g (n) — неотрицательные,
то монотонно неубывающей является также функция f (n) · g (n).
3.2-2. Докажите уравнение (3.15).
3.2-3. Докажите уравнение (3.18). Кроме того, докажите, что n! = ω (2n ) и n! =
= o (nn ).
 3.2-4. Является ли функция lg n
! полиномиально ограниченной? Является ли
полиномиально ограниченной функция lg lg n
?
 3.2-5. Какая из функций является асимптотически бо́льшей: lg (lg∗ n) или
lg∗ (lg n)?
3.2-6. Докажите методом математической индукции, что i-е число Фибоначчи
удовлетворяет следующему равенству:

φi − φi
Fi = √ ,
5

где φ — золотое сечение, а φ — его сопряженное.


3.2-7. Докажите, что при i  0 (i + 2)-ое число Фибоначчи удовлетворяет нера-
венству Fi+2 > φi .

Стр. 104
Глава 3. Рост функций 105

Задачи
3-1. Асимптотическое поведение полиномов
Пусть p (n) представляет собой полином степени d от n:


d
p (n) = ai ni ,
i=0

где ad > 0, и пусть k — некоторая константа. Докажите с помощью


определения асимптотических обозначений приведенные ниже свойства.
 
а) Если k  d, то p (n) = O nk .
 
б) Если k  d, то p (n) = Ω nk .
 
в) Если k = d, то p (n) = Θ nk .
 
г) Если k > d, то p (n) = o nk .
 
д) Если k < d, то p (n) = ω nk .
3-2. Сравнение скорости асимптотического роста
Для каждой пары (A, B) приведенных в таблице выражений укажите,
в каком отношении A находится по отношению к B: O, o, Ω, ω или Θ.
Предполагается, что k, ε и c — константы, причем k  1, ε > 0 и c >
> 1. Ответ должен быть представлен в виде таблицы, в каждой ячейке
которой следует указать значения “да” или “нет”.
A B O o Ω ω Θ
а. lgk n nε
б. nk cn

в. n nsin n
г. 2n 2n/2
д. nlg c clg n
е. lg (n!) lg (nn )

3-3. Упорядочение по скорости асимптотического роста


а) Расположите приведенные ниже функции по скорости их асимпто-
тического роста, т.е. постройте такую последовательность функций
g1 , g2 , . . . , g30 , что g1 = Ω (g2 ), g2 = Ω (g3 ), . . . , g29 = Ω (g30 ). Раз-
бейте список на классы эквивалентности, в которых функции f (n)
и g (n) принадлежат одному и тому же классу тогда и только тогда,
когда f (n) = Θ (g (n)).

Стр. 105
106 Часть I. Основы

∗ √ lg n
lg (lg∗ n) 2lg n 2 n2 n! (lg n)!
n
(3/2)n n3 lg2 n lg (n!) 22 n1/lg n
ln ln n lg∗ n n · 2n nlg lg n ln n 1

2lg n (lg n)lg n en 4lg n (n + 1)! lg n

lg∗ (lg n)
n+1
2 2 lg n n 2n n lg n 2 2

б) Приведите пример неотрицательной функции f (n), такой что ни для


одной из представленных в задании а) функций gi (n) функция f (n)
не принадлежит ни множеству O (gi (n)), ни множеству Ω (gi (n)).
3-4. Свойства асимптотических обозначений
Пусть f (n) и g (n) — асимптотически положительные функции. Дока-
жите или опровергните справедливость каждого из приведенных ниже
утверждений.
а) Из f (n) = O (g (n)) следует g (n) = O (f (n)).
б) f (n) + g (n) = Θ (min (f (n) , g (n))).
в) Из f (n) = O (g (n)) следует lg (f (n)) = O (lg (g (n))), где при до-
статочно больших n lg (g (n))  1 и f (n)  1.
 
г) Из f (n) = O (g (n)) следует 2f (n) = O 2g(n) .
 
д) f (n) = O (f (n))2 .
е) Из f (n) = O (g (n)) следует g (n) = Ω (f (n)).
ж) f (n) = Θ (f (n/2)).
з) f (n) + o (f (n)) = Θ (f (n)).
3-5. Вариации в определениях O и Ω
Определения Ω некоторых авторов несколько отличаются от нашего; в ка-
честве обозначения этих альтернативных определений мы будем исполь-

зовать символ Ω (читается “Ω бесконечность”). Говорят, что f (n) =

= Ω (g (n)), если существует положительная константа c, такая что
f (n)  cg (n)  0 для бесконечно большого количества целых n.
а) Покажите, что для любых двух асимптотически неотрицательных
функций f (n) и g (n) выполняется одно из соотношений f (n) =

= O (g (n)) или f (n) = Ω (g (n)) (или оба), в то время как при

использовании Ω вместо Ω это утверждение оказывается ложным.
б) Опишите потенциальные преимущества и недостатки использова-

ния Ω вместо Ω для характеристики времени работы программы.

Стр. 106
Глава 3. Рост функций 107

Определения O некоторых авторов несколько отличаются от наше-


го; в качестве обозначения этих альтернативных определений мы
будем использовать символ O . Говорят, что f (n) = O (g (n)) тогда
и только тогда, когда |f (n)| = O (g (n)).
в) Что произойдет, если теорему 3.1 перефразировать таким образом,
что вместо O подставить O , а Ω оставить без изменений?
Некоторые авторы определяют Õ (читается “о с тильдой”) для обо-
значения O, в котором игнорируются логарифмические множители:
 
 f (n) : существуют положительные константы 
 
Õ (g (n)) = c, k и n0 , такие что 0  f (n)  cg (n) lgk (n) .

 

для всех n  n0

Дайте аналогичные определения Ω̃ и Θ̃. Докажите для них аналог


теоремы 3.1.
3-6. Итерированные функции
Оператор итераций ∗ , использующийся в функции lg∗ , можно приме-
нять к любой монотонно неубывающей функции f (n), определенной на
множестве действительных чисел. Для данной константы c ∈ R итериро-
ванная функция fc∗ определяется как
 
fc∗ (n) = min i  0 : f (i) (n)  c ,

где функция f (i) (n) должна быть корректно определена для всех i. Други-
ми словами, величина fc∗ (n) — это функция f , последовательно применя-
ющаяся столько раз, сколько это необходимо для уменьшения аргумента
до значения, не превышающего c.
Для каждой из приведенных ниже функций f (n) и констант c дайте
максимально точную оценку функции fc∗ (n).
f (n) c fc∗ (n)
а. n−1 0
б. lg n 1
в. n/2 1
г. n/2 2

д. n 2

е. n 1
ж. n1/3 2
з. n/ lg n 2

Стр. 107
108 Часть I. Основы

Заключительные замечания
Кнут (Knuth) [182] попытался выяснить происхождение O-обозначений и об-
наружил, что впервые они появились в 1892 году в учебнике Бахманна (P. Bach-
mann) по теории чисел. O-обозначения были введены в 1909 году Ландау (E. Lan-
dau) при обсуждении распределения простых чисел. Введение Ω- и Θ-обозначений
приписываются Кнуту [186], который исправил неточность популярного в литера-
туре, но технически неаккуратного применения O-обозначений как для верхней,
так и для нижней асимптотических границ. Многие продолжают использовать O-
обозначения там, где более уместны были бы Θ-обозначения. Дальнейшее об-
суждение исторического развития асимптотических обозначений можно найти
у Кнута [182, 186], а также Брассарда (Brassard) и Брейтли (Bratley) [46].
Определения асимптотических обозначений у разных авторов иногда разли-
чаются, однако в большинстве часто встречающихся ситуаций они дают согласу-
ющиеся результаты. В некоторых альтернативных случаях подразумевается, что
функции не являются асимптотически неотрицательными, поэтому ограничению
подлежит их абсолютное значение.
Равенство (3.19) было получено Роббинсом (Robbins) [260]. Другие свойства
элементарных математических функций можно найти в любом хорошем справоч-
нике по математике, например, справочнике Абрамовича (Abramowitch) и Стеган
(Stegun) [1] или Цвиллингера (Zwillinger) [320], а также в книгах по вычислитель-
ной математике, таких как книги Апостола (Apostol) [18] или Томаса (Thomas)
и Финни (Finney) [296]. В книге Кнута [182], а также Грехема (Graham), Кнута
и Паташника (Patashnik) [132] содержится множество материала по дискретной
математике, который используется в информатике.

Стр. 108
ГЛАВА 4
Рекуррентные соотношения

Как было сказано в главе 2, если алгоритм рекуррентно вызывает сам себя,
время его работы часто можно описать с помощью рекуррентного соотноше-
ния. Рекуррентное соотношение (recurrence) — это уравнение или неравенство,
описывающее функцию с использованием ее самой, но только с меньшими аргу-
ментами. Например, мы узнали, что время работы процедуры MERGE_SORT T (n)
в самом неблагоприятном случае описывается с помощью следующего рекуррент-
ного соотношения:

Θ (1) при n = 1,
T (n) = (4.1)
2T (n/2) + Θ (n) при n > 1.

Решением этого соотношения является функция T (n) = Θ (n lg n).


В настоящей главе вниманию читателя предлагаются три метода решения ре-
куррентных уравнений, т.е. получения асимптотических Θ- и O-оценок решения.
В методе подстановок (substitution method) нужно догадаться, какой вид имеют
граничные функции, а затем с помощью метода математической индукции дока-
зать, что догадка правильная. В методе деревьев рекурсии (recursion-tree method)
рекуррентное соотношение преобразуется в дерево, узлы которого представляют
время выполнения каждого уровня рекурсии; затем для решения соотношения
используется метод оценки сумм. В основном методе (master method) граничные
оценки решений рекуррентных соотношений представляются в таком виде:

T (n) = aT (n/b) + f (n) ,

где a  1, b > 1, а функция f (n) — это заданная функция; для применения этого
метода необходимо запомнить три различных случая, но после этого определение

Стр. 109
110 Часть I. Основы

асимптотических границ во многих простых рекуррентных соотношениях стано-


вится очень простым.

Технические детали
На практике в процессе формулировки и решения рекуррентных соотноше-
ний определенные технические моменты опускаются. Так, например, есть одна
особенность, о которой часто умалчивают, — это допущение о том, что аргу-
мент функции является целочисленным. Обычно время работы T (n) алгоритма
определено лишь для целочисленных n, поскольку в большинстве алгоритмов ко-
личество входных элементов выражается целым числом. Например, рекуррентное
соотношение, описывающее время работы процедуры MERGE_SORT в наихудшем
случае, на самом деле записывается так:

Θ (1) при n = 1,
T (n) = (4.2)
T ( n/2
) + T ( n/2 ) + Θ (n) при n > 1.
Граничные условия — еще один пример технических особенностей, которые
обычно игнорируются. Поскольку время работы алгоритма с входными данными
фиксированного размера выражается константой, то в рекуррентных соотноше-
ниях, описывающих время работы алгоритмов, для достаточно малых n обычно
справедливо соотношение T (n) = Θ (1). Поэтому для удобства граничные усло-
вия рекуррентных соотношений, как правило, опускаются и предполагается, что
для малых n время работы алгоритма T (n) является константой. Например, ре-
куррентное соотношение (4.1) обычно записывается как
T (n) = 2T (n/2) + Θ (n) , (4.3)
без явного указания значений T (n) для малых n. Причина состоит в том, что хотя
изменение значения T (1) приводит к изменению решения рекуррентного соотно-
шения, это решение обычно изменяется не более, чем на постоянный множитель,
а порядок роста остается неизменным.
Формулируя и решая рекуррентные соотношения, мы часто опускаем гранич-
ные условия, а также тот факт, что аргументами неизвестных функций являются
целые числа. Мы продвигаемся вперед без этих деталей, а потом выясняем, важ-
ны они или нет. Даже если технические детали, которые опускаются при анализе
алгоритмов, несущественны, то важно убедиться, что они не влияют на поря-
док роста решения. В подобных рассуждениях помогает опыт, а также некоторые
теоремы. В этих теоремах формулируются условия, когда упомянутые детали не
влияют на асимптотическое поведение рекуррентных соотношений, возникающих
при анализе алгоритмов (см. теорему 4.1). Однако в данной главе мы не будем
опускать технические детали, так как это позволит продемонстрировать некото-
рые тонкости, присущие методам решения рекуррентных соотношений.

Стр. 110
Глава 4. Рекуррентные соотношения 111

4.1 Метод подстановки


Метод подстановки, применяющийся для решения рекуррентных уравнений,
состоит из двух этапов:
1. делается догадка о виде решения;
2. с помощью метода математической индукции определяются константы и до-
казывается, что решение правильное.
Происхождение этого названия объясняется тем, что предполагаемое решение
подставляется в рекуррентное уравнение. Это мощный метод, но он применим
только в тех случаях, когда легко сделать догадку о виде решения.
Метод подстановки можно применять для определения либо верхней, либо
нижней границ рекуррентного соотношения. В качестве примера определим верх-
нюю границу рекуррентного соотношения

T (n) = 2T ( n/2 ) + n, (4.4)

подобного соотношениям (4.2) и (4.3). Мы предполагаем, что решение имеет


вид T (n) = O (n lg n). Наш метод заключается в доказательстве того, что при
подходящем выборе константы c > 0 выполняется неравенство T (n)  cn lg n.
Начнем с того, что предположим справедливость этого неравенства для величины
n/2 , т.е. что выполняется соотношение T ( n/2 )  c n/2 lg ( n/2 ). После
подстановки данного выражения в рекуррентное соотношение получаем:

T (n)  2 (c n/2 lg ( n/2 )) + n 


 cn lg (n/2) + n =
= cn lg n − cn lg 2 + n =
= cn lg n − cn + n 
 cn lg n,

где последнее неравенство выполняется при c  1.


Теперь, согласно методу математической индукции, необходимо доказать, что
наше решение справедливо для граничных условий. Обычно для этого достаточ-
но показать, что граничные условия являются подходящей базой для доказатель-
ства по индукции. В рекуррентном соотношении (4.4) необходимо доказать, что
константу c можно выбрать достаточно большой для того, чтобы соотношение
T (n)  cn lg n было справедливо и для граничных условий. Такое требование
иногда приводит к проблемам. Предположим, что T (1) = 1 — единственное гра-
ничное условие рассматриваемого рекуррентного соотношения. Далее, для n = 1
соотношение T (n)  cn lg n дает нам T (1)  c · 1 · lg 1 = 0, что противоречит
условию T (1) = 1. Следовательно, данный базис индукции нашего доказатель-
ства не выполняется.

Стр. 111
112 Часть I. Основы

Эту сложность, возникающую при доказательстве предположения индукции


для указанного граничного условия, легко обойти. Например, в рекуррентном со-
отношении (4.4) можно воспользоваться преимуществами асимптотических обо-
значений, требующих доказать неравенство T (n)  cn lg n только для n  n0 ,
где n0 — выбранная нами константа. Идея по устранению возникшей проблемы
заключается в том, чтобы в доказательстве по методу математической индукции
не учитывать граничное условие T (1) = 1. Обратите внимание, что при n > 3
рассматриваемое рекуррентное соотношение явным образом от T (1) не зависит.
Таким образом, выбрав n0 = 2, в качестве базы индукции можно рассматри-
вать не T (1), а T (2) и T (3). Заметим, что здесь делается различие между базой
рекуррентного соотношения (n = 1) и базой индукции (n = 2 и n = 3). Из рекур-
рентного соотношения следует, что T (2) = 4, а T (3) = 5. Теперь доказательство
по методу математической индукции соотношения n  cn lg n для некоторой кон-
станты c  1 можно завершить, выбрав ее достаточно большой для того, чтобы
были справедливы неравенства T (2)  2c lg 2 и T (3)  3c lg 3. Оказывается, что
для этого достаточно выбрать c  2. В большинстве рекуррентных соотношений,
которые нам предстоит рассмотреть, легко расширить граничные условия таким
образом, чтобы предположение индукции оказалось верным для малых n.

Как угадать решение


К сожалению, не существует общего способа, позволяющего угадать правиль-
ное решение рекуррентного соотношения. Для этого требуется опыт, удача и твор-
ческое мышление. К счастью, существуют определенные эвристические приемы,
которые могут помочь сделать правильную догадку. Кроме того, для получения
предполагаемого вида решения можно воспользоваться деревьями рекурсии, с ко-
торыми мы ознакомимся в разделе 4.2.
Если рекуррентное соотношение подобно тому, которое мы только что рас-
сматривали, то разумно предположить, что решения этих соотношений будут по-
хожими. Например, рассмотрим рекуррентное соотношение

T (n) = 2T ( n/2 + 17) + n,

которое выглядит более сложным, поскольку в аргументе функции T в его правой


части добавлено слагаемое “17”. Однако интуитивно понятно, что это допол-
нительное слагаемое не может сильно повлиять на асимптотическое поведение
решения. При достаточно больших n разность между T ( n/2 ) и T ( n/2 + 17)
становится несущественной: оба эти числа приблизительно равны половине чис-
ла n. Следовательно, можно предположить, что T (n) = O (n lg n), и проверить
это с помощью метода подстановки (упражнение 4.1-5).
Другой способ найти решение — сделать грубую оценку его верхней и нижней
границ, а затем уменьшить неопределенность до минимума. Например, в рекур-

Стр. 112
Глава 4. Рекуррентные соотношения 113

рентном соотношении (4.4) в качестве начальной нижней границы можно было


бы выбрать T (n) = Ω (n), поскольку в нем содержится слагаемое
 2  n; можно также
доказать, что грубой верхней границей является T (n) = O n . Далее верхняя
граница постепенно понижается, а нижняя — повышается до тех пор, пока не будет
получено правильное асимптотическое поведение решения T (n) = Θ (n lg n).

Тонкие нюансы
Иногда бывает так, что можно сделать правильное предположение об асимп-
тотическом поведении решения рекуррентного соотношения, но при этом возни-
кают трудности, связанные с выполнением доказательства по методу математиче-
ской индукции. Обычно проблема заключается в том, что выбрано недостаточно
сильное предположение индукции, которое не позволяет провести подробное рас-
смотрение. Натолкнувшись на такое препятствие, пересмотрите предположение
индукции, избавившись от членов низшего порядка. При этом часто удается про-
вести строгое математическое доказательство.
Рассмотрим рекуррентное соотношение:

T (n) = T ( n/2 ) + T ( n/2


) + 1.

Можно предположить, что его решение — O (n). Попытаемся показать, что для
правильно выбранной константы c выполняется неравенство T (n)  cn. Подста-
вив предполагаемое решение в рекуррентное соотношение, получим выражение

T (n)  c n/2 + c n/2


+ 1 = cn + 1,

из которого не следует T (n)  cn ни для какого c. Можно попытаться


 2 сделать
другие предположения, например, что решение — T (n) = O n , но на самом
деле правильным является именно наше первоначальное предположение. Однако
чтобы это показать, необходимо выбрать более сильное предположение индукции.
Интуитивно понятно, что наша догадка была почти правильной: мы ошиблись
всего лишь на константу, равную 1, т.е. на величину низшего порядка. Тем не
менее, математическая индукция не работает, если в предположении индукции
допущена даже такая, казалось бы, незначительная ошибка. Эту трудность можно
преодолеть, если вычесть в первоначальном предположении слагаемое низшего
порядка. Таким образом, теперь предположение индукции имеет вид T (n)  cn−
− b, где b  0 — константа. Теперь мы имеем следующее соотношение:

T (n)  (c n/2 − b) + (c n/2


− b) + 1 =
= cn − 2b + 1  cn − b,

которое справедливо при b  1. Как и раньше, чтобы выполнялись граничные


условия, константу c необходимо выбрать достаточно большой.

Стр. 113
114 Часть I. Основы

Большинство считает прием, при котором вычитаются слагаемые низшего по-


рядка, трудным для понимания. Однако что же остается делать, когда математика
не работает, если не усилить предположение? Чтобы понять этот шаг, важно пом-
нить, что мы используем математическую индукцию: более сильное утверждение
для данного фиксированного значения можно доказать, лишь предположив истин-
ность более сильного утверждения для меньших значений.

Остерегайтесь ошибок
Используя асимптотические обозначения, легко допустить ошибку. Например,
для рекуррентного соотношения легко “доказать”, что T (n) = O (n), предполо-
жив справедливость соотношения T (n)  cn, а затем выписав цепочку соотно-
шений

T (n)  2 (c n/2 ) + n  cn + n =
= O (n) , ← не верно!!!

поскольку c — константа. Ошибка заключается в том, что не была доказана гипо-


теза индукции в точном виде, т.е. в виде T (n)  cn.

Замена переменных
Иногда с помощью небольших алгебраических преобразований удается до-
биться того, что неизвестное рекуррентное соотношение становится похожим на
то, с которым мы уже знакомы. Например, рассмотрим следующее рекуррентное
соотношение: √ 
T (n) = 2T n + lg n,
которое выглядит довольно сложным. Однако его можно упростить, выполнив
замену переменных. Для удобства мы не станем беспокоиться об округлении

таких значений, как n, до целых чисел. Воспользовавшись заменой m = lg n,
получим:  
T (2m ) = 2T 2m/2 + m.
Теперь можно переименовать функцию S (m) = T (2m ), после чего получается
новое рекуррентное соотношение:

S (m) = 2S (m/2) + m,

которое очень похоже на рекуррентное соотношение (4.4). Это рекуррентное соот-


ношение имеет такое же решение — S (m) = O (m lg m). Сделав обратную замену
S (m) на T (n), получим

T (n) = T (2m ) = S (m) = O (m lg m) = O (lg n lg lg n) .

Стр. 114
Глава 4. Рекуррентные соотношения 115

Упражнения
4.1-1. Покажите, что O (lg n) является решением рекуррентного соотношения
T (n) = T ( n/2
) + 1.
4.1-2. Мы убедились, что O (n lg n) является решением рекуррентного соотно-
шения T (n) = 2T ( n/2 ) + n. Покажите, что Ω (n lg n) также являет-
ся решением этого рекуррентного соотношения. Отсюда можно сделать
вывод, что решением рассматриваемого рекуррентного соотношения яв-
ляется Θ (n lg n).
4.1-3. Покажите, что преодолеть трудность, связанную с граничным условием
T (1) = 1 в рекуррентном соотношении (4.4), можно путем выбора дру-
гого предположения индукции, не меняя при этом граничных условий.
4.1-4. Покажите, что Θ (n lg n) является решением “точного” рекуррентного
соотношения (4.2) для сортировки слиянием.
4.1-5. Покажите, что O (n lg n) является решением рекуррентного соотношения
T (n) = 2T ( n/2 + 17) + n.

4.1-6. Решите рекуррентное соотношение T (n) = 2T ( n) + 1 с помощью
замены переменных. Решение должно быть асимптотически точным. При
решении игнорируйте тот факт, что значения являются целыми числами.

4.2 Метод деревьев рекурсии


Хотя метод подстановок способен обеспечить краткий путь к подтверждению
того факта, что предполагаемое решение рекуррентного соотношения является
правильным, однако сделать хорошую догадку зачастую довольно трудно. По-
строение дерева рекурсии, подобного тому, с которым мы имели дело в главе 2
при анализе рекуррентного соотношения, описывающего время сортировки сли-
янием, — прямой путь к хорошей догадке. В дереве рекурсии (recursion tree)
каждый узел представляет время, необходимое для выполнения отдельно взятой
подзадачи, которая решается при одном из многочисленных рекурсивных вызо-
вов функций. Далее значения времени работы отдельных этапов суммируются
в пределах каждого уровня, а затем — по всем уровням дерева, в результате чего
получаем полное время работы алгоритма. Деревья рекурсии находят практиче-
ское применение при рассмотрении рекуррентных соотношений, описывающих
время работы алгоритмов, построенных по принципу “разделяй и властвуй”.
Деревья рекурсии лучше всего подходят для того, чтобы помочь сделать до-
гадку о виде решения, которая затем проверяется методом подстановок. При этом
в догадке часто допускается наличие небольших неточностей, поскольку впослед-
ствии она все равно проверяется. Если же построение дерева рекурсии и сумми-
рование времени работы по всем его составляющим производится достаточно

Стр. 115
116 Часть I. Основы

тщательно, то само дерево рекурсии может стать средством доказательства кор-


ректности решения. В данном разделе деревья рекурсии применяются для полу-
чения предположений о виде решения, а в разделе 4.4 — непосредственно для
доказательства теоремы, на которой базируется основной метод.
Например, посмотрим, как с помощью дерева рекурсии можно догадаться 
о виде решения рекуррентного соотношения T (n) = 3T ( n/4 ) + Θ n2 . Нач-
нем с того, что оценим решение сверху. Как известно, при решении рекуррентных
соотношений тот факт, что от аргументов функций берется целая часть, обычно
является несущественным (это пример отклонений, которые можно допустить),
поэтому построим дерево рекурсии для рекуррентного соотношения T (n) =
= 3T (n/4) + cn2 , записанного с использованием константы c > 0.
На рис. 4.1 проиллюстрирован процесс построения дерева рекурсии для ре-
куррентного соотношения T (n) = 3T (n/4) + cn2 . Для удобства предположим,
что n — степень четверки (еще один пример допустимых отклонений). В части а)
показана функция T (n), которая затем все более подробно расписывается в частях
б)–г) в виде эквивалентного дерева рекурсии, представляющего анализируемое ре-
куррентное соотношение. Член cn2 в корне дерева представляет время верхнего
уровня рекурсии, а три поддерева, берущих начало из корня, — времена выпол-
нения подзадач размера n/4. В части в) добавлен еще один шаг, т.е. выполнено
представление в виде поддерева каждого узла с временем T (n/4). Время выпол-
нения, соответствующее каждому из трех дочерних поддеревьев, равно c (n/4)2 .
Далее каждый лист дерева преобразуется в поддерево аналогичным образом, в со-
ответствии с рекуррентным соотношением.
Поскольку по мере удаления от корня дерева размер подзадач уменьшается,
в конце концов мы должны дойти до граничных условий. Сколько же уровней
дерева нужно построить, чтобы их достичь? Размер вспомогательной задачи, со-
ответствующей уровню, который находится на i-ом уровне глубины, равен n/4i .
Таким образом, размер подзадачи становится равным 1, когда n/4i = 1 или, что
то же самое, когда i = log4 n. Таким образом, всего в дереве log4 n + 1 уровней.
Затем мы определяем время выполнения для каждого уровня дерева. На каж-
дом уровне в три раза больше узлов, чем на предыдущем уровне, поэтому количе-
ство узлов на i-м уровне равно 3i . Поскольку размеры вспомогательных подзадач
при спуске на один уровень уменьшаются в четыре раза, время выполнения каж-
 2
дого узла на i-м уровне (i = 0, log4 n − 1) равно c n/4i . Умножая количество
узлов на уровне на время выполнения одного узла, получаем, что суммарное вре-
мя выполнения вспомогательных подзадач, соответствующих узлам i-го уровня,
 2
равно 3i c n/4i = (3/16)i cn2 . Последний уровень, находящийся на глубине
log4 n, имеет 3log4 n = nlog4 3 узлов, каждый из которых дает в общее время ра-
боты вклад, равный T (1). Поэтому время  работы
 этого уровня равно величине
nlog4 3 T (1), которая ведет себя как Θ nlog4 3 .

Стр. 116
Глава 4. Рекуррентные соотношения 117

T(n) cn2 cn2

c ( 4n ) c ( 4n ) c ( 4n )
2 2 2
T ( 4n ) T ( 4n ) T ( 4n )

T ( 16n ) T ( 16n ) T ( 16n ) T ( 16n ) T ( 16n ) T ( 16n ) T ( 16n ) T ( 16n ) T ( 16n )

а) б) в)

cn2 cn2

c ( 4n ) c ( 4n ) c ( 4n )
2 2 2
3
16 cn2

log4 n
c ( 16n ) c ( 16n ) c ( 16n ) c ( 16n ) c ( 16n ) c ( 16n ) c ( 16n ) c ( 16n ) c ( 16n )
2 2 2 2 2 2 2 2 2
( 163 )2 cn2

T(1) T(1) T(1) T(1) T(1) T(1) T(1) T(1) T(1) T(1) ... T(1) T(1) T(1) Θ(nlog4 3 ) …

nlog4 3
2
г) Всего: O(n )

Рис. 4.1. Построение дерева рекурсии для рекуррентного соотношения T (n) = 3T (n/4)+
+ cn2

Теперь мы суммируем времена работы всех уровней дерева и определяем


время работы дерева целиком:
 2  log4 n−1  
3 2 3 3
T (n) = cn2 + cn + cn2 + · · · + cn2 + Θ nlog4 3 =
16 16 16

log4 n−1     (3/16)log4 n − 1  
3 i 2
= cn + Θ nlog4 3 = cn2 + Θ nlog4 3 .
16 (3/16) − 1
i=0

Стр. 117
118 Часть I. Основы

Эта формула выглядит несколько запутанной, но только до тех пор, пока мы не


догадаемся воспользоваться той небольшой свободой, которая допускается при
асимптотических оценках, и не используем в качестве верхней границы одного
из слагаемых бесконечно убывающую геометрическую прогрессию. Возвращаясь
на один шаг назад и применяя формулу (A.6), получаем:
log4 n−1    
 3 i 2
T (n) = cn + Θ nlog4 3 <
16
i=0
∞    
3 i 2
< cn + Θ nlog4 3 =
16
i=0
1  
= cn2 + Θ nlog4 3 =
1 − (3/16)
16  
= cn2 + Θ nlog4 3 =
13 
= O n2 .

Таким  образом,
 для исходного рекуррентного соотношения T (n) = 3T ( n/4 ) +
+ Θ n2 получен предполагаемый вид решения T (n) = O n2 . В рассматри-
ваемом примере коэффициенты при cn2 образуют геометрическую прогрессию,
а их сумма, согласно уравнению (A.6), ограничена сверху константой 16/13. По-
скольку вклад корня дерева в полное время работы равен cn2 , время работы корня
представляет собой некоторую постоянную часть от общего времени работы всего
дерева в целом. Другими словами, полное время работы всего дерева в основном
определяется временем работы его корня.

В действительности, если O n2 в действительности представляет собой
верхнюю границу (в чем мы вскоре убедимся), то эта граница должна быть асимп-
тотически точной оценкой. Почему? Потому что первый рекурсивный  вызов
 дает
вклад в общее время работы алгоритма, который выражается как Θ n2 , поэтому 
нижняя граница решения рекуррентного соотношения представляет собой Ω n2 .
Теперь с помощью метода подстановок
 2 проверим корректность нашей догад-
ки, т.е. убедимся, что T (n) = O
 2 n — верхняя граница рекуррентного соотноше-
ния T (n) = 3T ( n/4 )+Θ n . Нам надо показать, что для некоторой константы
d > 0 выполняется неравенство T (n)  dn2 . Используя ту же константу c > 0,
что и раньше, получаем:

T (n)  3T ( n/4 ) + cn2  3d n/4 2 + cn2 


3
 3d (n/4)2 + cn2 = dn2 + cn2  dn2 ,
16
где последнее неравенство выполняется при d  (16/13) c.

Стр. 118
Глава 4. Рекуррентные соотношения 119

cn cn

c( 3n ) c( 23n ) cn

log3 2 n

c( 9n ) c( 29n ) c( 29n ) c( 49n ) cn

...
...

Всего: O(n lg n)

Рис. 4.2. Дерево рекурсии, соответствующее рекуррентному


соотношению T (n) = T (n/3) + T (2n/3) + cn

Давайте теперь рассмотрим более сложный пример. На рис. 4.2 представлено


дерево рекурсии для рекуррентного соотношения:
T (n) = T (n/3) + T (2n/3) + cn.
(Здесь также для простоты опущены функции “пол” и “потолок”.) Пусть c, как
и раньше, представляет постоянный множитель в члене O (n). При суммировании
величин по всем узлам дерева, принадлежащим определенному уровню, для каж-
дого уровня мы получаем величину cn. Самый длинный путь от корня дерева до
его листа имеет вид n → (2/3) n → (2/3)2 n → . . . → 1. Из (2/3)k n = 1 следует,
что высота дерева равна log3/2 n.
Интуитивно понятно, что асимптотическое поведение предполагаемого реше-
ния рекуррентного соотношения определяетсяпроизведением  количества уровней
на время выполнения каждого уровня, т.е. O cn log3/2 n = O (n lg n). В данном
случае время выполнения равномерно распределяется по всем уровням дерева
рекурсии. Здесь возникает одна сложность: нужно определить время работы каж-
дого листа. Если бы рассматриваемое дерево рекурсии было полностью бинарным
с высотой log3/2 n, то всего имелось бы 2log3/2 n = nlog3/2 2 листьев. Поскольку
время выполнения каждого листавыражается  константой, сумма этих констант
log3/2 2
по всем листьям была бы равна Θ n , т.е. ω (n lg n). На самом деле это де-
рево рекурсии нельзя назвать полностью бинарным, поэтому у него меньше, чем
nlog3/2 2 листьев. Более того, по мере удаления от корня отсутствует все большее
количество внутренних узлов. Следовательно, не для всех уровней время их ра-
боты выражается как cn; более низкие уровни дают меньший вклад. Можно было
бы попытаться аккуратно учесть вклады всех элементов дерева, однако вспом-
ним, что мы всего лишь угадываем вид решения, чтобы затем воспользоваться

Стр. 119
120 Часть I. Основы

методом подстановок. Давайте отклонимся от точного решения и допустим, что


асимптотическая верхняя граница решения ведет себя как O (n lg n).
С помощью метода подстановок мы можем убедиться, что сделанное выше
предположение корректно. Покажем, что при подходящем выборе положительной
константы d выполняется неравенство T (n)  dn lg n. Можно записать цепочку
соотношений:

T (n)  T (n/3) + T (2n/3) + cn 


 d (n/3) lg (n/3) + d (2n/3) lg (2n/3) + cn =
= (d (n/3) lg n − d (n/3) lg 3) + (d (2n/3) lg n − d (2n/3) lg (3/2)) + cn =
= dn lg n − d ((n/3) lg 3 + (2n/3) lg (3/2)) + cn =
= dn lg n − d ((n/3) lg 3 + (2n/3) lg 3 − (2n/3) lg 2) + cn =
= dn lg n − dn (lg 3 − 2/3) + cn 
 dn lg n,

которые выполняются при d  c/(lg 3 − (2/3)) . Таким образом, решение не


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

Упражнения
4.2-1. Определите с помощью дерева рекурсии асимптотическую верхнюю гра-
ницу рекуррентного соотношения T (n) = 3T ( n/2 ) + n. Проверьте
ответ методом подстановок.
4.2-2. Обратившись к дереву рекурсии, докажите, что решение рекуррентного
соотношения T (n) = T (n/3) + T (2n/3) + cn, где c — константа, ведет
себя как Ω (n lg n).
4.2-3. Постройте дерево рекурсии для рекуррентного соотношения T (n) =
= 4T ( n/2 )+cn, где c — константа, и найдите точную асимптотическую
границу его решения. Проверьте ее с помощью метода подстановок.
4.2-4. Найдите с помощью дерева рекурсии точную асимптотическую оценку
решения рекуррентного соотношения T (n) = T (n − a) + T (a) + cn, где
a  1 и c > 0 — константы.
4.2-5. Найдите с помощью дерева рекурсии точную асимптотическую оценку
решения рекуррентного соотношения T (n) = T (αn)+T ((1 − α) n)+cn,
где 0 < α < 1 и c > 0 — константы.

Стр. 120
Глава 4. Рекуррентные соотношения 121

4.3 Основной метод


Основной метод является своего рода “сборником рецептов”, по которым стро-
ятся решения рекуррентных соотношений вида

T (n) = aT (n/b) + f (n) , (4.5)

где a  1 и b > 1 — константы, а f (n) — асимптотически положительная функция.


Чтобы научиться пользоваться этим методом, необходимо запомнить три случая,
что значительно облегчает решение многих рекуррентных соотношений, а часто
это можно сделать даже в уме.
Рекуррентное соотношение (4.5) описывает время работы алгоритма, в кото-
ром задача размером n разбивается на a вспомогательных задач, размером n/b
каждая, где a и b — положительные константы. Полученные в результате раз-
биения a подзадач решаются рекурсивным методом, причем время их решения
равно T (n/b). Время, требуемое для разбиения задачи и объединения результатов,
полученных при решении вспомогательных задач, описывается функцией f (n).
Например, в рекуррентном соотношении, возникающем при анализе процедуры
MERGE_SORT, a = 2, b = 2, а f (n) = Θ (n).
Строго говоря, при определении приведенного выше рекуррентного соотно-
шения допущена неточность, поскольку число n/b может не быть целым. Однако
замена каждого из a слагаемых T (n/b) выражением T ( n/b ) или T ( n/b
) не
влияет на асимптотическое поведение решения (это будет доказано в следующем
разделе). Поэтому обычно при составлении рекуррентных соотношений подобно-
го вида, полученных методом “разделяй и властвуй”, обычно мы будем игнори-
ровать функции “пол” и “потолок”, с помощью которых аргумент преобразуется
к целому числу.

Основная теорема
Основной метод базируется на приведенной ниже теореме.

Теорема 4.1 (Основная теорема). Пусть a  1 и b > 1 — константы, f (n) —


произвольная функция, а T (n) — функция, определенная на множестве неотри-
цательных целых чисел с помощью рекуррентного соотношения

T (n) = aT (n/b) + f (n) ,

где выражение n/b интерпретируется либо как n/b , либо как n/b
. Тогда асимп-
тотическое поведение функции T (n) можно выразить следующим образом.
 
1. Если f (n) = O nlogb a−ε для некоторой константы ε > 0, то T (n) =
= Θ nlogb a .

Стр. 121
122 Часть I. Основы

   
2. Если f (n) = Θ nlogb a , то T (n) = Θ nlogb a lg n .
 log a+ε 
3. Если f (n) = Ω n b для некоторой константы ε > 0, и если
af (n/b)  cf (n) для некоторой константы c < 1 и всех достаточно боль-
ших n, то T (n) = Θ (f (n)). 

Перед тем как применение основной теоремы будет продемонстрировано на


конкретных примерах, попытаемся понять ее суть. В каждом из трех выделенных
в теореме случаев функция f (n) сравнивается с функцией nlogb a . Интуитивно
понятно, что асимптотическое поведение решения рекуррентного соотношения
определяется большей из двух функций. Если большей является функция nlogb a ,
 log
как в случае 1, то решение — T (n) = Θ n b a . Если быстрее возрастает функция
f (n), как в случае 3, то решение — T (n) = Θ (f (n)). Если же обе функции срав-
нимы, как в случае 2, топроисходит умножение на логарифмический множитель
и решение — T (n) = Θ nlogb a lg n = Θ (f (n) lg n).
Кроме этих интуитивных рассуждений, необходимо разобраться с некоторыми
техническими деталями. В первом случае функция f (n) должна быть не просто
меньше функции nlogb a , а полиномиально меньше. Это означает, что функция f (n)
должна быть асимптотически меньше функции nlogb a в nε раз, где ε — некоторая
положительная константа. Аналогично, в третьем случае функция f (n) должна
быть не просто больше функции nlogb a , а полиномиально больше, и, кроме того,
удовлетворять условию “регулярности”, согласно которому af (n/b)  cf (n). Это
условие выполняется для большинства полиномиально ограниченных функций,
с которыми мы будем иметь дело.
Важно понимать, что этими тремя случаями не исчерпываются все возможно-
сти поведения функции f (n). Между случаями 1 и 2 есть промежуток, в котором
функция f (n) меньше функции nlogb a , но не полиномиально меньше. Аналогич-
ный промежуток имеется между случаями 2 и 3, когда функция f (n) больше функ-
ции nlogb a , но не полиномиально больше. Если функция f (n) попадает в один
из этих промежутков, или если для нее не выполняется условие регулярности из
случая 3, основной метод неприменим для решения рекуррентных соотношений.

Использование основного метода


Чтобы использовать основной метод, достаточно определить, какой из част-
ных случаев основной теоремы (если такой есть) применим в конкретной задаче,
а затем записать ответ.
В качестве первого примера рассмотрим такое рекуррентное соотношение:
T (n) = 9T (n/3) + n.
 
logb a = nlog3 9 = Θ n2 . Поскольку
В этом случае
 a = 9,b = 3, f (n) = n, так что n
f (n) = O nlog3 9−ε , где ε = 1, можно применить
  случай 1 основной теоремы
и сделать вывод, что решение — T (n) = Θ n2 .

Стр. 122
Глава 4. Рекуррентные соотношения 123

Теперь рассмотрим следующее рекуррентное соотношение:

T (n) = T (2n/3) + 1,

в котором a = 1, b = 3/2, f (n) = 1, а nlogb a = nlog3/2 1 = n0 = 1. Здесь


применим случай 2, поскольку f (n) = Θ nlogb a = Θ (1), поэтому решение —
T (n) = Θ (lg n).
Для рекуррентного соотношения

T (n) = 3T (n/4) + n lg n
 
выполняются условия a = 3, b = 4, f (n) = n lg n, и nlogb a = nlog4 3 = O n0.793 .
Поскольку f (n) = Ω nlog4 3+ε , где ε ≈ 0.2, применяется случай 3 (если удастся
показать выполнение условия регулярности для функции f (n)). При достаточно
больших n условие af (n/b) = 3 (n/4) lg (n/4)  (3/4) n lg n = cf (n) выполняет-
ся при c = 3/4. Следовательно, согласно случаю 3, решение этого рекуррентного
соотношения — T (n) = Θ (n lg n).
К рекуррентному соотношению

T (n) = 2T (n/2) + n lg n

основной метод неприменим, несмотря на то, что оно имеет надлежащий вид:
a = 2, b = 2, f (n) = n lg n, и nlogb a = n. Может показаться, что к нему применим
случай 3, поскольку функция f (n) = n lg n асимптотически больше, чем nlogb a =
= n. Однако проблема заключается в том, что данная функция не полиномиально
больше. Отношение f (n)/nlogb a = (n lg n)/n = lg n асимптотически меньше
функции nε для любой положительной константы ε. Следовательно, рассматрива-
емое рекуррентное соотношение находится “в промежуточном состоянии” между
случаями 2 и 3. (См. упражнение 4.4-2.)

Упражнения
4.3-1. С помощью основной теоремы найдите точные асимптотические грани-
цы следующих рекуррентных соотношений.
а) T (n) = 4T (n/2) + n.
б) T (n) = 4T (n/2) + n2 .
в) T (n) = 4T (n/2) + n3 .
4.3-2. Рекуррентное соотношение T (n) = 7T (n/2) + n2 описывает время ра-
боты алгоритма A. Время работы альтернативного алгоритма A выража-
ется рекуррентным соотношением T  (n) = aT  (n/4) + n2 . Чему равно
наибольшее целое значение a, при котором алгоритм A работает асимп-
тотически быстрее, чем алгоритм A.

Стр. 123
124 Часть I. Основы

4.3-3. Покажите с помощью основного метода, что T (n) = Θ (lg n) является


решением рекуррентного соотношения T (n) = T (n/2) + Θ (1), возника-
ющего при анализе алгоритма бинарного поиска. (Алгоритм бинарного
поиска описывается в упражнении 2.3-5.)
4.3-4. Можно ли применить основной метод к рекуррентному соотношению
T (n) = 4T (n/2) + n2 lg n? Обоснуйте ваш ответ. Найдите асимптотиче-
скую верхнюю границу решения этого рекуррентного соотношения.
 4.3-5. Рассмотрим условие регулярности af (n/b)  cf (n) для некоторой кон-
станты c < 1, являющееся частью случая 3 основной теоремы. Приведите
примеры констант a  1 и b > 1 и функции f (n), которые удовлетворяют
всем условиям случая 3 основной теоремы, кроме условия регулярности.

 4.4 Доказательство основной теоремы


В этом разделе приводится доказательство основной теоремы (теорема 4.1).
Для применения самой теоремы понимать доказательство необязательно.
Доказательство состоит из двух частей. В первой части анализируется “ос-
новное” рекуррентное соотношение (4.5) с учетом упрощающего предположения,
согласно которому T (n) определена только для точных степеней числа b > 1,
т.е. для n = 1, b, b2 , . . . . В этой части представлены все основные идеи, необ-
ходимые для доказательства основной теоремы. Во второй части доказательство
обобщается на множество всех натуральных n; кроме того, здесь при доказа-
тельстве аккуратно выполняются все необходимые округления с использованием
функций “пол” и “потолок”.
В данном разделе асимптотические обозначения будут несколько видоизмене-
ны: с их помощью будет описываться поведение функций, определенных только
для целых степеней числа b, хотя согласно определению асимптотических обозна-
чений неравенства должны доказываться для всех достаточно больших чисел, а не
только для степеней числа b. Поскольку можно ввести новые  асимптотические
обозначения, применимые к множеству чисел bi : i = 0, 1, . . . , а не ко всему
множеству неотрицательных целых чисел, упомянутое видоизменение несуще-
ственно.
Тем не менее, применяя асимптотические обозначения на суженной области
определения, необходимо быть внимательным, чтобы не прийти к неверным вы-
водам. Например, если доказано, что T (n) = O (n), если n — степень двойки, то
это еще не означает, что T (n) = O (n) для всех n. Функция T (n) может быть
определена так:

n при n = 1, 2, 4, 8, . . . ,
T (n) = .
n2 для остальных значений n.

Стр. 124
Глава 4. Рекуррентные соотношения 125

В этом
 случае
 наилучшей верхней границей, как легко доказать, является T (n) =
= O n2 . Во избежание подобных ошибок, мы никогда не будем использовать
асимптотические обозначения на ограниченной области определения функции,
если только из контекста не будет вполне очевидно, что именно мы собираемся
делать.

4.4.1 Доказательство теоремы для точных степеней


В первой части доказательства основной теоремы анализируется рекуррентное
соотношение (4.5)
T (n) = aT (n/b) + f (n) ,
в предположении, что n — точная степень числа b > 1 (b — не обязательно целое
число). Анализ производится путем доказательства трех лемм. В первой из них
решение основного рекуррентного соотношения сводится к вычислению выраже-
ния, содержащего суммирование. Во второй лемме определяются границы этой
суммы. В третьей лемме с помощью первых двух доказывается версия основной
теоремы для случая, когда n — точная степень b.
Лемма 4.2. Пусть a  1 и b > 1 — константы, а f (n) — неотрицательная функция,
определенная для точных степеней числа b. Определим функцию T (n) на множе-
стве точных степеней числа b с помощью такого рекуррентного соотношения:

Θ (1) при n = 1,
T (n) =
aT (n/b) + f (n) при n = bi ,
где i — положительное целое число. Тогда получаем:
  log
b n−1
 
logb a
T (n) = Θ n + aj f n/bj . (4.6)
j=0

Доказательство. Воспользуемся деревом рекурсии, представленным на рис. 4.3.


Время выполнения, соответствующее корню дерева, равняется f (n). Корень име-
ет a дочерних ветвей, время выполнения каждой из которых равно f (n/b). (В хо-
де этих рассуждений, особенно для визуального представления дерева рекурсии,
удобно считать число a целым, хотя это и не следует из каких либо математиче-
ских соотношений.) Каждая из этих дочерних ветвей, в свою  очередь,
 тоже имеет
a дочерних ветвей, время выполнения которых равно f n/b2 . Таким образом,
получается, что на втором уровне дерева имеется a2 узлов. Обобщая эти рассуж-
дения, можно сказать, что j
 уровне находится a узлов, а время выполнения
 на jj-м
каждого из них равно f n/b . Время выполнения каждого листа равно T (1) =
= Θ (1), и все листья находятся на глубине logb n уровня, поскольку n/blogb n = 1.
Всего же у дерева alogb n = nlogb a листьев.

Стр. 125
126 Часть I. Основы

f (n) f (n)

f ( n b) f ( n b) f ( n b) af (n b )

a a a
log b n
2
f (n b 2 ) f (n b 2 ) ... f (n b 2 ) f (n b 2 ) f (n b 2 ) ... f (n b 2 ) f (n b 2 ) f (n b 2 ) ... f (n b 2 ) a 2 f (n b )

a a a a a a a a a
... ... ... ... ... ... ... ... ... ...

Θ(l) Θ(l) Θ(l) Θ(l) Θ(l) Θ(l) Θ(l) Θ(l) Θ(l) Θ(l) ... Θ(l) Θ(l) Θ(l) Θ(nlogb a )

nlogb a
Всего: Θ(nlogb a ) + ∑ a j f (n b j )

Рис. 4.3. Дерево рекурсии, генерируемое рекуррентным соотношением T (n) = aT (n/b)+


+ f (n). Дерево является полным a-арным деревом высотой logb n с nlogb a листьями.
Время выполнения каждого уровня показано справа, а сумма этих величин выражается
уравнением (4.6)

Уравнение (4.6) можно получить, суммируя время выполнения всех листьев


дерева, как показано
 на рисунке. Время выполнения внутренних узлов уровня j
j j
равно a f n/b , так что полное время выполнения всех внутренних узлов можно
записать так:

logb n−1
 
aj f n/bj .
j=0

В алгоритме разбиения, лежащем в основе анализируемого рекуррентного соотно-


шения, эта сумма соответствует полному времени, затрачиваемому на разбиение
поставленной задачи на вспомогательные подзадачи, и последующему объедине-
нию их решений. Суммарное время выполнения всех листьев, т.е. время решения
всех nlogb a вспомогательных задач с размером 1, равно Θ nlogb a . 

Три частных случая основной теоремы, выраженные в терминах дерева ре-


курсии, соответствуют ситуациям, когда полное время выполнения дерева 1) пре-
имущественно определяется временем выполнения его листьев, 2) равномерно
распределено по всем уровням дерева или 3) преимущественно определяется вре-
менем выполнения корня. Сумма в уравнении (4.6) описывает время, затрачивае-
мое на этапы разбиения и объединения в алгоритме разбиения, лежащем в основе

Стр. 126
Глава 4. Рекуррентные соотношения 127

анализируемого рекуррентного соотношения. В следующей лемме обосновыва-


ются асимптотические оценки порядка роста этой суммы.

Лемма 4.3. Пусть a  1 и b > 1 — константы, а f (n) — неотрицательная функция,


определенная для точных степеней числа b. Тогда функцию g (n), определенную
на множестве целых степеней числа b с помощью соотношения


logb n−1
 
g (n) = aj f n/bj , (4.7)
j=0

можно асимптотически оценить следующим образом.


1. Если для некоторой
 константы
 ε  > 0 выполняется условие f (n) =
= O nlogb a−ε , то g (n) = O nlogb a .
   
2. Если f (n) = Θ nlogb a , то g (n) = Θ nlogb a lg n .
3. Если для некоторой константы c < 1 и всех n  b справедливо соотношение
af (n/b)  cf (n), то g (n) = Θ (f (n)).

Доказательство. В первом случае справедливо соотношение f (n) =


 log a−ε   j
  j
logb a−ε 
=O n b , из которого следует, что f n/b = O n/b . Подставив
это равенство в уравнение (4.7), получим следующее соотношение:
 

logb n−1  n logb a−ε
g (n) = O  aj j . (4.8)
b
j=0

Оценим сумму в O-обозначении. Для этого вынесем из-под знака суммирова-


ния постоянный множитель и выполним некоторые упрощения, в результате чего
сумма преобразуется в возрастающую геометрическую прогрессию:

 n logb a−ε logb n−1  j



logb n−1
 abε
j logb a−ε
a =n =
bj blogb a
j=0 j=0


logb n−1
= nlogb a−ε (bε )j =
j=0
 
bε logb n − 1
logb a−ε
=n =
bε − 1
 ε 
logb a−ε n − 1
=n .
bε − 1

Стр. 127
128 Часть I. Основы

Поскольку b и ε — константы,  последнее выражение можно переписать в виде


nlogb a−ε O (nε ) = O nlogb a . Подставляя
 log это  выражение под знак суммы в урав-
нении (4.8), получим, что g (n) = O n a , и тем самым доказываем случай 1.
b

В случае 2 делается предположение, что f (n) = Θ nlogb a , поэтому
   logb a
f n/bj = Θ n/bj . Подставляя это соотношение в уравнение (4.7), по-
лучаем:  

logb n−1  n logb a
g (n) = Θ  aj j . (4.9)
b
j=0

Как и в случае 1, оценим сумму, стоящую под знаком Θ, но на этот раз геомет-
рическая прогрессия не получается. В самом деле, нетрудно убедиться, что все
слагаемые суммы одинаковы:


logb n−1  n logb a logb n−1 
 a j
j logb a
a =n =
bj blogb a
j=0 j=0


logb n−1
logb a
=n 1=
j=0

= nlogb a logb n.

Подставив это выражение для суммы в уравнение (4.9), получим соотношение


 
g (n) = Θ nlogb a logb n =
 
= Θ nlogb a lg n ,

доказывающее случай 2.
Случай 3 доказывается аналогично. Поскольку функция f (n) фигурирует
в определении (4.7) функции g (n), и все слагаемые функции g (n) неотрица-
тельные, можно заключить, что g (n) = Ω (f (n)) для точных степеней числа b.
Согласно нашему предположению, для некоторой константы c < 1 соотношение
af (n/b)  cf (n) справедливо для  n  b, откуда f (n/b)  (c/a) f (n).
 всех
Итерируя j раз, получаем, что f n/bj  (c/a)j f (n), или, что то же самое,
 
aj f n/bj  cj f (n). После подстановки в уравнение (4.7) и некоторых упроще-
ний получаем геометрическую прогрессию. В отличие от прогрессии из случая 1,
члены данной прогрессии убывают (при рассмотрении приведенных преобразо-

Стр. 128
Глава 4. Рекуррентные соотношения 129

ваний не забывайте, что c — константа):



logb n−1
  
logb n−1
g (n) = a f n/bj 
j
cj f (n) 
j=0 j=0

  
1
 f (n) j
c = f (n) = O (f (n)) .
1−c
j=0

Таким образом, можно прийти к заключению, что g (n) = Θ (f (n)) для точных
степеней числа b. Этим доказательством случая 3 завершается доказательство
леммы. 

Теперь можно приступить к доказательству основной теоремы в частном слу-


чае, когда n — точная степень числа b.
Лемма 4.4. Пусть a  1 и b > 1 — константы, а f (n) — неотрицательная функция,
определенная для точных степеней числа b. Определим функцию T (n) для точных
степеней числа b с помощью следующего рекуррентного соотношения:

Θ (1) при n = 1,
T (n) =
aT (n/b) + f (n) при n = bi ,
где i — положительное целое число. Тогда для этой функции можно дать следую-
щую асимптотическую оценку (справедливую для точных степеней числа b).
1. Если для некоторой
 константы
 ε > 0 выполняется условие f (n) =
= O nlogb a−ε , то T (n) = Θ nlogb a .
   
2. Если f (n) = Θ nlogb a , то T (n) = Θ nlogb a lg n .
3. Если  для некоторой
 константы ε > 0 выполняется условие f (n) =
= Ω nlogb a+ε , и если для некоторой константы c < 1 и всех достаточ-
но больших n справедливо соотношение af (n/b)  cf (n), то T (n) =
= Θ (f (n)).

Доказательство. С помощью асимптотических границ, полученных в лемме 4.3,


оценим сумму (4.6) из леммы 4.2. В случае 1 имеем:
   
T (n) = Θ nlogb a + O nlogb a =
 
= Θ nlogb a ,

в случае 2 —
   
T (n) = Θ nlogb a + Θ nlogb a lg n =
 
= Θ nlogb a lg n ,

Стр. 129
130 Часть I. Основы

 
а в случае 3, поскольку f (n) = Ω nlogb a+ε —
 
T (n) = Θ nlogb a + Θ (f (n)) =
= Θ (f (n)) . 

4.4.2 Учет округления чисел


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

T (n) = aT ( n/b
) + f (n) (4.10)

и верхнюю границу для выражения

T (n) = aT ( n/b ) + f (n) (4.11)

не составляет труда, поскольку в первом случае для получения нужного результа-


та можно использовать неравенство n/b
 n/b, а во втором — неравенство
n/b  n/b. Чтобы получить нижнюю границу рекуррентного соотношения
(4.11), необходимо применить те же методы, что и при нахождении верхней гра-
ницы для рекуррентного соотношения (4.10), поэтому здесь будет показан поиск
только последней.
Дерево рекурсии, представленное на рис. 4.3, модифицируется и приобретает
вид, показанный на рис. 4.4. По мере продвижения по дереву рекурсии вниз, мы
получаем следующую рекурсивную последовательность аргументов:

n,
n/b
,
n/b
/b
, .
n/b
/b
/b
,
..
.

Обозначим j-й элемент этой последовательности как nj , где



n при j = 0,
nj = (4.12)
nj−1 /b
при j > 0.

Стр. 130
Глава 4. Рекуррентные соотношения 131

f (n) f (n)

f (n1 ) f (n1 ) f (n1 ) af (n b )

a a a
log b n
2
f (n2 ) f (n2 ) ... f (n2 ) f (n2 ) f (n2 ) ... f (n2 ) f (n2 ) f (n2 ) ... f (n2 ) a 2 f (n b )

a a a a a a a a a
... ... ... ... ... ... ... ... ... ..
.

Θ(l) Θ(l) Θ(l) Θ(l) Θ(l) Θ(l) Θ(l) Θ(l) Θ(l) Θ(l) ... Θ(l) Θ(l) Θ(l) Θ(nlogb a )

Θ(nlogb a )
Âñåãî: Θ(nlogb a ) + ∑ a j f (n b j )

Рис. 4.4. Дерево рекурсии, определенное рекуррентным соотношением T (n) =


= aT ( n/b
) + f (n). Аргумент рекурсии nj определяется уравнением (4.12)

Сейчас наша цель — определить глубину k, такую что nk — константа. Используя


неравенство x
 x + 1, получаем:

n0  n,
n
n1  + 1,
b
n 1
n2  2 + + 1,
b b
n 1 1
n3  3 + 2 + + 1,
b b b
..
.

В общем случае получаем:



n 1 n 1
j−1
n b
nj  j + < + = j + .
b bi bj bi b b−1
i=0 i=0

Полагая, что j = logb n , мы получим:

n b n b n b b
nlogb n < + < log n−1 + = + = b+ = O (1) ,
blogb n b−1 b b b−1 n/b b − 1 b−1

Стр. 131
132 Часть I. Основы

откуда видно, что на уровне logb n размер задачи не превышает постоянную


величину.
Из рис. 4.4 видно, что выполняется соотношение

  log
b n−1

T (n) = Θ nlogb a + aj f (nj ) (4.13)


j=0

того же вида, что и уравнение (4.6) (с тем отличием, что здесь n — произвольное
целое число, а не точная степень числа b).
Теперь можно приступить к оценке суммы
logb n−1

g (n) = aj f (nj ), (4.14)
j=0

фигурирующей в уравнении (4.13). Эта сумма будет оцениваться аналогично тому,


как это делалось при доказательстве леммы 4.3. Начнем с рассмотрения случая 3.
Если для n > b + b/(b − 1) выполняется соотношение af ( n/b
)  cf (n), где
c < 1 — константа, то отсюда следует, что aj f (nj )  cj f (n). Таким образом,
сумму в уравнении (4.14) можно оценить точно так же,как это  было сделано
в лемме 4.3. В случае 2 выполняется условие f(n) = Θ nlog b a . Если мы смо-
   logb a 
жем показать, что f (nj ) = O nlogb a /aj = O n/bj , то доказательство
случая 2 леммы 4.3 будет завершено. Заметим, что из неравенства  log ja  logb n
следует неравенство b /n  1. Наличие границы f (n) = Θ n b
j подразу-
мевает, что существует такая константа c > 0, что при достаточно больших nj
выполняются такие соотношения:
 logb a
n b
f (nj )  c + =
bj b−1
  logb a
n bj b
=c j 1+ · =
b n b−1
 log a    j logb a
n b b b
=c 1+ · 
aj n b−1
 log a   b
logb a
n b
c 1+ =
aj b−1
 log a 
n b
=O ,
aj

поскольку c (1 + b/(b − 1))logb a — константа. Таким образом, случай 2 доказан.


Доказательство случая 1 почти идентично только что рассмотренному. Главное —

Стр. 132
Глава 4. Рекуррентные соотношения 133

 
доказать справедливость границы f (nj ) = O nlogb a−ε . Это делается аналогично
доказательству в случае 2, хотя алгебраические выкладки при этом оказываются
несколько более сложными.
Итак, мы доказали соблюдение верхних границ в основной теореме для всех
целых n. Соблюдение нижних границ доказывается аналогично.

Упражнения
 4.4-1. Приведите простое и точное выражение для nj в уравнении (4.12) для
случая, когда b — положительное целое число (а не произвольное дей-
ствительное).
 
 4.4-2. Покажите, что если выполняется соотношение f (n) = Θ nlogb a lgk n ,
где k  0, то решение
 основного рекуррентного соотношения —T (n) =
= Θ nlogb a lgk+1 n . Для простоты рассмотрите только случай целых
степеней b.
 4.4-3. Покажите, что в случае 3 основной теоремы одно из условий излишнее
в том плане, что из условия регулярности af (n/b)  cf (n) для некото-
рой константы
 log ca+ε
< 1 следует, что существует константа ε > 0, такая что
f (n) = Ω n b .

Задачи
4-1. Примеры рекуррентных соотношений
Определите верхнюю и нижнюю асимптотические границы функции
T (n) для каждого из представленных ниже рекуррентных соотношений.
Считаем, что при n  2T (n) — константа. Попытайтесь сделать эту
оценку как можно более точной и обоснуйте свой ответ.
а) T (n) = 2T (n/2) + n3 .
б) T (n) = T (9n/10) + n.
в) T (n) = 16T (n/4) + n2 .
г) T (n) = 7T (n/3) + n2 .
д) T (n) = 7T (n/2) + n2 .

е) T (n) = 2T (n/4) + n.
ж) T (n) = T (n − 1) + n.

з) T (n) = T ( n) + 1.
4-2. Поиск отсутствующего целого числа
Массив A [1..n] содержит все целые числа от 0 до n за исключением
одного. Отсутствующее число можно легко определить за время O (n),

Стр. 133
134 Часть I. Основы

располагая вспомогательным массивом B [1..n], предназначенным для за-


писи имеющихся чисел из массива A. Однако в этой задаче мы лишены
средств, позволяющих получить доступ к целому числу из массива A
посредством одной операции. Элементы массива A представлены в дво-
ичной системе счисления, и единственная операция, с помощью которой
можно осуществлять к ним доступ, это “извлечение j-го бита элемента
A [i]”, которая выполняется в течение фиксированного времени.
Покажите, что, располагая только этой операцией, отсутствующее целое
число все же можно определить за время O (n).
4-3. Время, затрачиваемое на передачу констант
В этой книге предполагается, что передача параметров при вызове проце-
дуры занимает фиксированное время, даже если передается N -элемент-
ный массив. Для большинства вычислительных систем это предположе-
ние справедливо, поскольку передается не сам массив, а указатель на
него. В данной задаче исследуются три стратегии передачи параметров.
i) Массив передается посредством указателя. Время выполнения этой
операции T = Θ (1).
ii) Массив передается путем копирования. Время выполнения этой опе-
рации T = Θ (N ), где N — размер массива.
iii) Массив передается путем копирования только некоторого поддиапа-
зона, к которому обращается вызываемая процедура. Время передачи
подмассива A [p..q] равно T = Θ (q − p + 1).
а) Рассмотрите рекурсивный алгоритм бинарного поиска, предназна-
ченный для нахождения числа в отсортированном массиве (см. уп-
ражнение 2.3-5). Приведите рекуррентные соотношения, описываю-
щие время бинарного поиска в наихудшем случае, если массивы пе-
редаются с помощью каждого из описанных выше методов, и дайте
точные верхние границы решений этих рекуррентных соотношений.
Пусть размер исходной задачи равен N , а размер подзадачи — n.
б) Выполните задание части а) для алгоритма MERGE_SORT, описанно-
го в разделе 2.3.1.
4-4. Примеры рекуррентных соотношений
Дайте верхнюю и нижнюю асимптотические оценки функции T (n) в каж-
дом из приведенных ниже рекуррентных соотношений. Предполагается,
что для достаточно малых значений n T (n) — постоянная величина. По-
старайтесь, чтобы оценки были как можно более точными и обоснуйте
ответы.
а) T (n) = 3T (n/2) + n lg n.

Стр. 134
Глава 4. Рекуррентные соотношения 135

б) T (n) = 5T (n/5) + n/lg n.



в) T (n) = 4T (n/2) + n2 n.
г) T (n) = 3T (n/3 + 5) + n/2.
д) T (n) = 2T (n/2) + n/lg n.
е) T (n) = T (n/2) + T (n/4) + T (n/8) + n.
ж) T (n) = T (n − 1) + 1/n.
з) T (n) = T (n − 1) + lg n.
и) T (n) = T (n − 2) + 2 lg n.
√ √
к) T (n) = nT ( n) + n.
4-5. Числа Фибоначчи
В этой задаче раскрываются свойства чисел Фибоначчи, определенных
с помощью рекуррентного соотношения (3.21). Воспользуемся для ре-
шения рекуррентного соотношения Фибоначчи методом генераторных
функций. Определим производящую функцию (или формальный сте-
пенной ряд) F как


F= Fi z i = 0 + z + z 2 + 2z 3 + 3z 4 + 5z 5 + 8z 6 + 13z 7 + 21z 8 + . . . ,
i=0

где Fi — i-е число Фибоначчи.


а) Покажите, что F (z) = z + zF (z) + z 2 F (z).
б) Покажите, что
 
z z 1 1 1
F (z) = =  =√ − ,
1−z−z 2

(1 − φz) 1 − φz 5 
1 − φz 1 − φz

где √
1+ 5
φ= = 1.61803 . . .
2
и √
1 − 5
φ = = −0.61803 . . . .
2

∞  
в) Покажите, что F (z) = √1 φi−φ i z i .
5
i=0

г) Докажите, что Fi = φi /
5 (округленное до ближайшего
  целого) для
 
всех i > 0. (Указание: обратите внимание на то, что φ < 1.)

Стр. 135
136 Часть I. Основы

д) Докажите, что Fi+2  φi для i  0.


4-6. Тестирование СБИС
В распоряжении профессора есть n предположительно идентичных
СБИС1 , которые в принципе способны тестировать друг друга. В тести-
рующее приспособление за один раз можно поместить две микросхемы.
При этом каждая микросхема тестирует соседнюю и выдает отчет о ре-
зультатах тестирования. Исправная микросхема всегда выдает правиль-
ные результаты тестирования, а результатам неисправной микросхемы
доверять нельзя. Таким образом, возможны четыре варианта результатов
тестирования, приведенные в таблице ниже.
Отчет Отчет Вывод
микросхемы А микросхемы Б
Б исправна А исправна Либо обе микросхемы исправны,
либо обе неисправны
Б исправна А неисправна Неисправна хотя бы одна микросхема
Б неисправна А исправна Неисправна хотя бы одна микросхема
Б неисправна А неисправна Неисправна хотя бы одна микросхема
а) Покажите, что если неисправно более n/2 микросхем, то с помощью
какой бы то ни было стратегии, основанной на попарном тестиро-
вании, профессор не сможет точно определить, какие микросхемы
исправны. (Предполагается, что неисправные микросхемы не смо-
гут тайно сговориться, чтобы обмануть профессора.)
б) Рассмотрим задачу о поиске одной исправной микросхемы среди
n микросхем, если предполагается, что исправно более половины
всех микросхем. Покажите, что n/2 попарных тестирований до-
статочно для сведения этой задачи к подзадаче, размер которой при-
близительно в два раза меньше.
в) Покажите, что можно найти все исправные микросхемы с помощью
Θ (n) попарных тестирований, если предполагается, что более поло-
вины микросхем исправны. Сформулируйте и решите рекуррентное
соотношение, описывающее количество тестирований.

1
СБИС — это аббревиатура от “сверхбольшие интегральные схемы” (микросхемы, созданные по
технологии, которая используется при производстве большинства современных микропроцессоров).

Стр. 136
Глава 4. Рекуррентные соотношения 137

4-7. Массивы Монжа


Массив A, состоящий из действительных чисел и имеющий размер m ×
× n, называется массивом Монжа (Monge array), если для всех i, j, k
и l, таких что 1  i < k  m и 1  j < l  n, выполняется соотношение

A [i, j] + A [k, l]  A [i, l] + A [k, j] .

Другими словами, при любом выборе двух срок и двух столбцов из масси-
ва Монжа (при этом элементы массива на пересечении выбранных строк
и столбцов образуют прямоугольник) сумма элементов в левом верхнем
и правом нижнем углах полученного прямоугольника не превышает сум-
му элементов в левом нижнем и правом верхнем его углах. Приведем
пример массива Монжа:

10 17 13 28 23
17 22 16 29 23
24 28 22 34 24
11 13 6 17 7
45 44 32 37 23
36 33 19 21 6
75 66 51 53 34

а) Докажите, что массив является массивом Монжа тогда и только


тогда, когда для всех i = 1, 2, . . . , m − 1 и j = 1, 2, . . . , n − 1 спра-
ведливо соотношение

A [i, j] + A [i + 1, j + 1]  A [i, j + 1] + A [i + 1, j] .

(Указание: для прямого доказательства воспользуйтесь методом ма-


тематической индукции; индукцию нужно провести отдельно для
строк и столбцов.)
б) Приведенный ниже массив не является массивом Монжа. Измените
в нем один элемент таким образом, чтобы он стал массивом Монжа.
(Указание: воспользуйтесь результатами части a.)

37 23 22 32
21 6 7 10
53 34 30 31
32 13 9 6
43 21 15 8

Стр. 137
138 Часть I. Основы

в) Пусть f (i) — индекс столбца, содержащего крайний слева мини-


мальный элемент в строке i. Докажите, что для любого массива
Монжа размером m × n справедливо соотношение f (1)  f (2) 
 · · ·  f (m).
г) Ниже описан алгоритм разбиения, предназначенный для вычисле-
ния крайнего слева минимального элемента в каждой строке, вхо-
дящей в состав массива Монжа A размером m × n.
Постройте подматрицу A матрицы A, состоящую из ее
нечетных строк. С помощью рекурсивной процедуры най-
дите в каждой строке матрицы A крайний слева минималь-
ный элемент. Затем найдите крайний слева минимальный
элемент среди четных строк матрицы A.
Объясните, как найти крайний слева минимальный элемент среди
нечетных строк матрицы A (предполагается, что крайний слева ми-
нимальный элемент среди четных столбцов этой матрицы известен)
за время O (m + n).
д) Запишите рекуррентное соотношение, описывающее время работы
алгоритма из части г. Покажите, что его решение — O (m + n log m).

Заключительные замечания
Рекуррентные соотношения изучались еще с 1202 года, со времен Леонар-
до Фибоначчи (L. Fibonacci). А. де Муавр (A. De Moivre) впервые использовал
метод производящих функций (см. задачу 4-5) для решения рекуррентных соот-
ношений. Основной метод был принят на вооружение после появления работы
Бентли (Bentley), Хакена (Haken) и Сакса (Saxe) [41], в которой был предложено
расширение метода, обоснованного в упражнении 4.4-2. Кнут (Knuth) и Лью (Liu)
[205] показали, каким образом можно решать линейные рекуррентные соотноше-
ния с использованием производящих функций. Подробное обсуждение методов
решения рекуррентных соотношений можно найти в книгах Пурдома (Purdom)
и Брауна (Brown) [252], а также Грехема (Graham), Кнута и Паташника (Patash-
nik) [132].
Некоторые исследователи, в число которых входят Акра (Akra) и Баззи (Bazzi)
[13], Роура (Roura) [262] и Верма (Verma) [306], предложили методы решения
более общих рекуррентных соотношений для алгоритмов разбиения. Ниже пред-
ставлены результаты Акра и Баззи. Их метод подходит для решения рекуррентных
соотношений вида
k
T (n) = ai T ( n/bi ) + f (n) , (4.15)
i=1

Стр. 138
Глава 4. Рекуррентные соотношения 139

где k  1; все коэффициенты ai положительны, и в сумме содержится хо-


тя бы одно слагаемое; все bi больше 1; функция f (n) ограниченная, положи-
тельная и неубывающая. Кроме того, для любой константы c > 1 существуют
константы n0 и d > 0, такие что для всех n > n0 справедливо соотношение
f (n/c)  df (n). Этот метод позволяет найти решение рекуррентного соотноше-
ния T (n) = T ( n/3 ) + T ( 2n/3 ) + O (n), для которого неприменим основной
метод. Чтобы решить рекуррентное
 соотношение (4.15), сначала найдем такое
значение p, при котором ki=1 ai b−p
i = 1. (Такое p всегда существует, причем оно
всегда единственное и положительное.) Решением рекуррентного соотношения
является  n 

f (x)
T (n) = Θ (np ) + Θ np dx ,
xp+1
n

где n — достаточно большая константа. Метод Акры-Баззи на практике может ока-


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

Стр. 139
ГЛАВА 5
Вероятностный анализ
и рандомизированные алгоритмы

Эта глава знакомит читателя с вероятностным анализом и рандомизирован-


ными алгоритмами. Тем, кто не знаком с основами теории вероятности, следу-
ет обратиться к приложению В, в котором представлен обзор этого материала.
В данной книге мы будем несколько раз возвращаться к вероятностному анализу
и рандомизированным алгоритмам.

5.1 Задача о найме сотрудника


Предположим, предприниматель хочет взять на работу нового офис-менедже-
ра. Сначала он попытался найти подходящую кандидатуру самостоятельно, но
неудачно, поэтому решил обратиться в агентство по трудоустройству. Согласно
договоренности, агентство должно присылать работодателю по одному кандидату
в день. Предприниматель проводит с каждым из этих кандидатов собеседование,
после чего принимает окончательное решение, брать его на работу или нет. За каж-
дого направленного ему кандидата предприниматель платит агентству небольшую
сумму. Однако наем выбранного кандидата на работу фактически стоит дороже,
поскольку при этом необходимо уволить офис-менеджера, работающего в данное
время, а также уплатить агентству более значительную сумму за выбранного кан-
дидата. Предприниматель пытается сделать так, чтобы должность всегда занимал
наиболее достойный из всех кандидатов. Как только квалификация очередного
претендента окажется выше квалификации офис-менеджера, который работает
в данное время, этот офис-менеджер будет уволен, а его место займет более

Стр. 140
Глава 5. Вероятностный анализ и рандомизированные алгоритмы 141

подходящий кандидат. Работодатель готов понести все необходимые расходы, но


хочет оценить, во что обойдется ему подбор нового сотрудника.
В представленной ниже процедуре HIRE_ASSISTANT эта стратегия найма пред-
ставлена в виде псевдокода. В ней предполагается, что кандидаты на должность
офис-менеджера пронумерованы от 1 до n. Предполагается также, что после собе-
седования с i-м кандидатом есть возможность определить, является ли он лучшим,
чем все предыдущие. В процессе инициализации процедура создает фиктивного
кандидата номер 0, квалификация которого ниже квалификации всех остальных:
HIRE_ASSISTANT(n)
1 Best ← 0  Кандидат 0 — наименее квалифицированный
 фиктивный кандидат
2 for i ← 1 to n
3 do Собеседование с кандидатом i
4 if кандидат i лучше кандидата Best
5 then Best ← i
6 Нанимаем кандидата i
Модель стоимости этой задачи отличается от модели, описанной в главе 2.
Если ранее в качестве стоимости алгоритма мы рассматривали время его работы,
то теперь нас интересует денежная сумма, затрачиваемая на собеседование и наем
при использовании данного алгоритма найма работника. На первый взгляд может
показаться, что анализ стоимости этого алгоритма очень сильно отличается, на-
пример, от анализа времени работы алгоритма сортировки методом слияния. Од-
нако оказывается, что при анализе стоимости и времени выполнения применяются
одинаковые аналитические методы. В обоих случаях подсчитывается, сколько раз
выполняется та или иная операция.
Обозначим стоимость собеседования через ci , а стоимость найма — через ch .
Пусть m — количество нанятых сотрудников. Тогда полная стоимость, затраченная
при работе этого алгоритма, равна O (nci + mch ). Независимо от того, сколько
сотрудников было нанято, интервью нужно провести с n кандидатами, поэтому
суммарная стоимость всех интервью является фиксированной и равна nci . Таким
образом, нас интересует анализ величины mch — стоимости найма, которая, как
нетрудно понять, меняется при каждом выполнении алгоритма.
Этот сценарий служит моделью распространенной вычислительной парадиг-
мы. Часто встречаются ситуации, когда нужно найти максимальное или мини-
мальное значение последовательности, для чего каждый ее элемент сравнивается
с текущим “претендентом” на звание подходящего. Задача о найме моделирует
частоту, с которой придется заново присваивать метку “победителя” текущему
элементу.

Стр. 141
142 Часть I. Основы

Анализ наихудшего случая


В наихудшем случае работодателю приходится нанимать каждого кандидата,
с которым проведено собеседование. Эта ситуация возникает, когда кандидаты
приходят в порядке возрастания их квалификации. При этом процедура найма
повторяется n раз, а полная стоимость найма равна O (nch ).
Однако маловероятно, чтобы кандидаты поступали в указанном порядке. Фак-
тически мы не имеем представления о том, в каком порядке они будут приходить,
и никак не можем повлиять на этот порядок. Таким образом, возникает законо-
мерный вопрос, чего следует ожидать в типичном (или среднем) случае.

Вероятностный анализ
Вероятностный анализ — это анализ задачи, при котором используются ве-
роятности тех или иных событий. Чаще всего он используется при определении
времени работы алгоритма. Иногда такой анализ применяется и для оценки других
величин, таких как стоимость найма в процедуре HIRE_ASSISTANT. Для проведения
вероятностного анализа необходимо располагать знаниями (или сделать предпо-
ложение) о распределении входных данных. При этом в ходе анализа алгоритма
вычисляется математическое ожидание времени его работы. На эту величину вли-
яет распределение входных величин, поэтому производится усреднение времени
работы по всем возможным входным данным.
Делая предположение о распределении входных величин, нужно быть очень
осторожным. В некоторых задачах такие предположения вполне оправданны и поз-
воляют воспользоваться вероятностным анализом как методом разработки эффек-
тивных алгоритмов и как средством для более глубокого понимания задачи. В дру-
гих задачах разумное описание распределения входных величин не удается, и в
таких случаях вероятностный анализ неприменим.
В задаче о найме сотрудника можно предположить, что претенденты приходят
на собеседование в случайном порядке. Что это означает в контексте рассмат-
риваемой задачи? Предполагается, что любых двух кандидатов можно сравнить
и решить, кто из них квалифицированнее, т.е. в принципе всех кандидатов мож-
но расположить в определенном порядке. (Определение полностью упорядочен-
ных множеств приведено в приложении Б.) Таким образом, каждому кандидату
можно присвоить уникальный порядковый номер от 1 до n. Назовем операцию
присвоения порядкового номера i-му претенденту rank (i) и примем соглаше-
ние, что более высокий ранг соответствует специалисту с более высокой квали-
фикацией. Упорядоченное множество rank (1) , rank (2) , . . . , rank (n) является
перестановкой множества 1, 2, . . . , n. Утверждение, что кандидаты приходят на
собеседование в случайном порядке, эквивалентно утверждению, что вероятность
любого порядка рангов одинакова и равна количеству перестановок чисел от 1 до
n, т.е. n!. Другими словами, ранги образуют случайную равновероятную пере-

Стр. 142
Глава 5. Вероятностный анализ и рандомизированные алгоритмы 143

становку, т.е. каждая из n! возможных перестановок появляется с одинаковой


вероятностью.
Вероятностный анализ задачи о найме сотрудника приведен в разделе 5.2.

Рандомизированные алгоритмы
Чтобы провести вероятностный анализ, нужно иметь некоторые сведения
о распределении входных данных. Во многих случаях мы знаем о них очень ма-
ло. Даже если об этом распределении что-то известно, может случиться так, что
знания, которыми мы располагаем, нельзя численно смоделировать. Несмотря на
это вероятность и случайность часто можно использовать в качестве инструмен-
та при разработке и анализе алгоритмов, путем придания случайного характера
части алгоритма.
В задаче о найме сотрудника все выглядит так, как будто кандидатов присы-
лают на собеседование в случайном порядке, но на самом деле у нас нет никакой
возможности узнать, так ли это на самом деле. Поэтому, чтобы разработать рандо-
мизированный алгоритм для этой задачи, необходимо повысить степень контроля
над порядком поступления претендентов на должность. Внесем в модель неболь-
шие изменения. Допустим, бюро по трудоустройству подобрало n кандидатов.
Работодатель договаривается, чтобы ему заранее прислали список кандидатов,
и каждый день самостоятельно случайным образом выбирает, с кем из претен-
дентов проводить собеседование. Несмотря на то, что работодатель по-прежнему
ничего не знает о претендентах на должность, задача существенно изменилась.
Теперь не нужно гадать, будет ли очередность кандидатов случайной; вместо это-
го мы взяли этот процесс под свой контроль и сами сделали случайным порядок
проведения собеседований.
В общем случае алгоритм называется рандомизированным (randomized), если
его поведение определяется не только набором входных величин, но и значе-
ниями, которые выдает генератор случайных чисел. Будем предполагать, что
в нашем распоряжении имеется генератор дискретных случайных чисел RANDOM.
При вызове процедура RANDOM(a, b) возвращает целое число, принадлежащее
интервалу от a до b и равномерно распределенное в этом интервале. Например,
RANDOM(0, 1) с вероятностью 1/2 выдает 0 и с той же вероятностью — 1. В ре-
зультате вызова RANDOM(3, 7) числа 3, 4, 5, 6 и 7 возвращаются с вероятностью
1/5. Вероятность возврата процедурой RANDOM любого целого числа не зависит
от того, какие числа были возвращены в предыдущем вызове этой процедуры.
Процедуру RANDOM можно представить в виде рулетки с (b − a + 1) делениями.
На практике большинство сред программирования предоставляют в распоряже-
ние программиста генератор псевдослучайных чисел, т.е. детерминированный
алгоритм, который возвращающий числа, которые ведут себя при статистическом
анализе как случайные.

Стр. 143
144 Часть I. Основы

Упражнения
5.1-1. Покажите, что из предположения о том, что в строке 4 алгоритма HIRE_
ASSISTANT всегда можно определить, какой кандидат лучше, следует, что
мы знаем общий порядок рангов кандидатов.
 5.1-2. Опишите реализацию процедуры RANDOM(a, b), которая может исполь-
зовать только один вызов — процедуры RANDOM(0, 1). Чему равно ма-
тематическое ожидание времени работы вашей реализации и как оно
зависит от a и b?
 5.1-3. Предположим, что нам надо выводить 0 и 1 с вероятностью 50%. В на-
шем распоряжении имеется процедура BIASED_RANDOM, которая с ве-
роятностью p выдает 0, и с вероятностью 1 − p — число 1; значение
p нам неизвестно. Сформулируйте алгоритм, использующий в качестве
подпрограммы процедуру BIASED_RANDOM и возвращающий равномер-
но распределенные числа 0 и 1, т.е. вероятность вывода каждого из них
равна 50%. Чему равно математическое ожидание времени работы такой
процедуры и как оно зависит от p?

5.2 Индикаторная случайная величина


При анализе многих алгоритмов, в том числе того, с помощью которого ре-
шается задача о найме сотрудника, будет использоваться индикаторная случайная
величина. Она предоставляет удобный метод перехода от вероятности к математи-
ческому ожиданию. Предположим, что в нашем распоряжении имеется простран-
ство выборки S и событие A. Тогда индикаторная случайная величина (indicator
random variable) I {A}, связанная с событием A, определяется так:

1 если событие A произошло,
I {A} = (5.1)
0 если событие A не произошло.

В качестве примера определим математическое ожидание того, что при под-


брасывании монеты выпадет орел. Пространство событий в этом случае имеет
простой вид S = {H, T }, где вероятности выпадения орла (это событие обозна-
чено как H (head)) и решки (T (tail)) равны: Pr {H} = Pr {T } = 1/2. Далее,
можно определить индикаторную случайную величину XH , связанную с выпаде-
нием орла, т.е. событием H. Эта величина подсчитывает количество выпадений
орла. Если выпадает орел, она равна 1, а если решка — 0. Запишем это с помощью
формальных обозначений:

1 если выпал орел,
XH = I {H} =
0 если выпала решка.

Стр. 144
Глава 5. Вероятностный анализ и рандомизированные алгоритмы 145

Математическое ожидание того, что выпадет орел, равняется математическому


ожиданию индикаторной величины XH :

E [XH ] = E [I {H}] =
= 1 · Pr {H} + 0 · Pr {T } =
= 1 · (1/2) + 0 · (1/2) =
= 1/2.

Таким образом, математическое ожидание того, что выпадет орел, равно 1/2. Как
показано в приведенной ниже лемме, математическое ожидание индикаторной
случайной величины, связанной с событием A, равно вероятности этого события.

Лемма 5.1. Пусть имеется пространство событий S и событие A, принадлежащее


этому пространству, и пусть XA = I {A}. Тогда E [XA ] = Pr {A}.

Доказательство. Согласно определению (5.1) индикаторной случайной величи-


ны и определению математического ожидания, имеем:

E [XA ] = E [I {A}] =
 
= 1 · Pr {A} + 0 · Pr Ā =
= Pr {A} .

где Ā обозначает S − A, т.е. дополнение события A. 

Хотя индикаторные случайные величины могут показаться неудобными в при-


менении, например, при вычислении математического ожидания выпадений ор-
ла при бросании монеты, однако они оказываются полезны при анализе ситуа-
ций, в которых делаются повторные испытания. Например, индикаторная случай-
ная величина позволяет простым путем получить результат в уравнении (В.36).
В этом уравнении количество выпадений орла при n бросаниях монеты вычис-
ляется путем отдельного рассмотрения вероятности того события, что орел вы-
падет 0, 1, 2 и т.д. раз. В уравнении (В.37) предлагается более простой метод,
в котором, по сути, неявно используется индикаторные случайные величины.
Чтобы было понятнее, обозначим через Xi индикаторную случайную величи-
ну, связанную с событием, когда при i-м выбрасывании выпадает орел: Xi =
= I {При i-м броске выпал орел}. Пусть X — случайная величина, равная общему
количеству выпадений орла при n бросаниях монеты. Тогда


n
X= Xi .
i=1

Стр. 145
146 Часть I. Основы

Нам надо вычислить математическое ожидание выпадения орла. Для этого при-
меним операцию математического ожидания к обеим частям приведенного выше
уравнения:  n

E [X] = E Xi .
i=1

Левая часть полученного уравнения представляет собой математическое ожидание


суммы n случайных величин. Математическое ожидание каждой из этих случай-
ных величин легко вычисляется при помощи леммы 5.1. Пользуясь уравнением
(В.20), выражающим свойство линейности математического ожидания, матема-
тическое ожидание суммы случайных величин можно выразить как сумму мате-
матических ожиданий каждой величины. Благодаря линейности математического
ожидания использование индикаторной случайной величины становится мощным
аналитическим методом, который применим даже в том случае, когда между слу-
чайными величинами имеется зависимость. Теперь мы можем легко вычислить
математическое ожидание количества выпадений орла:
 n

E [X] = E Xi =
i=1

n
= E [Xi ] =
i=1

n
1
= =
2
i=1
n
= .
2
Таким образом, индикаторные случайные величины значительно упростили вы-
числения по сравнению с методом, использующимся в уравнении (В.36). Вы еще
не раз встретитесь с этими величинами в данной книге.

Анализ задачи о найме сотрудника с помощью


индикаторных случайных величин
Вернемся к задаче о найме сотрудника, в которой нужно вычислить матема-
тическое ожидание события, соответствующего найму нового менеджера. Чтобы
провести вероятностный анализ, предположим, что кандидаты приходят на со-
беседование в случайном порядке (этот вопрос уже обсуждался в предыдущем
разделе; в разделе 5.3 будет показано, каким образом можно обойтись без этого
предположения). Пусть X — случайная величина, значение которой равно количе-
ству наймов нового офис-менеджера. Далее можно воспользоваться определением

Стр. 146
Глава 5. Вероятностный анализ и рандомизированные алгоритмы 147

математического ожидания (уравнение В.19) и получить соотношение


n
E [X] = x Pr {X = x},
x=1

но эти вычисления окажутся слишком громоздкими. Вместо этого воспользуемся


индикаторными случайными величинами, благодаря чему вычисления значитель-
но упростятся.
Чтобы воспользоваться индикаторными случайными величинами, вместо вы-
числения величины E [X] путем определения одного значения, связанного с чис-
лом наймов нового менеджера, определим n величин, связанных с наймом кон-
кретных кандидатов. В частности, пусть Xi — индикаторная случайная величина,
связанная с событием найма i-го кандидата. Таким образом,

1 если i-й кандидат нанят,
Xi = I {i-й кандидат нанят} = (5.2)
0 если i-й кандидат отклонен,
и
X = X1 + X2 + · · · + Xn . (5.3)
Из леммы 5.1 следует, что

E [Xi ] = Pr {i-й кандидат нанят} ,

и теперь нам надо вычислить вероятность выполнения строк 5 и 6 процедуры


HIRE_ASSISTANT.
Работодатель нанимает кандидата под номером i (строка 5), только если он
окажется лучше всех предыдущих (от 1-го до i−1-го). Поскольку мы предположи-
ли, что кандидаты приходят на собеседование в случайном порядке, это означает,
что первые i кандидатов также прибыли в случайном порядке. Любой из первых
i кандидатов с равной вероятностью может оказаться лучшим. Поэтому веро-
ятность того, что квалификация претендента с номером i выше квалификации
претендентов с номерами от 1 до i − 1, и что он будет взят на работу, равна 1/i.
Пользуясь леммой 5.1, можно прийти к заключению, что

E [Xi ] = 1/i. (5.4)

Стр. 147
148 Часть I. Основы

Теперь можно приступить к вычислению величины E [X]:


 n

E [X] = E Xi = (согласно (5.3)) (5.5)
i=1

n
= E [Xi ] = (согласно линейности математического ожидания)
i=1

n
1
= = (согласно (5.4))
i
i=1
= ln n + O (1) (согласно (A.7)) (5.6)

Даже если проведено интервью с n кандидатами, в среднем будет нанято только


ln n из них. Этот результат подытожен в приведенной ниже лемме.

Лемма 5.2. В случае собеседования с кандидатами в случайном порядке полная


стоимость найма при использовании алгоритма HIRE_ASSISTANT равна O (ch ln n).

Доказательство. Эта оценка непосредственно следует из определения стоимо-


сти найма и уравнения (5.6). 

Порядок роста представленной стоимости найма значительно ниже порядка


роста функции O (ch n), характеризующей стоимость найма в наихудшем случае.

Упражнения
5.2-1. Чему равна вероятность того, что в процедуре HIRE_ASSISTANT будет на-
нят ровно один кандидат, если предполагается, что собеседование с кан-
дидатами проводится в случайном порядке?
5.2-2. Чему равна вероятность того, что в процедуре HIRE_ASSISTANT будет
нанято ровно два кандидата, если предполагается, что собеседование
с кандидатами проводится в случайном порядке? Чему равна вероятность
того, что будет нанято ровно n кандидатов?
5.2-3. Вычислите математическое ожидание суммы очков на n игральных ко-
стях с помощью индикаторных случайных величин.
5.2-4. Решите с помощью индикаторных случайных величин задачу, известную
как задача о гардеробщике. Каждый из n посетителей ресторана сдает
свою шляпу в гардероб. Гардеробщик возвращает шляпы случайным
образом. Чему равно математическое ожидание количества посетителей,
получивших обратно свои собственные шляпы?

Стр. 148
Глава 5. Вероятностный анализ и рандомизированные алгоритмы 149

5.2-5. Пусть A [1..n] — массив, состоящий из n различных чисел. Если i < j


и A [i] > A [j], то пара (i, j) называется инверсией массива A (более
детальную информацию об инверсиях можно найти в задаче 2-4). Пред-
положим, что элементы массива A образуют равномерную случайную
перестановку чисел 1, 2, . . . , n. Воспользуйтесь индикаторными слу-
чайными величинами для поиска математического ожидания количества
инверсий в массиве.

5.3 Рандомизированные алгоритмы


В предыдущем разделе было показано, как знание информации о распреде-
лении входных данных может помочь проанализировать поведение алгоритма
в среднем случае. Однако часто встречаются ситуации, когда такими знаниями
мы не располагаем, и анализ среднего поведения невозможен. Но, как упомина-
лось в разделе 5.1, иногда есть возможность использовать рандомизированные
алгоритмы.
В задачах наподобие задачи о найме сотрудника, в которой важную роль играет
предположение о равновероятности всех перестановок входных данных, вероят-
ностный анализ приводит к разработке рандомизированного алгоритма. Вместо
того чтобы предполагать то или иное распределение входных данных, мы по-
просту навязываем его. В частности, перед запуском алгоритма мы производим
случайную перестановку кандидатов, чтобы добиться выполнения условий рав-
новероятности всех перестановок. Такая модификация алгоритма не влияет на
величину математического ожидания найма сотрудника, примерно равную ln n.
Это означает, что такое поведение алгоритма присуще любому множеству вход-
ных данных, независимо от его конкретного распределения.
Давайте рассмотрим отличия вероятностного анализа от рандомизированных
алгоритмов. В разделе 5.2 мы выяснили, что если кандидаты приходят на собе-
седование в случайном порядке, то математическое ожидание количества наймов
новых менеджеров приблизительно равно ln n. Обратите внимание на то, что пред-
ставленный алгоритм является детерминированным, т.е. для любых конкретных
входных данных количество наймов новых менеджеров всегда будет одним и тем
же. Кроме того, эта величина различна для разных входных данных и зависит от
распределения рангов кандидатов. Поскольку количество наймов зависит только
от рангов кандидатов, каждый отдельно взятый набор входных данных можно
представить в виде перечисления рангов кандидатов в порядке нумерации по-
следних, т.е. в виде последовательности rank (1) , rank (2) , . . . , rank (n). В слу-
чае списка рангов A1 = 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 новый менеджер будет всегда
наниматься 10 раз, поскольку каждый очередной кандидат лучше предыдущего,
и строки 5–6 будут выполняться при каждой итерации алгоритма. Если же список

Стр. 149
150 Часть I. Основы

рангов имеет вид A2 = 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, то новый менеджер будет нанят


только один раз, во время первой итерации. Список A3 = 5, 2, 1, 8, 4, 7, 10, 9, 3, 6
дает три найма, после интервью с кандидатами, имеющими ранг 5, 8 и 10. На-
помним, что стоимость алгоритма зависит от того, сколько раз происходит найм
нового сотрудника. Легко убедиться в том, что есть дорогостоящие входные дан-
ные (такие как A1 ), дешевые входные данные (такие как A2 ), и входные данные
средней стоимости, такие как A3 .
Рассмотрим теперь рандомизированный алгоритм, в котором сначала выпол-
няется перестановка кандидатов на должность и только после этого определяется,
кто из них самый лучший. В этом случае рандомизация представляет собой часть
алгоритма, а не входных данных. При этом для отдельно взятого набора входных
данных, например, для представленного выше массива A3 , нельзя заранее сказать,
сколько наймов будет выполнено, поскольку эта величина изменяется при каждом
запуске алгоритма. При первом запуске алгоритма с входными данными A3 в ре-
зультате перестановки могут получиться входные данные A1 , и найм произойдет
10 раз, а при втором запуске перестановка может дать входные данные A2 , и найм
будет только один. Запустив алгоритм третий раз, можно получить еще какое-
нибудь количество наймов. При каждом запуске алгоритма стоимость его рабо-
ты зависит от случайного выбора и, скорее всего, будет отличаться от стоимости
предыдущего запуска. В этом и во многих других рандомизированных алгорит-
мах никакие входные данные не могут вызвать наихудшее поведение алгоритма.
Даже ваш злейший враг не сможет подобрать плохой входной массив, поскольку
дальнейшая случайная перестановка приводит к тому, что порядок входных эле-
ментов становится несущественным. Рандомизированные алгоритмы плохо ведут
себя лишь тогда, когда генератор случайных чисел выдаст “неудачную” переста-
новку.
Единственное изменение, которое нужно внести в алгоритм найма сотрудника
для рандомизации, — это добавить код случайной перестановки.
RANDOMIZED_HIRE_ASSISTANT(n)
1 Случайная перестановка списка кандидатов.
2 Best ← 0  Кандидат 0 — наименее квалифицированный
 фиктивный кандидат
3 for i ← 1 to n
4 do собеседование с кандидатом i
5 if кандидат i лучше кандидата Best
6 then Best ← i
7 Нанимаем кандидата i
С помощью этого простого изменения создается рандомизированный алгоритм,
производительность которого такая же, как и в случае предположения о том, что
собеседование с кандидатами производится в случайном порядке.

Стр. 150
Глава 5. Вероятностный анализ и рандомизированные алгоритмы 151

Лемма 5.3. Математическое ожидание стоимости процедуры RANDOMIZED_HIRE_


ASSISTANT равно O (ch ln n).

Доказательство. После перестановки элементов входного массива возникает си-


туация, идентичная рассмотренной при вероятностном анализе алгоритма HIRE_
ASSISTANT. 

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

Массивы, полученные в результате случайной


перестановки
Во многих алгоритмах входные данные рандомизируются путем перестановки
элементов исходного входного массива (впрочем, существуют и другие способы
рандомизации). Мы рассмотрим два метода, позволяющие выполнить эту опера-
цию. Будем считать, что у нас имеется массив A, который содержит элементы от 1
до n. Наша цель — случайным образом переставить элементы массива.
Один из распространенных методов заключается в том, чтобы присвоить каж-
дому элементу A [i] случайный приоритет P [i], а затем отсортировать элементы
массива A в соответствии с их приоритетами. Например, если исходный массив
имеет вид A = 1, 2, 3, 4 и выбраны случайные приоритеты P = 36, 3, 97, 19, то
в результате будет получен массив B = 2, 4, 1, 3, поскольку приоритет второго
элемента самый низкий, за ним по приоритету идет четвертый элемент, после
него — первый и, наконец, третий. Назовем эту процедуру PERMUTE_BY_SORTING:
PERMUTE_BY_SORTING(A)
1 n ← length[A]
2 for i ← 1 to n
3 do P [i] = RANDOM(1, n3 )
4 Отсортировать массив A с ключом сортировки P
5 return A
В строке 3 выбирается случайное число от 1 до n3 . Такой интервал выбран для
того, чтобы снизить вероятность наличия одинаковых приоритетов в массиве P .
(В упражнении 5.3-5 требуется доказать, что вероятность отсутствия в массиве
одинаковых приоритетов составляет по меньшей мере 1 − 1/n, а в упражне-
нии 5.3-6 — реализовать алгоритм, даже если несколько приоритетов могут иметь
одинаковые значения.) Будем считать, что одинаковых приоритетов в массиве нет.

Стр. 151
152 Часть I. Основы

В представленном выше псевдокоде наиболее трудоемкая процедура — сор-


тировка в строке 4. Как будет показано в главе 8, если использовать сортировку
сравнением, то время ее работы будет выражаться как Ω (n lg n). Эта нижняя грань
достижима, поскольку мы уже убедились, что сортировка слиянием выполняется
в течение времени Θ (n lg n). (Во второй части книги мы познакомимся и с други-
ми видами сортировки, время работы которых также имеет вид Θ (n lg n).) Если
приоритет P [i] является j-м в порядке возрастания, то после сортировки элемент
A [i] будет расположен в позиции с номером j. Таким образом мы достигнем
требуемой перестановки входных данных. Осталось доказать, что в результате
выполнения этой процедуры будет получена случайная перестановка с равно-
мерным распределением, другими словами, что все перестановки чисел от 1 до n
генерируются с одинаковой вероятностью.
Лемма 5.4. В предположении отсутствия одинаковых приоритетов в результате
выполнения процедуры PERMUTE_BY_SORTING получается случайная перестанов-
ка входных значений с равномерным распределением.

Доказательство. Начнем с рассмотрения частной перестановки, в которой каж-


дый элемент A [i] получает i-й приоритет (в порядке возрастания). Покажем, что
вероятность такой перестановки равна 1/n!. Обозначим через Xi (i = 1, 2, . . . , n)
событие, состоящее в том, что элемент A [i] получает i-й приоритет. Теперь вы-
числим вероятность того, что события Xi происходят для всех i. Эта вероятность
равна
Pr {X1 ∩ X2 ∩ X3 ∩ . . . ∩ Xn−1 ∩ Xn } .
Воспользовавшись результатами упражнения В.2-6, можно показать, что эта ве-
роятность равна
Pr {X1 } · Pr {X2 | X1 } · Pr {X3 | X2 ∩ X1 } · Pr {X4 | X3 ∩ X2 ∩ X1 } · . . . ·
· Pr {Xi | Xi−1 ∩ Xi−2 ∩ . . . ∩ X1 } · . . . · Pr {Xn | Xn−1 ∩ . . . ∩ X1 } .
Вероятность события X1 равна Pr {X1 } = 1/n, поскольку это вероятность того,
что приоритет выбранного наугад одного из n элементов окажется минималь-
ным. Теперь заметим, что Pr {X2 | X1 } = 1/(n − 1), поскольку при условии, что
приоритет элемента A [1] минимальный, каждый из оставшихся n − 1 элементов
имеет одинаковые шансы располагать вторым наименьшим приоритетом. В общем
случае при i = 2, 3, . . . , n выполняется соотношение Pr{Xi | Xi−1 ∩ Xi−2 ∩ . . . ∩
∩X1 } = 1/(n − i + 1), поскольку при условии, что приоритеты элементов от A [1]
до A [i − 1] равны от 1 до i − 1 (в порядке возрастания), каждый из оставших-
ся n − (i − 1) элементов имеет одинаковые шансы располагать i-м наименьшим
приоритетом. Таким образом, справедливо следующее выражение:
     
1 1 1 1 1
Pr {X1 ∩ X2 ∩ X3 ∩ . . . ∩ Xn−1 ∩ Xn } = ... = .
n n−1 2 1 n!

Стр. 152
Глава 5. Вероятностный анализ и рандомизированные алгоритмы 153

Тем самым доказано, что вероятность получения тождественной перестановки


равна 1/n!.
Это доказательство можно обобщить на случай произвольной перестанов-
ки приоритетов. Рассмотрим произвольную фиксированную перестановку σ =
= σ (1) , σ (2) , . . . , σ (n) множества {1, 2, . . . , n}. Обозначим через ri ранг при-
оритета, присвоенного элементу A [i], причем элемент с j-м приоритетом имеет
ранг, равный j. Если мы определим Xi как событие, при котором элемент A [i]
получает σ (i)-й приоритет (т.е. ri = σ (i)), то можно провести доказательство,
аналогичное приведенному выше. Таким образом, при вычислении вероятности
получения той или иной конкретной перестановки рассуждения и расчеты будут
идентичными изложенным выше. Поэтому вероятность получения такой переста-
новки также равна 1/n!. 

Возможно, некоторые читатели сделают вывод, что для доказательства того,


что все случайные перестановки распределены с равной вероятностью, достаточ-
но показать, что для каждого элемента A [i] вероятность оказаться в позиции j
равна 1/n. Выполнив упражнение 5.3-4, можно убедиться, что это условие недо-
статочное.
Более предпочтительным методом получения случайной перестановки явля-
ется способ, который заключается в перестановке элементов заданного массива
“на месте”. С помощью процедуры RANDOMIZE_IN_PLACE эту операцию можно
выполнить за время O (n). В ходе i-й итерации элемент A [i] случайным обра-
зом выбирается из множества элементов от A [i] до элемента A [n], после чего
в последующих итерациях этот элемент больше не изменяется:
RANDOMIZE_IN_PLACE(A)
1 n ← length[A]
2 for i ← 1 to n
3 do Обменять A[i] ↔ A[RANDOM(i, n)]
Покажем с помощью инварианта цикла, что в результате выполнения проце-
дуры RANDOMIZE_IN_PLACE получаются случайные перестановки с равномерным
распределением. Назовем k-перестановкой (размещением) данного n-элементно-
го множества последовательность, состоящую из k элементов, выбранных среди
n элементов исходного множества (см. приложение В). Всего имеется n!/(n − k)!
возможных k-перестановок.

Лемма 5.5. В результате выполнения процедуры RANDOMIZE_IN_PLACE получа-


ются равномерно распределенные перестановки.

Стр. 153
154 Часть I. Основы

Доказательство. Воспользуемся сформулированным ниже инвариантом цикла.


Непосредственно перед i-й итерацией цикла for из строк 2–3, веро-
ятность того, что в подмассиве A [1..i − 1] находится определенная
(i − 1)-перестановка, равна (n − i + 1)!/n!.
Необходимо показать, что это утверждение истинно перед первой итерацией цик-
ла, что итерации сохраняют истинность инварианта и что оно позволяет показать
корректность алгоритма по завершении цикла.
Инициализация. Рассмотрим ситуацию, которая имеет место непосредственно
перед первой итерацией цикла (i = 1). Согласно формулировке инварианта
цикла, вероятность нахождения каждого размещения из 0 элементов в под-
массиве A [1..0] равна (n − i + 1)!/n! = n!/n! = 1. Подмассив A [1..0] —
пустой, а 0-размещение по определению не содержит ни одного элемента.
Таким образом, подмассив A [1..0] содержит любое 0-размещение с вероят-
ностью 1, и инвариант цикла выполняется перед первой итерацией.
Сохранение. Мы считаем, что перед i-ой итерацией вероятность того, что в под-
массиве A [1..i − 1] содержится заданное размещение i − 1 элементов, равна
(n − i + 1)!/n!. Теперь нам нужно показать, что после i-ой итерации каж-
дая из возможных i-перестановок может находиться в подмассиве A [1..i]
с вероятностью (n − i)!/n!.
Рассмотрим i-ю итерацию. Рассмотрим некоторое конкретное размещение
i элементов и обозначим его элементы как x1 , x2 , . . . , xi . Это размещение
состоит из размещения i − 1 элемента x1 , x2 , . . . , xi−1 , за которым сле-
дует значение xi , которое помещается в ходе выполнения алгоритма в эле-
мент A [i]. Пусть E1 обозначает событие, при котором в результате первых
i − 1 итераций в подмассиве A [1..i − 1] создается определенное размещение
i − 1 элементов x1 , x2 , . . . , xi−1 . Согласно инварианту цикла, Pr {E1 } =
= (n − i + 1)!/n!. Пусть теперь E2 — событие, при котором в ходе i-ой ите-
рации элементу A [i] присваивается значение xi . Размещение x1 , x2 , . . . , xi 
формируется в подмассиве A [1..i] только при условии, что происходят оба
события — и E1 , и E2 , так что мы должны вычислить значение Pr {E2 ∩ E1 }.
Воспользовавшись уравнением (В.14), получаем:
Pr {E2 ∩ E1 } = Pr {E2 | E1 } Pr {E1 } .
Вероятность Pr {E2 | E1 } равна 1/(n − i + 1), поскольку в строке 3 алго-
ритма производится случайный выбор величины xi из n − i + 1 элемента
подмассива A [i..n]. Таким образом, получаем:
Pr {E2 ∩ E1 } = Pr {E2 | E1 } Pr {E1 } =
1 (n − i + 1)! (n − i)!
= · = .
n−i+1 n! n!

Стр. 154
Глава 5. Вероятностный анализ и рандомизированные алгоритмы 155

Завершение. После завершения алгоритма i = n + 1, поэтому заданное размеще-


ние n элементов находится в подмассиве A [1..n] с вероятностью
(n − n)!/n! = 1/n!.
Таким образом, в результате выполнения процедуры RANDOMIZE_IN_PLACE
получаются равномерно распределенные случайные перестановки. 

Рандомизированный алгоритм зачастую является самым простым и наиболее


эффективным путем решения задачи. Время от времени вы будете встречаться
с такими алгоритмами в данной книге.

Упражнения
5.3-1. У профессора возникли возражения против инварианта цикла, использу-
ющегося при доказательстве леммы 5.5. Он сомневается, что этот инвари-
ант выполняется перед первой итерацией. Согласно его доводам, пустой
подмассив не содержит никаких размещений из 0 элементов, поэтому
вероятность того, что в таком подмассиве находится то или иное разме-
щение, должна быть равна 0. Из этих рассуждений следует, что инвариант
цикла перед первой итерацией не выполняется. Перепишите процедуру
RANDOMIZE_IN_PLACE таким образом, чтобы связанный с нею инвари-
ант цикла перед первой итерацией применялся к непустому подмассиву,
и соответствующим образом модифицируйте доказательство леммы 5.5.
5.3-2. Профессор решил разработать алгоритм, в результате выполнения кото-
рого получались бы все случайные перестановки, кроме тождественной.
Он предложил такую процедуру:
PERMUTE_WITHOUT_IDENTITY(A)
1 n ← length[A]
2 for i ← 1 to n − 1
3 do Обменять A[i] ↔ A[RANDOM(i + 1, n)]
Добьется ли профессор поставленной цели с помощью этого кода?
5.3-3. Предположим, что вместо того, чтобы менять местами элемент A [i] со
случайно выбранным элементом из подмассива A [i..n], мы меняем его
местами с любым случайно выбранным элементом массива A:
PERMUTE_WITH_ALL(A)
1 n ← length[A]
2 for i ← 1 to n
3 do Обменять A[i] ↔ A[RANDOM(1, n)]
Получится ли в результате выполнения этого кода равномерная случай-
ная перестановка? Обоснуйте ваш ответ.

Стр. 155
156 Часть I. Основы

5.3-4. Профессор предложил для генерирования случайных перестановок с од-


нородным распределением такую процедуру:

PERMUTE_BY_CYCLIC(A)
1 n ← length[A]
2 offset ← RANDOM(1, n)
3 for i ← 1 to n
4 do dest ← i + offset
5 if dest > n
6 then dest ← dest −n
7 B[dest] ← A[i]
8 return B

Покажите, что элемент A [i] оказывается в определенной позиции мас-


сива B с вероятностью 1/n. Затем покажите, что алгоритм профессора
ошибочен в том смысле, что полученные в результате его выполнения
перестановки не будут распределены равномерно.
 5.3-5. Докажите, что в результате выполнения процедуры PERMUTE_BY_SOR-
TING вероятность отсутствия одинаковых элементов в массиве P не мень-
ше величины 1 − 1/n.
5.3-6. Объясните, как следует реализовать алгоритм PERMUTE_BY_SORTING
в случае, когда два или более приоритета идентичны. Другими слова-
ми, алгоритм должен выдавать случайные равномерно распределенные
перестановки даже в случае, когда два или большее количество приори-
тетов имеют одинаковые значения.

 5.4 Вероятностный анализ и дальнейшее


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

Стр. 156
Глава 5. Вероятностный анализ и рандомизированные алгоритмы 157

5.4.1 Парадокс дней рождения


Первым будет рассмотрен парадокс дней рождения. Сколько людей нужно
собрать в одной комнате, чтобы вероятность совпадения даты рождения у двух
из них достигла 50%? Полученное в результате решения этой задачи число на
удивление мало. Парадокс в том, что это число намного меньше, чем количество
дней в году.
Чтобы решить задачу, присвоим всем, кто находится в комнате, номера от 1
до k, где k — количество людей в комнате. Наличие високосных годов проигно-
рируем и предположим, что в каждом году n = 365 дней. Пусть bi — дата, на
которую приходится день рождения i-й персоны (i = 1, 2, . . . , k, 1  bi  n).
Предположим также, что дни рождения равномерно распределены по всему году,
так что Pr {bi = r} = 1/n для всех i = 1, 2, . . . , k и r = 1, 2, . . . , n.
Вероятность того, что даты рождения двух человек i и j совпадают, зависит
от того, является ли случайный выбор этих дат независимым. В дальнейшем
предполагается, что дни рождения независимы, поэтому вероятность того, что
i-й и j-й посетители комнаты родились в день r, можно вычислить следующим
образом:
Pr {bi = r и bj = r} = Pr {bi = r} Pr {bj = r} = 1/n2 .
Таким образом, вероятность того, что эти люди родились в один и тот же день,
равна

n 
n
1 1
Pr {bi = bj } = Pr {bi = r и bj = r} = = . (5.7)
n2 n
r=1 r=1

Интуитивно легче понять такое утверждение: если день рождения человека bi


зафиксирован, то вероятность того, что день рождения человека bj выпадет на
эту же дату, равна 1/n. Таким образом, вероятность того, что даты рождения двух
человек i и j совпадают, равна вероятности того, что день рождения одного из
них выпадет на заданную дату. Однако обратите внимание, что это совпадение
основано на предположении о независимости дней рождения.
Теперь можно проанализировать вероятность того, что хотя бы двое из k людей
родились в один и тот же день, рассматривая дополняющее событие. Вероятность
совпадения хотя бы двух дней рождения равна 1, уменьшенной на величину веро-
ятности того, что все дни рождения различаются. Событие, при котором все дни
рождения различаются, можно представить так:

!
n
Bk = Ai ,
i=1

где Ai — событие, состоящее в том, что день рождения i-го человека отличается
от дня рождения j-го человека для всех i < j. Поскольку мы можем записать

Стр. 157
158 Часть I. Основы

Bk = Ak ∩ Bk−1 , из уравнения (В.16) мы получим следующее рекуррентное


соотношение:
Pr {Bk } = Pr {Bk−1 } Pr {Ak | Bk−1 } , (5.8)
начальное условие которого — Pr {B1 } = Pr {A1 } = 1. Другими словами, ве-
роятность того, что дни рождения b1 , b2 , . . . , bk различны, равна произведению
вероятности того, что различны дни рождения b1 , b2 , . . . , bk−1 , на вероятность
того, что bk = bi для i = 1, 2, . . . , k − 1 при условии, что b1 , b2 , . . . , bk−1 различны.
Если b1 , b2 , . . . , bk−1 различны, то условная вероятность того, что bk = bi для
i = 1, 2, . . . , k − 1, равна Pr { Ak | Bk−1 } = (n − k + 1)/n, поскольку из n дней
незанятыми остались только n − (k − 1). Итеративно применяя рекуррентное со-
отношение (5.8), получим:

Pr {Bk } = Pr {Bk−1 } Pr {Ak | Bk−1 } =


= Pr {Bk−2 } Pr {Ak−1 | Bk−2 } Pr {Ak | Bk−1 } =
..
.
= Pr {B1 } Pr {A2 | B1 } Pr {A3 | B2 } . . . Pr {Ak | Bk−1 } =
    
n−1 n−2 n−k+1
=1· ... =
n n n
    
1 2 k−1
=1· 1− 1− ... 1 − .
n n n

Неравенство (3.11), 1 + x  ex , дает нам следующее соотношение:


 k−1
Pr {Bk }  e−1/n e−2/n . . . e−(k−1)/n = e− i=1 i/n
= e−k(k−1)/2n  1/2,

которое справедливо при −k (k − 1)/2n  ln (1/2). Итак, вероятность того, что


все k дней рождения различаются, не меньше величины 1/2 при условии, что
k (k − 1)  2n ln 2. Решая квадратное
 уравнение
√ относительно
 k, получим, что
это условие выполняется при k  1 + 1 + 8n ln 2 /2, что при n = 365 дает
условие k  23. Таким образом, если в комнате не менее 23 человек, вероятность
того, что хотя бы двое из них родились в один и тот же день, не меньше 1/2.
А, например, на Марсе год длится 669 дней, поэтому для того, чтобы добиться
того же эффекта исходя из продолжительности марсианского года, понадобилось
бы собрать не менее 31 марсианина.

Анализ с помощью индикаторных случайных величин


Индикаторные случайные величины дают возможность проведения простого
(хотя и приближенного) анализа парадокса дней рождения. Определим для каждой

Стр. 158
Глава 5. Вероятностный анализ и рандомизированные алгоритмы 159

пары (i, j) находящихся в комнате k людей индикаторную случайную величину


Xij (1  i < j  k) следующим образом:

Xij = I {Дни рождения i и j совпадают} =



1 если дни рождения i и j совпадают,
=
0 в противном случае.

Согласно уравнению (5.7) вероятность того, что дни рождения двух людей совпа-
дают, равна 1/n, поэтому, в соответствии с леммой 5.1, получаем:

E [Xij ] = Pr {Дни рождения i и j совпадают} = 1/n.

Полагая X случайной величиной, которая представляет собой количество пар


людей, дни рождения которых совпадают, получим:


k 
k
X= Xij .
i=1 j=i+1

Применяя к обеим частям этого равенства операцию вычисления математического


ожидания и воспользовавшись свойством ее линейности, получаем:
 
k  k k  k  
k 1 k (k − 1)
E [X] = E  Xij  = E [Xij ] = · = .
2 n 2n
i=1 j=i+1 i=1 j=i+1

Поэтому если k (k − 1)  2n, математическое ожидание количества пар людей,


родившихся в √ один и тот же день, не меньше 1. Таким образом, если в комнате
как минимум 2n + 1 людей, то можно ожидать, что хотя бы у двух из них дни
рождения совпадают. При n = 365 и k = 28 математическое ожидание количества
пар, родившихся в один и тот же день, равно (27 · 28)/(2 · 365) ≈ 1.0356. Таким
образом, если в комнате находится 28 человек, то следует ожидать, что хотя бы
у двух из них день рождения совпадет. На Марсе, где год длится 669 дней, для того,
чтобы добиться того же эффекта, понадобилось бы собрать не менее 38 марсиан.
В первом анализе, в котором использовались только вероятности, опреде-
лялось, сколько людей нужно собрать, чтобы вероятность существования пары
с совпадающими днями рождения превысила 1/2. В ходе второго анализа, в ко-
тором использовались индикаторные случайные величины, определялось коли-
чество людей, при котором математическое ожидание числа пар с одним днем
рождения на двоих равно 1. Несмотря на то, что в этих двух ситуациях точное ко-

личество людей различается, в асимптотическом пределе оно одинаково: Θ ( n).

Стр. 159
160 Часть I. Основы

5.4.2 Шары и урны


Рассмотрим процесс случайного наполнения b урн одинаковыми шарами, про-
нумерованными натуральными числами от 1 до b. Шары опускаются в урны неза-
висимо друг от друга, и вероятность того, что шар окажется в некоторой из урн,
одинакова для всех урн и равна 1/b. Таким образом, процесс заполнения урн
шарами представляет собой последовательность испытаний по схеме Бернулли
(см. приложение В) с вероятностью успеха 1/b, где успех состоит в том, что шар
попадает в заданную урну. В частности, эта модель может оказаться полезной при
анализе хеширования (см. главу 11).
Сколько шаров окажется в заданной урне? Количество шаров, попавших
в определенную урну, подчиняется биномиальному распределению b (kn, 1/b).
Если всего в урны было опущено n шаров, то, согласно уравнению (В.36), мате-
матическое ожидание количества шаров в урне равно n/b.
Сколько в среднем требуется шаров для того, чтобы в данной урне оказал-
ся один шар? Количество шаров, которое требуется для того, чтобы в данной
урне оказался шар, подчиняется геометрическому распределению с вероятностью
1/b и, согласно уравнению (В.31), математическое ожидание количества шаров,
которое следует опустить в урны, равно 1/ (1/b) = b.
Сколько шаров нужно опустить в урны для того, чтобы в каждой из них
оказалось хотя бы по одному шару? Назовем “попаданием” опускание шара в пу-
стую урну. Нам надо определить математическое ожидание количества опусканий
n, необходимых для b попаданий.
С помощью попаданий, n опусканий можно разбить на этапы. Этап под но-
мером i длится от i − 1-го попадания до i-го попадания. Первый этап состоит
из одного (первого) опускания, поскольку если все урны пусты, то мы с необ-
ходимостью получим попадание. На i-м этапе имеется i − 1 корзина с шарами
и b − i + 1 пустых корзин. Таким образом, при каждом опускании шара на i-м
этапе вероятность попадания равна (b − i + 1)/b.
Пусть ni — количество опусканий на i-м этапе. b Тогда количество шаров,
необходимых для попадания в b урн, равно n = i=1 ni . Все случайные зна-
чения ni подчиняются геометрическому распределению с вероятностью успеха
(b − i + 1)/b, и, в соответствии с уравнением (В.31),

b
E [ni ] = .
b−i+1
Пользуясь линейностью математического ожидания, получаем:
 b
 b 
b
b b
1
E [n] = E ni = E [ni ] = =b = b (ln b + O (1)) .
b−i+1 i
i=1 i=1 i=1 i=1

Стр. 160
Глава 5. Вероятностный анализ и рандомизированные алгоритмы 161

Последняя строка этой цепочки следует из границы (A.7) конечного гармониче-


ского ряда. Таким образом, прежде чем в каждой урне окажется хотя бы по одному
шару, понадобится приблизительно b ln b шаров.

5.4.3 Последовательности выпадения орлов


Предположим, что монета подбрасывается n раз. Какое количество последо-
вательных выпадений орла можно ожидать? Как покажет последующий анализ,
эта величина ведет себя как Θ (lg n).
Докажем сначала, что математическое ожидание длины наибольшей последо-
вательности орлов представляет собой O (lg n). Вероятность того, что при оче-
редном подбрасывании выпадет орел, равна 1/2. Пусть Aik — событие, когда
последовательность выпадений орлов длиной не менее k начинается с i-го под-
брасывания, или, более строго, Aik — событие, когда при k последовательных
подбрасываниях монеты i, i + 1, . . . , i + k − 1 (1  k  n и 1  i  n − k + 1)
будут выпадать одни орлы. Поскольку подбрасывания монеты осуществляются
независимо, для каждого данного события Aik вероятность того, что во всех k
подбрасываниях выпадут одни орлы, определяется следующим образом:

Pr {Aik } = 1/2k . (5.9)

Для k = 2 lg n
можно записать
 
Pr Ai,2lg n = 1/22lg n  1/22 lg n = 1/n2 ,

так что вероятность того, что последовательность повторных выпадений орлов


длиной не менее 2 lg n
начинается с i-го подбрасывания, довольно невелика.
Имеется не более n − 2 lg n
+ 1 подбрасываний, с которых может начаться ука-
занная последовательность орлов. Таким образом, вероятность того, что после-
довательность повторных выпадений орлов длиной не менее 2 lg n
начинается
при любом подбрасывании, равна
 
n−2lg
&n +1  n−2lg
n +1 1 n
1 1
Pr Ai,2lg n  < = . (5.10)
  n2 n2 n
i=1 i=1 i=1

Справедливость этого соотношения следует из неравенства Буля (В.18), согласно


которому вероятность объединения событий не превышает сумму вероятностей
отдельных событий. (Заметим, что неравенство Буля выполняется даже для тех
событий, которые не являются независимыми.)
Теперь воспользуемся неравенством (5.10) для ограничения длины самой длин-
ной последовательности выпадения орлов. Пусть Lj (j = 0, 1, 2, . . . , n) —событие,

Стр. 161
162 Часть I. Основы

когда длина самой длинной последовательности выпадения орлов равна j. В со-


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

n
E [L] = j Pr {Lj }. (5.11)
j=0

Можно попытаться оценить эту сумму с помощью верхних границ каждой из


величин Pr {Lj } аналогично тому, как это было сделано в неравенстве (5.10).
К сожалению, этот метод не может обеспечить хороших оценок. Однако доста-
точно точную оценку можно получить с помощью некоторых интуитивных рас-
суждений, которые вытекают из проведенного выше анализа. Присмотревшись
внимательнее, можно заметить, что в сумме (5.11) нет ни одного слагаемого,
в котором оба множителя — j и Pr {Lj } — были бы большими. Почему? При
j  2 lg n
величина Pr {Lj } очень мала, а при j < 2 lg n
оказывается невели-
ко само значение j. Выражаясь более формально, можно заметить, что события
Lj , j = 0, 1, . . . , n несовместимые, поэтому вероятность того, что непрерывная по-
следовательность выпадения орлов n длиной не менее 2 lg n
начинается с любого
подбрасывания монеты, равна
n j=2lg n Pr {Lj }. Согласно
n
неравенству (5.10),
мы имеем j=2lg n Pr {Lj } < 1/n. Кроме того, из j=0 Pr {Lj } = 1 вытекает
2lg n −1
j=0 Pr {Lj }  1. Таким образом, мы получаем:


n 
2lg n −1

n
E [L] = j Pr {Lj } = j Pr {Lj } + j Pr {Lj } <
j=0 j=0 j=2lg n


2lg n −1

n
< (2 lg n
) Pr {Lj } + n Pr {Lj } =
j=0 j=2lg n


2lg n −1

n
= 2 lg n
Pr {Lj } + n Pr {Lj } <
j=0 j=2lg n

< 2 lg n
· 1 + n · (1/n) = O (lg n) .

Шансы на то, что длина последовательности непрерывных выпадений орла


превысит величину r lg n
, быстро убывает с ростом r. Для r  1 вероятность
того, что последовательность r lg n
начнется с i-го подбрасывания, равна
 
Pr Ai,rlg n = 1/2rlg n  1/nr .

Таким образом, вероятность образования непрерывной цепочки из последователь-


ных выпадений орла, имеющей длину не менее r lg n
, не превышает n/nr =
= 1/nr−1 . Это утверждение эквивалентно утверждению, что длина такой цепочки
меньше величины r lg n
с вероятностью не менее чем 1 − 1/nr−1 .

Стр. 162
Глава 5. Вероятностный анализ и рандомизированные алгоритмы 163

В качестве примера рассмотрим серию из n = 1000 подбрасываний моне-


ты. Вероятность того, что в этой серии орел последовательно выпадет не менее
2 lg n
= 20 раз, не превышает 1/n = 1/1000. Вероятность непрерывного выпа-
дения орла более 3 lg n
= 30 раз не превышает 1/n2 = 1/1 000 000.
Теперь давайте рассмотрим дополняющую нижнюю границу и докажем, что
математическое ожидание длины самой длинной непрерывной последовательно-
сти выпадений орлов в серии из n подбрасываний равно Ω (lg n). Чтобы доказать
справедливость этого утверждения, разобьем серию из n подбрасываний прибли-
зительно на n/s групп по s подбрасываний в каждой. Если выбрать s = (lg n)/2 ,
то можно показать, что с большой вероятностью по крайней мере в одной из этих
групп окажутся все орлы, т.е. самая длинная последовательность выпадения орлов
имеет длину как минимум s = Ω (lg n). Затем мы покажем, что математическое
ожидание длины такой последовательности равно Ω (lg n).
Итак, разобьем серию из n испытаний на несколько групп. Количество групп
должно быть не менее n/ (lg n)/2 ; их длина не превышает (lg n)/2 подбра-
сываний. Оценим вероятность того, что в каждой из групп выпадет хотя бы по
одной решке. Согласно уравнению (5.9), вероятность того, что в группе, которая
начинается с i-го подбрасывания, выпадут все орлы, определяется как
  1 1
Pr Ai,(lg n)/2 = √ .
2(lg n)/2 n

Таким образом, вероятность того, что последовательность непрерывного выпа-


дения орлов длиной не менее (lg n)/2 не начинается с i-го подбрасывания,

не превышает величину 1 − 1/ n. Поскольку все n/ (lg n)/2 групп образу-
ются из взаимно исключающих независимых подбрасываний, вероятность того,
что каждая такая группа не будет последовательностью выпадений орлов длиной
(lg n)/2 , не превышает величину
 √ n/(lg n)/2  √ n/(lg n)/2−1  √ 2n/lg n−1
1 − 1/ n  1− n  1− n 
√  
 e−(2n/lg n−1)/ n = O e− lg n = O (1/n) .

В приведенной выше цепочке соотношений было использовано неравенство (3.11),


1 + x  ex , и тот факт, что при достаточно больших n справедливо соотношение

(2n/lg n − 1)/ n  lg n (при желании вы можете в этом убедиться самостоятель-
но).
Таким образом, вероятность того, что длина самой большой последовательно-
сти выпадений орлов превосходит величину (lg n)/2 , равна


n
Pr {Lj }  1 − O (1/n) . (5.12)
j=(lg n)/2+1

Стр. 163
164 Часть I. Основы

Теперь можно вычислить нижнюю границу математического ожидания длины са-


мой длинной последовательности орлов. Воспользовавшись в качестве отправной
точки уравнением (5.11) и выполнив преобразования, аналогичные проведенным
при анализе верхней границы, с учетом неравенства (5.12), получим:
(lg n)/2

n  
n
E [L] = j Pr {Lj } = j Pr {Lj } + j Pr {Lj } 
j=0 j=0 j=(lg n)/2+1
(lg n)/2
 
n
 0 · Pr {Lj } + (lg n)/2 Pr {Lj } =
j=0 j=(lg n)/2+1
(lg n)/2
 
n
=0· Pr {Lj } + (lg n)/2 Pr {Lj } 
j=0 j=(lg n)/2+1

 0 + (lg n)/2 (1 − O (1/n)) = Ω (lg n) .


Как в предыдущем примере, более простой, но менее точный анализ можно
провести с помощью индикаторных случайных величин. Пусть Xik = I {Aik } —
индикаторная случайная величина, связанная с последовательным выпадением не
менее k орлов, начиная с i-го подбрасывания монеты. Чтобы подсчитать количе-
ство таких последовательностей, определим:

n−k+1
X= Xik .
i=1

Вычисляя от обеих частей этого равенства математическое ожидание, получим:


n−k+1
 
n−k+1
E [X] = E Xik = E [Xik ] =
i=1 i=1

n−k+1 
n−k+1
1 n−k+1
= Pr {Aik } = k
= .
2 2k
i=1 i=1

Подставляя в полученное соотношение различные значения k, можно опреде-


лить математическое ожидание количества последовательностей длины k. Если
это число окажется большим (намного превышающим единицу), то последова-
тельности выпадения орлов, имеющие длину k, встречаются с большой вероят-
ностью. Если же это число намного меньше единицы, то встретить такую после-
довательность в серии испытаний маловероятно. Если k = c lg n для некоторой
положительной константы c, то мы получим:
 
n − c lg n + 1 n − c lg n + 1 1 (c lg n − 1)/n 1
E [X] = = = c−1 − =Θ .
2c lg n nc n nc−1 nc−1

Стр. 164
Глава 5. Вероятностный анализ и рандомизированные алгоритмы 165

Если число c достаточно большое, математическое ожидание количества непре-


рывных выпадений орла длиной c lg n очень мало, из чего можно заключить, что
это событие маловероятно.
 С другой
 стороны, если c < 1/2, то мы получаем, что
E [X] = Θ 1/n1/2−1 = Θ n1/2 , и можно ожидать, что будет немало последо-
вательностей орлов длины (1/2) lg n. Поэтому вероятность того, что встретится
хотя бы одна такая последовательность, достаточно велика. Исходя лишь из этих
грубых оценок, можно заключить, что ожидаемая длина самой длинной последо-
вательности орлов равна Θ (lg n).

5.4.4 Задача о найме сотрудника в оперативном режиме


В качестве последнего примера рассмотрим одну из разновидностей задачи
о найме сотрудника. Предположим, что в целях выбора наиболее подходяще-
го кандидата мы не будем проводить собеседовани�