- •1. Рекурсивные алгоритмы
- •1.1. Основные определения
- •1.2. Накопления суммы или произведения.
- •Упражнение
- •Варианты задач
- •1.3. Быстрая сортировка
- •1.5.1.Основные определения
- •1.5.2. Пример задания некоторого языка l1 в бнф
- •1.5.3. Пример разработки программы "Синтаксический анализатор "
- •Ошибок нет Нет символа "[" Ошибка в выраженииВыходная форма:
- •2 Этап. Разбор второго правила. Раскрытие процедуры SimpExpr.
- •1.5.4. Лабораторная работа " Синтаксический анализатор "
- •Итак, метод для рассматриваемой задачи будет описан в виде синтаксических
- •На этом этапе можно продолжить проектирование в одном из двух направле-
- •2. Рекурсивный тип данных
- •Основные определения
- •2.2. Динамическое распределение памяти
- •Упражнение
- •Лабораторная работа “Генеалогическое дерево”
- •Варианты заданий
- •Литература
- •Содержание
1. Рекурсивные алгоритмы
1.1. Основные определения
Объект называется рекурсивным, если он содержит сам себя или определен с помощью самого себя. Например,
1. Натуральные числа:
(а) 1 есть натуральное число
(б) целое число, следующее за натуральным, есть натуральное число.
2. Древовидные структуры:
(а) есть дерево (называемое пустым деревом),
(б) если t1 и t2 деревья, то t1 t2 есть дерево.
Рекурсия является мощным средством в математических определениях. Мощ-
ность рекурсии связана с тем, что она позволяет определить бесконечное множество объектов с помощью конечного высказывания.
Рекурсия в программировании используется как для записи алгоритма («cодержит сам себя»), так и для определения типа данных («определен с помощью самого себя»).
При записи алгоритмов для повторяемых вычислений, как правило, используетсяитерация, реализуемая в программе оператором цикла. Точно также повторяемые вычисления в программе можно описать с помощью рекурсивной процедуры, при этом программа не содержит явных циклов. Если метод решения задачи содержит рекурсию, то и алгоритм более естественно построить рекурсивным.
P
P
Q
В общем виде рекурсивную процедуру P можно изобразить как композицию К базовых структур Si (не содержащих P) и самой P:
P = К [Si , if B then P]. (1)
Чтобы работа процедуры P завершалась, необходимо, чтобы рекурсивное обращение к процедуре P подчинялось некоторому условию В.
Рассмотрим задачи, использующие рекурсию для своего решения. Отметим, что язык Паскаль имеет средства для организации как рекурсивных вычислений, так и для организации рекурсивных типов данных.
1.2. Накопления суммы или произведения.
Рассмотрим простейшую задачу накопления произведения чисел.
Спецификация задачи.
1. Задача. Найти произведение натуральных чисел от 1 до n:
P = n! = 12...n
2. Входные данные.
цел n - граница накопления, n 0
3. Выходные данные.
цел P - произведение
4. Аномалии.
n < 0 (в программе не рассматриваются)
5. Метод.
Возможны два способа математической записи произведения:
1) P = 1 2 ... n = ;
2a) 0! = 1,
2б) если n > 0, то n! = n (n-1)!
Способ 1 содержит многоточие, поэтому не может являться строгим правилом вычисления. Такое описание метода традиционно реализуется в виде итерации.
Способ 2 с помощью конечного высказывания задает строгий метод вычисления произведения любого количества чисел с использованием рекурсии.
Опишем способ 2 в виде рекурсивной процедуры и рекурсивной функции.
Наиболее надежный способ обеспечить окончание процедуры - это связать с процедурой Р параметр - значение n и рекурсивно вызывать Р со значением этого параметра n-1. Тогда замена условия В из формулы (1) на n>0 гарантирует окончание работы: P(n) = K [Si , if n>0 then P(n-1)]
{ вычисление P=n! } { вычисление P=n! }
procedure fact (n:integer; var P:integer); function P (n:integer):integer;
begin begin
if n > 0 then if n > 0 then
begin P := n * P(n-1)
fact(n-1,P); else
P:=P*n P := 1
end end;
else
P:=1
end;
На рис.1 отображен рекурсивный процесс выполнения процедуры fact(n,P)
для n=3.
Рекурсивный процесс включает прямой и обратный ход.
Прямой ход. При каждом рекурсивном обращении к процедуре запоминается состояние памяти этой процедуры с текущими значениями всех используемых ею переменных и создается новая копия этой процедуры.
Глубина рекурсии определяется количеством выполненных рекурсивных вызовов. Цепочка из процедур в памяти создает связную последовательность ее областей, организованную по принципу стека: доступной является только самая верхняя область. Прямой ход завершается процедурой, в которой имеется условие для выполнения ее не рекурсивной части (в нашем примере это 0>0).
Обратный ход. Начинается с вычислений не рекурсивной части (P=1 в ветви else), после чего процедура удаляется и становится доступной предыдущая копия процедуры. Выполняется оператор, следующий за оператором рекурсивного вызова (P=P*1) , выход из этой копии процедуры и удаление этой копии и т.д.
Рекурсивные процедуры требуют значительно больше машинных ресурсов, чем обычные процедуры и труднее отлаживаются. Но для многих задач рекурсивные методы более естественны и дают простую и наглядную запись алгоритма (например, алгоритм быстрой сортировки). Существуют также задачи, которые могут быть решены только рекурсивными методами.
Прямой ход: глубина рекурсии равна 4.
n=3 P=6 Создаются в памяти 4 копии про-
fact(3,P) if 3>0 then n=2 P=2 последняя - сверху.
fact(2,P) if 2>0 then n=1 P=1
end fact(1,P) if 1>0 then n=0 P=1
end fact (0,P) if 0>0 then
. . . P:=P*1 begin
Обратный ход. end . . .
Производятся вычисле- . . . end
ния, начиная с последней копии. else
При завершении процедуры освобождается P:=1
выделенная под нее память и происходит возврат в
точку ее вызова предыдущей копии.
Рис.1.