КОМБИНАТОРНЫЕ...

118
МИНИСТЕРСТВО ОБРАЗОВАНИЯ И НАУКИ РФ НОВОСИБИРСКИЙ ГОСУДАРСТВЕННЫЙ УНИВЕРСИТЕТ Факультет информационных технологий Т. И. Федоряева КОМБИНАТОРНЫЕ АЛГОРИТМЫ Учебное пособие Новосибирск 2011

Transcript of КОМБИНАТОРНЫЕ...

Page 1: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

МИНИСТЕРСТВО ОБРАЗОВАНИЯ И НАУКИ РФНОВОСИБИРСКИЙ ГОСУДАРСТВЕННЫЙ УНИВЕРСИТЕТ

Факультет информационных технологий

Т. И. Федоряева

КОМБИНАТОРНЫЕ АЛГОРИТМЫ

Учебное пособие

Новосибирск

2011

Page 2: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

УДК 519.1+510.52+519.683ББК В127я73-1Ф337

Федоряева Т.И. Комбинаторные алгоритмы: Учебное пособие /

Новосиб. гос. ун-т. Новосибирск, 2011. 118 с.

ISBN 978-5-4437-0019-9

Учебное пособие написано на основе курса "Комбинаторные алго-ритмы", читаемого автором студентам факультета информационныхтехнологий НГУ. Наряду с теоретическими знаниями даётся описаниеважнейших комбинаторных алгоритмов над объектами дискретной ма-тематики, приводится строгое обоснование рассматриваемых алгорит-мов и детально изучается их асимптотическая сложность.

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

Рецензенты:

зав. лабораторией совершенных комбинаторных структур Институтаматематики им.С. Л. Соболева, канд.физ.-мат.наук С. В. Августинович;доцент факультета информационных технологий Новосибирского госу-дарственного университета, канд.физ.-мат.наук А. Л.Пережогин

Учебное пособие разработано в соответствии с требованиями ФГОСВПО к структуре и результатам освоения основных образовательныхпрограмм по профессиональному циклу по направлению подготовки"Информатика и вычислительная техника". Издание подготовлено врамках реализации Программы развития государственного образова-тельного учреждения высшего профессионального образования "Ново-сибирский государственный университет" на 2009–2018 годы.

c⃝ Новосибирский государственныйуниверситет, 2011

c⃝ Т.И. Федоряева, 2011ISBN 978-5-4437-0019-9

Page 3: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

ОглавлениеПредисловие 4Глава 1. Введение 71. Машинные алгоритмы и их сложность 72. Асимптотический формализм оценок времени работы алгоритмов 103. Алгоритм нахождения n-факториального представления числа 17Глава 2. Генерация комбинаторных объектов 201. Перестановки и алгоритмы их порождения 211.1. Индекс перестановки 231.2. Генерация перестановок в лексикографическом порядке 271.3. Порождение перестановок через векторы инверсий 321.4. Алгоритм Джонсона – Троттера генерации перестановок 362. Подмножества конечного множества 462.1. Генерация двоичных векторов и подмножеств 472.2. Коды Грея и алгоритм их генерации 503. Генерация сочетаний в лексикографическом порядке 56Глава 3. Генерация случайных комбинаторных объектов 601. Алгоритм построения случайной перестановки 602. Алгоритм генерации случайного подмножества и сочетания 63Глава 4. Разбиения чисел и множеств 651. Упорядоченные и неупорядоченные разбиения числа n 652. Генерация разбиений числа n в словарном порядке 673. Разбиения конечного множества 724. Генерация разбиений n-элементного множества 73Глава 5. Сортировка комбинаторных объектов 791. Задача сортировки 802. Нижние оценки сложности алгоритма сортировки сравнением 843. Алгоритм сортировки вставками и оценки времени его работы 894. Алгоритм пузырьковой сортировки и оценки времени его работы 935. Алгоритм быстрой сортировки и оценки времени его работы 956. Алгоритм пирамидальной сортировки и оценки его трудоёмкости 1057. Линейный алгоритм сортировки подсчётом 114Список литературы 117

3

Page 4: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

Предисловие

Комбинаторные алгоритмы предназначены для выполнения вычис-

лений на различного рода объектах, возникающих в прикладных ком-

бинаторных задачах и при исследовании дискретных математических

структур. Необходимость разработки эффективных, быстрых комби-

наторных алгоритмов уже давно не вызывает сомнений. На практике

нужны не просто алгоритмы, а хорошие алгоритмы в широком смыс-

ле. Одним из основных критериев качества алгоритма является время,

необходимое для его выполнения.

Разработке и анализу вычислительной сложности комбинаторных

алгоритмов над классическими комбинаторными объектами посвящено

настоящее учебное пособие. Наряду с теоретическими знаниями даётся

описание таких важнейших алгоритмов, приводится их строгое обосно-

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

мых алгоритмов. Мы познакомим читателя с широким кругом понятий

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

программисту. Пополним запас примеров нетривиальных алгоритмов

над объектами дискретной математики, помогающих существенно обо-

гатить навыки самостоятельного конструирования алгоритмов и сфор-

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

анализа при разработке эффективных алгоритмов для решения прак-

тических задач и оценке их сложности.

Для понимания материала учебного пособия требуется знание ос-

новных понятий и фактов из дискретной математики и математической

логики. Читатель должен обладать минимальным опытом программи-

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

позволяющим реализовать рассматриваемый алгоритм на доступном

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

основы математического анализа и теории вероятностей.

4

Page 5: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

Практически все рассматриваемые задачи и алгоритмы их решения,

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

ные доказательства и обоснования оценок сложности оригинальны. Мы

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

его возникновения. Особое внимание уделяется выявлению интуитив-

ных идей, лежащих в основе алгоритмов, и иллюстрации работы изу-

чаемых алгоритмов на примерах.

Материал учебного пособия организован следующим образом. В пер-

вой главе обсуждаются понятие сложности машинных алгоритмов и

язык псевдокода, на котором записываются алгоритмы. Даётся асимп-

тотический формализм для оценок времени работы алгоритмов и при-

водятся примеры асимптотических соотношений. На примере алгорит-

ма n-факториального представления числа изучаются введённые поня-

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

Вторая глава посвящена задаче генерации комбинаторных объек-

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

ного множества, двоичные векторы, коды Грея и сочетания из n эле-

ментов по k. Основное внимание уделено линейным алгоритмам гене-

рации объектов в лексикографическом порядке и в порядке минималь-

ного изменения. Для перестановок изучаются понятия индекса отно-

сительно лексикографического порядка и вектора инверсий. Детально

исследуются линейные алгоритмы генерации перестановок в лексико-

графическом порядке и Джонсона – Троттера в порядке минимального

изменения. Далее рассматриваются алгоритмы порождения двоичных

векторов и подмножеств n-элементного множества, понятие кода Грея

и линейный алгоритм генерации двоично-отраженных кодов Грея. На-

конец, в лексикографическом порядке порождаются все сочетания. Для

всех изучаемых алгоритмов обосновывается их корректность и оцени-

вается асимптотическая сложность.

Третья глава знакомит с алгоритмами генерации случайных ком-

бинаторных объектов с равномерным распределением и даёт начальное

5

Page 6: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

представление о теории вероятностных алгоритмов. Она содержит алго-

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

и случайного сочетания.

В четвёртой главе рассматривается задача порождения разбиений

совокупности комбинаторных объектов. Изучаются разбиения чисел и

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

числа n. Изучаются алгоритмы генерации разбиений числа n в сло-

варном порядке и разбиений n-элементного множества, которые имеют

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

Пятая глава посвящена задаче сортировки комбинаторных объек-

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

и максимального времени работы алгоритма. Сначала устанавливаются

нижние оценки сложности произвольного алгоритма сортировки срав-

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

вой, быстрой и пирамидальной сортировки. Для каждого из алгоритмов

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

максимального времени их работы. Глава заканчивается изучением ли-

нейного алгоритма сортировки подсчётом.

Учебное пособие написано на основе курса "Комбинаторные алго-

ритмы", читаемого автором студентам факультета информационных

технологий Новосибирского государственного университета. Оно преж-

де всего ориентировано на студентов программистских специальностей,

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

горитмов и анализом их вычислительной сложности. Предоставляемые

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

щественно обогащают навыки конструирования эффективных алгорит-

мов. Изучение комбинаторных алгоритмов также будет полезно любо-

му заинтересованному читателю для развития самостоятельных навы-

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

дискретной математики и применения методов дискретного анализа в

своей профессиональной деятельности.

6

Page 7: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

Глава 1Введение

1.Машинные алгоритмы и их сложность

Понятие алгоритма, подобно многим фундаментальным понятиям ма-

тематики, является настолько интуитивно "понятным", насколько и

сложным при его строгой формализации и скорее должно рассматри-

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

ритмом часто понимается формально описанная вычислительная про-

цедура, получающая исходные данные, называемые входными данны-

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

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

формализации данного понятия вне рамок настоящего пособия.

Алгоритмы строятся для решения тех или иных вычислительных

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

удовлетворять входные и выходные данные, а алгоритм, решающий эту

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

чи, записываемое в выходные данные. Такой алгоритм, когда для каж-

дого допустимого ввода результатом его работы является требуемый

в задаче вывод, называют корректным. Некорректный алгоритм для

некоторых входных данных может вообще не завершить свою работу

или выдать выходные данные, отличные от требуемых в задаче.

При анализе алгоритма решения поставленной задачи нас в первую

очередь будет интересовать его трудоёмкость, под которой мы пони-

маем время выполнения соответствующей программы на ЭВМ. Ясно,

что этот показатель существенно зависит от типа используемого ком-

пьютера. Чтобы сделать наши выводы о трудоёмкости алгоритмов в

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

производятся на некой абстрактной вычислительной машине. Такая ма-

шина в состоянии выполнять арифметические операции, сравнения, пе-

ресылки и операции условной и безусловной передачи управления. Эти7

Page 8: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

операции считаются элементарными. Мы принимаем, что каждая из

элементарных операций выполняется за единицу времени (т. е. не учи-

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

мих этих операций), и, следовательно, время работы алгоритма равно

числу выполненных им элементарных операций. Память рассматривае-

мой абстрактной вычислительной машины состоит из неограниченного

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

входным данным задачи присвоены конкретные значения, они разме-

щаются в памяти компьютера.

С входными данными алгоритма связано некоторое натуральное

число (или набор целочисленных параметров), называемое размерно-

стью данных , которое выражает меру их количества. Как определяет-

ся размерность данных? Это зависит от вида рассматриваемой задачи.

В одних случаях размерностью разумно считать число элементов на

входе, например, для задачи сортировки. В других более естественно

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

ния всех входных данных в памяти (такой подход является наиболее

употребительным). Как уже отмечалось, иногда размерность данных

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

для графов с n вершинами и m рёбрами. При изучении конкретных ал-

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

вызывает трудностей, поэтому мы не будем останавливаться на её дета-

лизации (чаще всего это будет длина входных данных). Отметим также,

что в литературе размерность данных иногда называют размерностью

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

Определим сложность, или трудоёмкость алгоритма решения

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

соответствие каждому n наибольшее время T (n) работы алгоритма на

входных данных размерности n. Заданную таким образом сложность

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

памяти, определяющей величину объёма памяти, использованного ал-

8

Page 9: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

горитмом, как функцию размерности данных. Анализ эффективности

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

проса: как быстро растёт функция T (n) с ростом n? Иными словами,

нас интересует только асимптотическая сложность, т. е. асимптотиче-

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

ность данных неограниченно растёт.

Алгоритмы будем записывать на неформальном языке программи-

рования, содержащем обычные общеизвестные конструкции. При этом

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

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

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

вается при записи окончательного кода программы. Приведем основные

операторы, используемые при написании псевдокода:

• операторные скобки для задания составного оператора

begin. . .end;

• операторы цикла

for i = 0 to n do A(i) (выполнять оператор A(i) последовательно

для i = 0, 1, 2, . . . , n);

for i = n downto 0 do A(i) (выполнять оператор A(i) последова-

тельно для i = n, n− 1, n− 2, . . . , 0);

for x ∈ X do A(x) (выполнять оператор A(x) для всех элементов

x множества X в произвольной последовательности);

while условие A do B (выполнять оператор B до тех пор, пока

выполняется условие A);

• оператор присваивания x := y;

• условный оператор if условие A then B else C (если выполняется

условие A, выполнить оператор B, иначе выполнить оператор C );9

Page 10: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

• оператор x↔ y (обмен значениями между x и y). В случае отсут-

ствия в используемом языке программирования оператор обмена

↔ реализуется следующей процедурой Swap.

Procedure Swap(x, y);z := x;

x := y;

y := z

• оператор вывода данных write.

2.Асимптотический формализм оценоквремени работы алгоритмов

Несмотря на то, что функция сложности алгоритма в некоторых слу-

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

точное значение не имеет смысла. Дело в том, что для данных достаточ-

но большой размерности постоянные множители и слагаемые низшего

порядка, участвующие в выражении для такой функции, вносят крайне

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

чением размерности данных. Простейший эффект такого рода можно

наблюдать, когда функция T (n) сложности алгоритма представляет со-

бой некоторый многочлен T (n) = am nm + am−1 nm−1 + . . . + a1 n+ a0.

В этом случае роль одночленов ai ni степени, меньшей степени m само-

го многочлена, несущественна, и ими можно пренебречь при достаточно

большой размерности n.

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

ным, так как предлагает наглядную характеристику эффективности

алгоритма — асимптотический порядок роста функции его сложности и

позволяет сравнивать производительность различных алгоритмов. Что-

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

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

ние роста этих величин. Например, известная формула Стирлинга даёт

10

Page 11: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

широко применяемое приближение

n! ≈√2πn

(ne

)n.

Paul Bachmann в 1894 г. ввел очень удобное обозначение O (читается

"o большое") для использования в приближенных формулах, до сих пор

этот формализм применяется во многих математических дисциплинах,

в том числе и при анализе алгоритмов.

Определение 1. Неотрицательная функция f(n) не превосходит

по порядку функцию g(n) (используем запись f(n) = O(g(n)) ), если

существуют положительные константы N и c такие, что f(n) ≤ c g(n)

для любого n ≥ N .

В этом случае функция g(n) является асимптотически верхней оцен-

кой функции f(n)1. Часто встречающемуся выражению "трудоёмкость

(сложность) алгоритма есть или равна O(g(n))" придаётся именно такой

смысл. Это означает, что для некоторой постоянной c алгоритм на про-

извольном входе размерности n заканчивает работу не более, чем через

c g(n) элементарных операций для всех достаточно больших n. В част-

ности, трудоёмкость O(1) означает, что время работы соответствующего

алгоритма не зависит от размерности входа и не превосходит некоторой

константы. Алгоритм с трудоёмкостью O(n), где n — размерность вхо-

да, называют линейным. Алгоритм сложности O(nc) называется поли-

номиальным. Алгоритм, сложность которого есть O(2nc)

, называется

экспоненциальным. Здесь c — положительная константа.

Записи с символом O дают верхнюю оценку скорости роста функ-

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

время работы алгоритма есть O(n2), это не означает, что это же вре-

мя не может составлять O(n). Для нижних оценок применяется другая

запись с символом Ω (читается "омега большое").1 С точностью до константы.

11

Page 12: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

Определение 2. Неотрицательная функция f(n) не меньше по по-

рядку функции g(n) (используем запись f(n) = Ω(g(n)) ), если сущес-

твуют положительные константы N и c такие, что f(n) ≥ c g(n) для

любого n ≥ N .

В этом случае функция g(n) является асимптотически нижней оцен-

кой функции f(n). И наконец, чтобы точно указать порядок роста функ-

ции f(n), не давая точных значений констант, применяется запись с

символом Θ (читается "тэта большое").

Определение 3. Неотрицательная функция f(n) асимптотиче-

ски равна функции g(n) (используем запись f(n) = Θ(g(n)) ), если су-

ществуют положительные константы N, c1, c2 такие, что выполняются

неравенства c1 g(n) ≤ f(n) ≤ c2 g(n) для всех n ≥ N.

Запись f(n) = Θ(g(n)) включает в себя две асимптотические оцен-

ки: верхнюю и нижнюю. Таким образом, начиная с некоторого N рост

функции f(n) полностью соответствует росту функции g(n). В дальней-

шем выражению "сложность алгоритма есть Θ(g(n))" будем придавать

именно этот смысл.

Отметим, что для функции многих переменных f(n1, . . . , nk) символ

Λ(f(n1, . . . , nk)), Λ ∈ O, Ω, Θ определяется подобным же образом.

Записи с асимптотическими обозначениями O, Ω, Θ очень удобны

и часто употребляются в уравнениях, но при этом требуют некоторой

осторожности. Дело в том, что используемый знак "=" — это не ра-

венство в обычном смысле, а несимметричное (!) отношение включе-

ния подмножеств "⊆". В этом случае с формальной точки зрения за-

пись O(g(n)) необходимо рассматривать как множество неотрицатель-

ных функций f(n), не превосходящих по порядку функцию g(n), а

функцию f(n) — как одноэлементное множество. Таким образом, запись

f(n) = O(g(n)) означает запись f(n) ∈ O(g(n)), запись O(n) = O(n2)

означает O(n) ⊆ O(n2) и т. д. В случае арифметических операций над

12

Page 13: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

такими множествами функций X,Y и функцией f(n) множества X+Y ,

X Y , f X определяются следующим естественным образом:

X + Y = x+ y | x ∈ X & y ∈ Y ,

X Y = x y | x ∈ X & y ∈ Y ,

f X = f x | x ∈ X.

Аналогично следует трактовать Λ-записи в общем случае, когда Λ ∈O, Ω, Θ. Используя введенные определения асимптотических обозна-

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

следующем утверждении.

Теорема 1. Пусть f(n), g(n), h(n) — неотрицательные функции и

Λ ∈ O, Ω, Θ. Тогда

(i) f(n) = Λ(f(n)) (рефлексивность);

(ii) если f(n) = Λ(g(n)) и g(n) = Λ(h(n)), то f(n) = Λ(h(n)) (транзи-

тивность);

(iii) f(n) = O(g(n)) ⇔ g(n) = Ω(f(n)) (обращение);

(iv) f(n) = Θ(g(n)) ⇔ g(n) = Θ(f(n)) (симметричность);

