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

          

Целые


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

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

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

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

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

(2.1)

в которой каждое

Комбинаторные алгоритмы для программистов
- целое, удовлетворяющее условию
Комбинаторные алгоритмы для программистов
и
Комбинаторные алгоритмы для программистов
. Нуль представляется последовательностью
Комбинаторные алгоритмы для программистов
.
Комбинаторные алгоритмы для программистов
называется основанием системы
Комбинаторные алгоритмы для программистов
. Целое, соответствующее последовательности (2.1), имеет вид

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

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

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

На протяжении истории использовались различные значения

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

Единственность этого представления можно доказать методом от противного. Числа

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

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

Если

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

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

и поскольку

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

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

(2.2)

что невозможно. Таким образом, мы должны иметь

Комбинаторные алгоритмы для программистов
. Аналогично, если
Комбинаторные алгоритмы для программистов
, мы имели бы снова неравенство (2.2) и отсюда с необходимостью
Комбинаторные алгоритмы для программистов
.
Следовательно, число

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


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

Для доказательства того, что каждое положительное целое имеет представление по основанию
Комбинаторные алгоритмы для программистов
, достаточно задать алгоритм, конструирующий (с необходимостью единственное) представление данного числа
Комбинаторные алгоритмы для программистов
.

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

Он строит последовательность
Комбинаторные алгоритмы для программистов
путем повторения деления на
Комбинаторные алгоритмы для программистов
и записи остатков. Пусть на первом шаге при делении
Комбинаторные алгоритмы для программистов
на
Комбинаторные алгоритмы для программистов
остаток будет
Комбинаторные алгоритмы для программистов
. Частное, полученное в результате первого шага, делим на
Комбинаторные алгоритмы для программистов
, вновь полученное частное делим на
Комбинаторные алгоритмы для программистов
и так далее. Полученная в результате такого процесса последовательность остатков и будет требуемым представлением
Комбинаторные алгоритмы для программистов
по основанию
Комбинаторные алгоритмы для программистов
.

Важным обобщением систем счисления с основанием
Комбинаторные алгоритмы для программистов
являются смешанные системы счисления, в которых задается не единственное основание
Комбинаторные алгоритмы для программистов
, а последовательность оснований
Комбинаторные алгоритмы для программистов
и последовательность (2.2) соответствует целому
Комбинаторные алгоритмы для программистов


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

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

Пример. Рассмотрим нашу систему измерения времени: секунды, минуты, часы, дни недели и годы. Это - в точности смешанная система с
Комбинаторные алгоритмы для программистов
.

Представление целого
Комбинаторные алгоритмы для программистов
в смешанной системе счисления
Комбинаторные алгоритмы для программистов
осуществляется с помощью алгоритма 2, который является простым обобщением алгоритма 1. Вместо того, чтобы для получения
Комбинаторные алгоритмы для программистов
в качестве делителя всегда использовалась
Комбинаторные алгоритмы для программистов
, в алгоритме 2 используется
Комбинаторные алгоритмы для программистов
.

Алгоритм 2. Преобразование числа
Комбинаторные алгоритмы для программистов
в его представление
Комбинаторные алгоритмы для программистов
в смешанной системе счисления
Комбинаторные алгоритмы для программистов



Последовательности


Бесконечная последовательность

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

формально определяется как функция

Комбинаторные алгоритмы для программистов
, областью определения которой является множество положительных целых чисел:
Комбинаторные алгоритмы для программистов
. Во многих случаях индексирование последовательности более удобно начинать с нуля; тогда областью определения
Комбинаторные алгоритмы для программистов
будет множество целых неотрицательных чисел. Аналогично определим конечную последовательность или список

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

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

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

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

(2.3)

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

а перестановка

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

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

В комбинаторных алгоритмах часто приходится встречаться с представлениями конечных последовательностей (или начальных сегментов бесконечных последовательностей) и операциями над ними.



Различные способы представлений


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

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

Рис. 2.1.

 

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

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

Так,

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

Описанное выше представление последовательности имеет ряд преимуществ. Во-первых, оно легко осуществимо и требует небольших расходов в смысле памяти. Кроме того, оно полезно и потому, что существует простое соотношение между

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

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

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

Например, чтобы представить массив размером

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

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

(2.4)

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

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

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

ячейка для

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

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

Это представление известно как построчная запись матрицы; постолбцовая запись получается, если (2.4) рассматривать как последовательность

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

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

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

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

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

Например, характеристический вектор начального сегмента последовательности (2.3)

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

характеристический вектор для простых чисел:

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

Здесь основной последовательностью является последовательность целых положительных чисел. В ЭВМ с 32-разрядными словами для запоминания простых чисел, меньших

Комбинаторные алгоритмы для программистов
, потребуется
Комбинаторные алгоритмы для программистов
слов. Замечая далее, что для
Комбинаторные алгоритмы для программистов
число
Комбинаторные алгоритмы для программистов
не простое, можно сэкономить половину этого поля, выписывая разряды только для чисел видов
Комбинаторные алгоритмы для программистов
, и запоминая, что простое число 2 отсутствует. Таким образом, простые числа, меньшие чем
Комбинаторные алгоритмы для программистов
, можно записать только 15625 словами. Поскольку число простых чисел, меньших
Комбинаторные алгоритмы для программистов
, равно 78498, последовательное представление, описанное ранее, потребовало бы поля в пять раз меньшего размера.

Характеристические векторы полезны в ряде случаев. Их полезность вытекает из их компактности, существования простого фиксированного соотношения между

Комбинаторные алгоритмы для программистов
и адресом
Комбинаторные алгоритмы для программистов
-го разряда и возможности при таком представлении очень легко исключать элементы.

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

Комбинаторные алгоритмы для программистов
. Кроме того, их трудно использовать, если не существует простого соотношения между
Комбинаторные алгоритмы для программистов
и
Комбинаторные алгоритмы для программистов
.Если такое соотношение сложное, то использование характеристических векторов может быть очень не экономичным в смысле времени обработки. Если последовательности недостаточно плотные, то значительным может оказаться объем памяти. В случае простых чисел между
Комбинаторные алгоритмы для программистов
и
Комбинаторные алгоритмы для программистов
имеется простое соотношение:
Комбинаторные алгоритмы для программистов
(или
Комбинаторные алгоритмы для программистов
, если использовать только нечетные числа). Теорема о простых числах утверждает, что число простых чисел, меньших
Комбинаторные алгоритмы для программистов
, приблизительно равно
Комбинаторные алгоритмы для программистов
; таким образом, простые числа относительно плотно распределены в множестве целых чисел.
Комбинаторные алгоритмы для программистов


в качестве основных объектов допускает


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

Программы


Программа 1. Создание списка.

// Алгоритм реализован на языке программирования Turbo-C++. #include <stdio.h> #include <conio.h> #include <dos.h>

struct List{int i; List*next; }; List*head=NULL;

void Hed(int i) {if(head==NULL){head=new List; head->i=1; head->next=NULL; }else { struct List*p,*p1; p=head; while(p->next!=NULL) p=p->next;

p1=new List; p1->i=i; p1->next=NULL; p->next=p1; } }

int s=0;

void Print(List*p) {cprintf(" %d",p->i); if(p->next!=NULL)Print(p->next); }

void delist() {List*p; while(head!=NULL) {p=head; head=head->next; delete(p); } } void Vstavka(int i1,int c) {List*p=head,*p1; while(p->i!=i1) p=p->next; p1=new List; p1->i=c; p1->next=p->next; p->next=p1; }

void main() { clrscr(); for(int i=1;i<=10;i++) Hed(i); textcolor(12); Print(head); textcolor(1); Vstavka(10,11); printf("\n"); Print(head); textcolor(11); Vstavka(3,12); printf("\n"); Print(head); textcolor(14); Vstavka(5,13); printf("\n"); Print(head); delist(); getch(); }

Программа 2. Создание стека и работа со стеком.

//Работа со стеком // Алгоритм реализован на языке программирования Turbo-C++. #include <stdio.h> #include <dos.h> #include <iostream.h> #include <PROCESS.H> #include <STDLIB.H> #include <conio.H> #define max_size 200 // char s[max_size]; //компоненты стека int s[max_size]; int next=0; // позиция стека int Empty() { return next==0; } int Full() { return next==max_size; } void Push() { if (next==max_size) { cout <<"Ошибка: стек полон"<<endl;}

