В этой теме рассматриваются элементы комбинаторики и алгоритмы решения комбинаторных задач.3. Размещения. Перестановки. Сочетания.
Имеется n различных предметов. Сколько из них можно составить k-расстановок? При этом две расстановки считаются различными, если они либо отличаются друг от друга хотя бы одним элементом, либо состоят из одних и тех же элементов, но расположенных в разном порядке. Такие расстановки называют размещениями без повторений.
Размещением элементов из множества Е={а1,...,аn} по k называется упорядоченное подмножество из k элементов, принадлежащих Е.
Например: Дано множество Е={a1,a2,a3}.Найти размещения из Е по 2 элемента.
Получаем:
(a1,a2);(a2,a1);(a1,a3);(a3,a1);(a2,a3);(a3,a2).
Число размещений обозначают Аnk.
При составлении k размещений без повторений из n предметов нам надо сделать k выборов. На первом шаге можно выбрать любой из n предметов. На втором шаге выбрать из оставшихся n-1 предметов - ведь повторять сделанный выбор нельзя. На третьем шаге для выбора остается лишь n-2 свободных предметов, на четвертом n-3 предметов... на k-м шаге выбираем из n-k+1 предметов. Получаем, что число k - размещений без повторений из n предметов выражается следующим образом:Ank =n(n-1)...(n-k+1).Еще эту формулу можно записать следующим образом:Ank =n! / (n-k)!Размещение с повторениями из n элементов множества Е={a1,a2,...,an} по k - всякая конечная последовательность, состоящая из k членов данного множества Е. Два размещения с повторениями считаются различными, если хотя бы на одном месте они имеют различные элементы множества Е. Число различных размещений с повторениями из n по k равно nk.
Задача. Напечатать все последовательности длины k из чисел 1..n.
Решение: Будем печатать их в лексикографическом порядке (последовательность а предшествует последовательности b, если для некоторого i их начальные отрезки длины i равны, а (i+1)-ый член последовательности а меньше). Первой будет последовательность <1,1,...,1>, последней - <n,n,...,n>. Будем хранить последнюю напечатанную последовательность в массиве x[1],..,x[k].
last[1],..,last[k] положим равным n. Для того, чтобы перейти к следующей последовательности надо: двигаясь с конца последовательности, найти самый правый член, меньший n, увеличить его на 1, а идущие за ним члены положить равными 1.Процедура print(x:massiv) - вывод последовательности.Функция egual(x,last:massiv):boolean - сравнивает элементы массивов, если x<>last, то egual:=false иначе egual:=true.for i:=1 to k do x[i]:=1;
print(x);
for i:=1 to k do last[i]:=n;
while not(equal(x,last)) do
begin
p:=k;
while not (x[p]<n) do p:=p-1;
x[p]:=x[p]+1;
for i:=p+1 to k do x[i]:=1;
print(x);
end;Перестановки из n элементов - частный случай размещения элементов из Е по k, при k=n.
Иными словами, перестановками называют размещения без повторений из n элементов, в которые входят все элементы. Можно также сказать, что перестановками из n элементов называют всевозможные n-расстановки, каждая из которых содержит все эти элементы по одному разу и которые отличаются друг от друга лишь порядком элементов.
Число перестановок вычисляется по формуле:Pn=Ann=n·(n-1)·...·2·1=n!Алгоритм генерации перестановок в лексикографическом порядке:
1. Просматриваем а1,...,аn с конца до тех пор, пока не попадется ai<ai+1. Если таковых нет, то генерация закончена.
2. Рассматриваем ai+1,ai+2,...,an. Найдем первый с конца am больший ai и поменяем их местами.
3. ai+1,ai+2,...,an переставим в порядке возрастания (для этого достаточно её переписать с конца).
4. Печатаем найденную перестановку.
5. Возвращаемся к пункту 1.Первой при этом будет перестановка <1,2,..,n>, последней - <n,..,2,1>.
Сочетания элементов из Е={a1,...,an} по k называется упорядоченное подмножество из k элементов, принадлежащих Е и отличающиеся друг то друга составом, но не порядком элементов.
Число сочетаний вычисляется по формулам:
C00 =Ck0=C0k=Ckk=1
Cnk =Cnk-1 + Cn-1k-1 или Cnk = Ank / k! = n! / (n-k)! k!
Алгоритм генерации сочетаний Cnk.
1. Для i:=1 до k ci:=i; печатаем ci, для i=1..k.
2. С конца находим такое i, что ci<>n-k+i. Если такого i нет, то генерация сочетаний закончена.
3. ci:=ci+1; для m=i+1,i+2,...,k выполним cm:=c m-1 +1; выводим ci для i=1,...,k.
4. Возвращаемся к пункту 2.Задача. Перечислить все возрастающие последовательности длины k из чисел 1..n в лексикографическом порядке (все сочетания из n по k).
Например: при n=5, k=2 получаем:Решение: Минимальной будет последовательность <1,2,...,k>, а максимальной - <n-k+1,...,n-1,n>. Каждый i-ый член последовательности можно увеличить, если он меньше n-k+i. После увеличения i-го элемента все следующие должны возрастать с шагом 1
12 13 14 15 23 24 25 34 35 45function poisk1(x:massiv):boolean;Функции poisk1 и poisk2 организуют поиск i-го элемента, который нужно увеличить на 1 Процедура print печатает полученную последовательность.
var q:boolean;
i:integer;
begin
q:=false;
for i:=m downto 1 do
if x[i]<>n-m+i then q:=true;
poisk1:=q;
end.
function poisk2(x:massiv):integer;
var i:integer;
begin
i:=m;
while not(x[i]<n-m+i) do
i:=i-1; poisk2:=i;
end;for j:=1 to m do x[j]:=j;
print(x);
while poisk1(x) do
begin
k:=poisk2(x);
x[k]:=x[k]+1;
for j:=k+1 to m do
x[j]:=x[j-1]+1;
print(x);
end;Задача. Перечислить все разбиения целого положительного числа n на целые положительные слагаемые (разбиения, отличающиеся лишь порядком слагаемых, считаются за одно).
Например: n=4 разбиения 1+1+1+1, 2+1+1, 2+2, 3+1, 4.Решение: Договоримся, что
1) в разбиениях слагаемые идут в невозрастающем порядке,
2) сами разбиения мы перечисляем в лексикографическом порядке.
Разбиение храним в начале массива x[1]..x[n], при этом количество входящих в него чисел обозначим k. В начале x[1]=...=x[n]=1, k=n, в конце x[1]=n,k=1. В каком случае x[i] можно увеличить, не меняя предыдущих?Во-первых, должно быть x[i-1]>x[i] или i=1. Во-вторых, i должно быть не последним элементом (увеличение i надо компенсировать уменьшением следующих). Увеличив i, все следующие элементы надо взять минимально возможными.procedure print(x:massiv;m:integer);Литература
var i:integer;
begin
for i:=1 to m do
write(x[i],' ');
end;
Begin
for j:=1 to n do x[j]:=1;
print(x,n);
k:=n;
while k<>1 do
begin
i:=k-1;
while not((i=1) or (x[i-1]>x[i])) do
i:=i-1;
x[i]:=x[i]+1;
sum:=0;
for j:=i+1 to k do
sum:=sum+x[j];
for j:=1 to sum-1 do
x[i+j]:=1; k:=i+sum-1;
print(x,k);
end;
end.1. Н.Я. Виленкин. Комбинаторика
2. Математический энциклопедический словарь.
Упражнения.1. Напечатать все размещения без повторений из чисел 1..n по k.
2. Напечатать все последовательности положительных целых чисел длины k, у которых i-ый член не превосходит i.
3. Напечатать все перестановки чисел 1..n (то есть последовательности длины n, в которые каждое из этих чисел входит по одному разу).
4. Перечислить все убывающие последовательности длины k из чисел 1..n в лексикографическом порядке (при n=5, k=2 получаем: 21 31 32 41 42 43 51 52 53 54).
5. Перечислить все разбиения целого положительного числа n на целые положительные слагаемые. Представляя разбиения как не возрастающие последовательности, перечислить их в порядке, обратном лексикографическому (для n=4, должно быть 4,3+1,2+2,2+1+1,1+1+1+1).