- •Практическое занятие №6 Тема: Комбинаторика.
- •Теоретическая часть
- •Основные комбинаторные принципы
- •Комбинаторные соединения
- •Основные виды комбинаторных соединений: размещения, перестановки и сочетания.
- •Контрольные вопросы
- •Индивидуальные задания
- •Методы программирования: переборные алгоритмы
- •Порождение и перебор комбинаторных объектов
- •Последовательности
- •Перестановки
- •Разбиения
- •Подсчет количеств
- •Рекурсия
- •Факториал
- •Ханойская башня
- •Последовательности (рекурсивный алгоритм)
- •Перестановки (рекурсивный алгоритм)
- •Основная программа:
- •Перебор с отходом назад
- •X[1],...,X[n],
Факториал
Еще раз напомним рекурсивный алгоритм вычисления факториала:
program Factorial;
var N:word;
function F(n:word):longint;
begin
if n=0 then F:=1 else F:=n*F(n-1)
end;
begin
write('N=');readln(N);
writeln('N!=',F(N))
end.
Ханойская башня
Игра "Ханойская башня" состоит в следующем. Есть три стержня. Hа первый из них надета пирамидка из N колец (большие кольца снизу, меньшие сверху). Требуется переместить кольца на другой стержень. Разрешается перекладывать кольца со стержня на стержень, но класть большее кольцо поверх меньшего нельзя. Составить программу, указывающую требуемые действия.
Hапишем рекурсивную процедуру перемещения M верхних колец с A-го стержня на B-ый в предположении, что остальные кольца больше по размеру и лежат на стержнях без движения:
procedure Move(M,A,B:integer);
var C:integer;
begin
if M=1 then begin writeln ('сделать ход ',A,'->',B) end
else
begin
C:=6-A-B; {C - третий стержень: сумма номеров равна 6}
Move(M-1,A,C);
Move(1,A,B);
Move(M-1,C,B)
end
end;
Сначала переносится пирамидка из M-1 колец на третий стержень C. После этого M-ое кольцо освобождается, и его можно перенести на B. Остается перенести пирамиду из N-1 кольца с C на B. Чем это проще первоначальной задачи? Тем, что количество колец стало на единицу меньше. Теперь основную программу можно записать в несколько строк:
program Hanoi;
var N:integer;
procedure Move(M,A,B:integer);
.............
begin
write('N=');readln(N);
Move(N,1,2)
end.
Если вы владеете основами компьютерной графики, можете попробовать "нарисовать" каждый ход на экране.
Таким образом, ОСHОВHАЯ ИДЕЯ любого рекурсивного решения - свести задачу к точно такой же, но с меньшим значением параметра. При этом какое-то минимальное значение параметра (например, 1 или 0) должно давать решение без рекурсивного вызова - иначе программа "зациклится" (последовательность рекурсивных вызовов будет бесконечной). Это напоминает метод математической индукции в математике. В некоторых задачах удобно наоборот, увеличивать значение параметра при рекурсивном вызове. Тогда, естественно, "безрекурсивное" решение должно предусматриваться для некоторого максимального значения параметра. Попробуем использовать эту идею для перебора комбинаторных объектов.
Последовательности (рекурсивный алгоритм)
Задача та же, что в пункте 1.1. Опишем рекурсивную процедуру Generate(k), предъявляющую все последовательности длины N из чисел 1,...,M, у которых фиксировано начало X[1],X[2],...,X[k]. Понятно, что при k=N мы имеем тривиальное решение: есть только одна такая последовательность - это она сама. При k<N будем сводить задачу к k+1:
procedure Generate(k:byte);
var i,j:byte;
begin
if k=N then
begin for i:=1 to N do write(X[i]);writeln end
else
for j:=1 to M do
begin X[k+1]:=j; Generate(k+1) end
end;
Основная программа теперь выглядит очень просто:
program SequencesRecursion;
type Sequence=array [byte] of byte;
var M,N:byte;
X:Sequence;
procedure Generate(k:byte);
............
begin
write('M,N=');readln(M,N);
Generate(0)
end.