(v) f(n) = Θ(g(n)) ⇔ (f(n) = O(g(n)) & f(n) = Ω(g(n)) (эквива-

лентность);

(vi) Λ(Λ(f(n))) = Λ(f(n)) (идемпотентность);

(vii) арифметические операции:

Λ(f(n)) Λ(g(n)) = Λ(f(n) g(n)),

f(n) Λ(g(n)) = Λ(f(n) g(n)),

Λ(f(n)) + Λ(g(n)) = Λ(f(n) + g(n)) = Λ(maxf(n), g(n)),cΛ(f(n)) = Λ(cf(n)) = Λ(f(n)), если c — положительная константа.

Условимся, что всюду далее при исследовании асимптотических

свойств функций будем использовать символ c, возможно с различными

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

аргумента изучаемых функций.

13

Page 14: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

Приведем пример, содержащий несколько иллюстраций введенных

понятий и обозначений.

Пример 1.

(i) f(n) = Θ(n2), где f(n) = c1n+

n∑i=1

c2 i+ c3.

Действительно, используя теорему 1 и формулу суммы арифметиче-

ской прогрессии, получаем f(n) = O(n) + c2(n2 + n)/2 +O(1) = O(n) +

O(n2)+O(n)+O(1) = O(maxn, n2) = O(n2). Очевидно, f(n) ≥ c2 n2/2 .

Поэтому f(n) = Ω(n2). Таким образом, f(n) = Θ(n2).

(ii) c n = O(√n).

В самом деле, пусть найдутся положительные константы c′ и N такие,

что c n ≤ c′√n для всех n ≥ N. Тогда n ≤ (c′/c) 2 для всех достаточно

больших n, пришли к очевидному противоречию.

(iii) c nm = Ω(nk), где k > m.

Доказывается аналогично (ii).

(iv) O(n) + c12n+c2 + c3 = O(2n).

Следует из теоремы 1 и неравенства 2n ≥ n.

(v) Hn = lnn+Θ(1) = Θ(lnn), 2 где

Hn =n∑

i=1

1

i.3

Для асимптотических оценок гармонических чисел Hn воспользуемся

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

функции f(x) с помощью конечных сумм:

n+1∫m

f(x) dx ≤n∑

i=m

f(i) ≤n∫

m−1

f(x) dx.

2 lnn — натуральный логарифм.3 Конечные суммы Hn известны как гармонические числа.

14

Page 15: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

Функция f(x) = 1/x убывает на интервале (1,+∞). Следовательно,

ln(n+ 1)− ln 2 =

n+1∫2

dx

x≤

n∑i=2

1

i≤

n∫1

dx

x= lnn.

Поэтому Hn = lnn + O(1) и Hn ≥ lnn + 1 − ln 2 = lnn + Ω(1). Таким

образом, Hn = lnn+Θ(1) = Θ(lnn).

Отметим, что аналогичные сравнения интеграла и конечной суммы

для произвольной возрастающей функции приводят к неравенствам

n∫m−1

f(x) dx ≤n∑

i=m

f(i) ≤n+1∫m

f(x) dx.

Такие оценки используются в нижеприведенных примерах (vi) и (viii).

(vi) ln(n!) = Θ(n lnn).

Действительно, ln(n!) =∑n

i=1 ln i. Отсюда очевидно, ln(n!) ≤ n lnn =

O(n lnn). Для доказательства нижней оценки воспользуемся сравнени-

ем интеграла и конечной суммы функции lnx, возрастающей и положи-

тельной на интервале (1,+∞). Имеем

n∑i=2

ln i ≥n∫

1

lnx dx = x lnx− x

∣∣∣∣n1

= n lnn− n+ 1 = Ω(n lnn).

Следовательно, ln(n!) = Θ(n lnn).

Заметим, что более точные верхнюю и нижнюю оценки величин n!

и ln(n!) можно получить из формулы Стирлинга

n! =√2π n

(ne

)n(1 + Θ(1/n)

).

(vii) lg(n!) = Θ(n lgn)4.

Непосредственно следует из примера (vi) и равенства lg(n!) = lg e ln(n!).

(viii)n∑

i=1

i ln i =n2

2lnn− n2

4+O(n lnn) = O(n2 lnn).

4 lgn — двоичный логарифм.

15

Page 16: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

Действительно, поскольку f(x) = x lnx — возрастающая функция, как

и в примере (v), получаем следующие неравенства:

n−1∑i=2

i ln i ≤n∫

2

x lnx dx =x2

2lnx− x2

4

∣∣∣∣n2

=

=n2

2lnn− n2

4− ln 4 + 1 ≤ n2

2lnn− n2

4.

Осталось заметить, чтоn∑

i=1

i ln i = n lnn+n−1∑i=2

i ln i.

(ix)⌈n/c⌉∑i=1

⌊lg i⌋ = Ω(n lg n)5.

Доказательство требуемой нижней оценки проведём с помощью метода

разбиения суммы на части. Для n ≥ 2c имеем6

⌈n/c⌉∑i=1

⌊lg i⌋ =

⌈n/2c⌉−1∑i=1

⌊lg i⌋ +⌈n/c⌉∑

i=⌈n/2c⌉

⌊lg i⌋ ≥

≥⌈n/c⌉∑

i=⌈n/2c⌉

⌊lg i⌋ ≥⌈n/c⌉∑

i=⌈n/2c⌉

⌊ lg⌈n/2c⌉ ⌋ ≥

≥ ( ⌈n/c⌉ − ⌈n/2c⌉+ 1 ) ( lg(n/2c)− 1 ) ≥

≥ n

2c( lg n− lg(2c)− 1 ) =

= Ω(n)Ω( lg n) = Ω(n lgn).

Несмотря на то, что основное внимание мы уделяем порядку

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

ста сложности алгоритма может иметь меньшую мультипликатив-

ную постоянную7, чем малый порядок роста сложности другого ал-

горитма. В таком случае алгоритм с быстро растущей сложностью5 ⌊x⌋ — наибольшее целое число, не превосходящее x;

⌈x⌉ — наименьшее целое число, не меньшее x.6 Здесь использованы неравенства x− 1 < ⌊x⌋ ≤ x ≤ ⌈x⌉ < x+ 1.7 Константа c в определении O(g(n)).

16

Page 17: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

может оказаться предпочтительнее для задачи с малой размерностью

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

практической точки зрения.

3.Алгоритм нахождения n-факториальногопредставления числа

Для знакомства с неформальным языком программирования, на кото-

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

ритма и используемой при его анализе асимптотической Λ-символикой

рассмотрим алгоритм нахождения n-факториального представления

числа (при фиксированном неотрицательном целом n).

Определение 4. n-факториальным представлением целого неот-

рицательного числа m называется последовательность целых чисел

(d0, d1, . . . , dn−1) такая, что 0 ≤ di ≤ i при i = 0, 1, . . . , n− 1 и

m = dn−1(n− 1)! + dn−2(n− 2)! + . . .+ d00!.

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

тельного числа m < n! существует8 n-факториальное представление

числа m, причём единственное. Рассмотрим следующую задачу: пусть

задано значение n, а m может быть любым целым неотрицательным

числом, не превосходящим n!, требуется найти n-факториальное пред-

ставление числа m. Эта задача легко решается c помощью простого

алгоритма. Неформально опишем его по шагам.

Шаг 0. Полагаем d0 = 0 и q0 = m.

Шаг 1. Делим q0 на 2, находим остаток от деления d1 и частное от

деления q1 = ⌊q0/2⌋. При этом имеем 0 ≤ d1 < 2.

8 Последовательность (d0, d1, . . . , dn−1) является записью числа m относительно

одной из разновидностей смешанных систем счисления.

17

Page 18: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

Переходим к следующему шагу. К шагу i ≥ 1 будут определены

числа d0, d1, . . . , di−1 и q0, q1, . . . , qi−1.

Шаг i. Делим qi−1 на i + 1, полагаем di — остаток от деления и

qi = ⌊qi−1/(i+ 1)⌋. При этом имеем 0 ≤ di < i+ 1.

Шаг n− 1. На заключительном шаге находим остаток dn−1 от деле-

ния qn−2 на n и частное qn−1 = ⌊qn−2/n⌋.

Нетрудно доказать, что последовательность (d0, d1, . . . , dn−1), вычис-

ленная посредством этого алгоритма, является n-факториальным пред-

ставлением числа m. Заметим, что пошаговый процесс можно закон-

чить, как только будет выполнено равенство qi = 0, все оставшиеся

числа qj при j > i будут также равны 0. Кроме того, можно отказать-

ся от массива из элементов qi, достаточно на каждом шаге записывать

требуемое значение элемента qi в единственную переменную q. Таким

образом, приходим к следующей программной реализации.

Алгоритм FDecomp(n,m) нахождения

n-факториального представления числа m < n!

i := 0;

d0 := 0;

q := m;

while q > 0 do

begin

i := i+ 1;

di := q mod i;

q := ⌊q/i⌋end;

i := i+ 1;

while i < n do di := 0.

Чему равна сложность T (n) алгоритма FDecomp(n,m) нахождения

n-факториального представления для произвольного числа m < n! ?18

Page 19: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

Оценим время работы алгоритма. Введем следующие обозначения:

• c1 — число элементарных операций, выполняющихся за одну ите-

рацию первого while-цикла;

• c2 — число элементарных операций, выполняющихся за одну ите-

рацию второго while-цикла;

• c3 — число элементарных операций, выполняющихся при инициа-

лизации переменных, вне обоих while-циклов;

• n1 — число итераций первого while-цикла;

• n2 — число итераций второго while-цикла.

Тогда время работы алгоритма FDecomp(n,m) равно c1n1 + 1+ c2n2 +

1 + c3, причём константы c1, c2, c3 не зависят от n, а числа n1, n2 опре-

деляются в зависимости от m и n1 + n2 = n − 1. Наибольшее число

операций будет выполняться для такого числа m, когда di > 0 для лю-

бого i > 0. Поэтому в худшем случае (по времени работы алгоритма)

n1 = n − 1, n2 = 0 и T (n) = c1(n − 1) + 2 + c3 = Θ(n). Причём и в

лучшем случае время работы алгоритма FDecomp(n,m) есть Θ(n), так

как выполняется равенство n1 + n2 = n− 1.

При определении n-факториального представления числа m ино-

гда ограничиваются последовательностью (d0, d1, . . . , dk), для которой

dk = 0 и dk+1 = . . . = dn−1 = 0 при m > 0 (k = 0, если m = 0).

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

него while-цикла, в котором присваивается нулевое значение оставшим-

ся членам последовательности di. Однако это не улучшит сложность

алгоритма, поскольку можно выбрать такое число m, что di > 0 для

всех i, например, m =n−1∑i=1

i!. Таким образом, сложность алгоритма

FDecomp(n,m) есть Θ(n).

19

Page 20: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

Глава 2Генерация комбинаторных объектов

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

элементы некоторого класса комбинаторных объектов. Такого рода за-

дачи решаются с помощью алгоритмов генерации. Наряду с обычным

выводом требуемых объектов без повторений, эти алгоритмы позволя-

ют одновременно производить анализ объектов, их обработку, отбор и

т. п. В этой главе мы познакомимся с различными алгоритмами гене-

рации перестановок, двоичных векторов, всех подмножеств конечного

множества, кодов Грея и сочетаний.

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

наторных объектов будут сводиться к выбору начальной конфигурации,

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

объекта в следующий и проверке условия окончания, которое опреде-

ляет момент прекращения вычислений. При этом особый интерес будут

представлять алгоритмы генерации объектов в порядке минимального

изменения, когда два "соседних" порождаемых объекта различаются в

подходящем смысле "минимально".

При рассмотрении класса комбинаторных объектов предполагает-

ся, что все его объекты имеют некоторую одинаковую количественную

меру, предварительно заданную целочисленным параметром (или на-

бором целочисленных параметров), который передается на вход алго-

ритма генерации. Нас прежде всего будет интересовать время работы

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

функция от размерности входных данных. Мы будем стремиться по-

лучить асимптотически наилучший алгоритм генерации. В частности,

в некоторых алгоритмах можно порождать множество всех требуемых

объектов за время, пропорциональное его мощности (при этом, есте-

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

торного объекта). В этом случае алгоритм имеет сложность O(k), где

k — число порождаемых объектов. Такой алгоритм генерации комби-20

Page 21: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

наторных объектов в литературе часто называют линейным. Хотя это

название несёт дуализм9, оно оправдано, поскольку такие алгоритмы

генерации имеют асимптотически наилучшую сложность. Мы также бу-

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

1.Перестановки и алгоритмы их порождения

Определение 5. Перестановкой множества A называется произ-

вольное взаимно-однозначное отображение α : A→ A.

Обычно перестановка конечного множества A определяется с помо-

щью таблицы с двумя строками, каждая из которых содержит все эле-

менты множества A, причем элемент α(a) помещается под элементом

a. Например, перестановку α : A→ A множества A = a, b, c, d такую,

что α(a) = d, α(b) = a, α(c) = c и α(d) = b, можно записать в виде

следующей таблицы

α =

(a b c d

d a c b

).

Иногда перестановкой называется вторая строка такой таблицы, а

сама функция α : A→ A, заданная таблицей, называется подстановкой.

Поскольку порядок элементов множества A будет всегда зафиксирован,

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

ции α, так и для обозначения нижней строки таблицы, определяющей

это отображение. Таким образом, в указанном примере перестановка

есть последовательность (d, a, c, b). В общем случае для n-элементного

множества A с зафиксированным порядком элементов a1, . . . , an пере-

становка — это произвольная последовательность длины n из различ-

ных элементов множества A. Так, последовательность (α1, . . . , αn), где

все элементы αi ∈ A различны, есть перестановка.

Обычно природа элементов множества A несущественна, поэтому

без уменьшения общности можно считать, что A = 1, 2, . . . , n (ина-9 Cравните с понятием линейного алгоритма из гл. 1.

21

Page 22: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

че необходимо перейти к номерам элементов). Обозначим множество

всех перестановок n-элементного множества через Sn. На множестве

Sn определена операция умножения перестановок α β как суперпози-

ция отображений α и β : αβ(i) = α(β(i)). Вообще говоря, эта операция

не коммутативна, т. е. α β = β α. При этом выполняются следующие

аксиомы группы:

• ∀α ∈ Sn∀β ∈ Sn∀γ ∈ Sn α (β γ) = (α β) γ (ассоциативность);

• ∃ e ∈ Sn ∀α ∈ Sn (α e = e α = α) (существование единичного

элемента e);

• ∀α ∈ Sn ∃α−1 ∈ Sn (α α−1 = α−1 α = e) (существование обрат-

ного элемента α−1).

Тождественная перестановка e является единичным элементом, а для

e =

(1 2 . . . n

1 2 . . . n

)

нахождения обратной перестановки α−1 достаточно сначала поменять

местами строки в таблице, определяющей перестановку α, а затем упо-

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

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

рации умножения , называемую симметрической группой степени n.

Её порядок, т. е. число элементов множества Sn, равен n!.

Рассмотрим задачу генерации всех перестановок n-элементного мно-

жества. Возникновение этой задачи относят к началу XVII в., когда в

Англии зародилось особое искусство колокольного боя, основанного, ес-

ли говорить упрощённо, на выбивании на n различных колоколах всех

n! перестановок. Перестановки эти следовало "выбивать по памяти",

что способствовало разработке сторонниками этого искусства первых

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

повторений. В Книге рекордов Гиннеса содержится упоминание о вы-

22

Page 23: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

бивании всех 8!=40320 перестановок на 8 колоколах в 1963 г., на это

потребовалось 17 часов 58,5 минут.

Далее в этом разделе мы познакомимся с несколькими алгоритмами

генерации всех перестановок n-элементного множества. Сначала опре-

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

новки относительно этого порядка и вектор инверсии. Затем рассмот-

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

ми. Наконец, детально изучим линейный алгоритм генерации переста-

новок в лексикографическом порядке и линейный алгоритм Джонсона –

Троттера генерации перестановок в порядке минимального изменения.

1.1.Индекс перестановки

На множестве всех перестановок n-элементного множества определим

бинарное отношение ≼ следующим образом:

(α1, α2, . . . , αn) ≼ (β1, β2, . . . βn)⇔ ∃ k ≥ 1 (αk < βk & ∀i < k (αi = βi)).

Очевидно, что отношение ≼ удовлетворяет следующим аксиомам:

• ∀α (α ≼ α) (рефлексивность);

• ∀α∀β (α ≼ β & β ≼ α⇒ α = β) (антисимметричность);

• ∀α∀β∀γ (α ≼ β & β ≼ γ ⇒ α ≼ γ) (транзитивность);

• ∀α∀β (α ≼ β ∨ β ≼ α) (сравнимость).

Таким образом, отношение ≼ есть линейный порядок на множестве Sn.

Такой порядок называется лексикографическим10. Например, последо-

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

рядке, имеет вид 123, 132, 213, 231, 312, 321 (здесь перестановки перечис-

лены в порядке возрастания получающихся чисел).10 В общем случае лексикографический порядок определяется на множестве An

слов конечного алфавита A (с заданным упорядочиванием букв) фиксированной

длины n.

23

Page 24: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

Ясно, что тождественная перестановка (1, 2, . . . , n) есть наименьший

элемент в (Sn,≼) (относительно порядка ≼), а перестановка (n, n − 1,

n− 2, . . . , 1) — наибольший элемент.

В дальнейшем будем использовать следующие очевидные свойства

перестановок. Рассмотрим различные элементы i1, . . . , ik ∈ 1, . . . , nи множество S(i1, . . . , ik) всех перестановок k-элементного множества

i1, . . . , ik. На множестве S(i1, . . . , ik) можно также рассмотреть лек-

сикографический порядок ≼.

Лемма 1.

(i) (S(i1, . . . , ik),≼) — линейно упорядоченное множество.

(ii) Если α1, . . . , αk ∈ i1, . . . , ik и α1 > α2 > . . . > αk, то перестанов-

ки (α1, α2, . . . , αk) и (αk, αk−1, . . . , α1) являются наибольшим и наимень-

шим элементами линейно упорядоченного множества (S(i1, . . . , ik),≼)соответственно.

(iii) Если перестановки α, β, γ ∈ Sn имеют общее начало длины k и

α ≼ γ ≼ β, то αk+1 ≤ γk+1 ≤ βk+1 и (αk+1, . . . , αn) ≼ (γk+1, . . . , γn) ≼(βk+1, . . . , βn).

Рассмотрим метод вычисления номера заданной перестановки в по-

следовательности всех перестановок из Sn, записанных в лексикогра-

фическом порядке, т. е. установим соответствие между целыми числами

0, 1, 2, . . . , n! − 1 и n! перестановками из Sn. Для этого индукцией по n

определим отображение

In : Sn → 0, 1, . . . , n!− 1.

При n = 1 полагаем I 1(α) = 0, где α = (1) и S1 = α. Пусть отобра-жения I i : Si → 0, 1, . . . , i!−1, i = 1, 2, . . . , n−1 уже определены. Для

произвольной перестановки α = (α1, . . . , αn) ∈ Sn полагаем

In(α) = (α1 − 1)(n− 1)! + In−1(α′),

где α′ — последовательность n−1 элементов, полученная из перестанов-24

Page 25: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

ки α удалением α1 и уменьшением на единицу всех элементов, больших

α1. Нетрудно доказать, что α′ ∈ Sn−1 и In(α) ≤ n!− 1.

Теорема 2. Отображение In : Sn → 0, 1, . . . , n!− 1 есть изомор-

физм линейно упорядоченных множеств (Sn,≼) и (0, 1, . . . , n!− 1,≤).

Доказательство проведем индукцией по n. Базис индукции при

n = 1 очевиден. Предположим, что утверждение теоремы верно для

n− 1, и докажем его для n. Конечные множества Sn и 0, 1, . . . , n!− 1равномощны. Поэтому если мы покажем, что In является разнознач-

ным отображением, то In есть биекция. Пусть α, β ∈ Sn и In(α) =

In(β). Из определения отображения In имеем

In(α) = (α1 − 1)(n− 1)! + In−1(α′),

In(β) = (β1 − 1)(n− 1)! + In−1(β′).

Так как In−1(α′) < (n − 1)! и In−1(β

′) < (n − 1)!, числа In−1(α′)

и In−1(β′) имеют некоторые (n − 1)-факториальные представления

(dα′

0 , . . . , dα′

n−2) и (d β′

0 , . . . , dβ′

n−2) соответственно. Тогда последователь-

ность (dα′

0 , . . . , dα′

n−2, α1 − 1) является n-факториальным представлени-

ем числа In(α), а последовательность (d β′

0 , . . . , dβ′

n−2, β1 − 1) есть n-

факториальное представление числа In(β). Из единственности такого

представления получаем α1 = β1. Следовательно, In−1(α′) = In−1(β

′)

и α′ = β′ в силу индукционного предположения. Поэтому α = β. Таким

образом, In есть биекция.

Докажем, что In — изоморфизм. Достаточно показать, что если

α, β ∈ Sn и α ≼ β, то In(α) ≤ In(β) (т. е. In сохраняет порядок). Пусть

α ≼ β. Тогда α1 ≤ β1. Если α1 = β1, то In(α) ≤ (β1 − 2)(n − 1)! + (n −1)!−1 = (β1−1)(n−1)!−1 ≤ In(β). Пусть теперь α1 = β1. Тогда α′ ≼ β′,

и по индукционному предположению имеем In−1(α′) ≤ In−1(β

′). Сле-

довательно, In(α) ≤ In(β). Теорема 2 доказана.

25

Page 26: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

Определение 6. Пусть α ∈ Sn. Целое неотрицательное число

In(α) называется индексом перестановки α.

Теорема 2 показывает, что In является нумерацией всех переста-

новок из Sn, упорядоченных в лексикографическом порядке, а индекс

In(α) есть номер перестановки α ∈ Sn в этой последовательности.

Индуктивное определение индекса перестановки α ∈ Sn фактически

задаёт n-факториальное представление (0, . . . , α′1−1, α1−1) числа In(α),

причём по n-факториальному представлению (d0, . . . , dn−1) индекса

In(α) также восстанавливается и сама перестановка α = (α1, . . . , αn).

Опустим формализацию процедуры PConstrF (d0, . . . , dn−1), осуществ-

ляющей такое восстановление перестановки. Теперь мы можем порож-

дать все перестановки из Sn в лексикографическом порядке следующим

образом: изменяя индекс i от 0 до n!−1, находим n-факториальное пред-

ставление числа i и по нему восстанавливаем перестановку.

Алгоритм PIndex(n) генерации

всех перестановок из Sn по индексам

for i = 0 to n!− 1 do

begin % нахождение n-факториального

FDecomp(n, i); % представления (d0, . . . , dn−1) числа i;

PConstrF (d0, . . . , dn−1); % восстановление перестановки α по

write(α1, . . . , αn) % n-факториальному представлению

end.

Время работы алгоритма PIndex(n) есть Ω(nn!), так как количество

итераций for-цикла равно n!, и алгоритм FDecomp(n, i) требует времени

Θ(n). Такая сложность не является оптимальной, в следующем разделе

мы познакомимся с линейным алгоритмом генерации всех перестановок

из Sn в лексикографическом порядке.

26

Page 27: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

1.2. Генерация перестановокв лексикографическом порядке

Будем говорить, что перестановка β ∈ Sn непосредственно следует за

перестановкой α ∈ Sn относительно лексикографического порядка ≼,если выполняются следующие условия:

• α ≺ β11,

• не существует такой перестановки γ ∈ Sn, что α ≺ γ ≺ β.

При генерации перестановок в лексикографическом порядке, начи-

ная с тождественной перестановки (1, 2, . . . , n), требуется переходить

от уже построенной перестановки α = (α1, . . . , αn) к непосредственно

следующей за ней перестановке β = (β1, . . . , βn) до тех пор, пока не

получим наибольшую перестановку (n, n− 1, . . . , 1) (относительно лек-

сикографического порядка).

Рассмотрим способ построения такой перестановки β. Просматрива-

ем справа налево перестановку α = (α1, . . . , αn) в поисках самой правой

позиции i такой, что αi < αi+1. Если такой позиции нет, то α1 > α2 >

. . . > αn, т. е. α = (n, n−1, . . . , 1) и генерировать больше нечего. Поэтому

считаем, что такая позиция i есть. Значит, αi < αi+1 > αi+2 > . . . > αn.

Далее ищем первую позицию j при переходе от позиции n к позиции i

такую, что αi < αj . Тогда i < j. Затем меняем местами элементы αi и αj ,

а в полученной перестановке α′ = (α′1, . . . , α

′n) отрезок α′

i+1, . . . , α′n−1, α

′n

переворачиваем. Построенную перестановку обозначим через β.

Например, пусть α = (2, 6, 5, 8, 7, 4, 3, 1). Тогда αi = 5 и αj = 7. Поме-

няем местами эти элементы, перевернём отрезок (8, 5, 4, 3, 1) и получим

перестановку β = (2, 6, 7, 1, 3, 4, 5, 8).

Лемма 2. Перестановка β непосредственно следует за перестанов-

кой α относительно лексикографического порядка.11 α ≺ β, если α ≼ β и α = β.

27

Page 28: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

Доказательство. В силу построения βs = α′s = αs для любой по-

зиции s < i. Так как βi = α′i = αj > αi, то α ≼ β.

Предположим, что α ≼ γ ≼ β, и покажем, что γ = α или γ = β.

Так как βs = αs для всех s < i, то из определения лексикографического

порядка получаем γs = αs при s < i. Тогда αi ≤ γi ≤ βi = αj в силу

леммы 1(iii). Предположим, что αi = γi. Тогда γi ∈ αi+1, . . . , αn. Но

αj > αi < αi+1 > αi+2 > . . . > αn. Следовательно, γi ≥ αj в силу

выбора j. Поэтому γi = αj . Таким образом, αi = γi или γi = βi.

Случай 1. αi = γi. Тогда (αi+1, . . . , αn) ≼ (γi+1, . . . , γn) по лемме

1(iii). В силу леммы 1(ii) имеем (γi+1 , . . . , γn) ≼ (αi+1, . . . , αn). Поэтому

по лемме 1(i) получаем (αi+1, . . . , αn) = (γi+1, . . . , γn). Таким образом,

справедливо равенство α = γ.

Случай 2. γi = βi. Покажем, что α′i+1, . . . , α

′n — убывающая после-

довательность. Действительно, последовательность αi+1, αi+2, . . . , αn

убывает в силу выбора i. Причём α′j = αi, j > i и α′

s = αs для всех

s таких, что s > i и s = j. В силу выбора позиции j получаем

α′j = αi ≥ αj+1 = α′

j+1, если j < n;

α′j−1 = αj−1 > αj > αi = α′

j , если j > i+ 1.

Следовательно, α′i+1, . . . , α

′n — убывающая последовательность. По-

сле переворота этой последовательности получим возрастающую по-

следовательность βi+1, . . . , βn. Тогда (βi+1, . . . , βn) ≼ (γi+1, . . . , γn) по

лемме 1(ii) и (γi+1, . . . , γn) ≼ (βi+1, . . . , βn) по лемме 1(iii). Поэтому

(βi+1, . . . , βn) = (γi+1, . . . , γn) по лемме 1(i). Таким образом, β = γ.

Лемма 2 доказана.

Перейдем к рассмотрению алгоритма генерации перестановок, в

котором применяется описанный способ перестроения перестановки в

непосредственно следующую за ней. При программной реализации это-

го алгоритма используется массив α размерности n + 1. В α1, . . . , αn

записываем текущую порождаемую перестановку из Sn, первоначально

тождественную. Значение α0 не изменяется и равно 0, поэтому всегда

справедливо неравенство α0 < α1. Это неравенство гарантирует нахож-

28

Page 29: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

дение самой правой позиции i ≥ 0 такой, что αi < αi+1. Алгоритм

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

Алгоритм PLex(n) генерации всех перестановок

в лексикографическом порядке

for j = 0 to n do αj := j;

i := 1;

while i = 0 do

begin

write (α1, . . . , αn);

i := n− 1;

while αi > αi+1 do i := i− 1;

j := n;

while αj < αi do j := j − 1;

Swap(αi, αj);

k := i+ 1;

m := i+ ⌊(n− i)/2⌋;while k ≤ m do

begin

Swap(αk, αn−k+i+1);

k := k + 1

end

end.

Пример 2. При n = 3 процесс работы алгоритма PLex(n) гене-

рации перестановок из S3 в лексикографическом порядке представлен

следующей последовательностью перестроений перестановок αi:

α1 = (1, 2, 3), α1i = 2, α1

j = 3;

α2 = (1, 3, 2), α2i = 1, α2

j = 2;

α3 = (2, 1, 3), α3i = 1, α2

j = 3;

α4 = (2, 3, 1), α4i = 2, α4

j = 3;

α5 = (3, 1, 2), α5i = 1, α5

j = 2;

α6 = (3, 2, 1), i = 0.29

Page 30: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

Теорема 3. Алгоритм PLex(n) корректен и строит все перестанов-

ки из Sn без повторений в лексикографическом порядке за время O(n!).

Доказательство. Используя лемму 2, нетрудно обосновать кор-

ректность алгоритма PLex(n).

Оценим сложность T (n) алгоритма PLex(n). Рассмотрим разбиение

множества Sn на n подмножеств Skn, k = 1, . . . , n. Множество Sk

n состо-

ит из всех перестановок, на первом месте которых стоит число k. То-

гда Skn содержит (n− 1)! перестановок. Относительно лексикографиче-

ского порядка каждая перестановка из Skn предшествует произвольной

перестановке из Smn при k < m, а упорядочивание на Sk

n соответству-

ет лексикографическому упорядочиванию множества всех перестановок

S(1, . . . , n \ k). Таким образом, последовательность α1, α2, . . . , αn!

всех перестановок из Sn, упорядоченных лексикографически, разбива-

ется на следующие n блоков:

(1, . . .) · · · (1, . . .)︸ ︷︷ ︸S1n

· · · (k, . . .) · · · (k, . . .)︸ ︷︷ ︸Skn

· · · (n, . . .) · · · (n, . . .)︸ ︷︷ ︸Snn

.

В силу леммы 1(ii) перестановки (k, 1, 2, . . . , k − 1, k + 1, . . . , n) и (k, n,

n−1, . . . , k+1, k−1, . . . , 1) являются первой и последней перестановкой

из Skn соответственно. Введём следующие обозначения:

• tn0 — число операций, выполняемых в алгоритме PLex(n) до пе-

чати перестановки α1;

• tni — число операций, выполняемых в алгоритме PLex(n), начиная

с печати αi и до печати αi+1;

• tnn! — число операций, выполняемых в алгоритме PLex(n), начи-

ная с печати αn! и до окончания работы программы.

30

Page 31: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

Тогда из алгоритма PLex(n) получаем

T (n) =n!∑i=0

tni = tn0 +n!∑i=1

tni = O(n) +n∑

k=1

∑i:αi∈Sk

n

tni =

= O(n) +

n∑k=1

(tnk(n−1)! +∑

i:αi∈Skn\αk(n−1)!

tni ),

∑i:αi∈Sk

n\αk(n−1)!

tni = T (n− 1)− c1n,

где константа c1 не зависит от n. Подсчитаем tnk(n−1)! — число опера-

ций начиная с печати последней перестановки (k, n, n − 1, . . . , k + 1,

k − 1, . . . , 1) из k-го блока Skn до печати первой перестановки (k + 1,

1, 2, . . . , k, k + 2, . . . , n) из (k + 1)-го блока Sk+1n . В этом случае будем

менять местами числа k и k+1, а затем переворачивать последователь-

ность n, n− 1, . . . , k + 2, k, k − 1, . . . , 1. Теперь понятно, что

tnk(n−1)! = c2n+ c3k,

где константы c2, c3 не зависят от n и k. Следовательно,

n∑k=1

tnk(n−1)! =n∑

k=1

(c2n+ c3k) = O(n2).

Таким образом, получаем

T (n) = O(n) +O(n2) +n∑

k=1

(T (n− 1)− c1n) =

= nT (n− 1) +O(n2).

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

ма PLex(n), можно считать, что T (n) = nT (n− 1)+ c n2 для некоторой

константы c. Решим это рекуррентное соотношение. Сделаем замену

T (n) = Pn − c n.

31

Page 32: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

Тогда Pn = nPn−1 + 2 c n. Следовательно,

Pn

n!=

Pn−1

(n− 1)!+

2 c

(n− 1)!.

Поэтому при n > 1 имеем

Pn

n!= P1 +

n−1∑i=1

2 c

i!= O(1).

Таким образом, справедливы соотношения Pn = O(n!) и T (n) = O(n!).

Теорема 3 доказана.

1.3.Порождение перестановок черезвекторы инверсий

Пусть α = (α1, α2, . . . , αn) есть перестановка из Sn.

Определение 7. Пара (αi, αj) называется инверсией перестановки

α = (α1, α2, . . . , αn), если i < j и αi > αj .

Инверсию (αi, αj) будем также называть j-инверсией перестановки α,

тем самым явно указывая номер меньшего элемента в инверсии (αi, αj).

Определение 8. Вектором инверсий перестановки α ∈ Sn назы-

вается последовательность целых чисел (d1, d2, . . . , dn) такая, что dj

есть число j-инверсий перестановки α.

Другими словами, dj есть число элементов перестановки α, больших αj

и находящихся левее αj в последовательности (α1, . . . , αn),

dj =| αi | i < j & αi > αj | 12.

Например, для перестановки (4, 3, 5, 2, 1, 7, 8, 6, 9) вектором инверсий бу-

дет вектор (0, 1, 0, 3, 4, 0, 0, 2, 0).

12 |X| — мощность множества X.

32

Page 33: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

Вектор инверсий содержит информацию о структуре "беспорядка"

перестановки. Такая информация оказывается полезной при разработ-

ке различных алгоритмов обработки данных. Именно этим объясня-

ется интерес к алгоритму генерации всех перестановок n-элементного

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

как будет показано далее, хуже сложности рассмотренного алгоритма

PLex(n) генерации перестановок в лексикографическом порядке.

Пусть (d1, . . . , dn) — вектор инверсий перестановки α ∈ Sn. Оче-

видно, 0 ≤ dj < j. Определим отображение Vn : Sn → Dn, полагая

Vn(α) = (d1, . . . , dn), где Dn = (d1, . . . , dn) | 0 ≤ dj < j, j = 1, . . . , n.

Теорема 4. Отображение Vn : Sn → Dn является биекцией, при-

чём любая перестановка α ∈ Sn однозначно восстанавливается по её

вектору инверсий Vn(α).

Доказательство. Очевидно, что множества Dn, Sn равномощны

и содержат n! элементов. Поэтому достаточно показать, что Vn яв-

ляется сюръективным отображением. Опишем метод, как для произ-

вольного вектора d = (d1, . . . , dn) ∈ Dn построить перестановку α =

(α1, . . . , αn) ∈ Sn такую, что Vn(α) = d, тем самым всё будет доказано.

Действительно, определим множество In = 1, 2, . . . , n. Располо-

жим его элементы в порядке возрастания и (dn + 1)-й элемент с кон-

ца этой возрастающей последовательности обозначим через αn. То-

гда среди чисел из In имеется ровно dn чисел, больших αn. Поэтому

в произвольной перестановке α вида α = (. . . , αn) число n-инверсий

будет равно dn. Далее определим множество In−1 = In \ αn, т. е.

вычеркнем αn. Расположим его элементы в порядке возрастания и

(dn−1 + 1)-й элемент с конца этой возрастающей последовательности

обозначим через αn−1. Тогда среди чисел из множества In−1 имеется

ровно dn−1 чисел, больших αn−1. Следовательно, в произвольной пере-

становке α = (. . . , αn−1, αn) чисел, больших αn−1 и не равных αn, будет

33

Page 34: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

ровно dn−1. Поэтому в перестановке α число (n− 1)-инверсий есть dn−1

и число n-инверсий равно dn.

На шаге i уже будут определены множество Ii+1 и вычеркнутые из

множества 1, 2, . . . , n элементы αn, αn−1, . . . , αi+1. Полагаем

Ii = Ii+1 \ αi+1 = In \ αn, αn−1, . . . , αi+1.

Далее снова расположим элементы множества Ii в порядке возраста-

ния и (di + 1)-й элемент с конца этой возрастающей последователь-

ности обозначим через αi. Тогда в произвольной перестановке α вида

(. . . , αi, αi+1, . . . , αn) число j-инверсий будет равно dj для всех j ≥ i.

Продолжим этот процесс до шага i = 1. В итоге получим перестанов-

ку α = (α1, . . . , αn), число i-инверсий которой равно di для любого i.

Теорема 4 доказана.

Пример 3. Процесс восстановления перестановки α = (α1, . . . , α5),

имеющей вектор инверсий d = (0, 0, 2, 1, 1), выглядит так:

i = 5, I5 = 1, 2, 3, 4, 5, d5 + 1 = 2, α5 = 4;

i = 4, I4 = 1, 2, 3, 5, d4 + 1 = 2, α4 = 3;

i = 3, I3 = 1, 2, 5, d3 + 1 = 3, α3 = 1;

i = 2, I2 = 2, 5, d2 + 1 = 1, α2 = 5;

i = 1, I1 = 2, d1 + 1 = 1, α1 = 2;

α = (2, 5, 1, 3, 4).

Формализуем данный алгоритм в виде псевдокода. Очевидным обра-

зом можно сэкономить используемую память и уменьшить число опе-

раций, отказавшись от создания множества Ii и упорядочивания его

элементов на каждом шаге i. Вместо этого достаточно хранить только

лишь метки для уже вычеркнутых чисел из множества 1, 2, . . . , n и

добавлять на шаге i метку для числа αi. Будем использовать массив

ϕ размерности n с булевыми значениями его элементов true и false для

создания таких меток.

34

Page 35: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

Алгоритм PConstrV (d1, . . . , dn) восстановления перестановки

α = (α1, . . . , αn) по её вектору инверсий d = (d1, . . . , dn)

for k = 1 to n do ϕk := true;

for i = n downto 1 do

begin

j := 1;

m := n;

while j ≤ di or ϕm = false do

begin

if ϕj = true then j := j + 1;

m := m− 1

end;

αi := m;

ϕm := false

end.

Оценим сложность T (n) алгоритма PConstrV (d1, . . . , dn). Число

итераций for-циклов равно n. Поскольку число итераций while-цикла

не превосходит n, имеем T (n) ≤ n + n(c1 + nc2 + c3) = O(n2). Зна-

чит, T (n) = O(n2). Докажем, что T (n) = Θ(n2). Действительно, рас-

смотрим вектор d = (0, 1, . . . , n − 1), являющийся вектором инвер-

сий перестановки α = (n, n − 1, . . . , 2, 1). Из алгоритма следует, что

In = 1, . . . , n, In−1 = 2, . . . , n, . . . , Ii = n − i+ 1, . . . , n и ϕn−i+1 =

. . . = ϕn = true. Поэтому для любого i = n, n− 1, . . . , 1 число итераций

while-цикла (при фиксированном i) равно di = i− 1. Следовательно,

T (n) ≥ n+

1∑i=n

(c1 + dic2 + c3) = Ω(n2).

Таким образом, сложность алгоритма PConstrV (d1, . . . , dn) есть Θ(n2).

Алгоритм PConstrV (d1, . . . , dn) восстановления перестановки по её

вектору инверсий даёт очевидный способ генерации всех перестановок

35

Page 36: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

из Sn. Сначала порождаем вектор инверсий d = (d1, . . . , dn) ∈ Dn,

а затем по вектору инверсий d восстанавливаем перестановку α =

(α1, . . . , αn) ∈ Sn. По теореме 4 множество векторов инверсий всех

перестановок из Sn есть Dn. С другой стороны, Dn есть множество

n-факториальных представлений всех целых неотрицательных чисел,

меньших n!. Таким образом, приходим к следующему алгоритму гене-

рации перестановок.

Алгоритм PV Inv(n) генерации всех

перестановок через векторы инверсий

for i = 0 to n!− 1 do

begin % нахождение n-факториального

FDecomp(n, i); % представления (d1, . . . , dn) числа i;

PConstrV (d1, . . . , dn); % восстановление перестановки α по

write(α1, . . . , αn) % вектору инверсий (d1, . . . , dn)

end.

Очевидно, что время работы алгоритма PV Inv(n) генерации пере-

становок через векторы инверсий есть Ω(nn!). Поэтому линейный алго-

ритм PLex(n) генерации перестановок в лексикографическом порядке

предпочтительнее. Однако алгоритмы, использующие векторы инвер-

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

векторе инверсий, настолько богата, что позволяет полностью восста-

новить саму перестановку.

1.4.Алгоритм Джонсона – Троттерагенерации перестановок

Мы рассмотрели несколько алгоритмов генерации перестановок из Sn.

При этом переход от предыдущей перестановки к следующей требовал,

вообще говоря, большого числа перемещений элементов исходной пе-

рестановки. Так, в лучшем из рассмотренных алгоритмов PLex(n) мы

36

Page 37: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

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

нечный отрезок перестановки. Поэтому естественно желание получить

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

ся настолько мало, насколько это возможно. Для того, чтобы такое раз-

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

должна отличаться от предшествующей транспозицией двух соседних

элементов. Возможно ли таким образом породить все перестановки без

повторений? Оказывается, такую последовательность перестановок лег-

ко построить рекурсивно по n.

При n = 1 последовательность из единственной перестановки

(1) будет требуемой. Предположим, что имеется последовательность

σ1, . . . , σi, . . . , σ(n−1)! всех перестановок из Sn−1 такая, что каждая сле-

дующая σi+1 получается из предыдущей σi перестановкой двух сосед-

них элементов. Построим последовательность πi, i = 1, . . . , n! всех пере-

становок из Sn с таким же свойством. Расширим каждую перестановку

σi = (σi1, . . . , σ

in−1), последовательно вставляя элемент n на каждое из

n возможных мест: перед элементом σi1, между элементами σi

j и σij+1,

после элемента σin−1. При этом элемент n вставляем в σ1 в направлении

справа налево, а в σ2 — слева направо:

(σ11 , . . . , σ

1n−1, n), . . . , (n, σ

11 , . . . , σ

1n−1),

(n, σ21 , . . . , σ

2n−1), . . . , (σ

21 , . . . , σ

2n−1, n)

и т. д., при переходе от σi к σi+1 меняем направление вставки элемента

n на обратное. Тогда внутри i-го блока (n перестановок из Sn, постро-

енных из σi ∈ Sn−1) каждая следующая перестановка получается из

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

есть n. При переходе от последней перестановки в i-м блоке к первой

в следующем (i+ 1)-м блоке по индукционному предположению также

переставляются только два элемента. Таким образом, для построенной

последовательности перестановок πi ∈ Sn, i = 1, . . . , n! выполняется

требуемое свойство.37

Page 38: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

Пример 4. На рис. 1 приведено рекурсивное построение последова-

тельности всех перестановок из S4 в порядке минимального изменения.

(1)

(12)

(123)

(1234)

(1243)

(1423)

(4123)

(132)

(4132)

(1432)

(1342)

(1324)

(312)

(3124)

(3142)

(3412)

(4312)

(21)

(321)

(4321)

(3421)

(3241)

(3214)

(231)

(2314)

(2341)

(2431)

(4231)

(213)

(4213)

(2413)

(2143)

(2134)

Рис. 1. Рекурсивное построение перестановок из S4

Описанный рекурсивный алгоритм, генерирующий последователь-

ность перестановок из Sn, применённый непосредственно, имеет огром-

ный недостаток: последовательность перестановок строится "целиком"

и требует хранения всех перестановок из Sn−1, Sn−2, . . . Очевидно, такой

38

Page 39: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

алгоритм использовал бы огромный объём памяти, поэтому он непри-

меним. Мы изучим нерекурсивную модификацию этого метода — алго-

ритм Джонсона – Троттера.

Получим рассмотренный выше порядок перестановок n элементов

без явной генерации перестановок для меньших значений n. Это можно

сделать, связав с каждой компонентой πi перестановки π = (π1, . . . , πn)

её направление. Будем указывать направление при помощи стрелки →("вправо") или ← ("влево") над рассматриваемой компонентой пере-

становки. Перестановку π вместе с заданными направлениями её ком-

понент обозначим через −→π . Будем писать −→π = −→σ , если π = σ и направ-

ления соответствующих компонент совпадают. Если π ∈ Sn, то через

π \ n обозначим перестановку из Sn−1, получающуюся из π удалени-

ем элемента n. Для перестановки π ∈ Sn число m ∈ 1, . . . , n будем

называть кандидатом для перемещения, если стрелка над m в −→π ука-

зывает на меньшее соседнее число. Такое соседнее число будем назы-

вать дублёром числа m и обозначать m∗. Например, для перестанов-

ки π = (5, 3, 4, 2, 1) рассмотрим следующую расстановку направлений−→π = (

←−5 ,←−3 ,←−4 ,−→2 ,←−1 ). Тогда числа 4, 2 — кандидаты для перемещения,

а остальные 5, 3, 1 не являются кандидатами. Для числа 4 дублером

является число 3, а число 1 есть дублер числа 2.

В рассматриваемом алгоритме в перестановке −→π будем менять ме-

стами наибольший кандидат для перемещения m с его дублёром m∗.

В дальнейшем наибольший кандидат для перемещения в перестановке−→π будем просто называть переставляемым элементом.

Замечание 1. Для любой перестановки −→π число 1 не является кан-

дидатом для перемещения.

Замечание 2. Для перестановки −→π , π ∈ Sn число n не является

переставляемым элементом тогда и только тогда, когда первая компо-

нента −→π есть ←−n или последняя компонента −→π есть −→n .

39

Page 40: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

Перейдем к описанию алгоритма Джонсона – Троттера генерации

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

Алгоритм Джонсона – Троттера PMin(n)

−→π := (←−1 ,←−2 , . . . ,←−n );

m := 0;

while m = 1 do

begin

write(π1, . . . , πn);

m := n;

while (m не кандидат для перемещения в π and m > 1) do m := m−1;π: m↔ m∗; (считаем 1∗ = 1)−→π : над всеми элементами перестановки π, большими m, меняем

стрелку на противоположную по направлению

end.

Пример 5. Рассмотрим работу алгоритма PMin(n) при n = 3:−→π 1 = (

←−1 ,←−2 ,←−3 ), m = 3, m∗ = 2;

−→π 2 = (←−1 ,←−3 ,←−2 ), m = 3, m∗ = 1;

−→π 3 = (←−3 ,←−1 ,←−2 ), m = 2, m∗ = 1;

−→π 4 = (−→3 ,←−2 ,←−1 ), m = 3, m∗ = 2;

−→π 5 = (←−2 ,−→3 ,←−1 ), m = 3, m∗ = 1;

−→π 6 = (←−2 ,←−1 ,−→3 ), m = 1.

Лемма 3. Алгоритм PMin(n) корректен и строит все перестановки

из Sn без повторений в порядке минимального изменения.

Доказательство. Обозначим через −→π i последовательность чисел

πi = (πi1, . . . , π

in), полученную в результате i-й печати при работе алго-

ритма PMin(n) вместе с направлениями, определенными в этот момент

для компонент πi1, . . . , π

in. Поскольку −→π 1 = (

←−1 ,←−2 , . . . ,←−n ) и −→π i полу-

чается из −→π i−1 перестановкой двух соседних элементов, πi ∈ Sn для

всех i. Нам потребуется следующее свойство.40

Page 41: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

Лемма 4. Если −→π i имеет переставляемый элемент m = n, то опре-

делены перестановки −→π i+1, . . . ,−→π i+n, число n является переставляе-

мым элементом в перестановке −→π j при j = i + 1, . . . , i + n − 1 и не

является переставляемым элементом в перестановке −→π i+n. Кроме того,−→π i+1 \ n = −→π i+2 \ n = . . . = −→π i+n \ n.

Доказательство леммы 4. По замечанию 1 имеем m = 1. По за-

мечанию 2 перестановка −→π i имеет вид (←−n , . . .) или (. . . ,−→n ). В теле

внешнего while-цикла в перестановке −→π i элементы m, m∗ меняются

местами, и над всеми элементами, большими чем m, стрелка меняет-

ся на противоположную. Полученная перестановка есть −→π i+1. Так как

n > m > m∗, перестановка −→π i+1 имеет вид (−→n , . . .) или (. . . ,←−n ). По

замечанию 2 число n является переставляемым элементом в −→π i+1. Да-

лее снова по замечанию 2 число n будет переставляемым элементом

в −→π i+2, . . . ,−→π i+n−1. Поэтому определена перестановка −→π i+n, которая

будет иметь вид (. . . ,−→n ) или (←−n , . . .). Понятно, что направления всех

чисел в перестановках −→π i+1, . . . ,−→π i+n совпадают. Лемма 4 доказана.

Теперь индукцией по n покажем, что алгоритм PMin(n) корректен,

строит все перестановки из Sn без повторений, и для любого i = n! пере-

становка −→π i имеет переставляемый элемент, а −→π n! не имеет кандидата

для перемещения. Базис индукции n = 1 очевиден. Пусть для n− 1 всё

доказано. Рассмотрим работу алгоритмов PMin(n) и PMin(n−1) одно-

временно. В силу замечания 2 алгоритмом PMin(n) будут напечатаны

следующие n перестановок:

−→π 1 = (←−1 ,←−2 , . . . ,

←−−−n− 1,←−n ),

−→π 2 = (←−1 ,←−2 , . . . ,←−n ,

←−−−n− 1),

...

−→π n = (←−n ,←−1 ,←−2 , . . . ,

←−−−n− 1).

Так как−→π 1 \ n = . . . = −→π n \ n = (

←−1 ,←−2 , . . . ,

←−−−n− 1)

41

Page 42: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

есть первая напечатанная алгоритмом PMin(n−1) перестановка, по ин-

дукционному предположению −→π n \ n имеет переставляемый элемент

m1, при условии (n − 1)! = 1, и m1 = n. Тогда m1 — переставляемый

элемент в −→π n, и по лемме 4 определены следующие n перестановок−→π n+1, . . . ,−→π 2n ∈ Sn, причём

−→π n+1 \ n = . . . = −→π 2n \ n

является второй напечатанной алгоритмом PMin(n−1) перестановкой.

По индукционному предположению −→π 2n \ n имеет переставляемый

элемент m2, при условии (n − 1)! = 2, и перестановка −→π 2n имеет вид

(←−n , . . .) или (. . . ,−→n ) по лемме 4 и замечанию 2. Поэтому m2 является

переставляемым элементом в −→π 2n и m2 = n. В общем случае в i-м блоке

определены следующие n перестановок −→π (i−1)n+1, . . . ,−→π (i−1)n+n ∈ Sn,

−→π (i−1)n+1 \ n = . . . = −→π in \ n

есть i-я напечатанная алгоритмом PMin(n − 1) перестановка и −→π in

имеет переставляемый элемент mi = n, при условии (n − 1)! = i.

При i = (n − 1)! − 1 перестановка −→π in имеет переставляемый эле-

мент mn!−n = n. По лемме 4 определены следующие n перестано-

вок −→π n!−n+1, . . . ,−→π n! ∈ Sn, причём число n не является переставляе-

мым элементом в −→π n!. По индукционному предположению перестанов-

ка −→π n! \ n из Sn−1 не имеет кандидата для перемещения. Поэтому

перестановка −→π n! также не имеет кандидата для перемещения. Таким

образом, для любого i = n! перестановка −→π i имеет переставляемый эле-

мент, а−→π n! не имеет кандидата для перемещения. После печати переста-

новки −→π n! и выхода из внутреннего while-цикла переменная m примет

значение 1. Поэтому алгоритм PMin(n) остановит свою работу.

В результате работы алгоритма PMin(n) напечатанные перестанов-

ки πi, i = 1, . . . , n! разбиты на (n − 1)! блоков по n штук в каждом.

В любом i-м блоке перестановки различаются между собой положени-

ем числа n, причём πin \ n является i-й напечатанной перестановкой42

Page 43: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

при работе алгоритма PMin(n−1). По индукционному предположению

получаем πin \ n = πi′n \ n при i = i′. Следовательно, перестановки

в разных блоках различны. Таким образом, мы получили все n! пере-

становок из Sn без повторений. Лемма 4 доказана.

Перейдём к программной реализации алгоритма Джонсона – Трот-

тера. Чтобы задать перестановку с направлениями её компонент −→π ,

введём два массива π и τ , где π — собственно сама перестановка и τ

— массив из чисел −1,+1. Причём τi указывает направление числа πi:

+1 означает стрелку →, а −1 означает стрелку ← . Для формализа-

ции условия внутреннего while-цикла по числу m требуется найти его

дублёра m∗. Если мы знаем number(m), номер позиции числа m в пе-

рестановке π и направление τm числа m, то дублёр определяется про-

сто как m∗ = πnumber(m)+τm . Поэтому для хранения номера числа m

в перестановке π введём ещё один массив σ = (σ1, . . . , σn) такой, что

σi = number(i) — номер позиции числа i в перестановке π. Другими

словами, σ — обратная перестановка к π. Тогда для любого m имеем

πσm= m и πσm+τm = m∗. Условие, что m не кандидат для перемещения

в −→π , означает, что πσm+τm > m, за исключением двух ситуаций, когда

мы находимся в крайних позициях следующего вида:

π = (←−m, . . . ), σm = 1, τm = −1;π = (. . . ,−→m), σm = n, τm = +1.

Поэтому нужно доопределить значения π0 и πn+1 так, чтобы в этих

ситуациях число m не являлось кандидатом для перемещения. Напри-

мер, полагаем π0 = πn+1 = n + 1. Тогда π0 > m и πn+1 > m для для

любого m ≤ n.

В условии внутреннего while-цикла есть неравенство m > 1. Его

можно исключить, если при m = 1 произойдёт выход из этого цикла.

Это означает, что неравенство πσm+τm > m не должно выполняться

при m = 1. Так как πσ1 = 1, для этого достаточно определить τ1 = 0.

При этом ничего не нарушится, поскольку по замечанию 1 число 1 не

является кандидатом для перемещения в любой перестановке −→π .

43

Page 44: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

Алгоритм PMin(n) генерации всех перестановок

в порядке минимального изменения

for i = 1 to n do

begin

πi := i;

τi := −1;σi := i

end;

π0 := n+ 1;

πn+1 := n+ 1;

τ1 := 0;

m := 0;

while m = 1 do

begin

write(π1, . . . , πn);

m := n;

while πσm+τm > m do

begin

τm := −τm;

m := m− 1

end;

Swap(πσm , πσm+τm);

Swap(σm, σπσm+τm)

end.

Теорема 5. Алгоритм PMin(n) корректен и строит все пере-

становки из Sn без повторений в порядке минимального изменения

за время O(n!).

Доказательство. Ввиду леммы 3 остается только оценить слож-

ность T (n) алгоритма PMin(n). Из доказательства леммы 3 выте-

кает, что последовательность π1, π2, . . . , πn! всех перестановок из Sn,44

Page 45: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

напечатанных в ходе работы алгоритма PMin(n), разбивается на n-

элементные блоки Skn, k = 1, . . . , (n− 1)!. Введём обозначения:

• In — число сравнений, выполняемых в условии внутреннего while-

цикла во время работы алгоритма PMin(n);

• tin — число сравнений, выполняемых в условии внутреннего while-

цикла во время работы алгоритма PMin(n), начиная с печати

перестановки πi и до печати πi+1, i < n!;

• tn!n — число сравнений, выполняемых в условии внутреннего while-

цикла во время работы алгоритма PMin(n), начиная с печати

перестановки πn! и до окончания работы алгоритма.

Индукцией по n докажем, что

In =n∑

i=1

i!.

Действительно, базис индукции n = 1 очевиден. Пусть n ≥ 2 и для n−1

это равенство справедливо. Имеем

In =n!∑i=1

tin =

(n−1)!∑k=1

∑i:πi∈Sk

n

tin.

Из доказательства леммы 3 следует, что число n является перестав-

ляемым элементом в первых n − 1 перестановках каждого блока Skn,

πkn \n есть k-я напечатанная перестановка во время работы алгорит-

ма PMin(n−1), причём переставляемые элементы в перестановках πkn

и πkn \ n совпадают и не равны n. Следовательно, tknn = 1 + tkn−1 и

tin = 1 для любого i такого, что πi ∈ Skn и i = kn. Учитывая индукцион-

ное предположение, получаем

In =

(n−1)!∑k=1

(n− 1 + tknn ) =

(n−1)!∑k=1

(n+ tkn−1) = n! + In−1 =n∑

i=1

i!.

45

Page 46: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

Очевидно, T (n) = O(n) +O(In). Так как In =∑n

i=1 i! = n! + o(n!)13,

получаем T (n) = O(n!). Теорема 5 доказана.

Более детальное сравнение сложности линейных алгоритмов генера-

ции PLex(n) и PMin(n) показывает, что алгоритм PMin(n) быстрее.

Кроме того, алгоритм PMin(n) интересен тем, что перестановки выпи-

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

с порождаемой перестановкой в требуемом алгоритме необходимо про-

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

становки. В этом случае имеется возможность использовать результаты

вычислений, полученные для предыдущей перестановки, отличающейся

от следующей только лишь транспозицией соседних элементов.

2.Подмножества конечного множества

Пусть P(A) — множество всех подмножеств n-элементного множества

A = a0, a1, . . . , an−1. Мощность множества P(A) равна 2n. Существу-

ет биективное отображение f : P(A) → En из множества всех подмно-

жеств P(A) в n-мерный единичный куб E n,14 определяемое по правилу

f(B) = χB для произвольного подмножества B ⊆ A, где

χB = (χB0 , . . . , χ

Bn−1),

χBi =

1, если ai ∈ B,

0, если ai ∈ B.

Таким образом, задача генерации всех подмножеств заданного n эле-

ментного множества A сводится к задаче порождения всех n-разрядных

двоичных последовательностей. Также отметим, что генерация всех

двоичных векторов длины n по сути есть прохождение всех вершин

n-мерного единичного куба без повторений.

13 f(n) = o(g(n)), если limn→∞

f(n)g(n)

= 0.14 Здесь E n — множество вершин единичного куба, т. е. множество всех n-разряд-

ных двоичных последовательностей.

46

Page 47: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

Для любого целого положительного числа a существует его един-

ственное двоичное представление (bn−1bn−2 . . . b1b0) 2 = a, где bi опреде-

ляются из следующих соотношений:

a = b0 + b12 + b222 + . . .+ bn−12

n−1,

bi ∈ 0, 1, i = 0, . . . , n− 1.

Поэтому для любого a < 2n существует единственное двоичное n-раз-

рядное представление. Множество двоичных векторов длины n с лек-

сикографическим порядком (E n,≼) есть линейно упорядоченное мно-

жество с наименьшим элементом (0, 0, . . . , 0) и наибольшим элементом

(1, 1, . . . , 1), причём (bn−1 . . . b0) 2 есть номер вектора (bn−1, . . . , b0) от-

носительно этого упорядочивания. Поэтому все n-разрядные двоичные

последовательности можно сгенерировать следующим образом. Пробе-

гая значение индекса i от 0 до 2n − 1, по числу i восстанавливаем его

двоичное представление длины n, т. е. двоичный вектор с номером i в

лексикографическом порядке. Однако такой алгоритм генерации не бу-

дет линейным, так как время восстановления двоичного представления

длины n не ограничено константой. Поэтому требуется другой алгоритм

генерации двоичных векторов.

2.1. Генерация двоичных векторов и подмножеств

Будем порождать все двоичные векторы длины n в лексикографиче-

ском порядке, начиная с наименьшего элемента. Как перейти от задан-

ного вектора b = (bn−1, . . . , b0) к непосредственно следующему вектору

в лексикографическом порядке? Просматривая справа налево вектор b,

ищем самую правую позицию i такую, что bi = 0. Запишем в эту i-ю

позицию 1, а все элементы bj , j < i, стоящие справа от bi, полагаем

равными 0. Очевидно, что мы получим требуемый вектор.

Для программной реализации этого алгоритма дополним массив b =

(bn−1, . . . , b0) элементом bn. При инициализации полагаем bn = 0. Для

всех порождаемых последовательностей при поиске правой позиции i

47

Page 48: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

элемент bn не изменяется, за исключением ситуации, когда генерируется

последний вектор (1, 1, . . . , 1), i = n и bn станет равным 1. Поэтому

равенство bn = 1 будет условием остановки алгоритма. Программная

реализация данного алгоритма приведена в следующем псевдокоде.

Алгоритм V Lex(n) генерации всех

двоичных векторов длины n

for i = 0 to n do bi := 0;

while bn = 1 do

begin

write (bn−1, bn−2, . . . , b0);

i := 0;

while bi = 1 do

begin

bi := 0;

i := i+ 1

end;

bi := 1

end.

Оценим время T (n) работы алгоритма V Lex(n). Пусть Wn — число

проверок условия внутреннего while-цикла и Wni — число проверок ра-

венства bi = 1. Очевидно, что T (n) = O(n) +O(Wn) и Wn =∑n

i=0 Wni .

Проверка условия bi = 1 осуществляется тогда и только тогда, когда

cj = 1 для любого j < i, где (cn−1, cn−2, . . . , c0) — последний вектор,

выведенный на печать перед проверкой этого условия. Следовательно,

Wni = |(cn−1, cn−2, . . . , c0) ∈ E n| ∀j < i cj = 1| = 2n−i,

Wn =n∑

i=0

2n−i = 2n+1 − 1 = O(2n).

Поэтому T (n) = O(n) + O(2n) = O(2n). Таким образом, алгоритм

V Lex(n) генерации двоичных векторов линеен.48

Page 49: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

Учитывая биекцию f : P(A) → En, перевод алгоритма V Lex(n)

на язык подмножеств множества a0, a1, . . . , an−1 приводит к следу-

ющему алгоритму генерации всех подмножеств (здесь дополнительно

рассматривается фиктивный элемент an ∈ a0, . . . , an−1).

Алгоритм SLex(n) генерации подмножеств

n-элементного множества a0, . . . , an−1

B := Ø;

while an ∈ B do

begin

write(B);

i := 0;

while ai ∈ B do

begin

B := B \ ai;i := i+ 1

end;

B := B ∪ aiend.

Пример 6. Генерация двоичных векторов bi длины n = 3 и под-

множеств Bi множества A = a0, a1, a2, i = 1, . . . , 8.

b1 = (0, 0, 0), B1 = Ø, i = 0;

b2 = (0, 0, 1), B2 = a2, i = 1;

b3 = (0, 1, 0), B3 = a1, i = 0;

b4 = (0, 1, 1), B4 = a1, a2, i = 2;

b5 = (1, 0, 0), B5 = a0, i = 0;

b6 = (1, 0, 1), B6 = a0, a2, i = 1;

b7 = (1, 1, 0), B7 = a0, a1, i = 0;

b8 = (1, 1, 1), B8 = A, i = 3.

49

Page 50: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

2.2.Коды Грея и алгоритм их генерации

В этом разделе мы познакомимся с алгоритмом генерации всех n-

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

смотренные алгоритмы V Lex(n), SLex(n) порождения двоичных век-

торов и подмножеств не обладают этим свойством (см. пример 6), так

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

гому соответствуют добавлению или удалению ровно одного элемента,

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

векторы должны отличаться в одном разряде. Такие наборы двоичных

векторов известны как коды Грея.

Определение 9. n-разрядным кодом Грея называется упорядочен-

ная последовательность 2n различных двоичных n-разрядных векто-

ров (кодовых слов), последовательные векторы которой различаются в

одном разряде. Код Грея называется циклическим, если его первый и

последний векторы также различаются в одном разряде.

В этих терминах задача генерации всех n-разрядных двоичных век-

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

разрядного кода Грея. Очевидно, что всякий n-разрядный код Грея

определяет гамильтонов путь в n-мерном единичном кубе E n, и на-

оборот, всякий гамильтонов путь в E n задаёт n-разрядный код Грея.

Аналогичное соответствие имеется между циклическим кодом Грея и

гамильтоновым циклом. Единичный куб E n гамильтонов при n ≥ 2.

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

тивно: в двух копиях E n, разбивающих куб E n+1, выбираются такие

циклы и достраиваются до требуемого цикла (n+1)-мерного куба. Эти

рассуждения показывают существование различных (с точностью до

циклических сдвигов) циклических кодов Грея при n ≥ 3.

Рассмотрим другой способ задания циклического кода Грея. Ввиду

цикличности начальным кодовым словом считаем вектор (0, 0, . . . , 0).

50

Page 51: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

Определение 10. Пусть G(n) = G0, G1, . . . , G2n−1 — циклический

n-разрядный код Грея, ti — номер позиции (считая справа налево), ме-

няющейся при переходе от кодового слова Gi−1 к Gi, i = 1, . . . , 2n − 1.

Последовательность Tn = t1, t2, . . . , t2n−1 называется последовательно-

стью переходов кода Грея G(n).

Например, для циклического кода Грея G(2) = 00, 10, 11, 01 последо-

вательность переходов есть 2, 1, 2. Очевидно, что циклический код Грея

однозначно определяется по своей последовательности переходов.

Рассмотрим двоично-отражённые коды Грея G(n), определяемые

рекурсивным образом:

G(1) = 0, 1

G(n+ 1) = 0G0, 0G1, . . . , 0G2n−1, 1G2n−1, . . . , 1G1, 1G0, где

G(n) = G0, G1, . . . , G2n−1.

Очевидно, что так определенная последовательность G(n) является

циклическим n-разрядным кодом Грея. Докажем, что последователь-

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

T1 = 1

Tn+1 = Tn, n+ 1, Tn.

Действительно, из определения G(n) имеем T1 = 1 и Tn+1 = Tn, n+1, T ∗n ,

где T ∗n получается переворачиванием последовательности Tn. Очевидно,

что T ∗1 = T1 и T ∗

n = (T ∗n−1)

∗, n, T ∗n−1 = Tn. Поэтому последовательность

Tn+1 имеет требуемый вид.

Известно другое рекурсивное определение тех же кодов Грея G(n):

G(1) = 0, 1; G(n + 1) = G00, G01, G11, G10, G20, G21, . . . , Gn1, Gn0 и

последовательности его переходов T1 = 1, Tn+1 = 1, t1 + 1, 1, t2 + 1,

1, . . . , 1, t2n−1 + 1, 1, где Tn = t1, t2, . . . , t2n−1 (иными словами, все ком-

поненты Tn увеличиваются на 1, а затем 1 вставляется в начало, в конец

и между элементами полученной последовательности).

51

Page 52: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

Перейдём к алгоритму генерации двоично-отражённого кода Грея

G(n). На основе его индуктивного определения очевиден рекурсивный

алгоритм порождения G(n). Но такой алгоритм будет использовать

память, необходимую для хранения G(n − 1), G(n − 2), . . . Нам тре-

буется алгоритм, последовательно порождающий элементы кода G(n)

и использующий лишь ограниченный объём памяти. Для генерации

G(n) достаточно эффективно порождать его последовательность пере-

ходов Tn = t1, t2, . . . , t2n−1, так как, зная предыдущее кодовое слово

Gi = (gngn−1 . . . g1), можно перейти к Gi+1, просто полагая gti := gti .15

Теперь построим алгоритм порождения последовательности перехо-

дов Tn. Будем использовать стек16 An. Определим k-е состояние стека

Akn. В пустой стек последовательно добавим числа n, n−1, . . . , 2, 1. Полу-

ченное состояние есть A1n, и число 1 является верхним элементом стека.

Далее при переходе от k-го состояния к (k + 1)-му состоянию удалим

верхний элемент i из стека Akn, и если i = 1, то последовательно доба-

вим элементы i− 1, i− 2, . . . , 1. Получим (k+1)-е состояние стека Ak+1n .

Верхний элемент стека An при его k-ом состоянии обозначим через akn.

Индукцией по n нетрудно доказать следующие свойства.

Лемма 5.

(i) Akn = Ø и akn+1 = akn = n+ 1 для всех k < 2n;

(ii) A2n−1n содержит единственное число 1;

(iii) A2n

n = Ø и a2n

n+1 = n+ 1;

(iv) в непустом стеке Akn все элементы расположены в порядке стро-

гого возрастания сверху вниз;

(v) Tn = a1n, a2n, . . . , a

2n−1n .

15 f(n) = n — булева функция "отрицание".16 Стек (англ. stack — стопка) — структура данных, в которой организован после-

довательный доступ к элементам по правилу "первым пришёл — первым ушёл". До-

бавление элемента, называемое проталкиванием, возможно только в вершину стека

(добавленный элемент становится первым сверху). Удаление элемента, называемое

выталкиванием, также возможно только из вершины стека, при этом второй сверху

элемент становится верхним. Доступ имеется только к верхнему элементу стека.

52

Page 53: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

В силу леммы 5(v) последовательность выталкиваемых элементов

akn, k = 1, . . . , 2n − 1 порождает последовательность переходов Tn

двоично-отражённого кода Грея G(n), причем для каждого состояния,

за исключением k-го при k = 2n, стек An будет непустым в силу свойств

(i) и (iii) леммы 5. Поэтому равенство Akn = Ø служит условием окон-

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

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

элемента. Для этого перейдём к стеку An+1. В силу леммы 5 процесс

выталкиваний элементов akn+1 из стека An+1 до тех пор, пока akn+1 не

станет равен n+ 1, порождает ту же последовательность Tn.

Далее, мы стремимся получить линейный алгоритм. Это означает,

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

следующего элемента из последовательности переходов, должно быть

ограничено константой, не зависящей от n. В рассмотренном процессе

число изменяемых элементов в стеке An+1 зависит от значения верх-

него выталкиваемого элемента, т. е. оно непостоянно. Эту зависимость

можно устранить с помощью перехода от стека An+1 к такому масси-

ву B, в котором его k-е состояние Bk будет полностью соответствовать

состоянию стека Akn+1, а при смене состояний будет изменяться толь-

ко фиксированное число элементов. Воспользуемся свойством (iv) лем-

мы 5 и вместо записи самих элементов в стек в массив будем записы-

вать указатели на нижеследующие числа стека. Именно, введём массив

B = (b0, b1, . . . , bn) и определим его k-е состояние Bk = (bk0 , . . . , bkn) при

k ≤ 2n следующим образом. Полагаем bk0 = akn+1 и для j = 1, . . . , n

пусть bkj равен элементу стека Akn+1, находящемуся под числом j, если

j есть в стеке Akn+1, в противном случае полагаем bkj = j + 1. Заме-

тим, что элемент bkj определён корректно, так как ниже любого числа

j ≤ n из стека всегда есть следующий элемент (по крайней мере, n+ 1

находится на дне стека Akn), и число j входит в стек не более одного

раза в силу леммы 5(iv). Теперь нетрудно доказать, что при переходе

от k-го состояния стека Akn+1 к его (k+1)-му состоянию Ak+1

n+1 состояние

53

Page 54: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

массива Bk изменяется по следующему правилу:

bk+10 =

bki , если i = 1,

1, если i = 1;

bk+1j =

j + 1, если i = j = 1,

bkj , если i = 1 и j = 1,

bkj , если i = 1 и i < j ≤ n,

j + 1, если i = 1 и j = i,

bki , если i = 1 и j = i− 1,

j + 1 = bkj , если i = 1 и 1 ≤ j < i− 1;

i = bk0 = akn+1.

Таким образом, мы приходим к следующему алгоритму.

Алгоритм TranSeq(n) генерации последовательности

переходов двоично-отражённого кода Грея G(n)

for i = 0 to n do bi := i+ 1;

i := 0;

while i = n+ 1 do

begin

write(b0);

i := b0;

b0 := 1;

bi−1 := bi;

bi := i+ 1

end.

Лемма 6. Алгоритм TranSeq(n) корректен и строит последова-

тельность переходов двоично-отражённого кода Грея G(n).

54

Page 55: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

Доказательство. В силу леммы 5 последовательность Tn порож-

дается в результате выталкивания верхних элементов akn+1 стека An+1

до тех пор, пока akn+1 = n+ 1. Элемент akn+1 есть первый элемент k-го

состояния Bk массива B = (b0, . . . , bn). Причём B1 = (1, 2, . . . , n + 1) и

Bk+1 определяется через Bk с помощью вышеуказанной формулы для

bk+1j . Эта формула показывает, что в массиве B только три элемента

изменяются по следующему правилу:

bk+10 =

bki , если i = 1,

1, если i = 1;

bk+1i−1 = bki , если i = 1; bk+1

i = i+ 1; i = bk0 ,

что соответствует выполнению операторов тела while-цикла.

Лемма 6 доказана.

Алгоритм V min(n) генерации

двоично-отражённого кода Грея G(n)

for i = 0 to n− 1 do gi := 0;

for i = 0 to n do bi := i+ 1;

i := 0;

while i = n+ 1 do

begin

write(gn, gn−1, . . . , g1);

i := b0;

gi := gi;

b0 := 1;

bi−1 := bi;

bi := i+ 1

end.

Теорема 6. Алгоритм V min(n) корректен и строит двоично-отра-

жённый код Грея G(n) за время O(2n).

55

Page 56: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

Доказательство. Корректность алгоритма следует из леммы 6.

Так как код Грея G(n) содержит 2n кодовых слов, и число операций в

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

вого слова, ограничено константой, не зависящей от n, данный алгоритм

является линейным. Теорема 6 доказана.

3. Генерация сочетанийв лексикографическом порядке

В предыдущих разделах мы рассмотрели алгоритмы генерации всех

подмножеств заданного n-элементного множества. Часто в задачах воз-

никает необходимость генерировать не все подмножества, а лишь те,

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

щих ограничений является мощность подмножеств. Познакомимся с ал-

горитмом генерации всех подмножеств фиксированной мощности k.

Определение 11. Сочетанием из n элементов по k называется

неупорядоченная выборка k элементов из заданных n элементов.

Мы будем генерировать все сочетания из n по k для заданного n-

элементного множества A. Число сочетаний из n по k равно биноми-

альному коэффициенту

Ckn =

n!

k!(n− k)!.

Поэтому лучшая сложность, которую можно ожидать для алгоритма ге-

нерации всех сочетаний, есть O(Ckn). Без ограничения общности можно

предполагать A = 1, 2, . . . , n. Произвольное сочетание из n по k удоб-

но представить в виде конечной последовательности длины k из чисел,

упорядоченных по возрастанию слева направо. Все такие сочетания, как

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

рядке. Например, при n = 5 и k = 3 последовательность всех сочетаний

в лексикографическом порядке следующая: 123, 124, 125, 134, 135, 145,

234, 235, 245, 345.56

Page 57: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

Очевидно, что первый элемент в лексикографическом порядке есть

сочетание (1, 2, . . . , k), а последний — (n− k + 1, n− k + 2, . . . , n− 1, n).

Остаётся определить по данному сочетанию a = (a1, . . . , ak) вид непо-

средственно следующего сочетания b = (b1, . . . , bk). Такое сочетание b

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

торый ещё не достиг своего возможного максимального значения, его

увеличения на 1, а затем присвоения всем элементам справа от этого

элемента новых возможных наименьших значений. Для этого необхо-

димо найти самый правый элемент am такой, что чисел больших, чем

am + 1 найдётся по крайней мере k −m штук. Таким образом,

b = (a1, . . . , am−1, am + 1, am + 2, . . . , am + k −m+ 1), где m = m(a);

m(a) = maxi | ai < n− k + i, 1 ≤ i ≤ k.

Очевидно, что число m(a) определено тогда и только тогда, когда со-

четание a не является наибольшим относительно лексикографического

порядка. Предположим, что b не наибольшее сочетание, и вычислим

m(b). Из определения m(b) следует, что если bk < n, то m(b) = k. Пусть

теперь bk = n. Тогда bk−1 = n− 1, . . . , bm = n− k +m. Но bm = am + 1,

поэтому bm−1 = am−1 < am = bm − 1 = n − k +m − 1. Следовательно,

m(b) = m− 1 = m(a)− 1. Таким образом, доказана следующая

Лемма 7. Пусть a = (a1, . . . , ak), b = (b1, . . . , bk) — сочетания из n

по k, упорядоченные по возрастанию слева направо. Если b непосред-

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

bi =

ai, если 1 ≤ i < m(a),

am(a) + i−m(a) + 1, если m(a) ≤ i ≤ k,

причём если b не является наибольшим сочетанием, то

m(b) =

m(a)− 1, если bk = n,

k, если bk < n.

57

Page 58: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

Алгоритм CLex(n) генерации сочетаний из n по k

в лексикографическом порядке

for i = 1 to k do ai := i;

if k = n then m := 1

else m := k;

while m = 0 do

begin

write(a1, . . . , ak);

if ak = n then m := m− 1

else m := k;

if m = 0 then for i = m to k do ai := am + i−m+ 1

end.

Теорема 7. Алгоритм CLex(n) корректен и генерирует все соче-

тания из n по k без повторений в лексикографическом порядке за время

n+ 1

n+ 1− kCk

n O(1).

Доказательство. В случае k = n имеем единственное сочетание

a = (1, 2, . . . , k) из n по n, причём ak = n. При инициализации значение

m равно 1. Поэтому после печати сочетания a в теле while-цикла имеем

m = 0, и, следовательно, алгоритм закончит работу.

Далее считаем k < n. В силу леммы 7 для обоснования корректно-

сти алгоритма достаточно заметить, что при инициализации значение

m равно m(1, . . . , k) = k, и после печати наибольшего сочетания b ал-

горитм останавливается. Обозначим через a сочетание (n− k, n− k+2,

. . . , n − 1, n), непосредственно предшествующее b. Тогда m(a) = 1 и

bk = n. Следовательно, после печати сочетания b переменная m примет

значение 0, и алгоритм закончит работу.

Оценим время работы алгоритма CLex(n). Очевидно, что число ите-

раций while-цикла равно Ckn + 1. Поэтому сложность алгоритма есть

58

Page 59: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

k + Ckn O(1) + In,k O(1), где In,k — число итераций внутреннего for-

цикла. Подсчитаем In,k. Сначала заметим, что для любого сочетания

a = (a1, . . . , ak), не являющегося наибольшим, m(a) = maxi | ai =n − k + i, 1 ≤ i ≤ k. Это равенство следует из возрастания элемен-

тов ai слева направо. Рассмотрим разбиение A0, A1, . . . , Ak множества

всех сочетаний из n по k, где

A0 = (n− k + 1, . . . , n− 1, n),

Aj = a = (a1, . . . , ak) | a — сочетание и m(a) = j, 1 ≤ j ≤ k.

После печати произвольного сочетания a ∈ A0 внутренний for-цикл вы-

полняется k−m(a)+1 раз, а для наибольшего сочетания такой цикл не

выполняется. Следовательно, In,k =∑k

j=1 |Aj |(k−j+1). Для любого со-

четания a ∈ Aj имеем aj < n−k+j, aj+1 = n−k+j+1, . . . , an−1 = n−1,an = n. Следовательно, |Aj | = Cj

n−k+j−1 и

In,k =k∑

j=1

Cjn−k+j−1(k − j) + Ck

n − 1,

Далее, используя свойства биномиальных коэффициентов, получаем

k∑j=1

Cjn−k+j−1(k − j) =

k∑i=0

Ck−in−i−1 − k =

= kCkn −

k−1∑i=0

(k − i)Ck−in−i−1 − k =

= kCkn − (n− k)

k−1∑i=0

Ck−i−1n−i−1 − k =

= kCkn − (n− k)Ck−1

n − k =k

n+ 1− kCk

n − k.

Таким образом, справедливо равенство

In,k =n+ 1

n+ 1− kCk

n − k − 1.

Так как Ckn ≤ Ck

n (n+ 1)/(n+ 1− k), время работы алгоритма CLex(n)

есть O(1)Ckn (n+ 1)/(n+ 1− k). Теорема 7 доказана.

59

Page 60: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

Заметим, что (n + 1)/(n + 1 − k) < 2 при k ≤ (n/2), и следователь-

но алгоритм CLex(n) генерации сочетаний является линейным. Однако

(n+1)/(n+1−k) = O(n) при n−k = o(n). В этом случае можно приме-

нить алгоритм CLex(n) для генерации сочетаний из n по n− k, а затем

полученные сочетания "дополнить" до сочетаний из n по k.

Глава 3Генерация случайных комбинаторныхобъектов

Рассмотрим конечную совокупность Ξ комбинаторных объектов задан-

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

чайную величину ξ, принимающую значения из Ξ. Считаем, что задана

вероятностная мера. Поэтому для любого объекта σ ∈ Ξ определена ве-

роятность P(ξ = σ), а следовательно, и дискретная функция распреде-

ления случайной величины ξ. Алгоритм, на выходе которого случайным

образом получается некоторый комбинаторный объект из Ξ, задаёт слу-

чайную величину со значениями из Ξ. Общая задача состоит в постро-

ении алгоритма генерации случайного комбинаторного объекта ξ ∈ Ξ

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

равномерные распределения, и сгенерированный случайный объект с

равномерным распределением называем случайным объектом. Равно-

мерное распределение в этом случае означает равновероятноcть полу-

чения любого комбинаторного объекта из Ξ, т. е. P(ξ = σ) = 1/|Ξ| для

каждого объекта σ ∈ Ξ.

1.Алгоритм построения случайнойперестановки

Задача генерации таких случайных комбинаторных объектов, как слу-

чайный пароль заданной длины, случайный лабиринт, случайная кон-

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

становки (либо случайной перестановки с какими-либо ограничениями).60

Page 61: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

Как для заданного n-элементного множества A получить случай-

ную перестановку? Случайность в данном случае означает равноверо-

ятность получения любой из n! возможных перестановок множества A.

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

целое число от 0 до n!−1 и по нему восстановить саму перестановку. Од-

нако это восстановление потребует порядка n2 операций. Такой подход

неэффективен, так как случайную перестановку можно сгенерировать

за линейное время O(n).

Определим линейный алгоритм генерации случайной перестановки

множества A. Начиная с произвольной перестановки (a1, . . . , an) мно-

жества A, построим последовательность перестановок αn+1, αn, . . . , α1,

где αn+1 = (a1, . . . , an) и каждая αi получается из перестановки αi+1 =

(αi+11 , . . . , αi+1

n ) перестановкой i-го и k-го элементов перестановки αi+1.

Причём k выбирается как значение функции rand(1, i), порождающей

случайное целое число из интервала [1, i] с равномерным распределени-

ем. Таким образом, мы определяем

αn+1 = (a1, . . . , an);

αn : αn+1n ↔ αn+1

kn, kn = rand(1, n);

αn−1 : αnn−1 ↔ αn

kn−1, kn−1 = rand(1, n− 1);

...α1 : α2

1 ↔ α2k1, k1 = rand(1, 1) = 1.

Формализуем этот алгоритм в виде следующего простого псевдокода.

Алгоритм PRand(n) генерации случайной

перестановки (α1, . . . , αn) множества a1, . . . , an

for i = 1 to n do αi := ai;

for i = n downto 1 do

begin

k := rand(1, i);

Swap(αi, αk)

end.61

Page 62: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

Теорема 8. Алгоритм PRand(n) строит случайную перестановку

множества a1, . . . , an за время O(n).

Доказательство. Очевидно, что алгоритм PRand(n) требует по-

рядка n операций. Индукцией по n докажем, что для любой начальной

перестановки (a1, . . . , an) множества A на выходе алгоритма PRand(n)

равновероятно получение любой из n! перестановок множества A.

Базис индукции при n = 1 очевиден. Пусть утверждение справед-

ливо для n − 1. Полагаем αn+1 = (a1, . . . , an) и через αi = (αi1, . . . , α

in)

обозначим содержимое массива α после выполнения итерации второго

for-цикла, соответствующей значению i. Тогда α1 есть результат рабо-

ты алгоритма PRand(n). Так как αi получается из αi+1 перестановкой

двух элементов, для любого i = n+ 1, n, . . . , 1 имеем αi ∈ S(a1, . . . , an),

где S(a1, . . . , an) — множество всех перестановок множества A. Сле-

довательно, α1 ∈ S(a1, . . . , an). Рассмотрим произвольную переста-

новку β = (β1, . . . , βn) ∈ S(a1, . . . , an). Пусть α′ = (α11, . . . , α

1n−1) и

β′ = (β1, . . . , βn−1). Тогда

P(α1 = β) = P(α1n = βn &α′ = β′) = P(α1

n = βn) P(α′ = β′ |α1

n = βn)17.

Из алгоритма понятно, что αnn = αn−1

n = . . . = α1n. Поэтому α′

есть результат работы алгоритма PRand(n − 1) генерации случай-

ной перестановки множества αn1 , . . . , α

nn−1 с начальной перестановкой

(αn1 , . . . , α

nn−1). Если предположить α1

n = βn, то β′ ∈ S(αn1 , . . . , α

nn−1).

Тогда по индукционному предположению имеем

P(α′ = β′ |α1n = βn) =

1

(n− 1)!.

Далее, существует индекс m такой, что βn = am. Следовательно,

P(α1n = βn) = P(αn

n = βn) = P(arand(1,n) = βn) =

= P(arand(1,n) = am) = P(rand(1, n) = m) =1

n.

17 P(A|B) — условная вероятность события A при условии события B.

62

Page 63: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

Таким образом, получаем

P(α1 = β) =1

n!

1

(n− 1)!=

1

n!.

Теорема 8 доказана.

2.Алгоритм генерации случайногоподмножества и сочетания

Пусть A = a0, . . . , an−1 — n-элементное множество. Задача генера-

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

ной двоичной последовательности длины n. Такая последовательность

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

чайных целых чисел bi из интервала [0, 1] при выполнении цикла

for i = 0 to n− 1 do bi = rand(0, 1).

Последовательность (b0, . . . , bn−1) будет случайной. Действительно, для

произвольного двоичного вектора c = (c0, . . . , cn−1) имеем

P(b = c) = P(b0 = c0 & . . .& bn−1 = an−1) =

=n−1∏i=0

P(bi = ci) =1

2n=

1

|P(A)|.

Сложность описанной процедуры генерации случайного подмножества

множества A есть O(n).

Рассмотрим задачу генерации случайного сочетания из n элементов

множества A по k. Требуется построить случайное k-элементное под-

множество B ⊆ A. Случайным образом выберем первый элемент ar1 ∈A, затем случайным образом выберем второй элемент ar2 ∈ A \ ar1из оставшихся n − 1 элементов и продолжим этот процесс до тех пор,

пока не выберем ark ∈ A \ ar1 , . . . , ark−1. Очевидно, полученное под-

множество B = ar1 , . . . , ark является сочетанием из n по k. Пусть

B′ = b′1, . . . , b′k — произвольное сочетание. Вычислим вероятность

P(B = B′). Обозначим E1 = ⟨ ar1 ∈ B′ ⟩, E2 = ⟨ ar2 ∈ B′ | ar1 ∈ B′ ⟩, . . . ,63

Page 64: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

Ek = ⟨ ark ∈ B′ | ar1 , . . . , ark−1∈ B′ ⟩. Тогда

P(B = B′) = P(ar1 ∈ B′ & . . . & ark ∈ B′) =

= P(E1) P(E2) · · · P(Ek) =

=k

n

k − 1

n− 1· · · 1

n− k + 1=

1

Ckn

.

Таким образом, в процессе описанной процедуры равновероятно полу-

чение любого сочетания из n по k.

Перейдём к программной реализации требуемого алгоритма. Исход-

ное множество зададим массивом A = (a1, . . . , an) и случайное сочета-

ние из n по k будем формировать на первых k местах массива A. При

этом мы сохраним исходное множество a1, . . . , an, но порядок элемен-

тов в массиве A будет изменён. Предположим, что в (j−1)-м состоянии

массива A первые j − 1 элементов aj−11 , . . . , aj−1

j−1 являются элементами

требуемого сочетания B, а aj−1j , . . . , aj−1

n есть оставшиеся элементы мас-

сива A. Среди этих оставшихся элементов случайным образом выберем

элемент aj−1i , j ≤ i ≤ n и поменяем местами aj−1

i и aj−1j . Тем самым

придём к j-му состоянию массива A. Через k шагов получим требуемое

сочетание B = (ak1 , . . . , akk).

Алгоритм генерации случайного k-элементного сочетания

n-элементного множества A = (a1, . . . , an) на первых k местах

for j = 1 to k do

begin

i := rand(j, n);

Swap(ai, aj)

end.

Сложность данного алгоритма есть O(k).

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

тов исходного множества A, рассмотрим дополнительный массив M =

(m1, . . . ,mn). В M будем записывать номера элементов множества A.64

Page 65: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

Вместо перестановки элементов ai и aj , j ≤ i ≤ n массива A в соот-

ветствии с описанным алгоритмом переставляем номера mi и mj этих

элементов. При этом достаточно изменять только номер mi с большим

индексом i. Требуемое k-элементное сочетание записываем в массив

B = (b1, . . . , bk). Программная реализация данного алгоритма приве-

дена в следующем простом псевдокоде.

Алгоритм генерации случайного k-элементного сочетания

B = (b1, . . . , bk) n-элементного множества A = (a1, . . . , an)

for j = 1 to n do mj := j;

for j = 1 to k do

begin

i := rand(j, n);

bj := ami ;

mi := mj

end.

Глава 4Разбиения чисел и множеств

В этой главе мы рассмотрим линейные алгоритмы генерации разбиений

числа n и разбиений n-элементного множества.

1.Упорядоченные и неупорядоченныеразбиения числа n

Определение 12. Разбиением целого положительного числа n на-

зывается представление n в виде суммы n = x1 + x2 + . . . + xk целых

положительных чисел xi, i = 1, . . . , k.

Если дополнительно фиксируется порядок слагаемых x1, . . . , xk, по-

следовательность (x1, . . . , xk) называется упорядоченным разбиением

65

Page 66: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

числа n. Существует взаимно-однозначное соответствие между упоря-

доченными разбиениями числа n на k слагаемых и (k−1)-элементными

подмножествами (n− 1)-элементного множества, именно упорядочива-

нию соответствует расстановка k − 1 пробелов в n − 1 промежутках

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

доченных разбиений на k слагаемых есть Ck−1n−1. Следовательно, число

всех упорядоченных разбиений числа n равно∑n

k=1 Ck−1n−1 = 2n−1, и су-

ществует взаимно-однозначное соответствие между всеми упорядочен-

ными разбиениями числа n и всеми подмножествами (n−1)-элементного

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

ний числа n на k слагаемых сводится к уже решённой задаче генерации

сочетаний из n − 1 по k − 1, а задача генерации всех упорядоченных

разбиений числа n сводится к решённой задаче генерации всех подмно-

жеств (n− 1)-элементного множества.

Теория неупорядоченных разбиений значительно сложнее. Напом-

ним, что при неупорядоченном разбиении два разбиения числа n счи-

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

пример, для n = 4 разбиения 2 + 1 + 1, 1 + 2 + 1, 1 + 1 + 2 равны.

Класс равных неупорядоченных разбиений числа n однозначно зада-

ется последовательностью (a1, . . . , ak) такой, что n = a1 + . . . + ak и

a1 ≥ a2 ≥ . . . ≥ ak > 0, k ≥ 1. В дальнейшем будем рассматривать толь-

ко неупорядоченные разбиения, принимая эту интерпретацию. Обозна-

чим число разбиений числа n на k слагаемых через pk(n), а число всех

разбиений числа n через p(n). Очевидно, p(n) =∑n

k=1 pk(n). Для числа

p(n) известно следующее асимптотическое выражение18:

p(n) ∼ eπ√

2n/3

4n√3

19.

18 Получено G.H.Hardy и S.Ramanujan [8, т. 4; 21].19 f(n) ∼ g(n), если lim

n→∞f(n)g(n)

= 1.

66

Page 67: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

2. Генерация разбиений числа nв словарном порядке

На множестве целочисленных последовательностей определим словар-

ный порядок ≼ 20. Пусть a = (a1, . . . , aq) и b = (b1, . . . , bs) — произволь-

ные целочисленные последовательности, возможно разной длины.

Определение 13. (a1, . . . , aq) ≼ (b1, . . . , bs), если выполняется хотя

бы одно из условий: либо q ≤ s и ai = bi для любого i ≤ q, либо

существует p ≤ min(s, q) такое, что ap < bp и ai = bi для всех i < p.

Бинарное отношение ≼ является линейным порядком. Для разбиений

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

Замечание 3. Если a, b — разбиения числа n, то

a ≺ b⇔ ∃ p ≤ min(s, q) (ap < bp & ∀ i < p (ai = bi)).

Разбиения числа n будем порождать в словарном порядке. Ясно, что

первым разбиением (наименьшим элементом) в словарном порядке бу-

дет последовательность (1, 1, . . . , 1) длины n, а последним (наибольшим

элементом) — одноэлементная последовательность (n). Выясним вид

разбиения, непосредственно следующего за разбиением a = (a1, . . . , aq)

в словарном порядке. Найдём такую самую правую позицию p < q, в

которой число ap можно увеличить на 1, сохранив свойство убывания

последовательности. Далее сумму(∑q

i=p+1 ai − 1)

оставшихся слагае-

мых за вычетом 1 представим в виде суммы единиц. Нетрудно доказать,

что так определенное разбиение b непосредственно следует за a.

При переходе от a к b число необходимых изменений над последова-

тельностью a есть величина переменная, зависящая от n. Эту зависи-

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

ения a = (a1, . . . , aq). Упорядочим все различные числа ai1 , . . . , aik среди20 Сравните с лексикографическим порядком.

67

Page 68: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

a1, . . . , aq в порядке убывания ai1 > . . . > aik . Пусть mj — число вхожде-

ний aij в разбиение a и m = (m1, . . . ,mk). Ясно, что разбиение a числа

n однозначно определяется парой последовательностей (ai1 , . . . , aik) и

(m1, . . . ,mk). Поэтому в дальнейшем всякое разбиение a числа n будем

записывать в виде a = (m1 ·a1, . . . ,mk ·ak), где a1 > . . . > ak > 0, mi > 0,

i = 1, . . . , k, 1 ≤ k ≤ n и n =∑ k

i=1 miai. Элемент mi · ai представляет

собой последовательность ai, ai, . . . , ai длины mi и называется блоком

разбиения. Очевидно, что разбиение a является наименьшим при k = 1

и mk = n, а наибольшим при k = mk = 1. Такое представление разбие-

ний числа n исключает необходимость поиска позиции p при просмотре

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

для перестроения разбиения a в непосредственно следующее разбиение

b потребуется изменить не более двух блоков.

Лемма 8. Пусть a = (m1 · a1, . . . ,mk · ak) — разбиение числа n и

разбиение b непосредственно следует за a в словарном порядке. Тогда

(i) если mk = 1, то k ≥ 2 и

b = (m1 · a1, . . . ,mk−2 · ak−2, 1 · (ak−1 + 1),Σ′ · 1);

(ii) если mk ≥ 2, k ≥ 2 и ak−1 = ak + 1, то

b = (m1 · a1, . . . ,mk−2 · ak−2, (mk−1 + 1) · ak−1,Σ · 1);

(iii) если mk ≥ 2, k ≥ 2 и ak−1 = ak + 1, то

b = (m1 · a1, . . . ,mk−1 · ak−1, 1 · (ak + 1),Σ · 1);

(iv) если k = 1, то b = (1 · (ak + 1),Σ · 1).Здесь Σ′ = mk ak +mk−1ak−1 − (ak−1 + 1) и Σ = mk ak − (ak + 1).

Доказательство. При переходе от a к b необходимо самый пра-

вый возможный элемент разбиения a увеличить на 1. Такой элемент x

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

возможных случая.

Случай 1. mk = 1. Элемент ak нельзя увеличить, сохранив разбиение

числа n. Так как a не наибольшее разбиение, имеем k ≥ 2. Поскольку68

Page 69: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

ak−1+ak ≥ ak+1, то x = ak−1 и x является первым элементом (k−1)-го

блока. Этот элемент увеличиваем на 1, а сумму оставшихся слагаемых

Σ′ = mk ak +mk−1ak−1− (ak−1+1) представляем в виде суммы единиц.

Случай 2. mk ≥ 2. Тогда mk ak ≥ ak + 1. Следовательно, x = ak и x

является первым элементом k-го блока. Этот элемент увеличиваем на

1, а сумму оставшихся слагаемых Σ = mk ak − (ak + 1) представляем в

виде суммы единиц.

В каждом из этих случаев остаётся пересчитать значения ai и mi

для не более, чем двух изменившихся блоков. Лемма 8 доказана.

Замечание 4. Выбирая значение a0 такое, что a0 = a1+1, в лемме 8

можно исключить (iv) и условие k ≥ 2 в (ii),(iii), сохранив справедливым

утверждение леммы.

Пример 7. Разбиения числа n = 7 в словарном порядке:

(7 · 1) = (1, 1, 1, 1, 1, 1, 1),

(1 · 2, 5 · 1) = (2, 1, 1, 1, 1, 1),

(2 · 2, 3 · 1) = (2, 2, 1, 1, 1),

(3 · 2, 1 · 1) = (2, 2, 2, 1),

(1 · 3, 4 · 1) = (3, 1, 1, 1, 1),

(1 · 3, 1 · 2, 2 · 1) = (3, 2, 1, 1),

(1 · 3, 2 · 2) = (3, 2, 2),

(2 · 3, 1 · 1) = (3, 3, 1),

(1 · 4, 3 · 1) = (4, 1, 1, 1),

(1 · 4, 1 · 2, 1 · 1) = (4, 2, 1),

(1 · 4, 1 · 3) = (4, 3),

(1 · 5, 2 · 1) = (5, 1, 1),

(1 · 5, 1 · 2) = (5, 2),

(1 · 6, 1 · 1) = (6, 1),

(1 · 7) = (7).

Перейдём к программной реализации рассмотренного алгоритма.

69

Page 70: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

Алгоритм PartN(n) генерации разбиенийчисла n словарном порядке

m0 := 2;

a0 := 1− n;

a1 := 1;

m1 := n;

k := 1;

while k = 0 dobegin

write(m1 · a1, . . . ,mk · ak);Σ := mk ak;

if mk = 1 then begink := k − 1;

Σ := Σ +mk ak − (ak + 1);

mk := 1;

ak := ak + 1

endelse begin

Σ := Σ− (ak + 1);

if ak−1 = ak + 1 then begink := k − 1;

mk := mk + 1

endelse begin

ak := ak + 1;

mk := 1

endend;

if Σ = 0 then begink := k + 1;

ak := 1;

mk := Σ

end

end.70

Page 71: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

Теорема 9. Алгоритм PartN(n) корректен и генерирует все раз-

биения числа n без повторений в словарном порядке за время O(p(n)).

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

k, mi, ai, i = 1, . . . , k при инициализации соответствуют наименьшему

разбиению (n ·1), а в теле while-цикла вычисляются согласно формулам

из леммы 8(i)-(iii). Через a∗0, m∗0, a∗1 соответственно обозначим значения

переменных a0, m0, a1 при их инициализации. Тогда21

n+ (m∗0 − 1) a∗0 − 1 = 0;

a∗0 = a∗1 + 1, если n > 1;

a∗0 < 1 или a∗0 > n.

Из алгоритма понятно, что переменная a0 не изменяет своего значе-

ния a∗0 вплоть до тех пор, пока не будет выполнен оператор печати

write(m1 · a1, . . . ,mk · ak) при k = mk = 1. Кроме того, 0 < a1 < n

для любого разбиения (m1 · a1, . . . ,mk · ak) числа n, за исключением

случая, когда k = mk = 1. Используя лемму 8 и замечание 4, по ин-

дукции получаем, что после печати текущего разбиения числа n, начи-

ная с разбиения (n · 1), будет напечатано непосредственно следующее

в словарном порядке разбиение (m1 · a1, . . . ,mk · ak). Причём в момент

его печати будет выполняться неравенство a0 = a1 +1, за исключением

печати наибольшего разбиения (1 ·n), когда k = mk = 1. Теперь из алго-

ритма понятно, что переменная m0 не изменит своего первоначального

значения m∗0 вплоть до печати разбиения (1 · n).

Рассмотрим работу тела while-цикла после печати разбиения (1, n).

Имеем k = m1 = 1, a1 = n, a0 = a∗0, m0 = m∗0. После выполнения первого

условного оператора получим k = 0 и Σ = m1a1 + m∗0 a

∗0 − (a∗0 + 1) =

0. Поэтому переменная k не изменит своего нулевого значения после

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

PartN(n) закончит работу.21 Здесь рассмотрена более общая ситуация, чем в алгоритме PartN(n), чтобы

понять роль значений a∗0, m∗0.

71

Page 72: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

Поскольку число операций, необходимых для перехода от одного

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

зависящей от n, данный алгоритм генерации PartN(n) является линей-

ным. Теорема 9 доказана.

Заметим, что в алгоритме PartN(n) значения переменных a0, m0

при инициализации могут быть заданы произвольным образом при

условии выполнения трёх соотношений, приведенных в доказательстве

теоремы 9. В частности, можно определить a∗0 = n+ 1 и m∗0 = 0.

3. Разбиения конечного множества

Определение 14. Семейство множеств A = Aii∈I , где I — неко-

торое множество индексов, называется разбиением множества A, если

A =∪i∈I

Ai, Ai

∩Aj = Ø при i = j и Ai = Ø для любых i, j ∈ I. При

этом подмножества Ai, i ∈ I называются блоками разбиения A.

Для конечного множества A в качестве множества индексов I рассмат-

риваются конечные множества 1, 2, . . . , k и число k называется чис-

лом блоков разбиения. Число всех разбиений22 n-элементного множе-

ства называется числом Белла и обозначается Bn. Более формально,

Bn = |Π(A)|, где Π(A) — множество всех разбиений n-элементного мно-

жества A. Число разбиений n-элементного множества на k блоков на-

зывается числом Стирлинга второго рода и обозначается S(n, k). При

этом принимают B0 = 1 и S(0, 0) = 1. Очевидно, что Bn =∑n

k=0 S(n, k).Для чисел Белла справедливо следующее рекуррентное выражение

через биномиальные коэффициенты:

Bn+1 =

n∑i=0

C in Bi, B0 = 1.

22 Подсчитываются неупорядоченные разбиения, когда порядок блоков A1, . . . ,Ak

разбиения A не важен. Два разбиения считаются равными, если они равны как

множества.

72

Page 73: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

Действительно, имеется Bn+1 разбиений (n+1)-элементного множества

A = 1, 2, . . . , n + 1. Рассмотрим произвольное разбиение A ∈ Π(A).

Число n+1 принадлежит некоторому (i+1)-элементному блоку разби-

ения A, где 0 ≤ i ≤ n. Очевидно, что существует C in возможностей для

выбора такого блока. Оставшееся множество из n− i чисел может быть

разбито Bn−i способами. Суммируя по всем допустимым i, получаем

Bn+1 =∑n

i=0 C in Bn−i =

∑ni=0 C i

n Bi.

Для числа Bn известно следующее асимптотическое выражение23:

Bn ∼( n

lnn

)n.

4. Генерация разбиенийn-элементного множества

Познакомимся с алгоритмом генерации всех разбиений n-элементного

множества A = 1, 2, . . . , n в порядке минимального изменения. Пусть

A = A1, . . . ,Ak — разбиение множества A и ai — наименьший эле-

мент блока Ai. Упорядочим элементы ai, i = 1, . . . , k в порядке воз-

растания ai1 < . . . < aik . В соответствии с таким порядком определим

упорядочение блоков разбиения Ai1 , . . . ,Aik . Другими словами, разбие-

ние A представим в виде последовательности блоков A = (Ai1 , . . . ,Aik),

упорядоченной по возрастанию наименьших элементов, содержащихся

в блоке. В дальнейшем под разбиением будем понимать именно такую

последовательность. Наименьший элемент блока будем называть номе-

ром блока. Отметим, что номера соседних блоков, вообще говоря, не

являются соседними числами.

Идея алгоритма, который мы будем рассматривать, близка к идее

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

Каждое следующее разбиение будет получаться из предыдущего в ре-

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

"перемещения" некоторого элемента из одного блока в соседний. Под23 См., например, [8, т. 4].

73

Page 74: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

"перемещением" элемента мы понимаем удаление этого элемента из

блока (что может привести к удалению всего одноэлементного блока)

и либо добавление его в соседний блок, либо создание из него одноэле-

ментного соседнего блока.

Построим список всех разбиений Ai, i = 1, . . . , Bn n-элементного

множества A = 1, . . . , n без повторений со следующими свойствами:

• для произвольного x ∈ A в любом разбиении Ai над элементом x

указано возможное направление перемещения при помощи стрел-

ки → ("вправо") или ← ("влево");

• каждое следующее разбиение Ai+1 получается из предыдущего Ai

перемещением одного элемента xi из некоторого блока в соседний

блок. Перемещение осуществляется в направлении, указанном в

разбиении Ai над этим элементом xi;

• A1 = (−→1 ,−→2 , . . . ,−→n ).

Построение проведем индукцией по n. Для одноэлементного множе-

ства A существует единственное разбиение, состоящее из одного бло-

ка с единственным элементом 1. Для элемента 1 определим направле-

ние−→1 . Теперь предположим, что B i, i = 1, . . . , Bn−1 — список всех

разбиений (n − 1)-элементного множества 1, . . . , n − 1 с указанны-

ми свойствами. Каждому разбиению B i = (B i1 , . . . ,B i

ki) при нечётном

i сопоставим последовательность ki + 1 разбиений n-элементного мно-

жества A, последовательно добавляя в каждый блок B ij слева направо

(j = 1, . . . , ki) элемент −→n и на последнем шаге (при j = ki + 1) добав-

ляя к разбиению B i одноэлементный блок −→n . Каждому разбиению

B i = (B i1 , . . . ,B i

ki) при чётном i сопоставим последовательность ki + 1

разбиений n-элементного множества A, добавляя на первом шаге (при

j = ki + 1) к разбиению B i одноэлементный блок ←−n и далее после-

довательно добавляя в каждый блок B ij справа налево (j = ki, . . . , 1)

элемент ←−n . Нетрудно доказать, что полученный список разбиений

n-элементного множества A обладает требуемыми свойствами.74

Page 75: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

Описанный метод построения списка разбиений Ai, i = 1, . . . , Bn

даёт рекурсивный алгоритм генерации всех разбиений n-элементного

множества. Однако такой алгоритм потребует большого объёма памяти.

Чтобы устранить этот недостаток и отказаться от рекурсии, выясним,

какой элемент xi ∈ A перемещается в Ai при переходе к следующему

разбиению Ai+1 и каким образом. Введём следующие обозначения:

• Block(x) — номер блока, содержащего элемент x. Очевидно, что

элементы x, y принадлежат одному блоку разбиения тогда и толь-

ко тогда, когда Block(x) = Block(y). Более того, последователь-

ность (Block(1), . . . , Block(n)) полностью определяет разбиение;

• Next(i) — номер блока, непосредственно следующего за блоком с

номером i. Полагаем Next(i) = n + 1, если i — номер последнего

блока;

• Prev(i) — номер предыдущего блока для блока с номером i. По-

лагаем Prev(i) = 0, если i — номер первого блока;

• Dir(x) — число, определяющее возможное направление переме-

щения элемента x. Dir(x) = 1, если x перемещается вправо, и

Dir(x) = −1, если x перемещается влево.

Используя рекурсивное определение списка разбиений Ai, i = 1, . . . , Bn

множества A = 1, . . . , n, индукцией по n нетрудно доказать следую-

щие свойства разбиений этого списка.

Лемма 9. Пусть x ∈ A = 1, . . . , n. Для любого построенного раз-

биения A i = (A i1, . . . ,A i

ki), i = 1, . . . , Bn множества A справедливы

следующие свойства:

(i) номер первого блока A i1 равен 1;

(ii) элемент x перемещается тогда и только тогда, когда x есть наи-

большее число, удовлетворяющее одному из следующих двух условий:

либо Block(x) = x и Dir(x) = 1, либо Block(x) = 1 и Dir(x) = −1;

75

Page 76: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

(iii) если x перемещается, то создаётся одноэлементный блок xтогда и только тогда, когда Dir(x) = 1 и Next(Block(x)) > x;

(iv) если x перемещается, то удаляется одноэлементный блок xтогда и только тогда, когда Dir(x) = −1 и Block(x) = x;

(v) если x перемещается и y ∈ A, то направление элемента y меняется

на противоположное тогда и только тогда, когда y > x;

(vi) при i = Bn для любого элемента x ∈ A не выполняется каждое

из двух условий, указанных в (ii).

Лемма 9 даёт конструктивный способ нахождения перемещаемого

элемента x, а также условия создания и удаления одноэлементного бло-

ка x при перемещении x. Остаётся только переопределить значения

Next(i), Prev(i), Block(x), Dir(x) для изменившихся блоков при пере-

мещении x из одного блока в соседний блок. Описанный процесс постро-

ения разбиений реализован в нижеприведённом алгоритме PartS(n).

Пример 8. Генерация всех разбиений множества 1, 2, 3, 4:(−→1−→2−→3−→4 ), x = 4, i = 1, Next(i) > x;

(−→1−→2−→3 )(−→4 ), x = 3, i = 1, Next(i) > x;

(−→1−→2 )(−→3 )(←−4 ), x = 4, i = 4, Block(x) = x;

(−→1−→2 )(−→3←−4 ), x = 4, i = 3, Block(x) = x;

(−→1−→2←−4 )(−→3 ), x = 2, i = 1, Next(i) > x;

(−→1−→4 )(−→2 )(←−3 ), x = 4, i = 1, Next(i) ≯ x;

(−→1 )(−→4−→2 )(←−3 ), x = 4, i = 2, Next(i) ≯ x;

(−→1 )(−→2 )(←−3−→4 ), x = 4, i = 3, Next(i) > x;

(−→1 )(−→2 )(←−3 )(−→4 ), x = 3, i = 3, Block(x) = x;

(−→1 )(−→2←−3 )(←−4 ), x = 4, i = 4, Block(x) = x;

(−→1 )(−→2←−3←−4 ), x = 4, i = 2, Block(x) = x;

(−→1←−4 )(−→2←−3 ), x = 3, i = 2, Block(x) = x;

(−→1−→4←−3 )(−→2 ), x = 4, i = 1, Next(i) ≯ x;

(−→1←−3 )(−→2−→4 ), x = 4, i = 2, Next(i) > x;

(−→1←−3 )(−→2 )(−→4 ), x = 1.

76

Page 77: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

Алгоритм PartS(n) генерациивсех разбиений множества 1, 2, . . . , n

for i = 1 to n dobeginBlock(i) := 1;Dir(i) := 1

end;Next(1) := n+ 1;Prev(1) := 0;x := 0;while x = 1 dobeginwrite(Block(1), . . . , Block(n));x := n;while (x > 1) &

((Block(x) = x&Dir(x) = 1) ∨ (Block(x) = 1&Dir(x) = −1)) dobeginDir(x) := −Dir(x);x := x− 1

end;i := Block(x);if Dir(x) = 1then begin if Next(i) > x then

begin if Next(i) = n+ 1 then Next(x) := n+ 1else begin

Next(x) := Next(i);Prev(Next(i)) := xend;

Next(i) := x;Prev(x) := i

end;Block(x) := Next(i)

end;else begin if Block(x) = x then

if Next(i) = n+ 1 then Next(Prev(i)) := n+ 1else begin

Next(Prev(i)) := Next(i);Prev(Next(i)) := Prev(i)end;

Block(x) := Prev(i)end

end.77

Page 78: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

Теорема 10. Алгоритм PartS(n) корректен и генерирует все раз-

биения n-элементного множества без повторений за время O(Bn).

Доказательство. Рассмотрим построенный списокAi, i = 1, . . . , Bn

всех разбиений n-элементного множества A без повторений. Очевидно,

что разбиение A1 = (−→1 , . . . ,−→n ) определяется при инициализации пе-

ременных Block(i), Dir(i). В силу построения списка Ai, i = 1, . . . , Bn

каждое следующее разбиение Ai+1 получается из предыдущего Ai в

результате перемещения некоторого элемента xi в соседний блок в на-

правлении, соответствующем значению переменной Dir(xi). Согласно

лемме 9 (i)–(ii) перемещаемый элемент xi находится после окончания

работы внутреннего while-цикла. В соответствии с леммой 9 (v) в этом

цикле также меняются направления всех элементов, больших xi, на про-

тивоположные. Далее в зависимости от значения переменной Dir(xi)

осуществляется перемещение элемента xi в соседний блок.

Если xi перемещается вправо, то значение переменной Block(xi) из-

меняется на Next(Block(xi)) в случае, когда блок с номером Block(xi)

не является последним, и на xi, если это последний блок. Когда при

этом создаётся одноэлементный блок xi (согласно лемме 9 (iii) это

полностью определяется условием Next(Block(xi)) > xi), дополнитель-

но перенумеровываются изменившиеся блоки путём переопределения

значений переменных Next и Prev. Если xi перемещается влево, то

значение переменной Block(xi) изменяется на Prev(Block(xi)). Когда

при этом удаляется одноэлементный блок xi (согласно лемме 9 (iv)

это полностью определяется условием Block(xi) = xi), дополнительно

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

В силу леммы 9 (vi) после печати последнего разбиения ABn и вы-

полнения всех итераций внутреннего while-цикла переменная x примет

значение 1. Следовательно, алгоритм закончит работу.

Оценим время работы T (n) алгоритма PartS(n). Число проверок

условия внутреннего while-цикла обозначим через In. Индукцией по n

78

Page 79: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

докажем, что выполняется равенство

In =n∑

i=1

Bi.

Действительно, при n = 1 имеем I1 = B1 = 1. Пусть для n − 1 фор-

мула верна. Учитывая рекурсивное определение списка Ai всех разби-

ений n-элементного множества через список B j всех разбиений (n− 1)-

элементного множества, нетрудно доказать равенство In = Bn + In−1.

Применяя индукционное предположение, получаем требуемое выраже-

ние для In. В силу рекуррентной формулы для чисел Белла имеемn−1∑i=1

Bi ≤n−1∑i=0

C in−1Bi = Bn.

Следовательно, In = O(Bn). Очевидно, что T (n) = O(n) + O(In) =

O(Bn). Теорема 10 доказана.

Глава 5Сортировка комбинаторных объектов

Во многих компьютерных приложениях требуется переразместить за-

данную совокупность комбинаторных объектов в соответствии с неко-

торым заранее определённым порядком. Например, необходимо распо-

ложить данные по алфавиту или по возрастанию номеров либо отсор-

тировать товары по ценам. Всё это разновидности задачи сортировки,

которой посвящена настоящая глава.

Сначала задача сортировки рассматривается с теоретической точки

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

тивности алгоритмов сортировки. Для оценки эффективности оказыва-

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

мени работы алгоритма. Мы установим нижние оценки времени работы

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

принципе попарного сравнения элементов входной последовательности,

для этого познакомимся со свойствами бинарных деревьев. Любая сор-

тировка такого вида уже в среднем требует времени Ω(n lg n).79

Page 80: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

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

ковой, быстрой и пирамидальной сортировки. Для каждого из рассмот-

ренных алгоритмов будет обоснован асимптотический порядок мини-

мального, среднего и максимального времени их работы. В классе ал-

горитмов сортировки сравнением быстрая сортировка со средним вре-

менем работы Θ(n lg n) является асимптотически оптимальной в сред-

нем, а пирамидальная сортировка со сложностью Θ(n lg n) — в худшем

случае. Линейную сложность можно получить, если при сортировке ис-

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

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

нительную информацию о порядке расположения или природе входных

элементов. В качестве примера такого алгоритма изучается алгоритм

сортировки подсчётом со временем работы Θ(n).

1. Задача сортировки

Сортируемые объекты, как правило, являются записями, содержащи-

ми одно или несколько полей. Пусть задана последовательность таких

записей x1, . . . , xn и на множестве X всех записей, входящих в эту по-

следовательность24, определен некоторый линейный порядок ≤. Задача

сортировки состоит в переразмещении записей в порядке возрастания,

более формально в построении упорядочивания xπ1 ≤ xπ2 ≤ · · · ≤ xπn

заданной последовательности записей x1, . . . , xn относительно линейно-

го порядка ≤. При этом последовательность π = (π1, . . . , πn) будет пе-

рестановкой n-элементного множества 1, 2, . . . , n. В алгоритмах сор-

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

записей, а не упорядочивающую перестановку π. Хотя иногда быва-

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

упорядоченную последовательность записей.

Программное задание сортируемых записей часто приводит к тому,

что в каждой записи имеется (добавляется) специальное отведённое по-24 Записи x1, . . . , xn не обязательно различные.

80

Page 81: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

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

ного порядка для ключей25. Поэтому в дальнейшем, если не оговорено

особо, считаем, что X есть множество ключей с заданным линейным

порядком ≤. При работе алгоритма сортировки записи могут сорти-

роваться на месте, т. е. в той же области, которая была отведена для

входной последовательности данных, или с использованием дополни-

тельной памяти. В первом случае переразмещение записей должно про-

исходить внутри входной сортируемой последовательности x1, . . . , xn.

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

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

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

ние ограниченного количества ячеек памяти для размещения значений

используемых переменных.

Ранее для оценки эффективности алгоритмов мы ввели понятие

сложности или трудоёмкости алгоритма в худшем случае. Однако для

многих приложений с практической точки зрения оказывается полез-

ным оценить не только максимальное время, но и минимальное, среднее

и ожидаемое время работы алгоритма. D.E. Knuth назвал минимальное

время временем для оптимистов, среднее и ожидаемое время — для спе-

циалистов по теории вероятностей, а максимальное время — временем

для пессимистов.

Минимальное время Tmin(n) — это наименьшее время работы ал-

горитма на входах размерности n. Максимальное время Tmax(n) (или

сложность алгоритма) — это наибольшее время работы алгоритма на

входах размерности n.

Пусть a1, . . . ,ak — все входные данные размерности n для алгоритма

A и Pi — вероятность того, что на вход алгоритма A подаётся после-

довательность данных ai, 0 ≤ Pi ≤ 1,∑k

i=1 Pi = 1. При фиксирован-

ном n мы определили вероятностное распределение входных данных25 Обычно поле "ключ" имеет целый тип данных, и упорядочивание рассматрива-

ется относительно естественного линейного порядка для чисел.

81

Page 82: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

для алгоритма A. Рассмотрим время TA(n) работы алгоритма A как

случайную величину, принимающую одно из возможных значений

t1, . . . , tk, где ti — время работы алгоритма A на входных данных ai.

Ожидаемое время E(TA) работы алгоритма A — это математическое

ожидание случайной величины TA(n), т. е.

E(TA) =

k∑i=1

ti Pi .

Среднее время Tave(n) — это усреднённое время работы алгоритма

по всем входным данным размерности n, т. е.

Tave(n) =1

k

k∑i=1

ti.

Заметим, что если входные данные равновероятны, то среднее и

ожидаемое время работы алгоритма совпадают. Рассмотрение понятия

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

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

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

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

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

При определении ожидаемого и среднего времени мы предполага-

ли конечность множества входных данных размерности n для всех n.

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

если для любого n имеется разбиение множества всех входных данных

размерности n на подмножества A1, . . . ,Ak такие, что время работы

алгоритма A на каждом входе из фиксированного класса данных Ai

постоянно и принимает значение ti, причём задано распределение веро-

ятностей Pi, i = 1, . . . , k, где Pi — вероятность того, что на вход алгорит-

ма A подаются входные данные из класса Ai. Тогда ожидаемое время

E(TA) работы алгоритма A определяется как функция от размерности

данных n аналогичным образом. В случае равновероятности поступле-

ния входных данных из классов A1, . . . ,Ak для всех n мы получаем

понятие среднего времени Tave(n).82

Page 83: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

Для каждого из изучаемых алгоритмов сортировки мы будем ис-

следовать асимптотический порядок минимального Tmin(n), среднего

Tave(n) и максимального Tmax(n) времени работы, при этом под размер-

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

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

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

следовательности входных данных, а не их конкретной величиной. По-

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

чей x1, . . . , xn фиксированной длины, имеющие один и тот же поря-

док элементов по отношению друг к другу, естественно объединить в

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

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

ных классов. Так, при n = 5 последовательности данных 2, 3, 1, 1, 5 и

17, 20, 10, 10, 25 будут принадлежать одному классу данных, при n = 3

имеется ровно 13 классов, образующих разбиение множества всех по-

следовательностей длины 3, а вероятность появления входной после-

довательности из любого такого класса равна 1/13. Кроме того, для

вычисления минимального, среднего и максимального времени можно

ограничиться входными данными x1, . . . , xn размерности n, составлен-

ными из ключей абстрактного n-элементного линейно упорядоченного

множества X (вообще говоря, ключи xi могут повторяться)26. В даль-

нейшем считаем, что никакие два ключа в этой последовательности не

имеют одинаковых значений, т. е. если i = j, то либо xi < xj , либо

xi > xj . Это ограничение27 упростит дальнейший анализ без потери

общности, и при наличии равных ключей корректность рассматривае-

мых алгоритмов сортировки не нарушится.26 Иными словами, если допускаются равные ключи, входными данными размер-

ности n будут все перестановки множества ключей X = x1, . . . , xn с повторениями.27 При этом для алгоритма сортировки входными данными размерности n будут

все возможные перестановки множества ключей X = x1, . . . , xn. В действительно-

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

времени работы.

83

Page 84: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

2.Нижние оценки сложности алгоритмасортировки сравнением

Прежде чем переходить к нижним оценкам эффективности алгоритмов

сортировки, познакомимся с понятием бинарного дерева и рассмотрим

некоторые свойства бинарных деревьев.

Определение 15. Корневое дерево — это граф, рекурсивно опре-

деляемый следующим образом:

1) одна вершина есть дерево, эта вершина является корнем дерева и