else { next++;cout <<"Добавлен"<<endl; cout <<"Что поместить в стек?"<<endl; cin <<s[next-1]; } } void OUTst() {int i=0; if (next==0) { cout <<"Cтек пуст"<<endl;}

else { for(i=0;i <next;i++) cout <<s[i] <<"" <<endl; } } void Clear() { next=0; } Poz() { return next; } void Del() { int a; if (next==0) cout <<"Ошибка: стек пуст" <<endl; else {next--;cout <<"Удален" <<endl;} } void menu(){ cout <<"0: распечатать стек" <<endl; cout <<"1: добавить в стек" <<endl; cout <<"2: удалить из стека" <<endl; cout <<"3: узнать номер позиции в стеке" <<endl; cout <<"4: узнать, пуст ли стек" <<endl; cout <<"5: узнать, полон ли стек" <<endl; cout <<"6: очистить стек" <<endl; cout <<"7: выход" <<endl; } main() { char c; clrscr(); textcolor(11); do { menu(); cin"c; clrscr(); switch (c) { case "0":OUTst();getch();break; case "1":Push();break; case "2":Del();getch();break; case "3":cout <<"Hомер" <<Poz() <<endl;getch();break; case "4":if (Empty()==1) cout <<"Пуст" <<endl; else cout <<"Hе пуст" <<endl;getch();break; case '5':if (Full()==1)cout <<"Полн" <<endl; else cout <<"Hе полон" <<endl;getch();break; case '6':Clear();cout <<"Стек очищен" <<endl;getch();break; case '7':exit(1); } delay(200); } while (c!=7); return 0; }

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


Разновидности связанных списков


Тривиальной модификацией связанного списка, изображенного на рис 3.2, будет следующее несколько более гибкое представление последовательности: если

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

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

Рис. 3.5.  Циклический список

Еще большая гибкость достигается, если использовать дважды связанный список, когда каждый элемент

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

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

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

Рис. 3.6.  Дважды связанный список



Стеки и очередь


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

Стеки и очереди имеют важное значение. Для выполнения какой-либо определенной задачи может потребоваться выполнение ряда подзадач. Каждая подзадача может также привести к другим требующим выполнения подзадачам. И стеки, и очереди являются механизмом, посредством которого запоминаются подзадачи, подлежащие выполнению, а также порядок, в котором они должны быть выполнены. В некоторых случаях порядок таков: "Первым пришел - последним ушел"; тогда удобно использовать стеки. Если порядок подчиняется правилу "Первым пришел - первым ушел", то подходящим инструментом являются очереди.



Связанное распределение


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

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

поставлен в соответствии указатель (ссылка)

Комбинаторные алгоритмы для программистов
, отмечающий ячейку, в которой записаны
Комбинаторные алгоритмы для программистов
и
Комбинаторные алгоритмы для программистов
. Существует также указатель
Комбинаторные алгоритмы для программистов
, который указывает начальную ячейку последовательности, то есть ячейку с символами
Комбинаторные алгоритмы для программистов
и
Комбинаторные алгоритмы для программистов
. Все сказанное выше проиллюстрировано на рис. 3.1.

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

Рис. 3.1. 

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

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

Linked list - список с использованием указателей: список, в котором каждый элемент содержит указатель на следующий элемент или два указателя - на следующий и предыдущий. Поскольку для

Комбинаторные алгоритмы для программистов
следующего элемента не существует, будем использовать обозначение
Комбинаторные алгоритмы для программистов
, где
Комбинаторные алгоритмы для программистов
- пустой , или нулевой указатель . Так как точные значения
Комбинаторные алгоритмы для программистов
для программиста не существенны, то в более общем виде связанное представление, показанное на рис. 3.1, можно изобразить так, как показано на рис.3.2.

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

Рис. 3.2.  Другой, более употребительный, способ представления списка

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

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


после такой операции элемента
Комбинаторные алгоритмы для программистов
в последовательности больше не будет (рис.3.3). Чтобы в последовательность, изображенную на рис. 3.2, включить новый элемент
Комбинаторные алгоритмы для программистов
, необходимо только воспроизвести новый элемент в некоторой ячейке
Комбинаторные алгоритмы для программистов
с
Комбинаторные алгоритмы для программистов
и
Комбинаторные алгоритмы для программистов
и присвоить
Комбинаторные алгоритмы для программистов
.Это изображено на рис.3.4.

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

Рис. 3.3.  Исключение элемента из связанного списка

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

Рис. 3.4.  Включение элемента в связанный список

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

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

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


Модифицированный список


Задача 1. Создать список, элементами которого являются числа: 1 2 3 4 5 6 7 8 9. Вывести список на экран терминала. Включить в связанный список элемент 2005 после каждого элемента, который делится на 3. Модифицированный список вывести на экран терминала.
Задача 2. Очередью с приоритетом называется линейный список, который оперирует в режиме "первым включается - с высшим приоритетом исключается"; иными словами, каждому элементу очереди сопоставлено некоторое число - приоритет. Включения производятся в конец очереди, а исключения - в любом месте очереди, поскольку исключаемый элемент - это всегда элемент с высшим приоритетом. Нужно описать алгоритм (и его реализацию) включения и исключения для очередей с приоритетом.

Деревья


Конечное корневое дерево

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

поддеревьев

Комбинаторные алгоритмы для программистов
. Будем рассматривать только корневые деревья. Узлы, не имеющие поддеревьев, называются листьями; остальные узлы называются внутренними узлами.

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

Рис. 4.1.

 

Дерево с 11 узлами, помеченными буквами от

Комбинаторные алгоритмы для программистов
до
Комбинаторные алгоритмы для программистов
. Узлы с метками
Комбинаторные алгоритмы для программистов
являются листьями; другие узлы внутренние. Узел с меткой
Комбинаторные алгоритмы для программистов
- корень

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

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

В описании соотношений между узлами дерева используем терминологию, принятую в генеалогических деревьях. Так, говорят, что в дереве или поддереве все узлы являются потомками его корня, и наоборот, корень есть предок всех своих потомков. Корень именуют отцом корней его поддеревьев, которые в свою очередь будут сыновьями корня. Например, на рис. 4.1 узел

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

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

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

Рис. 4.2.  Различные деревья

Определим лес как упорядоченное множество деревьев; в связи с этим можно перефразировать определение дерева: дерево есть непустое множество узлов, такое, что существует один выделенный узел, называемый корнем дерева, а оставшиеся узлы образуют лес с

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

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

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

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

Рис. 4.3.  Различные бинарные деревья

Как деревья, однако, они не отличаются от дерева, изображенного на рис. 4.4.

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

Рис. 4.4.  Не бинарное дерево

Различие между деревом и бинарным деревом состоит в том, что дерево не может быть пустым, а каждый узел дерева может иметь произвольное число поддеревьев; в то же время, бинарное дерево может быть пустым. Каждая из вершин бинарного дерева может иметь 0, 1 или 2 поддерева, и существует различие между левым и правым поддеревьями.


Длина путей


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

Наиболее важные количественные характеристики деревьев связаны с уровнями узлов. Уровень

Комбинаторные алгоритмы для программистов
определяется рекурсивно и считается равным нулю, если
Комбинаторные алгоритмы для программистов
корень
Комбинаторные алгоритмы для программистов
; в противном случае уровень
Комбинаторные алгоритмы для программистов
определяется как
Комбинаторные алгоритмы для программистов
. Понятие уровня дает возможность определить высоту
Комбинаторные алгоритмы для программистов
дерева
Комбинаторные алгоритмы для программистов
:

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

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



Представления


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

Комбинаторные алгоритмы для программистов
и нескольких полей для указателей. Например, представление, которое будет удобным для изложения множества и мультимножества, для каждого узла имеет единственное поле для указателя
Комбинаторные алгоритмы для программистов
, указывающего на отца данного узла. При этом приведенное на рис. 4.1 дерево будет выглядеть так, как показано на рис. 4.6.

Такое представление полезно, если необходимо подниматься по дереву от потомков к предкам. Такая операция встречается довольно редко. Чаще требуется опуститься по дереву от предков к потомкам.

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

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

Рис. 4.5.  Дерево из рис. 4.1, представленное с помощью узлов с полем
Комбинаторные алгоритмы для программистов
и указателем
Комбинаторные алгоритмы для программистов

Каждый узел в этом случае имеет три поля:

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

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

Рис. 4.6.  Бинарное дерево и его представление с помощью узлов с тремя полями
Комбинаторные алгоритмы для программистов
,
Комбинаторные алгоритмы для программистов
,
Комбинаторные алгоритмы для программистов

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

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

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

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



Программa


Программа 1. Обход бинарного дерева в глубину

//Обход ориентированных графов - поиск в глубину - //обобщение обхода дерева в прямом порядке #include <stdio.h> #include <string.h> #include <conio.h> #include <stdlib.h>

int matr_sm[50][50]; int mark[50]; int n;

void vvod () {int v1,v2; printf("Введите кол-во вершин в графе: "); do { scanf("%d",&n); if (n>51) printf("Ошибка!! Введите кол-во вершин в графе: "); } while (n>51);

for (int i=0;i <50;i++) for (int j=0;j <50;j++) matr_sm[i][j]=0;

printf("\nВведите связанные вершины : \n"); do { scanf("%d ",&v1); if (v1>n) //исходящая вершина { printf("ОШИБКА ВВОДА !!!!!!!!!"); abort(); } if (v1==0){break;} // конец ввода scanf(" %d ",&v2); if (v2>n) // входящая вершина { printf("ОШИБКА ВВОДА !!!!!!!!!"); abort(); } if (v2==0){break;} //конец ввода matr_sm[v2-1][v1-1]=1; } while(1); }

void vivod() { for (int j=0;j <n;j++) { for (int i=0;i<n;i++) printf("%d ",matr_sm[j][i]); printf("\n"); } }

void dfs(int v) { int w; mark[v]=1; //посетили printf("%i ",v+1); //и выдали на экран for (w=0;w<n;w++) if (matr_sm[v][w]==1); else if (mark[w]==0) dfs(w); }

void main() { clrscr(); vvod(); // printf("\n"); printf("МАТРИЦА СМЕЖНОСТИ\n"); // vivod();

printf("\n РЕЗУЛЬТАТ ПОИСКА В ГЛУБИНУ \n"); for (int v=0;v<50;v++) mark[v]=0; for (v=0;v<n;v++) //если вершина не посещалась, то посетить if (mark[v]==0) dfs(v);

getch(); }

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


Прохождения


Во многих приложениях необходимо пройти лес, заходя в узлы, то есть обрабатывая их некоторым систематическим образом. Посещение каждого узла может быть связано с простой операцией, такой как печать содержимого, или со сложной, такой как вычисление функции. Будем предполагать, что при посещении узла структура леса не меняется. Рассмотрим четыре основных способа прохождения леса: в глубину, снизу вверх, в горизонтальном порядке и для бинарных деревьев - в симметричном порядке.

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

Посетить корень первого дерева.пройти в глубину поддерева первого дерева (если оно есть).Пройти в глубину оставшиеся деревья, если они есть.

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

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

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

Рис. 4.7.  Лес

Название "в глубину" отражает тот факт, что после посещения некоторого узла мы продолжаем прохождение в глубь дерева всякий раз, когда это возможно. Такой порядок особенно полезен в процедурах поиска.

Для бинарных деревьев эта процедура упрощается и выглядит следующим образом.

Посетить корень.Пройти в глубину левое поддеревоПройти в глубину правое поддерево.

Прохождение снизу вверх, известное также как обратный порядок или концевой порядок, осуществляется согласно следующей рекурсивной процедуре:

Пройти снизу вверх поддеревья первого дерева, если они есть.Посетить корень первого дерева.Пройти снизу вверх оставшиеся деревья, если они есть.

Название "снизу вверх" связано с тем, что в момент посещения произвольного узла его потомки оказываются уже пройденными. Такой порядок прохождения полезен, в частности, потому, что он позволяет вычислять рекурсивно определенные функции на лесах. При этом порядке прохождения узлы леса, показанного на рис. 4.7, проходятся в такой последовательности:

Комбинаторные алгоритмы для программистов
. Рекурсивная процедура прохождения снизу вверх применительно к бинарным деревьям имеет следующий вид:


Пройти снизу вверх левое дерево.Пройти снизу вверх правое дерево.Посетить корень.

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

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

Такой способ прохождения известен также как лексикографический порядок или внутренний порядок. Заметим, что прохождение леса снизу вверх эквивалентно прохождению в симметричном порядке бинарного дерева, соответствующего этому лесу (при естественном соответствии).

Сравнивая рекурсивные процедуры прохождения бинарных деревьев в глубину, снизу вверх и в симметричном порядке, можно обнаружить их значительное сходство:

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

Горизонтальный порядок прохождения. При таком способе узлы леса проходятся слева направо, уровень за уровнем от корня вниз. Таким образом, в соответствии с этой процедурой узлы леса, показанного на рис.4.7, будут проходиться в следующем порядке:
Комбинаторные алгоритмы для программистов
. Такое прохождение дерева полезно в определенных алгоритмах на графах.


Построить алгоритм обхода


Задача 1. Построить алгоритм обхода бинарного дерева (см. рис. 4.6,(а)) в глубину.

Число сочетаний


Рассмотрим подмножества множества, состоящего из пяти элементов, и подсчитаем их число. При этом записывать подмножества будем не с помощью букв, как обычно, а в виде последовательностей длиной пять, составленных из нулей и единиц. Каждая из единиц указывает на наличие в подмножестве соответствующего элемента. Например, подмножества, содержащие один элемент, будут изображаться следующими последовательностями: 10000, 01000, 00100, 00010, 00001. Пустое подмножество

Комбинаторные алгоритмы для программистов
будет соответствовать последовательности 00000. Подмножества, содержащие по два элемента из пяти, запишутся с помощью следующих последовательностей: 11000, 10100, 10010, 10001, 01100. 01010, 01001, 00110, 00101, 00011. Всего их
Комбинаторные алгоритмы для программистов
.

Вообще, число сочетаний из

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



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


С помощью леса можно представить перестановки из

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

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

Рис. 5.1.  Всевозможные перестановки прочитываются по этой схеме от корневой до висячей вершины соответствующего дерева. Ярус показывает номер места, на котором расположен элемент. Число висячих вершин леса равно числу перестановок



Комбинаторные задачи теории информации


Информация - сведения, неизвестные до их получения, или данные, или значения, приписанные данным.

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

Задачу, похожую на только что решенную, приходится решать в теории информации. Предположим, что сообщение передается с помощью сигналов нескольких типов. Длительность передачи сигнала первого типа равна

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

Задача 6. Сколько различных сообщений можно передать с помощью этих сигналов за

Комбинаторные алгоритмы для программистов
единиц времени? При этом учитываются лишь "максимальные" сообщения, то есть сообщения, к которым нельзя присоединить ни одного сигнала, не выйдя за рамки отведенного для передачи времени.

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

Комбинаторные алгоритмы для программистов
через
Комбинаторные алгоритмы для программистов
. Рассуждая точно так же, как и в задаче о марках, получаем, что
Комбинаторные алгоритмы для программистов
удовлетворяет соотношению
Комбинаторные алгоритмы для программистов

(5.8)

При этом снова
Комбинаторные алгоритмы для программистов
, если
Комбинаторные алгоритмы для программистов
и
Комбинаторные алгоритмы для программистов
Комбинаторные алгоритмы для программистов


Разные статистики


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

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

Вопрос о том, какой статистике подчиняются те или иные частицы, зависит от вида этих частиц. В классической статистической физике, созданной Максвеллом и Больцманом, частицы считаются различимыми друг от друга. Такой статистике подчиняются, например, молекулы газа. Известно, что

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

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

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

Комбинаторные алгоритмы для программистов
различных распределений. Эта статистика называется статистикой Дирака-Ферми.



В задачах, которые мы сейчас


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

Сколькими способами можно сделать такое


Общая постановка этих задач:
Задач 1. Раскладка по ящикам
Даны
Комбинаторные алгоритмы для программистов
различных предметов и
Комбинаторные алгоритмы для программистов
ящиков. Надо положить в первый ящик
Комбинаторные алгоритмы для программистов
предметов, во второй -
Комбинаторные алгоритмы для программистов
предметов,..., в
Комбинаторные алгоритмы для программистов
-й -
Комбинаторные алгоритмы для программистов
предметов, где
Комбинаторные алгоритмы для программистов
. Сколькими способами можно сделать такое распределение?
Число различных раскладок по ящикам равно
Комбинаторные алгоритмы для программистов
Эту формулу можно получить при решении следующей, на первый взгляд, совсем непохожей задачи:
Задача 2. Перестановки с повторением.
Имеются предметы
Комбинаторные алгоритмы для программистов
различных типов. Сколько различных перестановок можно сделать из
Комбинаторные алгоритмы для программистов
предметов первого типа,
Комбинаторные алгоритмы для программистов
предметов второго типа, ...,
Комбинаторные алгоритмы для программистов
предметов
Комбинаторные алгоритмы для программистов
-го типа? Число элементов в каждой перестановке равно
Комбинаторные алгоритмы для программистов
. Поэтому если бы все элементы были различны, то число перестановок равнялось бы
Комбинаторные алгоритмы для программистов
. Но из-за того, что некоторые элементы совпадают, получится меньшее число перестановок. В самом деле, возьмем, например, перестановку
Комбинаторные алгоритмы для программистов
(5.1)
в которой сначала выписаны все элементы первого типа, потом все элементы второго типа, ..., наконец, все элементы
Комбинаторные алгоритмы для программистов
-го типа. Элементы первого типа можно переставлять друг с другом
Комбинаторные алгоритмы для программистов
способами. Но так как все эти элементы одинаковы, то такие перестановки ничего не меняют. Точно так же ничего не меняют
Комбинаторные алгоритмы для программистов
перестановок элементов второго типа, ...,
Комбинаторные алгоритмы для программистов
перестановок элементов
Комбинаторные алгоритмы для программистов
-го типа.
Перестановки элементов первого типа, второго типа и так далее можно делать независимо друг от друга. Поэтому элементы перестановки 5.1. можно переставлять друг с другом
Комбинаторные алгоритмы для программистов
способами так, что она остается неизменной. То же самое верно и для любого другого расположения элементов. Поэтому множество всех
Комбинаторные алгоритмы для программистов
перестановок распадается на части, состоящие из
Комбинаторные алгоритмы для программистов
одинаковых перестановок каждая. Значит, число различных перестановок с повторениями, которые можно сделать из данных элементов, равно
Комбинаторные алгоритмы для программистов
(5.2)
где
Комбинаторные алгоритмы для программистов
.
Пользуясь формулой 5.2, можно ответить на вопрос: сколько перестановок можно сделать из букв слова "Миссисипи"? Здесь у нас одна буква "м", четыре буквы "и", три буквы "с" и одна буква "п", а всего 9 букв. Значит, по формуле 5.2 число перестановок равно
Комбинаторные алгоритмы для программистов
Чтобы установить связь между этими задачами, занумеруем все
Комбинаторные алгоритмы для программистов
мест, которые могут занимать наши предметы.
Каждой перестановке соответствует распределение номеров мест на
Комбинаторные алгоритмы для программистов
классов. В первый класс попадают номера тех мест, на которые попали предметы первого типа, во второй - номера мест предметов второго типа и так далее. Тем самым устанавливается соответствие между перестановками с повторениями и раскладкой номеров мест по "ящикам". Понятно, что формулы решения задач оказались одинаковыми.
В рассмотренных задачах мы не учитывали порядок, в котором расположены элементы каждой части. В некоторых задачах этот порядок надо учитывать.
Задача 3. Флаги на мачтах.
Имеется
Комбинаторные алгоритмы для программистов
различных сигнальных флагов и
Комбинаторные алгоритмы для программистов
мачт, на которые их вывешивают. Значение сигнала зависит от того, в каком порядке развешены флаги. Сколькими способами можно развесить флаги, если все флаги должны быть использованы, но некоторые из мачт могут оказаться пустыми?
Каждый способ развешивания флагов можно осуществить в два этапа. На первом этапе мы переставляем всеми возможными способами данные
Комбинаторные алгоритмы для программистов
флагов. Это можно сделать
Комбинаторные алгоритмы для программистов
способами. Затем берем один из способов распределения
Комбинаторные алгоритмы для программистов
одинаковых флагов по
Комбинаторные алгоритмы для программистов
мачтам (число этих способов
Комбинаторные алгоритмы для программистов
). Пусть этот способ заключается в том, что на первую мачту надо повесить
Комбинаторные алгоритмы для программистов
флагов, на вторую -
Комбинаторные алгоритмы для программистов
флагов, ..., на
Комбинаторные алгоритмы для программистов
Комбинаторные алгоритмы для программистов
флагов, где
Комбинаторные алгоритмы для программистов
. Тогда берем первые
Комбинаторные алгоритмы для программистов
флагов данной последовательности и развешиваем в полученном порядке на первой мачте; следующие
Комбинаторные алгоритмы для программистов
флагов развешиваем на второй мачте и т.д. Ясно, что используя все перестановки
Комбинаторные алгоритмы для программистов
флагов и все способы распределения
Комбинаторные алгоритмы для программистов
одинаковых флагов по
Комбинаторные алгоритмы для программистов
мачтам, получим все способы решения поставленной задачи. По правилу произведения получаем, что число способов развешивания флагов равно
Комбинаторные алгоритмы для программистов

(5.3)
Вообще, если имеется
Комбинаторные алгоритмы для программистов
различных вещей, то число способов распределения этих вещей по
Комбинаторные алгоритмы для программистов
различным ящикам равно
Комбинаторные алгоритмы для программистов
.

Задачи на разбиение чисел


Теперь мы переходим к задачам, в которых все разделяемые предметы совершенно одинаковы. В этом случае можно говорить не о разделении предметов, а о разбиении натуральных чисел на слагаемые (которые, конечно, тоже должны быть натуральными числами).

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

Задача 4. Отправка бандероли.

За пересылку бандероли надо уплатить 18 рублей. Сколькими способами можно оплатить ее марками стоимостью 4, 6, и 10 рублей, если два способа, отличающиеся порядком марок, считаются различными (запас марок различного достоинства считаем неограниченным)?

Обозначим через

Комбинаторные алгоритмы для программистов
число способов, которыми можно наклеить марки в 4, 6 и 10 рублей так, чтобы общая стоимость этих марок равнялась
Комбинаторные алгоритмы для программистов
. Тогда для
Комбинаторные алгоритмы для программистов
справедливо следующее соотношение:
Комбинаторные алгоритмы для программистов

(5.4)

Пусть имеется некоторый способ наклейки марок с общей стоимостью
Комбинаторные алгоритмы для программистов
, и пусть последней наклеена марка стоимостью 4 рубля. Тогда все остальные марки стоят (
Комбинаторные алгоритмы для программистов
) рубля. Наоборот, присоединяя к любой комбинации марок общей стоимостью (
Комбинаторные алгоритмы для программистов
) рубля одну четырехрублевую марку, получаем комбинацию марок стоимостью
Комбинаторные алгоритмы для программистов
рублей. При этом из разных комбинаций стоимостью (
Комбинаторные алгоритмы для программистов
) рублей получается разные комбинации стоимостью
Комбинаторные алгоритмы для программистов
рублей. Итак, число искомых комбинаций, где последней наклеена марка стоимостью 4 рубля, равно
Комбинаторные алгоритмы для программистов
.

Точно так же доказывается, что число комбинаций, оканчивающихся на на шестирублевую марку, равно

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

Соотношение 5.4 позволяет свести задачу о наклеивании марок на сумму

Комбинаторные алгоритмы для программистов
рублей к задачам о наклеивании марок на меньшие суммы. Но при малых значениях
Комбинаторные алгоритмы для программистов
задачу легко решить непосредственно. Простой подсчет показывает, что
Комбинаторные алгоритмы для программистов
Равенство
Комбинаторные алгоритмы для программистов
означает, что сумму в 0 рублей можно уплатить единственным образом: совсем не наклеивая марок. А сумму в 1,2,3,5,7 и 9 рублей вообще никак нельзя получить с помощью марок стоимостью 4, 6 и 10 рублей.
Используя значения
Комбинаторные алгоритмы для программистов
для
Комбинаторные алгоритмы для программистов
, легко найти
Комбинаторные алгоритмы для программистов
:
Комбинаторные алгоритмы для программистов
После этого находим
Комбинаторные алгоритмы для программистов
Комбинаторные алгоритмы для программистов
и т.д. Наконец, получаем значение
Комбинаторные алгоритмы для программистов
. Таким образом, марки можно наклеить восемью способами. Эти способы таковы:
Комбинаторные алгоритмы для программистов
;
Комбинаторные алгоритмы для программистов
;
Комбинаторные алгоритмы для программистов
;
Комбинаторные алгоритмы для программистов
;
Комбинаторные алгоритмы для программистов
;
Комбинаторные алгоритмы для программистов
;
Комбинаторные алгоритмы для программистов
;
Комбинаторные алгоритмы для программистов
. Отметим, что значения
Комбинаторные алгоритмы для программистов
для
Комбинаторные алгоритмы для программистов
можно было получить иначе, не приводя непосредственно проверки. Дело в том, что при
Комбинаторные алгоритмы для программистов
имеем
Комбинаторные алгоритмы для программистов
, поскольку отрицательную сумму нельзя уплатить, наклеивая неотрицательное количество марок. В то же время, как мы видели,
Комбинаторные алгоритмы для программистов
. Поэтому
Комбинаторные алгоритмы для программистов
.
Точно так же получаем значение
Комбинаторные алгоритмы для программистов
, а для
Комбинаторные алгоритмы для программистов
имеем
Комбинаторные алгоритмы для программистов
.
Задача 5.Общая задача о наклейке марок.
Разобранная задача является частным случаем следующей общей задачи:
Имеются марки достоинством в
Комбинаторные алгоритмы для программистов
. Сколькими способами можно оплатить с их помощью сумму в
Комбинаторные алгоритмы для программистов
рублей, если два способа, отличающиеся порядком, считаются различными? Все числа
Комбинаторные алгоритмы для программистов
различны, а запас марок неограничен. Здесь на первом месте мы будем указывать число слагаемых, на втором – разбиваемое число и на последнем - ограничения на величину слагаемых.
В этом случае число
Комбинаторные алгоритмы для программистов
способов удовлетворяет соотношению
Комбинаторные алгоритмы для программистов

(5.5)
При этом
Комбинаторные алгоритмы для программистов
, если
Комбинаторные алгоритмы для программистов
и
Комбинаторные алгоритмы для программистов
. С помощью соотношения 5.5 можно найти
Комбинаторные алгоритмы для программистов
для любого
Комбинаторные алгоритмы для программистов
, последовательно вычисляя
Комбинаторные алгоритмы для программистов
.
Рассмотрим частный случай этой задачи, когда
Комбинаторные алгоритмы для программистов
,
Комбинаторные алгоритмы для программистов
. Мы получаем всевозможные разбиения числа
Комбинаторные алгоритмы для программистов
на слагаемые
Комбинаторные алгоритмы для программистов
, причем разбиения, отличающиеся порядком слагаемых, считаются различными. Обозначим число этих разбиений через
Комбинаторные алгоритмы для программистов
. (На первом месте мы будем указывать число слагаемых, на втором - разбиваемое число и на последнем – ограничения на величину слагаемых.) Из соотношения 5.5 следует, что
Комбинаторные алгоритмы для программистов

(5.6)
При этом
Комбинаторные алгоритмы для программистов
и
Комбинаторные алгоритмы для программистов
,если
Комбинаторные алгоритмы для программистов
. Вычисление
Комбинаторные алгоритмы для программистов
можно упростить, если заметить, что
Комбинаторные алгоритмы для программистов
и потому
Комбинаторные алгоритмы для программистов

(5.7)
Ясно, что слагаемые не могут быть больше
Комбинаторные алгоритмы для программистов
. Поэтому
Комбинаторные алгоритмы для программистов
равно числу всех разбиений на
Комбинаторные алгоритмы для программистов
на натуральные слагаемые (включая и "разбиение"
Комбинаторные алгоритмы для программистов
. Если число слагаемых равно
Комбинаторные алгоритмы для программистов
, то получаем
Комбинаторные алгоритмы для программистов
разбиений. Поэтому
Комбинаторные алгоритмы для программистов
Итак, мы доказали, что натуральное число
Комбинаторные алгоритмы для программистов
можно разбить на слагаемые
Комбинаторные алгоритмы для программистов
способами. Напомним, что при этом учитывается порядок слагаемых.Например, число 5 можно разбить на слагаемые
Комбинаторные алгоритмы для программистов
способами.
5 = 55 = 3 + 1 + 15 = 1 + 2 + 2
5 = 4 + 15 = 1 + 3+ 15 = 2 + 1 + 1 + 1
5 = 1 + 45 = 1 + 1 + 35 = 1 + 2 + 1 + 1
5 = 2 + 35 = 2 + 2 + 15 = 1 + 1 + 2 + 1
5 = 3 + 25 = 2 + 1 + 25 = 1 + 1 + 1 + 2
5 = 1 + 1 + 1 + 1 + 1

Формула включений и исключений


Пусть имеется

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

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

(6.3)

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

Комбинаторные алгоритмы для программистов
(без учета их порядка), причем знак + ставится, если число учитываемых свойств четно, и знак
Комбинаторные алгоритмы для программистов
, если это число нечетно. Например,
Комбинаторные алгоритмы для программистов
входит со знаком +, а
Комбинаторные алгоритмы для программистов
со знаком
Комбинаторные алгоритмы для программистов
. Формулу 6.3 называют формулой включений и исключений - сначала исключаются все предметы, обладающие хотя бы одним из свойств
Комбинаторные алгоритмы для программистов
, потом включаются предметы, обладающие, по крайней мере, двумя из этих свойств, затем исключаются имеющие, по крайней мере, три и т.д.



Множества и мультимножества


Не существует формального определения множества; считается что это понятие первичное и не определяется. Так, можно говорить, что множество есть объединение различных элементов, но при этом мы оставляем неопределяемыми понятия "объединение" и "элементы". Дадим следующее определение множеству: множество - это неупорядоченная совокупность различных объектов или структура данных, используемая для представления множества. Мультимножество есть объединение не обязательно различных элементов; его можно считать множеством, в котором каждому элементу поставлено в соответствие положительное целое число, называемое кратностью.

Конечное множество

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

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

где

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

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

Здесь все

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

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

Наиболее общими операциями на множествах и мультимножествах являются операции объединения и пересечения. Для множеств эти операции будем обозначать

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

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

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

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


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

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


(6.1)
В каждом из подмножеств, взятый в скобки элемент является его именем. Если нам нужно найти подмножество, в котором содержится восьмерка, искомым ответом будет 7, то есть имя подмножества, содержащего восьмерку. Если нужно взять объединение подмножеств с именами 2 и 10, получим разбиение множества
Комбинаторные алгоритмы для программистов


следующего вида:

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


Именем множества
Комбинаторные алгоритмы для программистов
Комбинаторные алгоритмы для программистов


Комбинаторные алгоритмы для программистов
может быть или 2, или 10.Предполагаем, что вначале имеется разбиение множества
Комбинаторные алгоритмы для программистов


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


(6.2)
и имя каждого из них есть просто этот единственный элемент. Это разбиение преобразуется путем применения операций объединения вперемешку с операциями отыскания. Такая кажущаяся на первый взгляд надуманной задача чрезвычайно полезна в определенных комбинаторных алгоритмах; пример ее полезности виден в "жадном" алгоритме (лекция 16).

Для реализации операций и объединения, и отыскания опишем процедуры (операции)
Комбинаторные алгоритмы для программистов
и
Комбинаторные алгоритмы для программистов
. Процедура (операция)
Комбинаторные алгоритмы для программистов
по именам двух различных подмножеств
Комбинаторные алгоритмы для программистов


и
Комбинаторные алгоритмы для программистов
образует новое подмножество, содержащее все элементы множеств
Комбинаторные алгоритмы для программистов
и
Комбинаторные алгоритмы для программистов
. Процедура (операция)
Комбинаторные алгоритмы для программистов


выдает имя множества, содержащего
Комбинаторные алгоритмы для программистов
. Например, если нужно множество,содержащее
Комбинаторные алгоритмы для программистов
, объединить с множеством, содержащим
Комбинаторные алгоритмы для программистов
, необходимо выполнить следующую последовательность операторов:
Комбинаторные алгоритмы для программистов


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


Такой структурой данных является представление в виде леса с указателями отца, как показано на рис. 4.5 лекции 4. Каждый элемент
Комбинаторные алгоритмы для программистов
множества будет узлом леса, а отцом его будет элемент из того же подмножества, что и
Комбинаторные алгоритмы для программистов
. Если элемент не имеет отца, то есть является корнем, то он будет именем своего подмножества. В соответствии с этим разбиение 5.1 может быть представлено так:

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

Рис. 6.1.  Представление разбиения

При таком представлении процедура (операция)
Комбинаторные алгоритмы для программистов
состоит в переходах по указателям отцов от
Комбинаторные алгоритмы для программистов
до корня, то есть имени, его подмножества. Процедура (операция)
Комбинаторные алгоритмы для программистов
состоит в связывании вместе некоторым образом деревьев, имеющих корни
Комбинаторные алгоритмы для программистов
и
Комбинаторные алгоритмы для программистов
. Например, такую связь можно осуществить, сделав
Комбинаторные алгоритмы для программистов
отцом
Комбинаторные алгоритмы для программистов
.

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


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

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

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


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


Примеры программы


Программа 1. Решето Эратосфена.

{В примере, иллюстрирующем работу с множествами, реализуется алгоритм выделения из первой сотни натуральных чисел всех простых чисел. В основе алгоритма лежит прием "решета Эратосфена". Алгоритм написан на языке программирования Turbo-Pascal.}

Uses crt; Const N=100; {количество элементов исходного множества} Type SetN=set of 1..N; var n1, next, I : word; {вспомогательные переменные } BeginSet, {исходное множество } PrimerSet: SetN; {множество простых чисел } Begin Clrscr; {почистить экран} BeginSet:=[2..N]; {создать исходное множество} PrimerSet:=[1]; {первое простое число} next:=2; {следующее простое число} while BeginSet <> [ ] do {начало основного цикла} begin n1:=next; {n1-число, кратное очередному простому (next)} while n1<=N do {цикл удаления из исходного множества непростых чисел} begin BeginSet:=BeginSet-[n1]; n1:=n1+next {следующее кратное} end; {конец цикла удаления} repeat {получить следующее простое число, которое есть первое не вычеркнутое из исходного множества} inc(next) until(next in BeginSet) or (next > N) end; {конец основного цикла} {вывод результата} textcolor(15); {задание цвета} for I:=1 to N do if i in PrimerSet then write(I:8); readlen; end.

Программа 2. Простые числа в порядке убывания от 200.

{Находит и пишет все простые числа в порядке убывания от 2 до 200. Алгоритм написан на языке программирования Turbo-Pascal.}

Uses crt; const n=197; var

i,q,w,e,r,t:integer; prost:array[1..n] of integer;

begin clrscr; e:=1; r:=0; for q:=1 to n do begin r:=0; for w:=2 to n-1 do if (q<>w) and (q mod w = 0) then r:=1; {prost[e]:=q; e:=e+1;}

if r=0 then begin{begin write(q,' ');}

prost[e]:=q; e:=e+1; end; end;

for i:=e downto 2 do begin write (prost[i],' '); if wherex>70 then writeln;

end; readln; end.

Программа 3. Поиск литер в строке.

{Поиск числа вхождений в данную сроку литер a, c, e, h. Алгоритм написан на языке программирования Turbo-Pascal.}

Uses crt; type liter_set = set of char;

var c:integer; let: liter_set; a:char;

begin clrscr; let:=['a','c','e','h'];

repeat a:=readkey; write(a); if a in let then c:=c+1; until a = '.'; writeln; writeln('Общее число вхожений литер a,c,e,h в вашу запись:',c); readln;

end.

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


Решето Эратосфена


Одной из самых больших загадок математики является расположение простых чисел в ряду всех натуральных чисел. Иногда два простых числа идут через одно, (например, 17 и 19, 29 и 31), а иногда подряд идет миллион составных чисел. Сейчас ученые знают уже довольно много о том, сколько простых чисел содержится среди

Комбинаторные алгоритмы для программистов
первых натуральных чисел. В этих подсчетах весьма полезным оказался метод, восходящий еще к древнегреческому ученому Эратосфену. Он жил в третьем веке до новой эры в Александрии.

Эратосфен занимался самыми различными вопросами - ему принадлежат интересные исследования в области математики, астрономии и других наук. Впрочем, такая разносторонность привела его к некоторой поверхностности. Современники несколько иронически называли Эратосфена "во всем второй": второй математик после Евклида, второй астроном после Гиппарха и т.д.

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

Комбинаторные алгоритмы для программистов
. (Эратосфен считал 1 простым числом. Сейчас математики считают 1 числом особого вида, которое не относится ни к простым, ни к составным числам.) Он придумал для этого следующий способ. Сначала вычеркивают все числа, делящиеся на 2 (исключая само число 2). Потом берут первое из оставшихся чисел (а именно 3). Ясно, что это число - простое. Вычеркивают все идущие после него числа, делящиеся на 3. Первым оставшимся числом будет 5. Вычеркивают все идущие после него числа, делящиеся на 5, и т.д. Числа, которые уцелеют после всех вычеркиваний, и являются простыми. Так как во времена Эратосфена писали на восковых табличках и не вычеркивали, а "выкалывали" цифры, то табличка после описанного процесса напоминала решето. Поэтому метод Эратосфена для нахождения простых чисел получил название "решето Эратосфена".

Подсчитаем, сколько останется чисел в первой сотне, если мы вычеркнем по методу Эратосфена числа, делящиеся на 2, 3 и 5. Иными словами, поставим такой вопрос: сколько чисел в первой сотне не делится ни на одно из чисел 2, 3, 5? Эта задача решается по формуле включения и исключения.


Обозначим через
Комбинаторные алгоритмы для программистов
свойство числа делиться на 2, через
Комбинаторные алгоритмы для программистов
- свойство делимости на 3 и через
Комбинаторные алгоритмы для программистов
- свойство делимости на 5. Тогда
Комбинаторные алгоритмы для программистов


означает, что число делится на 6,
Комбинаторные алгоритмы для программистов
означает, что оно делится на 10, и
Комбинаторные алгоритмы для программистов
- оно делится на 15. Наконец,
Комбинаторные алгоритмы для программистов
означает, что число делится на 30. Надо найти, сколько чисел от 1 до 100 не делится ни на 2, ни на 3, ни на 5, то есть не обладает ни одним из свойств
Комбинаторные алгоритмы для программистов
,
Комбинаторные алгоритмы для программистов
,
Комбинаторные алгоритмы для программистов
. По формуле 6.3 имеем
Комбинаторные алгоритмы для программистов


Но чтобы найти, сколько чисел от 1 до
Комбинаторные алгоритмы для программистов
делится на
Комбинаторные алгоритмы для программистов
, надо разделить
Комбинаторные алгоритмы для программистов
на
Комбинаторные алгоритмы для программистов
и взять целую часть получившегося частного. Поэтому

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


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


и значит,

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


Таким образом, 32 числа от 1 до 100 не делятся ни на 2, ни на 3, ни на 5. Эти числа и уцелеют после первых трех шагов процесса Эратосфена. Кроме них останутся сами числа 2, 3 и 5. Всего останется 35 чисел.

А из первой тысячи после первых трех шагов процесса Эратосфена останется 335 чисел. Это следует из того, что в этом случае
Комбинаторные алгоритмы для программистов


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



Другой метод доказательства


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

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

(7.5)

что и числа Фибоначчи. В самом деле, возьмем любую

Комбинаторные алгоритмы для программистов
-последовательность нулей и единиц, удовлетворяющую условию, что никакие две единицы не идут подряд. Она может оканчиваться или на 0, или на 1. Если она оканчивается на 0, то, отбросив его, получим
Комбинаторные алгоритмы для программистов
-последовательность, удовлетворяющую нашему условию. Если взять любую
Комбинаторные алгоритмы для программистов
- последовательность нулей и единиц, в которой подряд не идут две единицы, и приписать к ней нуль, то получим
Комбинаторные алгоритмы для программистов
-последовательность с тем же свойством. Таким образом доказано, что число последовательностей, оканчивающихся на нуль, равно
Комбинаторные алгоритмы для программистов
.

Пусть теперь последовательность оканчивается на 1. Так как двух единиц подряд быть не может, то перед этой единицей стоит нуль. Иными словами, последовательность оканчивается на 01. Остающаяся же после отбрасывания 0 и 1

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

Таким образом, получено то же самое рекуррентное соотношение. Отсюда еще не вытекает, что числа

Комбинаторные алгоритмы для программистов
и
Комбинаторные алгоритмы для программистов
совпадают.



Перестановки


При составлении размещений без повторений из

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

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

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



Процесс последовательных разбиений


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

Применим описанный прием для решения следующей задачи.

Пусть дано некоторое множество из

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

Обозначим число способов разбиения для множества из

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

предметов через

Комбинаторные алгоритмы для программистов
. На первом шагу это множество может быть разбито
Комбинаторные алгоритмы для программистов
способами (первая часть может содержать один предмет, два предмета,…,
Комбинаторные алгоритмы для программистов
предметов). В соответствии с этим множество всех процессов разбиений распадается на
Комбинаторные алгоритмы для программистов
классов - в
Комбинаторные алгоритмы для программистов
- класс входят процессы, при которых первая часть состоит из
Комбинаторные алгоритмы для программистов
предметов.

Подсчитаем число процессов в

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

процессами. По правилу произведения получаем, что

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

(7.6)

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

Комбинаторные алгоритмы для программистов
. Двоичный поиск, поиск делением пополам. Поиском по числам Фибоначчи называется поиск, основанный на том, что область поиска делится в точках, являющихся числами Фибоначчи.



Размещения без повторений


Имеется

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



Рекуррентные соотношения


При решении многих комбинаторных задач пользуются методом сведения данной задачи к задаче, касающейся меньшего числа предметов. Метод сведения к аналогичной задаче для меньшего числа предметов называется методом рекуррентных соотношений (от латинского "recurrere" - "возвращаться").

Понятие рекуррентных соотношений проиллюстрируем классической проблемой, которая была поставлена около 1202 года Леонардо из Пизы, известным как Фибоначчи. Важность чисел Фибоначчи для анализа комбинаторных алгоритмов делает этот пример весьма подходящим.

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

Пусть

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

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

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

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

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

(7.1)

Выбор начальных условий для последовательности чисел Фибоначчи не важен; существенное свойство этой последовательности определяется рекуррентным соотношением. Будем предполагать

Комбинаторные алгоритмы для программистов
(иногда
Комбинаторные алгоритмы для программистов
).

Рассмотрим эту задачу немного иначе.

Пара кроликов приносит раз в месяц приплод из двух крольчат (самки и самца), причем новорожденные крольчата через два месяца после рождения уже приносят приплод. Сколько кроликов появится через год, если в начале года была одна пара кроликов?

Из условия задачи следует, что через месяц будет две пары кроликов. Через два месяца приплод даст только первая пара кроликов, и получится 3 пары.
А еще через месяц приплод дадут и исходная пара кроликов, и пара кроликов, появившаяся два месяца тому назад. Поэтому всего будет 5 пар кроликов. Обозначим через

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


количество пар кроликов по истечении
Комбинаторные алгоритмы для программистов
месяцев с начала года. Ясно, что через
Комбинаторные алгоритмы для программистов


месяцев будут эти
Комбинаторные алгоритмы для программистов
пар и еще столько новорожденных пар кроликов, сколько было в конце месяца
Комбинаторные алгоритмы для программистов
, то есть еще
Комбинаторные алгоритмы для программистов
пар кроликов. Иными словами, имеет место рекуррентное соотношение
Комбинаторные алгоритмы для программистов


(7.2)
Так как, по условию,
Комбинаторные алгоритмы для программистов
и
Комбинаторные алгоритмы для программистов
, то последовательно находим
Комбинаторные алгоритмы для программистов
и т.д.

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

Числа
Комбинаторные алгоритмы для программистов
называются числами Фибоначчи. Они обладают целым рядом замечательных свойств. Теперь выведем выражение этих чисел через
Комбинаторные алгоритмы для программистов
. Для этого установим связь между числами Фибоначчи и следующей комбинаторной задачей.

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


Чтобы установить эту связь, возьмем любую такую последовательность и сопоставим ей пару кроликов по следующему правилу: единицам соответствуют месяцы появления на свет одной из пар "предков" данной пары (включая и исходную), а нулями - все остальные месяцы. Например, последовательность 010010100010 устанавливает такую "генеалогию": сама пара появилась в конце 11-го месяца, ее родители - в конце 7-го месяца, "дед" - в конце 5-го месяца и "прадед" - в конце второго месяца. Исходная пара кроликов тогда зашифровывается последовательностью 000000000000.

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

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

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


(7.3)
где
Комбинаторные алгоритмы для программистов
, если
Комбинаторные алгоритмы для программистов
нечетно, и
Комбинаторные алгоритмы для программистов
, если
Комбинаторные алгоритмы для программистов
четно.


Иными словами,
Комбинаторные алгоритмы для программистов
- целая часть числа
Комбинаторные алгоритмы для программистов
( в дальнейшем будем обозначать целую часть числа
Комбинаторные алгоритмы для программистов
через
Комбинаторные алгоритмы для программистов
; таким образом,
Комбинаторные алгоритмы для программистов
).

В самом деле,
Комбинаторные алгоритмы для программистов
- это число всех
Комбинаторные алгоритмы для программистов
- последовательностей из 0 и 1, в которых никакие две единицы не стоят рядом. Число же таких последовательностей, в которые входит ровно
Комбинаторные алгоритмы для программистов
единиц и
Комбинаторные алгоритмы для программистов
нулей, равно
Комбинаторные алгоритмы для программистов
. Так как при этом должно выполняться неравенство
Комбинаторные алгоритмы для программистов
, то
Комбинаторные алгоритмы для программистов
изменяется от 0 до
Комбинаторные алгоритмы для программистов
. Применяя правило суммы, приходим к соотношению (7.3).

Равенство (7.3) можно доказать и иначе. Положим
Комбинаторные алгоритмы для программистов


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


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


(7.4)
Кроме того, ясно, что
Комбинаторные алгоритмы для программистов
и
Комбинаторные алгоритмы для программистов
. Так как обе последовательности
Комбинаторные алгоритмы для программистов
и
Комбинаторные алгоритмы для программистов


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


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


Сочетания


В тех случаях, когда нас не интересует порядок элементов в комбинации, а интересует лишь ее состав, говорят о сочетаниях. Итак,

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

элементов называют всевозможные

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

Формула для числа сочетаний получается из формулы для числа размещений. В самом деле, составим сначала все

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

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

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



Затруднение мажордома"


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

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

Задача: "Затруднение мажордома". Однажды мажордом короля Артура обнаружил, что к обеду за круглым столом приглашено 6 пар враждующих рыцарей. Сколькими способами можно рассадить их так, чтобы никакие два врага не сидели рядом?

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

Введем следующие обозначения. Пусть число рыцарей равно

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

Выведем сначала формулу, выражающую

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

последующие рассуждения теряют силу.

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

Проще всего посадить их, если за столом рядом сидят две пары врагов. В этом случае один из вновь пришедших садится между рыцарями первой пары, а другой – между рыцарями второй пары. Это можно сделать двумя способами. Но так как число способов рассадки

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

Пусть теперь рядом сидит только одна пара врагов.
Один из вернувшихся должен сесть между ними. Тогда за столом окажутся

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

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

(7.7)

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

способами, а так как их еще можно поменять местами, то получится
Комбинаторные алгоритмы для программистов
способов. Комбинируя их со всеми способами посадки
Комбинаторные алгоритмы для программистов
пар рыцарей, при которых нет соседей врагов, получаем
Комбинаторные алгоритмы для программистов
способов.


Наконец, номер ушедшей и вернувшейся пары рыцарей мог быть любым от 1 до
Комбинаторные алгоритмы для программистов
. Отсюда вытекает, что рекуррентное соотношение для
Комбинаторные алгоритмы для программистов
имеет вид
Комбинаторные алгоритмы для программистов

(7.8)

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

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

способами.
Второй же вариант может быть в
Комбинаторные алгоритмы для программистов
случаях. Имеется
Комбинаторные алгоритмы для программистов
случаев, когда какая-нибудь пара врагов сидит рядом. Если указать, какая именно пара должна сидеть рядом, получим в
Комбинаторные алгоритмы для программистов
раз меньше случаев.
Здесь тоже можно вернуться к исходной компании 4 способами, и мы получаем всего
Комбинаторные алгоритмы для программистов
способов. Отсюда вытекает, что при
Комбинаторные алгоритмы для программистов

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

(7.9)

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

(7.10)

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

(7.11)

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

(7.12)

Они справедливы при
Комбинаторные алгоритмы для программистов
. Но простой подсчет показывает, что
Комбинаторные алгоритмы для программистов
. Поэтому из соотношений 7.10-7.12 вытекает, что
Комбинаторные алгоритмы для программистов
. Продолжая далее, находим, что гостей можно посадить за стол требуемым образом
Комбинаторные алгоритмы для программистов
способами.
Комбинаторные алгоритмы для программистов

Линейные рекуррентные соотношения с постоянными коэффициентами


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

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

(8.3)

где

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

Сначала рассмотрим, как решаются такие соотношения при

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

(8.4)

Решение этих соотношений основано на следующих двух утверждениях.

Если

Комбинаторные алгоритмы для программистов
и
Комбинаторные алгоритмы для программистов
являются решениями рекуррентного соотношения (8.4), то при любых числах
Комбинаторные алгоритмы для программистов
и
Комбинаторные алгоритмы для программистов

последовательность

Комбинаторные алгоритмы для программистов
также является решением этого соотношения.

В самом деле, по условию, имеем

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

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

Умножим эти равенства на

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

А это означает, что

Комбинаторные алгоритмы для программистов
является решением соотношения(8.4).

Если

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

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

является решением рекуррентного соотношения

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

В самом деле, если

Комбинаторные алгоритмы для программистов
, то
Комбинаторные алгоритмы для программистов
и
Комбинаторные алгоритмы для программистов
. Подставляя эти значения в соотношение (8.4), получаем равенство
Комбинаторные алгоритмы для программистов

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

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

также является решением соотношения (8.4). Для доказательства достаточно использовать утверждение (8.4), положив в нем

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

Из утверждений 1 и 2 вытекает следующее правило решения линейных рекуррентных соотношений второго порядка с постоянными коэффициентами.

Пусть дано рекуррентное соотношение

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

(8.5)

Составим квадратное уравнение

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

(8.6)

которое называется характеристическим для данного соотношения. Если это уравнение имеет два различных корня

Комбинаторные алгоритмы для программистов
, то общее решение соотношения (8.5) имеет вид
Комбинаторные алгоритмы для программистов

Чтобы доказать это правило, заметим сначала, что по утверждению 2

Комбинаторные алгоритмы для программистов
являются решениями нашего соотношения. А тогда по утверждению 1 и
Комбинаторные алгоритмы для программистов
является его решением. Надо только показать, что любое решение соотношения (8.5) можно записать в этом виде. Но любое решение соотношения второго порядка определяется значениями
Комбинаторные алгоритмы для программистов
. Поэтому достаточно показать, что система уравнений
Комбинаторные алгоритмы для программистов

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

имеет решение при любых

Комбинаторные алгоритмы для программистов
.
Этими решениями являются
Комбинаторные алгоритмы для программистов


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


(Случай, когда оба корня уравнения (8.6) совпадут друг с другом, разберем в следующем пункте.)

Пример на доказанное правило.

При изучении чисел Фибоначчи мы пришли к рекуррентному соотношению
Комбинаторные алгоритмы для программистов


(8.7)
Для него характеристическое уравнение имеет вид
Комбинаторные алгоритмы для программистов


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


Поэтому общее решение соотношения Фибоначчи имеет вид
Комбинаторные алгоритмы для программистов


(8.8)
(Мы воспользовались сделанным выше замечанием и взяли показатели
Комбинаторные алгоритмы для программистов


вместо
Комбинаторные алгоритмы для программистов
). Мы называли числами Фибоначчи решения соотношения (8.7), удовлетворяющее начальным условиям
Комбинаторные алгоритмы для программистов
( то есть последовательность
Комбинаторные алгоритмы для программистов
). Часто бывает более удобно добавить к этой последовательности вначале числа 0 и 1, то есть рассматривать последовательность
Комбинаторные алгоритмы для программистов
Ясно, что эта последовательность удовлетворяет тому же самому рекуррентному соотношению (8.6) и начальным условиям
Комбинаторные алгоритмы для программистов
. Полагая в формуле (8.6)
Комбинаторные алгоритмы для программистов
, получаем для
Комбинаторные алгоритмы для программистов


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


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


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


(8.9)
На первый взгляд кажется удивительным, что это выражение при всех натуральных значениях
Комбинаторные алгоритмы для программистов
принимает целые значения.


Производящие функции


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

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

(8.12)

называемый производящей функцией для данной последовательности. Название формальный ряд для данной последовательности означает, что (8.12) мы трактуем только как удобную запись нашей последовательности - в данном случае несущественно, для каких (действительных или комплексных) значений переменной

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

мы определим операцию сложения:

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

(8.13)

операцию умножения на число (действительное или комплексное):

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

(8.14)

и произведение Коши

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

(8.15)

где

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

(8.16)

Если

Комбинаторные алгоритмы для программистов
для
Комбинаторные алгоритмы для программистов
, то ряд (8.12) будем отождествлять с многочленом
Комбинаторные алгоритмы для программистов
. Из математического анализа известно, что если ряд (8.12) сходится в некоторой окрестности нуля, то его сумма
Комбинаторные алгоритмы для программистов
является аналитической функцией в этой окрестности и
Комбинаторные алгоритмы для программистов

(8.17)

(

Комбинаторные алгоритмы для программистов
обозначает значение
Комбинаторные алгоритмы для программистов
-й производной функции
Комбинаторные алгоритмы для программистов
для
Комбинаторные алгоритмы для программистов
; ряд 8.12 - это не что иное, как ряд Маклорена функции
Комбинаторные алгоритмы для программистов
). Более того, когда
Комбинаторные алгоритмы для программистов
являются аналитическими функциями в окрестности нуля, то формулы (8.13)-(8.16) будут справедливы, если
Комбинаторные алгоритмы для программистов
трактовать как значения функций
Комбинаторные алгоритмы для программистов
в точке
Комбинаторные алгоритмы для программистов
, а ряды понимать в обычном смысле, т.е. так, как в математическом анализе. Это сохраняющее операции взаимно однозначное соответствие между рядами, сходящимися в окрестности нуля, и функциями, аналитическими в окрестности нуля, позволяет отождествить формальный ряд (8.12) с определенной через него аналитической функцией в случае рядов, сходящихся в окрестности нуля (несмотря на то, что ряды мы будем трактовать всегда как формальные ряды, то есть только как формальную запись их коэффициентов). Таким образом, будем писать, например,
Комбинаторные алгоритмы для программистов

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

и т.д.

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


Решение рекуррентных соотношений


Будем говорить, что рекуррентное соотношение имеет порядок

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

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

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

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

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

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

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

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

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

В самом деле, общий член этой последовательности имеет вид

Комбинаторные алгоритмы для программистов
. Значит,
Комбинаторные алгоритмы для программистов
. Но при любом
Комбинаторные алгоритмы для программистов
имеет место тождество
Комбинаторные алгоритмы для программистов
. Поэтому
Комбинаторные алгоритмы для программистов
является решением указанного соотношения.

Решение рекуррентного соотношения

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

и путем подбора этих постоянных можно получить любое решение данного соотношения. Например, для соотношения

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

(8.1)

общим решением будет

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

(8.2)

В самом деле, легко проверить, что последовательность (8.2) обращает (8.1) в тождество. Поэтому нам надо только показать, что любое решение нашего соотношения можно представить в виде (8.2). Но любое решение соотношения (8.1) однозначно определяется значениями

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


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

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

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

Комбинаторные алгоритмы для программистов
имеет решение. Поэтому (8.2) действительно является общим решением соотношения (8.1).

Случай равных корней характеристического уравнения


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

Комбинаторные алгоритмы для программистов
. В этом случае выражение
Комбинаторные алгоритмы для программистов
уже не будет общим решением. Ведь из-за того, что
Комбинаторные алгоритмы для программистов
, это решение можно записать в виде
Комбинаторные алгоритмы для программистов

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

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

А тогда рекуррентное соотношение имеет такой вид:

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

(8.10)

Проверим, что

Комбинаторные алгоритмы для программистов
действительно являются его решением. Имеем
Комбинаторные алгоритмы для программистов
, а
Комбинаторные алгоритмы для программистов
. Подставляя эти значения в соотношение (8.10), получаем очевидное тождество
Комбинаторные алгоритмы для программистов

Значит,

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

Итак, имеются два решения

Комбинаторные алгоритмы для программистов
и
Комбинаторные алгоритмы для программистов
заданного соотношения. Его общее решение запишется так:
Комбинаторные алгоритмы для программистов
Теперь уже путем подбора
Комбинаторные алгоритмы для программистов
можно удовлетворить любым начальным условиям.

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

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

(8.11)

Составим характеристическое уравнение

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

Если все корни

Комбинаторные алгоритмы для программистов
этого алгебраического уравнения
Комбинаторные алгоритмы для программистов
-й степени различны, то общее решение соотношения (8.3) имеет вид
Комбинаторные алгоритмы для программистов

Если же, например,

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

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

рекуррентного соотношения (8.11). В общем решении этому корню соответствует часть

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

Составляя такое выражение для всех корней и складывая их, получаем общее решение соотношения (8.3).

Например, решим рекуррентное соотношение

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

Характеристическое уравнение в этом случае имеет вид

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

Решая его, получим корни

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

Значит, общее решение нашего соотношения имеет следующий вид:

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



Алгебраические дроби и степенные ряды


При делении многочлена

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

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

(9.2)

Рассмотрим, например, разложение

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

(9.3)

Мы не пишем здесь знака равенства, так как не знаем, какой смысл имеет стоящая справа сумма бесконечного числа слагаемых. Чтобы выяснить это, попробуем подставлять в обе части соотношения (9.3) различные значения

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

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

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

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

эти суммы приближаются к значению

Комбинаторные алгоритмы для программистов
которое приняла левая часть соотношения (9.3) при
Комбинаторные алгоритмы для программистов
.

То же самое получится, если вместо

Комбинаторные алгоритмы для программистов
подставить в обе части (9.3) число
Комбинаторные алгоритмы для программистов
. Левая часть равенства примет значение 2, а правая превратится в бесконечный числовой ряд
Комбинаторные алгоритмы для программистов
Беря последовательно одно, два, три, четыре, слагаемых, мы получим числа 1;
Комбинаторные алгоритмы для программистов
;
Комбинаторные алгоритмы для программистов
;
Комбинаторные алгоритмы для программистов
,…,
Комбинаторные алгоритмы для программистов
. Ясно, что с возрастанием
Комбинаторные алгоритмы для программистов
эти числа стремятся к числу 2.

Однако, если взять

Комбинаторные алгоритмы для программистов
, то левая часть (9.3) примет значение
Комбинаторные алгоритмы для программистов
, а в правой получим ряд
Комбинаторные алгоритмы для программистов

Если последовательно складывать члены этого ряда, то получаются суммы 1; 5; 21; 85; … Эти суммы неограниченно увеличиваются и не приближаются к числу

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

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

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

(9.4)

Говорят, что бесконечный числовой ряд сходится к числу

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

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

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

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

В этом случае число

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

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

Если не существует числа

Комбинаторные алгоритмы для программистов
, к которому сходится данный ряд (9.4), то этот ряд называют расходящимся.

Проведенное выше исследование показывает, что

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


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


в то время как ряд
Комбинаторные алгоритмы для программистов
... расходится. Более тщательное исследование показывает, что если
Комбинаторные алгоритмы для программистов
, то ряд
Комбинаторные алгоритмы для программистов
сходится к
Комбинаторные алгоритмы для программистов
, а если
Комбинаторные алгоритмы для программистов
, то он расходится. Чтобы доказать это утверждение, достаточно заметить, что

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


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


получаем расходящиеся числовые ряды
Комбинаторные алгоритмы для программистов


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

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


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

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


Она показывает, что для значений
Комбинаторные алгоритмы для программистов
, лежащих в некоторой области, а именно при
Комбинаторные алгоритмы для программистов
, стоящий справа ряд сходится к
Комбинаторные алгоритмы для программистов
. Говорят, что функция
Комбинаторные алгоритмы для программистов
при
Комбинаторные алгоритмы для программистов
разлагается в степенной ряд
Комбинаторные алгоритмы для программистов
.Теперь уже можно выяснить и более общий вопрос.

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


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

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


(9.7)
В степенные ряды можно разлагать не только алгебраические дроби, но и многие другие функции. В математическом анализе доказывают, например, что

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


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


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


Отметим еще следующее важное утверждение: функция
Комбинаторные алгоритмы для программистов


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



Действия над степенными рядами


Перейдем теперь к действиям над степенными рядами. Пусть функции

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

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

(9.12)

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

(9.13)

Тогда

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

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

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

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

(9.14)

Ряд, стоящий в правой части равенства (9.14), называется суммой степенных рядов (9.12) и (9.13).

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

Комбинаторные алгоритмы для программистов
и
Комбинаторные алгоритмы для программистов
. Мы имеем

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

(9.15)

Оказывается, что как и в случае многочленов, ряды, стоящие в правой части равенства (9.15), можно почленно перемножать. Мы опускаем доказательство этого утверждения. Найдем ряд, получающийся после почленного перемножения. Свободный член этого ряда равен

Комбинаторные алгоритмы для программистов
. Члены, содержащие
Комбинаторные алгоритмы для программистов
, получатся дважды: при умножении
Комбинаторные алгоритмы для программистов
на
Комбинаторные алгоритмы для программистов
и при умножении
Комбинаторные алгоритмы для программистов
на
Комбинаторные алгоритмы для программистов
. Они дают

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

Точно так же вычисляются члены, содержащие

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

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

(9.16)

Ряд, стоящий в правой части равенства (9.16), называется произведением рядов(9.12) и (9.13).

В частности, возводя ряд (9.12) в квадрат, получаем

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

(9.17)

Посмотрим теперь, как делят друг на друга степенные ряды. Пусть свободный член ряда (9.13) отличен от нуля. Покажем, что в этом случае существует такой степенной ряд

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

(9.18)

что

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

(9.19)

Для доказательства перемножим ряды в левой части этого равенства. Мы получим ряд

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

Для того чтобы этот ряд совпадал с рядом (9.12), необходимо и достаточно, чтобы выполнялись равенства

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

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

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

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

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

Эти равенства дают бесконечную систему уравнений для отыскания коэффициентов Из первого уравнения системы получаем

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

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

из которого находим, что

Комбинаторные алгоритмы для программистов
. Вообще, если уже найдены коэффициенты
Комбинаторные алгоритмы для программистов
, то для отыскания
Комбинаторные алгоритмы для программистов
имеем уравнение

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

Это уравнение разрешимо, поскольку

Комбинаторные алгоритмы для программистов
.Итак, мы доказали существование ряда (9.18), удовлетворяющего соотношению (9.19). Ряд (9.18) называют частным при делении рядов (9.12) и (9.13). Можно доказать, что он получается при разложении функции
Комбинаторные алгоритмы для программистов
. Таким образом, степенные ряды можно складывать, умножать и делить (последнее - при условии, что свободный член делителя отличается от нуля). Эти действия соответствуют действиям над разлагаемыми функциями.

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


Деление многочленов


Если заданы два многочлена

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

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

Ясно, что процесс деления никогда не закончится ( так же, например, как при обращении числа

Комбинаторные алгоритмы для программистов
в бесконечную десятичную дробь). С помощью индукции легко убедиться, что все коэффициенты частного равны единице. Поэтому в качестве частного получается бесконечный ряд
Комбинаторные алгоритмы для программистов
. Вообще, если
Комбинаторные алгоритмы для программистов
и
Комбинаторные алгоритмы для программистов
- два многочлена

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

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

причем свободный член

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

отличен от нуля,

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

на

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

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

(9.1)

Лишь в случае, когда

Комбинаторные алгоритмы для программистов
делится без остатка на
Комбинаторные алгоритмы для программистов
, ряд (9.1) обрывается и мы получаем многочлен.



Метод рекуррентных соотношений позволяет решать


Метод рекуррентных соотношений позволяет решать многие комбинаторные задачи. Но в целом ряде случаев рекуррентные соотношения довольно трудно составить, а еще труднее решить. Зачастую эти трудности удается обойти, использовав производящие функции. Поскольку понятие производящей функции связано с бесконечными степенными рядами, познакомимся с этими рядами.

Бином Ньютона


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

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

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

и

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

Эти равенства являются частными случаями более общей формулы, дающей разложение для

Комбинаторные алгоритмы для программистов
. Запишем
Комбинаторные алгоритмы для программистов
в виде

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

(10.4)

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

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

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

(10.5)

а

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

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

(10.6)

Видно, что в формулу (10.5) входят все размещения с повторениями, составленные из букв

Комбинаторные алгоритмы для программистов
и
Комбинаторные алгоритмы для программистов
по две буквы в каждом размещении, а в формулу (10.6) - размещения с повторениями из тех же букв, но состоящие из трех букв каждое. То же самое и в общем случае — после раскрытия скобок в формуле (10.4) мы получим всевозможные размещения с повторениями букв
Комбинаторные алгоритмы для программистов
и
Комбинаторные алгоритмы для программистов
, состоящие из
Комбинаторные алгоритмы для программистов
элементов. Приведем подобные члены. Подобными будут члены, содержащие одинаковое количество букв
Комбинаторные алгоритмы для программистов
(тогда и букв
Комбинаторные алгоритмы для программистов
в них будет поровну). Найдем, сколько будет членов, в которые входит
Комбинаторные алгоритмы для программистов

букв

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

и

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

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

Отсюда вытекает, что после приведения подобных членов выражение

Комбинаторные алгоритмы для программистов
войдет с коэффициентом
Комбинаторные алгоритмы для программистов
. Итак, мы доказали, что

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

(10.7)

Равенство (10.7) принято называть формулой бинома Ньютона. Если положить в этом равенстве

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

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

(10.8)

Мы видим, что

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



Об едином нелинейном рекуррентном соотношении


При решении задачи о разбиении последовательности мы пришли к рекуррентному соотношению

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

(10.14)

где

Комбинаторные алгоритмы для программистов
.Покажем, как решить соотношение (10.14). Для этого составим производящую функцию.

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

(10.15)

Положим

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

(10.16)

и возведем

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

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

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

Но по рекуррентному соотношению (10.14),

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

Значит,

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

Полученный ряд есть не что иное, как

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

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

Для функции

Комбинаторные алгоритмы для программистов
получилось квадратное уравнение (10.17). Решая его, находим, что

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

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

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

мы имели бы

Комбинаторные алгоритмы для программистов
, а из разложения (10.16) видно, что
Комбинаторные алгоритмы для программистов
.

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


Применение степенных рядов для доказательства тождеств


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

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

Рассмотрим, например, известное нам разложение

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

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

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

(10.1)

Если заменить здесь

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

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

(10.2)

Перемножив разложения (10.1) и (10.2), выводим, что

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

(10.3)

Очевидно, что коэффициенты при нечетных степенях

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

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

Но функцию

Комбинаторные алгоритмы для программистов
можно разложить в степенной ряд и иным образом. Мы имеем

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

А разложение для

Комбинаторные алгоритмы для программистов
получается из разложения (10.1), если заменить в нем
Комбинаторные алгоритмы для программистов
на
Комбинаторные алгоритмы для программистов
:

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

(10.4)

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

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

разложении (10.3) должен равняться коэффициенту при

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

в разложении (10.4). Отсюда вытекает следующее тождество:

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