СДЕЛАЙТЕ СВОИ УРОКИ ЕЩЁ ЭФФЕКТИВНЕЕ, А ЖИЗНЬ СВОБОДНЕЕ

Благодаря готовым учебным материалам для работы в классе и дистанционно

Скидки до 50 % на комплекты
только до

Готовые ключевые этапы урока всегда будут у вас под рукой

Организационный момент

Проверка знаний

Объяснение материала

Закрепление изученного

Итоги урока

Информатика. Алгоритмы в Паскале – Pascal - 1. Рекурсивные алгоритмы.

Категория: Информатика

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

Алгоритмы в Паскале – Pascal - 1. Рекурсивные алгоритмы.

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

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

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

Примеры рекурсивных программ.

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

 

Факториал

{

n! = n * (n-1)!, при n>0

1, n=0

Function Fact(n:byte):longint; begin   if n=0      then Fact:=1     else Fact:=n*Fact(n-1) end;

Числа Фибоначчи

{

Фn = Фn-1 + Фn-2, при n>1

Ф0 = 1, Ф1 = 1

Function F(n:byte):longint; begin   if n <= 1      then F:=1     else F:= F(n-1)+F(n-2) end;

 

Рекурсия и итерация

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

К примеру, вычисление факториала и чисел Фибоначчи можно реализовать без рекурсии:

Факториал

Function Fact(n:byte):longint; var F, i : byte; begin   F:=1;   for i:=1 to n do F:=F*i;   Fact:=F end;

Function Fact(n:byte):longint; begin   if n=0      then Fact:=1     else Fact:=n*Fact(n-1) end;

Числа Фибоначчи

Function F(n:byte):longint; var F0, F1, F2 : longint; i : byte; begin   F0:=1; F1:=1;   for i:=2 to n do     begin       F2:=F1+F0; F0:=F1; F1:=F2;     end;   F:=F1 end;

Function F(n:byte):longint; begin   if n <= 1      then F:=1     else F:= F(n-1)+F(n-2) end;

При выполнении рекурсивной подпрограммы сначала происходит «рекурсивное погружение», а затем «возврат вычисленных результатов». Например, вычисление 5! при помощи вызова Fact(5) будет происходить следующим образом:

Fact(5) ® 5 * Fact(4)                     5 * 24    ®   120                  ¯                             ­               4* Fact(3)                   4 * 6                    ¯                           ­                 3 * Fact(2)                3 * 2                        ¯                        ­                    2 * Fact(1)             2 * 1                          ¯                     ­                          1          ®         1 

Как видно на примере вычисления 5!, при «рекурсивном погружении» функция Fact вызывает точно такой же новый экземпляр самой себя. При этом сама функция как бы еще не завершилась, а новый ее экземпляр уже начинает работать. И только когда новый экземпляр завершит работу («вернет вычисленные результаты»), будет продолжена работа самой функции.

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

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

Пример рекурсивной процедуры:

1

2

3

4

5

6

procedure Rec(a: integer);

begin

  if a>0 then

    Rec(a-1);

  writeln(a);

end;

Рассмотрим, что произойдет, если в основной программе поставить вызов, например, вида Rec(3).

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

 

 

Рис. 1. Блок схема работы рекурсивной процедуры.

Процедура Rec вызывается с параметром a = 3. В ней содержится вызов процедуры Rec с параметром a = 2. Предыдущий вызов еще не завершился, поэтому можете представить себе, что создается еще одна процедура и до окончания ее работы первая свою работу не заканчивает. Процесс вызова заканчивается, когда параметр a = 0. В этот момент одновременно выполняются 4 экземпляра процедуры. Количество одновременно выполняемых процедур называют глубиной рекурсии.

Четвертая вызванная процедура (Rec(0)) напечатает число 0 и закончит свою работу. После этого управление возвращается к процедуре, которая ее вызвала (Rec(1)) и печатается число 1. И так далее пока не завершатся все процедуры. Результатом исходного вызова будет печать четырех чисел: 0, 1, 2, 3.

Еще один визуальный образ происходящего представлен на рис. 2.

 

 

Рис. 2. Выполнение процедуры Rec с параметром 3 состоит из выполнения процедуры Rec с параметром 2 и печати числа 3. В свою очередь выполнение процедуры Rec с параметром 2 состоит из выполнения процедуры Rec с параметром 1 и печати числа 2. И т. д.

В качестве самостоятельного упражнения подумайте, что получится при вызове Rec(4). Также подумайте, что получится при вызове описанной ниже процедуры Rec2(4), где операторы поменялись местами.

1

2

3

4

5

6

procedure Rec2(a: integer);

begin

  writeln(a);

  if a>0 then

    Rec2(a-1);

end;

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

 

Рекуррентное соотношение – это рекурсивная функция с целочисленными значениями.

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

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

Например, пусть мы пытаемся вычислить числа Фибоначчи по рекурсивной схеме

F(i) = F(i-1) + F(i-2), при N >= 1; F(0) = 1; F(1) = 1;

с помощью указанной выше рекурсивной подпрограммы-функции F(n).

Требующееся при вычислении значения F(N) по такой схеме количество рекурсивных вызовов может быть получено из решения рекуррентного выражения

TN = TN-1 + TN-2, при N >= 1; T0 = 1; T1 = 1

TN приблизительно равно ФN , где Ф »1.618 - золотая пропорция («золотое сечение»), т.е. приведенная выше программа потребует экспоненциальных временных затрат на вычисления.


Скачать

Рекомендуем курсы ПК и ППК для учителей

Вебинар для учителей

Свидетельство об участии БЕСПЛАТНО!