не имеет сыновей;

2) пусть D1, . . . , Dk, k ≥ 1 — деревья с корнями v1, . . . , vk соот-

ветственно, причём множества вершин этих деревьев попарно не пе-

ресекаются. Соединим рёбрами новую вершину v с каждой из вершин

v1, . . . , vk. Полученный граф есть корневое дерево с корнем v и подде-

ревьями D1, . . . , Dk, соответствующими вершине v. Для вершины v

сыновьями будут вершины v1, . . . , vk, при этом сама вершина v явля-

ется родителем каждой из вершин v1, . . . , vk. Для остальных вершин

сыновья и родители остаются прежними;

3) всякое корневое дерево строится по одному из правил 1 или 2.

Для произвольной вершины v корневого дерева существует един-

ственный простой путь от корня до вершины v, длина этого пути назы-

вается глубиной вершины v, а вершины этого пути — предками v. Вер-

шина корневого дерева, не имеющая сыновей, называется его концевой

вершиной. Высота корневого дерева — это наибольшая длина просто-

го пути от корня дерева до его концевых вершин. Высота поддерева

с корнем v (вершинами этого поддерева являются вершина v и все её

потомки) называется высотой вершины v.

Определение 16. Бинарное (или двоичное) дерево — это корневое

дерево, у которого каждая вершина имеет не более двух сыновей, один

из которых называется левым, а другой — правым. Бинарное дерево,84

Page 85: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

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

ровно два сына, называется полным бинарным деревом.

В дальнейшем нам потребуются свойства бинарных деревьев, сфор-

мулированные в следующих трёх леммах.

Лемма 10. Пусть D — произвольное n-вершинное бинарное дерево

высоты h. Тогда

(i) в дереве D число вершин высоты i не превосходит 2h−i, где i ≤ h;

(ii) n ≤ 2h+1 − 1;

(iii) для полного бинарного дерева D в неравенствах из (i) и (ii)

достигается равенство;

(iv) h ≥ ⌊lg n⌋;(v) n ≥ 2k − 1, где k — число концевых вершин дерева D.

Доказательство (iii) нетрудно провести, используя индукцию по вы-

соте дерева h и формулу суммы геометрической прогрессии. Утвержде-

ния (i), (ii) вытекают из (iii), а (iv) непосредственно следует из (ii).

Утверждение (v) доказывается индукцией по числу вершин n.

Лемма 11. Пусть D — n-вершинное бинарное дерево высоты h, в

котором каждая вершина глубины меньшей, чем h− 1 имеет ровно два

сына. Тогда h = ⌊lg n⌋.

Доказательство. При n = 1 утверждение очевидно. Пусть n ≥ 2.

Нетрудно понять, что дерево D содержит полное бинарное дерево D′

высоты h − 1 и, по крайней мере, одну вершину, не принадлежащую

дереву D′. Следовательно, n ≥ 2h− 1+ 1 = 2h по лемме 10 (iii). Значит,

h ≤ ⌊lg n⌋. С другой стороны, h ≥ ⌊lg n⌋ по лемме 10 (iv). Лемма 11

доказана.

Лемма 12. Пусть D — n-вершинное бинарное дерево с k концевыми

вершинами, H(D) — сумма глубин всех его концевых вершин и F (D) —

85

Page 86: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

сумма глубин всех его вершин. Тогда

H(D) ≥ k lg k, F (D) ≥n∑

j=1

⌊lg j⌋.

Доказательство. Оценку числа H(D) получим индукцией по вы-

соте h дерева D. Базис индукции при h = 0 очевиден. Пусть h > 0 и

вершина v — корень дерева D. Возможны два случая.

Случай 1. Корень v имеет единственного сына. Пусть D′ — подде-

рево, соответствующее вершине v. Тогда H(D) = H(D′) + k. По индук-

ционному предположению H(D′) ≥ k lg k, так как D′ имеет k концевых

вершин, и высота дерева D′ равна h− 1. Следовательно, H(D) ≥ k lg k.

Случай 2. Корень v имеет два сына. Пусть D1, D2 — поддеревья,

соответствующие вершине v, и ki — число концевых вершин дерева

Di. Тогда H(D) = H(D1) + k1 + H(D2) + k2 и k = k1 + k2. Исполь-

зуя индукционное предположение, получаем H(D) ≥ f(k1) + k, где

f(x) = x lg x + (k − x) lg(k − x). Рассмотрим функцию f(x) на интер-

вале (0, k). Значение f(k/2) есть экстремум функции f(x), и вторая

производная f ′′(x) положительна. Следовательно, f(k/2) есть минимум

функции f(x). Поэтому H(D) ≥ f(k/2) + k = k lg k.

Получим оценку числа F (D) индукцией по n. Пусть v — вершина D

глубины s(v), равной высоте дерева D. Рассмотрим бинарное (n − 1)-

вершинное дерево D′, получающееся из D удалением вершины v. По

лемме 10 (iv) и индукционному предположению получаем

F (D) = s(v) + F (D′) ≥ ⌊lg n⌋+ F (D′) ≥n∑

j=1

⌊lg j⌋.

Лемма 12 доказана.

Среди алгоритмов сортировки выделяется большой класс алгорит-

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

записей. Такие алгоритмы будем называть алгоритмами сортировки

сравнением. Любой алгоритм A сортировки сравнением задаёт бинар-

ное дерево DA, определяемое следующим образом. Пусть y1, . . . , yn —86

Page 87: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

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

ной последовательности ключей. Рассмотрим бинарное дерево, в кото-

ром представлены все операции сравнения ключей, выполняющиеся во

время работы алгоритма A, при этом все другие операции, например,

перемещение данных и вычисления, мы игнорируем. Каждая неконце-

вая вершина этого дерева соответствует сравнению некоторых перемен-

ных yi и yj , сделанному алгоритмом в процессе его выполнения. Такую

вершину отмечаем меткой yi : yj , i ≤ j. Из этой вершины выходит не

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

ния. При этом левому поддереву соответствуют дальнейшие сравнения,

выполняющиеся во время работы алгоритма при yi ≤ yj , а правому

поддереву — сравнения, которые нужно выполнить при yi > yj . Ребро,

соответствующее невозможным соотношениям между уже рассмотрен-

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

либо правое поддерево будет пустым, а для любой входной последова-

тельности ключей соответствующая часть вычислений алгоритмом Aникогда не выполняется. Каждая концевая вершина дерева DA помече-

на перестановкой (π1, . . . , πn) множества 1, 2, . . . , n, которая опреде-

ляет окончательное упорядочивание yπ1 ≤ . . . ≤ yπn соответствующих

ключей входной последовательности. Полученное размеченное бинар-

ное дерево DA называется деревом решений алгоритма A. Очевидно,

что выполнение алгоритма A сортировки заданной входной последова-

тельности значений переменных y1, . . . , yn соответствует прохождению

пути от корня дерева решений DA до одной из его концевых вершин.

Отметим следующие свойства дерева решений DA:

• бинарное дерево DA имеет ровно n! концевых вершин;

• из корня дерева DA к каждой его концевой вершине проходит

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

щей входной последовательности размерности n.

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

концевая вершина помечена некоторой перестановкой π множества

87

Page 88: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

1, 2, . . . , n. Очевидно, что различным концевым вершинам соответ-

ствуют различные перестановки. Пусть π — произвольная перестановка

множества 1, 2, . . . , n и y1 ≤ . . . ≤ yn — упорядочивание входной по-

следовательности ключей x1, . . . , xn. Корректный алгоритм сортировки

сравнением должен произвести сортировку любой входной последова-

тельности размерности n. При работе алгоритма A на входной последо-

вательности yπ−1(1), . . . , yπ−1(n) выполненным последовательным срав-

нениям соответствует путь от корня дерева DA до концевой вершины,

помеченной перестановкой π. Следовательно, число концевых вершин

дерева DA равно числу всех перестановок n-элементного множества.

Остаётся заметить, что в каждом дереве существует единственный путь,

соединяющий произвольные две вершины.

Теорема 11. Сложность любого алгоритма сортировки сравнени-

ем есть Ω(n lgn).

Доказательство. Пусть A — произвольный алгоритм сортировки

сравнением. При получении нижних оценок сложности Tmax(n) алго-

ритма A можно ограничиться входными данными размерности n, со-

стоящими из различных ключей. Путь из корня дерева решений DA в

произвольную его концевую вершину соответствует работе алгоритма

A на подходящей входной последовательности размерности n. Длина

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

сортировки входной последовательности. Поэтому высота h дерева DA

есть число операций сравнений, выполняющихся алгоритмом A при его

работе на некоторой входной последовательности размерности n. Сле-

довательно, Tmax(n) ≥ h. Число концевых вершин бинарного дерева DA

не превосходит 2h в силу леммы 10 (ii),(v). Дерево решений DA имеет n!

концевых вершин. Поэтому h ≥ lg(n!). В силу примера 1 (vii) получаем

Tmax(n) = Ω(n lgn). Теорема 11 доказана.

Теорема 11 дает нижнюю оценку порядка максимального времени

работы произвольного алгоритма сортировки сравнением. На самом де-88

Page 89: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

ле, аналогичная оценка справедлива и для среднего времени.

Теорема 12. Среднее время работы любого алгоритма сортировки

сравнением есть Ω(n lg n).

Доказательство. Пусть A — произвольный алгоритм сортировки

сравнением с множеством ключей X = x1, . . . , xn. Входная последо-

вательность размерности n представляет собой произвольную переста-

новку множества X, их число равно n!. Сумму глубин всех концевых

вершин дерева решений DA обозначим через H(DA). Как и в теореме

11, в силу свойств дерева решений DA получаем Tave(n) ≥ H(DA)/n!,

где Tave(n) — среднее время работы алгоритма A на входных данных

размерности n. Причём дерево DA имеет n! концевых вершин. Следо-

вательно, Tave(n) ≥ lg(n!) = Ω(n lgn) в силу леммы 12 и примера 1 (vii).

Теорема 12 доказана28.

Следствие 1. В предположении равновероятности входных дан-

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

нием есть Ω(n lg n).

3.Алгоритм сортировки вставкамии оценки времени его работы

Рассматриваемый алгоритм называется сортировкой вставками. Этот

метод сортировки последовательности ключей x1, . . . , xn состоит в сле-

дующем. На i-м шаге ключ xi вставляется в нужную позицию среди

уже упорядоченных ключей x1, . . . , xi−1. После этой вставки первые i

28 Если допускаются равные ключи, работу алгоритма A необходимо представить

в виде тернарного дерева решений DA, в котором из каждой неконцевой верши-

ны c меткой yi : yj выходит три ребра, соответствующие трём возможным исходам

операции сравнения yi < yj , yi = yj и yi > yj , а концевые вершины помечены пере-

становками с повторениями, определяющими окончательное упорядочивание. При

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

го k концевых вершин, не меньше, чем log3 k.

89

Page 90: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

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

требуемой позиции ключа xi временно сохраняем его в дополнитель-

ной переменной x, далее ключи xj , j = i − 1, . . . , 1 последовательно

сравниваются с x и сдвигаются вправо. Этот процесс продолжается до

тех пор, пока x < xj . В найденную таким образом позицию помещаем

элемент xi. Чтобы обеспечить остановку этого процесса, удобно ввести

дополнительный ключ x0, значение которого меньше значения любого

ключа xk, k = 1, . . . , n. Программная реализация описанного алгоритма

приведена в следующем псевдокоде.

Алгоритм сортировки вставками

последовательности x1, . . . , xn

x0 := −∞;

for i = 2 to n do

begin

x := xi;

j := i− 1;

while x < xj do

begin

xj+1 := xj ;

j := j − 1

end;

xj+1 := x

end.

Здесь мы постулировали существование константы −∞, меньшей зна-

чения любого ключа, встречающегося в рассматриваемой задаче. Если

такую константу указать трудно, можно модифицировать алгоритм и

отказаться от её использования. Для этого необходима только дополни-

тельная проверка j > 0 в условии while-цикла.

90

Page 91: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

Пример 9. Записи извержений знаменитых вулканов имеют два

поля: название вулкана и год его извержения. Вулкан Пили — 1902 г.,

Этна — 1669 г., Кракатау — 1883 г., Агунг — 1963 г., Св.Елена — 1980 г.,

Везувий — 79 г. Отсортируем эти записи по году извержения вулка-

нов. На рис. 2 приведена последовательность ключей после выполнения

итерации for-цикла для указанного значения счётчика итераций i. При

выполнении этой итерации ключ xi вставляется в позицию с номером

j + 1. В итоге получим список вулканов в порядке года их извержения:

Везувий, Этна, Кракатау, Пили, Агунг, Св. Елена.

i j + 1 x0 x1 x2 x3 x4 x5 x6

−∞ 1902 1669 1883 1963 1980 79

2 1 −∞ 1669 1902 1883 1963 1980 79

3 2 −∞ 1669 1883 1902 1963 1980 79

4 4 −∞ 1669 1883 1902 1963 1980 79

5 5 −∞ 1669 1883 1902 1963 1980 79

6 1 −∞ 79 1669 1883 1902 1963 1980

Рис. 2. Сортировка вставками

Корректность алгоритма сортировки вставками вытекает из следу-

ющего свойства: после итерации for-цикла для выбранного значения i,

ключи x1, . . . , xi будут отсортированы, а оставшиеся ключи останутся

на прежних местах. Это свойство легко доказать индукцией по i.

Оценим минимальное Tmin(n) и максимальное Tmax(n) время работы

алгоритма сортировки вставками. Пусть T (n) — время работы алгорит-

ма на входной последовательности x1, . . . , xn и di — число элементов

xj , больших xi и находящихся в последовательности x1, . . . , xn левее xi.

Тогда 0 ≤ di < i. Заметим, что для фиксированного в for-цикле значе-

ния переменной i проверка условия while-цикла выполняется di+1 раз.

Действительно, после предыдущей итерации for-цикла мы имеем после-

довательность xk1 , . . . , xki−1 , xi, . . . , xn, в которой первые i−1 ключей —

91

Page 92: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

это отсортированная по возрастанию последовательность x1, . . . , xi−1.

Среди этих i− 1 ключей ровно di элементов, больших xi, которые идут

подряд и непосредственно предшествуют xi. Поэтому проверка усло-

вия while-цикла будет осуществляться di раз при перемещении этих di

элементов вправо и последний раз при выходе из while-цикла. Теперь

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

T (n) = c1 + (n− 1)c2 +

n∑i=2

(di + 1) + c3

n∑i=2

di =

= c′1 + c′2n+ c′3

n∑i=2

di.

Следовательно, время работы алгоритма сортировки вставками на воз-

растающей входной последовательности, когда di = 0 при i = 1, . . . , n,

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

следовательности, когдаn∑

i=2

di =n∑

i=2

(i− 1) =n(n− 1)

2,

будет максимальным. Поэтому Tmin(n) = Θ(n) и Tmax(n) = Θ(n2).

Оценим среднее время Tave(n). Предположим, что все входные дан-

ные равновероятны, т. е. любая перестановка ключей x1, . . . , xn равнове-

роятна на входе алгоритма. Тогда Tave(n) = E(T ), и мы будем оценивать

ожидаемое время E(T ). Имеем

E(T ) = Θ(n) + Θ(1)E(

n∑i=2

di).

E(

n∑i=2

di) =

n∑i=2

E(di).

Величина di принимает одно из значений 0, 1, . . . , i− 1. В силу равнове-

роятности входных данных получаем

E(di) =i−1∑j=0

j P(di = j) =i−1∑j=0

j

i=

i− 1

2.

92

Page 93: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

Поэтому выполняется следующее соотношение

E(T ) = Θ(n) + Θ(1)n(n− 1)

4.

Следовательно, Tave(n) = Θ(n2). Таким образом, доказана

Теорема 13. Минимальное время работы алгоритма сортировки

вставками равно Θ(n), среднее и максимальное время есть Θ(n2).

4.Алгоритм пузырьковой сортировкии оценки времени его работы

Следующий простой алгоритм сортировки сравнением называется алго-

ритмом пузырьковой сортировки. Чтобы описать основную идею этого

метода, запишем сортируемые ключи в массив, "расположенный верти-

кально". При сортировке ключи с большими значениями, относитель-

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

меньшими значениями. Ключи с большими значениями "всплывают"

вверх наподобие пузырька. При первом проходе снизу вверх первый

ключ сравнивается с непосредственно следующим. Если внизу оказы-

вается больший ключ, соответствующие ключи меняем местами. При

встрече большего ключа верхний ключ становится эталоном для срав-

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

ключом. В результате первого прохода всего массива самый большой

ключ окажется в самом верху, т. е. всплывает. Во время второго прохода

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

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

на вторую сверху позицию, и т. д. Отметим, что во время второго и по-

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

найденные за предыдущие проходы. Поэтому в алгоритме используем

переменную k, значение которой при каждом проходе устанавливается

равным наибольшему индексу i такому, что все ключи xi+1, xi+2, . . . , xn

уже находятся на своих окончательных позициях.

93

Page 94: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

Алгоритм пузырьковой сортировки

последовательности x1, . . . , xn

k := n;

while k = 0 do

begin

i := 0;

for j = 1 to k − 1 do

if xj > xj+1 then begin

Swap(xj , xj+1);

i := j

end;

k := i

end.

Пример 10. Выполним пузырьковую сортировку ключей, записан-

ных в первый столбец таблицы (см. рис. 3), большие ключи распола-

гаем выше меньших. На рис. 3 приведена последовательность ключей

перед итерацией while-цикла, соответствующей указанному значению

переменной k. При выполнении этой итерации (прохода массива) вы-

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

тельность ключей записана в последнем столбце.

По сравнению с алгоритмом сортировки вставками, метод пузырьков

оказывается более сложным, но, тем не менее, асимптотический поря-

док минимального, среднего и максимального времени работы этого ал-

горитма оказываются такими же 29. Минимальное время имеет порядок

n и достигается на уже отсортированной входной последовательности,

максимальное время имеет порядок n2 и достигается на входной после-

довательности, отсортированной в обратном порядке, а среднее время

имеет порядок n2.

29 Детальный анализ показывает, что метод пузырьковой сортировки требует при-

мерно в два раза больше времени, чем алгоритм сортировки вставками [8, т. 3].

94

Page 95: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

k = 8 k = 7 k = 6 k = 5 k = 4 k = 0

42 64 64 64 64 64

61 42 61 61 61 61

33 61 42 56 56 56

64 33 56 42 53 53

17 56 33 53 42 42

56 17 53 33 33 33

28 53 17 28 28 28

53 28 28 17 17 17

Рис. 3. Пузырьковая сортировка

Мы рассмотрели алгоритм сортировки вставками и алгоритм пу-

зырьковой сортировки, имеющие максимальное и среднее время работы

порядка n2. Хотя для небольших значений n эти алгоритмы достаточ-

но эффективны, тем не менее, их время работы как в среднем, так и в

худшем случае не является лучшим возможным временем работы алго-

ритмов сортировки сравнением, которое согласно теоремам 11, 12 имеет

порядок n lg n. Также отметим, что при небольших значениях числа n

можно применять простой в реализации алгоритм сортировки Шелла,

который является обобщением алгоритма сортировки вставками и име-

ет сложность O(n1.5)30.

5.Алгоритм быстрой сортировкии оценки времени его работы

Основная причина медленной работы рассмотренных алгоритмов сор-

тировки состоит в том, что все сравнения и обмены между ключами

входной последовательности x1, . . . , xn происходили для пар соседних

элементов. При таком подходе для постановки текущего ключа в нуж-

ную позицию сортируемой последовательности требуется относительно30 Описание алгоритма сортировки Шелла и его анализ см. в [3; 8, т. 3].

95

Page 96: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

много операций. Естественно попытаться ускорить этот процесс, срав-

нивая далекие друг от друга ключи. C. A. R. Hoare предложил и весьма

эффективно применил эту идею, сократив среднее время работы алго-

ритма до порядка n lgn. Он назвал свой метод QuickSort (быстрая сор-

тировка), и это название вполне соответствует действительности, так

как при его реализации на любом современном компьютере алгоритм

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

тельности осуществляется на месте.

В алгоритме QuickSort входная последовательность ключей запи-

сывается в массив x1, . . . , xn. Для сортировки элементов x1, . . . , xn сре-

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

соб его выбора, в качестве так называемого опорного элемента. Да-

лее элементы массива переставляются так, чтобы в полученном масси-

ве для некоторой позиции q все элементы x1, . . . , xq−1 имели значения,

меньшие, чем y, значение xq совпадало с y, а значения всех элемен-

тов xq+1, . . . , xn были бы не меньше y относительно линейного порядка

на ключах. Затем процедура быстрой сортировки рекурсивно приме-

няется к двум полученным подмассивам x1, . . . , xq−1 и xq+1, . . . , xn для

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

ся на месте, для их объединения никакие дополнительные действия не

нужны. Весь массив x1, . . . , xn оказывается отсортированным, так как

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

чей во втором подмассиве. Таким образом, мы приходим к следующей

программной реализации алгоритма.

Алгоритм QuickSort(p, r) быстрой сортировки

Procedure QuickSort(p, r);

if p<r then begin

q:=Partition(p, r);

QuickSort(p, q − 1);

QuickSort(q + 1, r)

end.96

Page 97: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

Здесь QuickSort(p, r) — рекурсивная процедура сортировки подмас-

сива xp, xp+1, . . . , xr. Сортировка всего массива x1, . . . , xn осуществля-

ется вызовом процедуры QuickSort(1, n).

Ключевой частью рассматриваемого алгоритма является функция

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

опорный элемент y после всей сортировки, и осуществляющая требу-

емую перестановку элементов массива. Для выбора опорного элемен-

та и способа переупорядочивания разработаны различные модифика-

ции алгоритма быстрой сортировки. Мы будем следовать одной из них,

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

элемент xr, а процесс перестановки осуществляется следующим обра-

зом. Последовательно сравниваются элементы xj , j = p, p+ 1, . . . , r − 1

с опорным элементом xr. Если xj < xr, то элементы xi, xj переставля-

ются и значение переменной i увеличивается на 1. Здесь i — это номер

текущей позиции для окончательного размещения элемента xr, перво-

начально i = p. В результате этого процесса позиция q определяется

как итоговое значение переменной i.

Function Partition(p, r);

i := p;

for j = p to r − 1 do

if xj < xr then begin

Swap(xi, xj);

i := i+ 1

end;

Swap(xi, xr)

return i.

Пример 11. Рассмотрим работу функции Partition(1, 6) на вход-

ной последовательности 4, 7, 2, 3, 6, 5, записанной в массив x1, . . . , x6. На

рис. 4 приведено состояние массива после итерации for-цикла для ука-

занного значения счётчика итераций j. При выполнении этой итерации

97

Page 98: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

выделенные элементы переставляются, текущая позиция i опорного эле-

мента x6 подчёркнута. Отсортированная последовательность записана

в последней строке.

j x1 x2 x3 x4 x5 x6 i

4 7 2 3 6 5 1

1 4 7 2 3 6 5 2

2 4 7 2 3 6 5 2

3 4 2 7 3 6 5 3

4 4 2 3 7 6 5 4

5 4 2 3 7 6 5 4

4 2 3 5 6 7

Рис. 4. Работа функции Partition(1, 6) на входной

последовательности 4, 7, 2, 3, 6, 5

Докажем корректность функции Partition. Индукцией по j нетруд-

но показать, что после итерации for-цикла при j = p, p + 1, . . . , r − 1

выполняются следующие свойства:

• ∀k (p ≤ k < i⇒ xk < xr);

• ∀k (i ≤ k ≤ j ⇒ xk ≥ xr);

• xr не перемещается;

• i ≤ j + 1 (здесь не учитывается изменение значения счётчика j).

По завершении работы for-цикла при j = r − 1 элементы масси-

ва xp, . . . , xr−1 разбиты на два множества: значения всех элементов

xp, xp+1, . . . , xi−1 меньше xr, а значения остальных больше или равны

xr. После выполнения for-цикла опорный элемент xr меняется места-

ми с xi. Таким образом, полученное упорядочивание элементов массива

удовлетворяет всем требуемым свойствам.

Корректность алгоритма быстрой сортировки QuickSort очевидным

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

Page 99: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

Пример 12. Рассмотрим работу алгоритма быстрой сортировки

QuickSort(1, 6) на входной последовательности 4, 7, 2, 3, 6, 5 (рис. 5).

Сначала функция Partition(1, 6) возвращает позицию i = 4 опорно-

го элемента 5 и переупорядочивает массив. Далее выполняется ал-

горитм QuickSort(1, 3). В результате работы функции Partition(1, 3)

получаем упорядочивание 2, 3, 4 с опорным элементом 3. Алгоритмы

QuickSort(1, 1) и QuickSort(3, 3) останавливают свою работу, так как не

выполняется условие оператора if. В итоге процедура QuickSort(1, 3) за-

вершает работу и на первых трёх местах имеется упорядочивание 2, 3, 4.

Затем аналогично выполняется алгоритм QuickSort(5, 6), и получается

упорядочивание 6, 7 на последних двух местах.

4 7 2 3 6 5︸ ︷︷ ︸4 2 3︸ ︷︷ ︸ 5 7 6︸ ︷︷ ︸2︸︷︷︸ 3 4︸︷︷︸ 6 7︸︷︷︸2 4 7︸ ︷︷ ︸2 3 4 5 6 7

Рис. 5. Быстрая сортировка

Оценим максимальное Tmax(n), минимальное Tmin(n) и среднее

Tave(n) время работы алгоритма быстрой сортировки QuickSort на

входных данных размерности n 31. Пусть T ′max(n), T ′

min(n) — соот-

ветственно максимальное и минимальное время работы алгоритма

Partition на входных данных размерности n. Непосредственно из алго-

ритма находятся положительные константы cmin, cmax такие, что для

любого n ≥ 2 выполняются неравенства

T ′min(n) ≥ cmin n, T ′

max(n) ≤ cmax n− 1.

31 n = r − p+ 1 и n = 0 соответствует условию r < p.

99

Page 100: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

Лемма 13. Максимальное время Tmax(n) работы алгоритма быст-

рой сортировки QuickSort есть Θ(n2).

Доказательство. Пусть c — фиксированная константа такая, что

c ≥ max cmax, Tmax(0), Tmax(1) . Индукцией по n докажем неравен-

ство Tmax(n) ≤ c(n2+1) для любого n ≥ 0. Базис индукции при n = 0, 1

очевиден. Пусть n ≥ 2. Из алгоритма QuickSort и индукционного пред-

положения получаем оценки

Tmax(n) ≤ maxq∈1,...,n

Tmax(q − 1) + Tmax(n− q) + T ′max(n) + 1 ≤

≤ max1≤q≤n

c(q − 1)2 + c(n− q)2 + 2 c+ cn =

= c(n− 1)2 + 2c+ cn ≤ c(n2 + 1),

здесь мы воспользовались равенством

max1≤q≤n

f(q) = f(1) = (n− 1)2

для параболы f(q), заданной уравнением f(q) = (q−1)2+(n−q)2. Таким

образом, Tmax(n) = O(n2).

Теперь покажем, что время порядка n2 достигается при работе ал-

горитма быстрой сортировки. Пусть T (n) — время работы алгорит-

ма QuickSort на уже отсортированной входной последовательности

x1 < . . . < xn длины n. При работе алгоритма на таких входных по-

следовательностях после каждого вызова функции Partition процеду-

ра QuickSort применяется к двум подмассивам, один из которых будет

пустой, а другой — также отсортированный, меньшей длины. Поэтому

T (n) = T (n − 1) + T (0) + Θ(n). Далее, как и выше, индукцией по n

нетрудно доказать, что T (n) = Ω(n2). Таким образом, Tmax(n)=Θ(n2).

Лемма 13 доказана.

Лемма 14. Минимальное время Tmin(n) работы алгоритма быстрой

сортировки QuickSort есть Θ(n lg n).

100

Page 101: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

Доказательство. Пусть c — произвольная константа такая, что

0 < 2c ≤ cmin. Индукцией по n докажем, что Tmin(n) ≥ cn lg n для

любого n ≥ 1. Действительно, базис индукции при n = 1 очевиден.

Пусть n ≥ 2. Из алгоритма QuickSort получаем

Tmin(n) ≥ minq∈1,...,n

Tmin(q − 1) + Tmin(n− q) + T ′min(n) ≥

≥ minq∈1,...,n

Tmin(q − 1) + Tmin(n− q) + 2c n.

Случай 1. Минимум указанного выражения достигается при q = 1

или q = n. Рассмотрим функцию g(x) = x lg x на интервале (0,∞).

Так как вторая производная g′′(x) > 0 положительна, функция g(x)

является выпуклой вниз 32. Поэтому

g(n)− g(n− 1) ≤ g′(n) = lg(en) ≤ n+ 1.

Используя индукционное предположение, получаем

Tmin(n) ≥ Tmin(0) + Tmin(n− 1) + 2c n ≥

≥ cg(n− 1) + 2c n ≥

≥ cg(n) = cn lg n.

Случай 2. Пусть не выполняется случай 1. В доказательстве леммы

12 показано, что минимум функции fk(x) = x lg x+(k−x) lg(k−x) на ин-

тервале (0, k) есть значение fk(k/2) = g(k)−k. Используя индукционное

предположение, получаем

Tmin(n) ≥ min1<q<n

c(q − 1) lg(q − 1) + c(n− q) lg(n− q) + 2c n =

= c min fn−1(x) | 0 < x < n− 1 + 2c n =

= c ( g(n− 1)− (n− 1) + 2n) ≥ cg(n) = cn lg n.

Таким образом, Tmin(n) = Ω(n lg n).

32 Функция g(x) называется выпуклой вниз на интервале (a, b), если для любого

x0 ∈ (a, b) касательная l(x) = g(x0) + g′(x0)(x− x0) лежит ниже графика функции,

т. е. l(x) ≤ g(x) для всех x ∈ (a, b).

101

Page 102: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

Теперь покажем, что время порядка n lg n достигается при работе ал-

горитма быстрой сортировки33. Сначала для любого n ≥ 2 определим

входную последовательность x1, . . . , xn такую, что при работе алгорит-

ма быстрой сортировки после каждого вызова функции Partition(p, r)

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

рых xp, . . . , xq−1 имеет размер ⌊(r − p + 1)/2 ⌋, а другой xq+1, . . . , xr —

размер ⌈(r− p+1)/2 ⌉− 1. Последовательность x1, . . . , xn с таким свой-

ством будем называть сбалансированной.

Индукцией по n докажем, что любую последовательность из n раз-

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

рованная последовательность. Пусть z1 < . . . < zn — упорядочивание

по возрастанию произвольной последовательности y1, . . . , yn различных

элементов. Если n = 2, последовательность z1, z2 очевидно является

сбалансированной. Пусть n > 2. Переставим элемент z⌊n/2⌋+1 на по-

следнее место, а последовательности z1, . . . , z⌊n/2⌋ и z⌊n/2⌋+2, . . . , zn со-

ответственно длины ⌊n/2⌋ и ⌈n/2⌉ − 1 упорядочим требуемым образом

согласно индукционному предположению. Далее во второй полученной

последовательности длины ⌈n/2⌉ − 1 её последний элемент переместим

на её первое место. Нетрудно понять, что построенная последователь-

ность является сбалансированной.

Обозначим через T (n) максимальное время работы алгоритма быст-

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

размерности n. Индукцией по n докажем справедливость неравенства

T (n) ≤ c n lg n

для любого n ≥ 2, где c — фиксированная константа такая, что c ≥max cmax, T (2), T (3), T (4) . Базис индукции при n = 2, 3, 4 очевиден.

Пусть n ≥ 5. Тогда ⌊n/2⌋ ≥ ⌈n/2⌉ − 1 ≥ 2. Из алгоритма QuickSort и33 Ввиду полученной в лемме 15 верхней оценки среднего времени Tave(n) =

O(n lgn) этого, вообще говоря, не требуется. Здесь мы строим пример входных дан-

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

102

Page 103: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

индукционного предположения получаем

T (n) ≤ T (⌊n/2⌋) + T (⌈n/2⌉ − 1) + T ′max(n) + 1 ≤

≤ c⌊n/2⌋ lg⌊n/2⌋+ c(⌈n/2⌉ − 1) lg(⌈n/2⌉ − 1) + cn ≤

≤ 2 cn

2lg

n

2+ cn = cn lg n.

Следовательно, T (n) = O(n lg n). Поэтому алгоритм быстрой сорти-

ровки на любой сбалансированной входной последовательности требует

времени порядка n lg n. Таким образом, минимальное время Tmin(n) есть

Θ(n lg n). Лемма 14 доказана.

Лемма 15. Среднее время Tave(n) работы алгоритма быстрой сор-

тировки QuickSort есть Θ(n lg n).

Доказательство. При вызове процедуры QuickSort сначала про-

исходит обращение к функции Partition, для которой потребуется не

более, чем cmax n операций. Определяется позиция опорного элемента,

и далее снова с помощью алгоритма QuickSort сортируются две подпо-

следовательности, длины которых равны соответственно q − 1 и n − q,

где 1 ≤ q ≤ n. Предполагая, что все входные данные равновероятны,

и оценивая ожидаемое время работы алгоритма QuickSort, нетрудно

получить следующее неравенство:

Tave(n) ≤ cmax n+1

n

n∑q=1

(Tave(q − 1) + Tave(n− q) ).

Так как для любой функции h(q) справедливы равенства

n∑q=1

h(q − 1) =n∑

q=1

h(n− q) =n−1∑q=0

h(q),

имеем следующую оценку среднего времени работы:

Tave(n) ≤ cmax n+2

n

n−1∑q=0

Tave(q).

103

Page 104: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

Пусть c — произвольная константа такая, что c ≥ max 2 cmax, Tave(0)+

Tave(1) . Докажем справедливость неравенства Tave(n) ≤ cn lgn для

любого n ≥ 2. При n = 2 имеем

Tave(2) ≤ 2 cmax + Tave(0) + Tave(1) ≤ 2 c lg 2.

В примере 1 (viii) мы доказали неравенство

n−1∑q=2

q ln q ≤ n2

2lnn− n2

4− ln 4 + 1.

Используя индукционное предположение, при n ≥ 3 получаем

Tave(n) ≤ 1

2cn+

2

n(Tave(0) + Tave(1) ) +

2

ncn−1∑q=2

q lg q ≤

≤ 1

2cn+

2

nc+

2

nc lg e

( n2

2lnn− n2

4− 2 ln 2 + 1

)=

= c( 1

2n− 2

n− n

2lg e+

2

nlg e

)+ cn lgn ≤

≤ cn lgn.

Таким образом, Tave(n) = O(n lg n). Учитывая лемму 14, получаем

Tave(n) = Θ(n lg n). Лемма 15 доказана.

Непосредственно из лемм 13–15 вытекает следующая теорема.

Теорема 14. Минимальное и среднее время работы алгоритма

быстрой сортировки есть Θ(n lg n), максимальное время равно Θ(n2).

Таким образом, в классе алгоритмов сортировки сравнением ал-

горитм QuickSort является асимптотически оптимальным в среднем.

С другой стороны, уже отсортированная входная последовательность

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

Θ(n2). В отличие от других рассмотренных методов сортировки, алго-

ритм QuickSort быстрой сортировки "предпочитает" неупорядоченные

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

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

Page 105: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

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

его работы в среднем.

Дальнейшее улучшение алгоритма быстрой сортировки связано с бо-

лее аккуратным выбором опорного элемента в функции Partition, на-

пример, случайным образом или как медиану трёх случайно определен-

ных элементов массива. В этом случае получаем рандомизированную

версию быстрой сортировки. Такой подход ускоряет время работы за

счёт уменьшения константы в выражении Θ(n lg n).

6.Алгоритм пирамидальной сортировкии оценки его трудоёмкости

При сортировке n-элементной входной последовательности алгоритм

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

среднем случае Θ(n lg n) и, следовательно, является оптимальным в

классе алгоритмов сортировки сравнением. При этом сортировка вход-

ной последовательности осуществляется на месте и не требует дополни-

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

пирамидой. Познакомимся с этим понятием.

Рассмотрим произвольный массив A = (a1, . . . , an) размерности n.

Свяжем с массивом A бинарное дерево DA, определяемое следующим

образом. Каждая вершина дерева DA соответствует элементу массива

A. В корне дерева находится элемент a1. Для любого i = 1, 2, . . . , ⌊n/2⌋вершина ai имеет двух сыновей a2i и a2i+1, за исключением случая, ко-

гда n чётно и i = ⌊n/2⌋. При этом a2i и a2i+1 являются левым и правым

сыном и располагаются под вершиной ai слева и справа соответственно.

Если n чётно, вершина an/2 имеет единственного сына an. Другие вер-

шины дерева DA не имеют сыновей. Пример такого бинарного дерева

DA для массива A = (7, 4, 3, 9, 8, 6, 1, 10) представлен на рис. 6.34 Пирамидальная сортировка была открыта J. W. J. Williams. Эффективный под-

ход к построению пирамиды предложил R.W.Floid.

105

Page 106: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

Рис. 6. Бинарное дерево DA для массива A

Мы получили представление произвольного массива A бинарным де-

ревом DA. Заметим, что это дерево обладает следующими свойствами:

• на всех уровнях35, кроме, быть может, последнего, дерево полно-

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

чем h− 1 имеет ровно двух сыновей, где h — высота дерева;

• родитель каждой вершины последнего уровня имеет ровно двух

сыновей, за исключением, возможно, крайней правой вершины v. Роди-

тель вершины v обязательно имеет левого сына.

Массив A, элементы которого являются вершинами бинарного дере-

ва DA, обладающего указанными свойствами, однозначно восстанавли-

вается по этому дереву DA. Действительно, занумеруем вершины дере-

ва DA, переходя по его уровням сверху вниз, а вершины одного уров-

ня нумеруем слева направо, последовательно переходя от левого сына

к правому сыну. Такая нумерация соответствует нумерации элементов

массива A в порядке возрастания его индексов. Поэтому можно отож-

дествлять массив A и бинарное дерево DA.

Пирамида — это структура данных, представляющая собой массив,

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

уровни занумерованы в соответствии со значением глубины их вершин.

106

Page 107: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

дополнительно обладающее следующим свойством убывания:

ai ≥ a2i, i = 1, . . . , ⌊n/2⌋,

aj ≥ a2j+1, j = 1, . . . , ⌊(n− 1)/2⌋.

Другими словами, значение каждого из сыновей не превышает значе-

ния его родителя. Из этого свойства следует, что значение корня пира-

миды является наибольшим среди значений всех её вершин. Для такой

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

убывания такие пирамиды называют убывающими пирамидами.

Не каждый массив является пирамидой. Например, для массива

A = (7, 4, 3, 9, 8, 6, 1, 10) нарушается условие a5 ≤ a2 (см. рис. 1). Од-

нако каждый массив A = (a1, . . . , an) можно преобразовать в массив,

состоящий из тех же элементов и являющийся пирамидой36. Действи-

тельно, в дереве DA элементы a⌊n/2⌋+1, . . . , an — концевые вершины, а

все вершины a1, . . . , a⌊n/2⌋ имеют сыновей. Перестроение массива A про-

ведём снизу вверх. Начиная с последней родительской вершины a⌊n/2⌋

и заканчивая корнем a1, проверяем, выполняется ли для текущей рас-

сматриваемой вершины с индексом i свойство убывания: ai ≥ a2i и

ai ≥ a2i+1 (последнее неравенство нужно проверять, когда 2i + 1 ≤ n).

Если это свойство не выполнено, элемент ai следует поменять с большим

из её сыновей a2i, a2i+1. Для этого в переменную largest записываем

индекс наибольшего из элементов ai, a2i, a2i+1. Если largest = i, то ai

уже погрузился до нужного места, иначе меняем местами элементы ai

и alargest. Далее начинаем проверку свойства убывания для элемента

alargest, находящегося на следующем уровне, и так до тех пор, пока

элемент ai не погрузится до нужного места в дереве DA. Погружение

вершины с индексом i оформим процедурой Pushdown с параметрами

pushingnumber — индекс элемента массива, погружаемого вниз до пра-

вильной позиции, и heapsize — размерность рассматриваемого массива36 Далее предполагаем n ≥ 2, так как одноэлементный массив является пирамидой.

107

Page 108: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

A (в дальнейшем мы будем уменьшать размерность пирамиды). Сам

алгоритм построения пирамиды оформим процедурой BuildHeap.

Procedure Pushdown(pushingnumber, heapsize);

i := pushingnumber;

while i ≤ ⌊heapsize/2⌋ do

begin

l := 2i;

r := 2i+ 1;

if al > ai then largest := l

else largest := i;

if (r ≤ heapsize) and (ar > alargest) then largest := r;

if largest = i then i := heapsize

else begin

Swap(ai, alargest);

i := largest

end

end.

Procedure BuildHeap(heapsize);

for i = ⌊heapsize/2⌋ downto 1 do Pushdown(i, heapsize).

Пример 13. На рис. 7 показано построение пирамиды процедурой

BuildHeap для массива A с бинарным деревом DA, изображённым на

рис. 6. Здесь приведено дерево DA для текущего состояния массива A

после выполнения процедуры Pushdown(i, 8), i = 4, 3, 2, 1.

Лемма 16. Пусть T (n) — время работы алгоритма Pushdown(i, n)

и k — высота вершины ai в дереве DA. Тогда существует такая по-

ложительная константа c, не зависящая от i, n и входного массива A

размерности n, что T (n) ≤ ck ≤ c lg n для любого n ≥ 2.

108

Page 109: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

Рис. 7. Построение пирамиды

Доказательство. Пусть h — высота бинарного дерева DA. Так как

каждая вершина дерева DA глубины меньшей, чем h−1 имеет ровно два

сына, h = ⌊lg n⌋ по лемме 11. Поэтому k ≤ h ≤ lg n. Осталось заметить,

что каждая итерация while-цикла означает спуск по дереву DA на один

уровень. Лемма 16 доказана.

Лемма 17. Минимальное, среднее и максимальное время работы

алгоритма BuildHeap(n) есть Θ(n).

Доказательство. Пусть h — высота бинарного дерева DA, со-

ответствующего массиву A размерности n. Время работы процедуры

Pushdown(i, n) зависит от высоты k вершины с индексом i и не превос-

ходит ck операций, где c — константа, определённая в лемме 16. Причём

k ≤ h и число вершин высоты k в бинарном дереве DA не превосходит

2h−k по лемме 10(i). Кроме того, n ≥ 2h в силу леммы 11. Таким об-109

Page 110: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

разом, для времени T (n) работы алгоритма BuildHeap(n) при n ≥ 2

выполняются следующие оценки

T (n) ≤h∑

k=0

2h−k ck = c2hh∑

k=0

k

2k≤

≤ cn∞∑k=0

k

2k= 2cn = O(n),

здесь при q = 1/2 мы воспользовались равенством37

∞∑k=0

kqk =q

(1− q)2.

В алгоритме BuildHeap(n) число вызовов процедуры Pushdown(i, n)

равно ⌊n/2⌋. Поэтому T (n) = Ω(n). Лемма 17 доказана.

Рассмотрим алгоритм пирамидальной сортировки. Требуется отсор-

тировать элементы массива A = (a1, . . . , an) в порядке возрастания.

С помощью процедуры BuildHeap(n) массив A преобразуем в пирами-

ду. Ввиду свойства убывания пирамиды наибольший элемент массива

будет находиться в корне пирамиды a1. Обменяем местами элементы a1

и an. В результате наибольший элемент массива A займёт требуемую

позицию. Перейдём к массиву меньшей размерности a1, . . . , an−1, кото-

рый также преобразуем в пирамиду. Заметим, что свойство убывания

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

нарушиться только для нового корневого элемента a1. Поэтому для по-

лучения пирамиды достаточно лишь погрузить элемент a1 до нужного

места с помощью процедуры Pushdown(1, n− 1). После этого массив A

на первых n − 1 позициях превратится в пирамиду. Снова обменяем

элементы a1, an−1 и перейдём к массиву размерности n− 2. Продолжа-

ем описанный процесс до тех пор, пока не дойдём до одноэлементного

массива a1. Программная реализация этого алгоритма приведена в сле-

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

грессии∞∑

k=0qk = 1

(1−q)и умножением на q при условии −1 < q < 1.

110

Page 111: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

Алгоритм HeapSort(n) пирамидальной сортировки

BuildHeap(n);

for i = n downto 2 do

begin

Swap(a1, ai);

Pushdown(1, i− 1)

end.

Пример 14. На рис. 8 показан пример работы алгоритма пирами-

дальной сортировки массива A = (7, 4, 3, 9, 8, 6, 1, 10) с бинарным дере-

вом DA, приведённым на рис. 6.

Теорема 15. Максимальное и среднее время работы алгоритма

пирамидальной сортировки есть Θ(n lgn). Минимальное время работы,

когда сортируются различные элементы, равно Θ(n lgn) и есть Θ(n),

если допускаются равные элементы сортируемой последовательности.

Доказательство. Оценим минимальное Tmin(n), среднее Tave(n) и

максимальное Tmax(n) время работы алгоритма HeapSort(n). В силу

лемм 16, 17 получаем Tmin(n) = Ω(n) и Tmax(n) = O(n)+(n−1)O(lgn) =

O(n lgn). По теореме 12 имеем Tave(n) = Ω(n lg n). Следовательно,

Tave(n) = Θ(n lg n) и Tmax(n) = Θ(n lgn).

Рассмотрим случай, когда допускаются равные элементы сортируе-

мой последовательности. На входной последовательности, состоящей из

равных элементов, в алгоритме HeapSort(n) время работы каждой из

вызываемых процедур Pushdown(1, i−1), i = n, n−1, . . . , 2 ограничено

одной константой, не зависящей от i и n (поскольку выполняется не

более одной итерации while-цикла). Поэтому Tmin(n) = Θ(n).

Теперь предположим, что все элементы сортируемого массива A раз-

мерности n различны. После выполнения процедуры BuildHeap(n) мас-

сив A является n-вершинной пирамидой. Пусть h — высота дерева DA.

111

Page 112: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

Рис. 8. Пирамидальная сортировка мас-

сива A = (7, 4, 3, 9, 8, 6, 1, 10)

112

Page 113: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

По лемме 11 имеем h = ⌊lg n⌋. Рассмотрим такое наибольшее положи-

тельное целое число h′, что вершины дерева DA, имеющие глубину, не

превосходящюю h′, образуют полное бинарное дерево. Тогда h′ — вы-

сота этого дерева и h − 1 ≤ h′ ≤ h. Рассмотрим число i0 ≥ 0 итераций

for-цикла, после которых элементы массива A на первых n− i0 местах

образуют полное бинарное дерево высоты h′. Это дерево является пи-

рамидой. Обозначим её через D′.

Упорядочим вершины дерева D′ в порядке возрастания их значе-

ний. Последние 2h′

вершин относительно этого порядка будем назы-

вать тяжёлыми вершинами. Оценим число m тяжёлых вершин глу-

бины, меньшей h′ в дереве D′. В силу свойства убывания пирамиды

D′ предки произвольной тяжёлой вершины также являются тяжёлы-

ми. Следовательно, тяжёлые вершины бинарного дерева D′ образуют

бинарное 2h′-вершинное дерево D′′ с тем же корнем. Используя лемму

10 (v), получаем 2h′= m+ k ≥ 2k − 1, где k — число концевых вершин

глубины h′ в дереве D′′. Поэтому выполняются неравенства

2h′−1 ≤ m ≤ 2h

′.

По лемме 10 (i),(iii) число концевых вершин дерева D′ равно 2h′, т. е.

числу тяжёлых вершин. Поэтому после 2h′итераций for-цикла, соответ-

ствующих значениям счётчика i = i0 +1, i0 +2, . . . , i0 +2h′, m тяжёлых

неконцевых вершин дерева D′ достигнут корня дерева в результате по-

следовательных обменов со своими непосредственными предками. Сле-

довательно, для времени T (n) работы алгоритма HeapSort(n) выпол-

няется неравенство T (n) ≥ S(D′′), где S(D′′) — сумма глубин всех m

вершин глубины, меньшей h′ в дереве D′′. Используя лемму 12, полу-

чаем S(D′′) ≥∑m

j=1⌊lg j⌋, причём m ≥ 2h′−1 ≥ ⌈n/8⌉. Учитывая пример

1(ix), заключаем

T (n) ≥⌈n/8⌉∑j=1

⌊lg j⌋ = Ω(n lg n).

Таким образом, Tmin(n) = Θ(n lgn). Теорема 15 доказана.113

Page 114: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

7.Линейный алгоритм сортировки подсчётом

До сих пор мы рассматривали алгоритмы сортировки, основанные на

сравнении входных элементов. Согласно теореме 11 время работы та-

ких алгоритмов составляет Ω(n lg n) в худшем случае. Познакомим-

ся с линейным алгоритмом сортировки подсчётом, предложенным

H.H. Seward. Разумеется, этот алгоритм улучшает оценку Ω(n lg n) за

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

В сортировке подсчётом предполагается, что все входные элементы

x1, . . . , xn — целые неотрицательные числа, не превосходящие некото-

рой заранее известной целой константы k. При программной реализа-

ции этого алгоритма используется входной массив A размерности n,

в массив B размерности n записывается отсортированная входная по-

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

размерности k + 1.

Основную идею сортировки подсчётом легко понять в случае, когда

все входные элементы различны. Предварительно для каждого элемен-

та xi входного массива A подсчитываем количество C(xi) элементов

входной последовательности, которые меньше xi. Далее элемент xi по-

мещаем в выходной массив B на позицию с номером C(xi) + 1. Напри-

мер, если элементов, меньших xi ровно 3, то в выходном массиве эле-

мент xi размещаем в B(4). Если во входной сортируемой последователь-

ности присутствуют равные числа xi1 = . . . = xim = x, i1 < . . . < im, то

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

элементы на одно место выходного массива. Для этого предварительно

подсчитываем количество C(x) элементов входной последовательности,

не превосходящих x, и размещаем элементы xi1 , xi2 , . . . , xim в выходном

массиве B на места с номерами C(x)− (m− 1), C(x)− (m− 2), . . . , C(x)

соответственно. В результате равные элементы входной последователь-

ности будут находиться в выходном массиве B в том же порядке, что и

во входном и на правильных местах.

114

Page 115: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

Алгоритм CountingSort(n) сортировки подсчётом

for i = 0 to k do C(i) := 0;

for i = 1 to n do C(A(i)) := C(A(i)) + 1;

for i = 1 to k do C(i) := C(i) + C(i− 1);

for i = n downto 1 do

begin

B(C(A(i))) := A(i);

C(A(i)) := C(A(i))− 1

end.

Пример 15. Процесс работы алгоритма CountingSort(4) на вход-

ной последовательности A = (4, 2, 4, 3) при k = 4 представлен в виде

состояний массивов B, C :

B = (∗, ∗, ∗, ∗), C = (∗, ∗, ∗, ∗, ∗);B = (∗, ∗, ∗, ∗), C = (0, 0, 0, 0, 0), i = k = 4;

B = (∗, ∗, ∗, ∗), C = (0, 0, 1, 1, 2), i = n = 4;

B = (∗, ∗, ∗, ∗), C = (0, 0, 1, 2, 4), i = k = 4;

B = (∗, 3, ∗, ∗), C = (0, 0, 1, 1, 4), i = n = 4;

B = (∗, 3, ∗, 4), C = (0, 0, 1, 1, 3), i = 3;

B = (2, 3, ∗, 4), C = (0, 0, 0, 1, 3), i = 2;

B = (2, 3, 4, 4), C = (0, 0, 0, 1, 2), i = 1.

Отсортированная последовательность (2, 3, 4, 4) записана в массиве B.

Теорема 16. Алгоритм CountingSort(n) корректно сортирует по-

следовательность длины n из целых неотрицательных чисел, не превос-

ходящих некоторой целой константы k. Минимальное, среднее и макси-

мальное время его работы есть Θ(n+ k).

Доказательство. Во втором for-цикле выполняется проверка каж-

дого входного элемента. Если его значение равно x, то к величине C(x)

прибавляется единица. Таким образом, после выполнения этого цикла

115

Page 116: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

для каждого i = 0, 1, . . . , k в C(i) будет записано количество элементов

входного массива A, равных i. Используя эту информацию, в следую-

щем for-цикле для каждого i = 0, 1, . . . , k в C(i) перезаписывается число

входных элементов, не превосходящих i. Наконец, в последнем for-цикле

каждый элемент A(i) входного массива помещается в надлежащую по-

зицию выходного массива B. Действительно, если все n входных эле-

ментов различны, то в выходном отсортированном массиве B число A(i)

должно стоять на месте с номером C(A(i)), поскольку имеется C(A(i))

элементов, меньших i. Если в массиве A встречаются повторения, то

после каждой записи числа A(i) в массив B число C(A(i)) уменьша-

ется на единицу, поэтому при следующей встрече с числом, равным

A(i), оно будет записано на одну позицию левее. Таким образом, алго-

ритм CountingSort корректно сортирует входную последовательность

элементов массива A.

Оценим время работы алгоритма CountingSort(n) на произвольной

входной последовательности. На выполнение первого и третьего for-

циклов затрачивается время Θ(k), а на выполнение второго и четвёрто-

го for-циклов — время Θ(n). Таким образом, полное время работы есть

Θ(n+ k). Теорема 16 доказана.

Следствие 2. Если k = Θ(n), то алгоритм CountingSort(n) явля-

ется линейным.

116

Page 117: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

Список литературы

1. Алексеев В.Б. Введение в теорию сложности алгоритмов. М.:

МГУ, 2002.

2. Андерсен Д.А. Дискретная математика и комбинаторика. М.: Издат.

дом "Вильямс", 2004.

3. Ахо А.В., Хопкрофт Д.Э., Ульман Д.Д. Структуры данных и ал-

горитмы. М.: Издат. дом "Вильямс", 2007.

4. Виленкин Н.Я. Комбинаторика. М.: Наука, 1969.

5. Гудман С., Хидетниеми С. Введение в разработку и анализ алго-

ритмов. М.: Мир, 1981.

6. Емеличев В.А., Мельников О.И., Сарванов В.И., Тышкевич Р.И.

Лекции по теории графов. М.: Наука, 1990.

7. Иванов Б.Н. Дискретная математика. Алгоритмы и программы. М.:

Физматлит, 2007.

8. Кнут Д.Э. Искусство программирования. М.: Издат. дом "Ви-

льямс", 2007. Т. 1–4.

9. Корман Т., Ривест Р., Лейзерсон Ч. Алгоритмы: построение и ана-

лиз. М.: Издат. дом "Вильямс", 2007.

10. Кузюрин Н.Н., Фомин С.А. Эффективные алгоритмы и сложность

вычислений. М.: МГУ, 2009.

11. Липский В. Комбинаторика для программистов. М.: Мир, 1988.

12. Макконнелл Дж. Основы современных алгоритмов. М.: Техно-

сфера, 2004.

13. Новиков Ф.А. Дискретная математика для программистов. СПб.:

Издат. дом "Питер", 2007.117

Page 118: КОМБИНАТОРНЫЕ АЛГОРИТМЫfit.nsu.ru/data_/courses/niu/daio_komb_alg_uchpos.pdfАлгоритм нахождения ... горитмов формализация

14. Пападимитриу Х., Стайглиц К. Комбинаторная оптимизация. Ал-

горитмы и сложность. М.: Мир, 1985.

15. Плотников А.Д. Дискретная математика. М.: Новое знание, 2005.

16. Рейнгольд Э., Нивергельт Ю., Део Н. Комбинаторные алгоритмы,

теория и практика. М.: Мир, 1980.

17. Рыбников К.А. Введение в комбинаторный анализ. М.: МГУ, 1985.

18. Харари Ф. Теория графов. М.: Мир, 1973.

19. Холл М. Комбинаторика. М.: Мир, 1970.

20. Шень А. Программирование: теоремы и задачи. М.: МЦНМО, 2004.

21. Эндрюс Г. Теория разбиений. М.: Наука, 1982.

Учебное издание

Федоряева Татьяна Ивановна

Комбинаторные алгоритмыУчебное пособие

Редактор Е. П.Войтенко

Подписано в печать 22.12.2011 г.

Формат 60×84 1/16. Оффсетная печать.

Уч.-изд. л. 7,4. Усл.-печ. л. 7. Тираж 100 экз.

Заказ

Редакционно-издательский центр НГУ.

630090, Новосибирск, 90, ул. Пирогова, 2.