Вы находитесь на странице: 1из 26

Содержание

ВВЕДЕНИЕ..............................................................................................................3
1 Рекурсия................................................................................................................4
1.1 Понятие рекурсии....................................................................................4
1.2 Применение рекурсивных алгоритмов.................................................8
2 Итерации.............................................................................................................13
2.1 Понятие итерационных алгоритмов..........................................13
2.1.1 Цикл с известным числом повторений (for)..........................13
2.1.2 Цикл с предусловием. Оператор while...................................15
2.1.3 Цикл с постусловием. Оператор repeat..................................16
2.1.4 Вложенные циклы....................................................................18
2.2 Применение итерационных алгоритмов.............................................19
3 Сравнение рекурсивных и итерационных алгоритмов...................................22
3.1 Вычисление N-го числа Фибоначчи....................................................22
3.2 Вычисление факториала.......................................................................23
ЗАКЛЮЧЕНИЕ.....................................................................................................26
Список литературы...............................................................................................27

2
ВВЕДЕНИЕ

В наше время все больше и большей задач решаются с помощью


компьютерных программ. Наверное уже ни одна сфера жизнедеятельности
уже не обходится без программного обеспечения, будь то медицина, физика,
экономика, бухгалтерия и т.д.
Большинство задач решаемых с помощью программ связанны с
обработкой массив данных различных объектов, для обработки которых
применяются итерационные и рекурсивные алгоритмы.
Объектом исследования является рекурсивные и итерационные
алгоритмы и их сравнение.
Целю работы является сравнение рекурсивные и итерационные
алгоритмы и рассмотрение примеров их реализации на одном из языков
программирования.
Задачи:
 рассмотреть понятие рекурсии и привести примеры ее
реализации;
 рассмотреть понятие итерации и привести примеры ее
реализации;
 сравнить их и выявить преимущества и недостатки данных типов
алгоритмов.

3
1 Рекурсия

1.1 Понятие рекурсии

Рекурсия - это такой способ организации вычислительного процесса,


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

4
Безусловные рекурсивные процедуры приводят к бесконечным
процессам, и на эту проблему нужно обратить особое внимание, так как
практическое использование процедур с бесконечным самовызовом
невозможно.
Следовательно, главное требование к рекурсивным процедурам
заключается в том, что вызов рекурсивной процедуры должен выполняться
по условию, которое на каком-то уровне рекурсии станет ложным.
Если условие истинно, то рекурсивный спуск продолжается. Когда оно
становится ложным, то спуск заканчивается и начинается поочередный
рекурсивный возврат из всех вызванных на данный момент копий
рекурсивной процедуры. Структура рекурсивной процедуры может
принимать три разных формы:
1) форма с выполнением действий до рекурсивного вызова (на
рекурсивном спуске);
procedure Rec;
begin
S;
if условие then
Rec;
end;
2) форма с выполнением действий после рекурсивного вызова (на
рекурсивном возврате);
procedure Rec;
begin
if условие then
Rec;
S;
end;

5
3) форма с выполнением действий как до, так и после рекурсивного
вызова (с выполнением действий как на рекурсивном спуске, так и на
рекурсивном возврате).
procedure Rec;
begin
S1;
if условие then
Rec;
S2 ;
end;
Все формы рекурсивных процедур находят применение на практике.
Многие задачи, в том числе вычисление факториала, безразличны к тому,
какая используется форма рекурсивной процедуры. Однако есть классы
задач, при решении которых программисту требуется сознательно управлять
ходом работы рекурсивных процедур и функций. Такими, в частности,
являются задачи, использующие списковые и древовидные структуры
данных.
Рассмотрим пример рекурсивной функции rever выводящею цифры
переданного ей числа n в обратном порядке.
procedure rever (n: integer);
begin
write (n mod 10);
if (n div 10) <> 0 then
rever (n div 10)
end;
begin
rever (3096);
end.
Алгоритм работы функции:

6
1. На вход функции rever в качестве параметра n поступает число
3096.
2. Процедура rever выводит на экран остаток от деления на 10. Это
число 6.
3. Переход на новую строку не происходит, т.к. используется write.
4. Проверяется условие того, что 3096 при деление нацело на 10
больше нуля.
5. Вызывается rever с фактическим параметром, равным 309.
6. Вторая запущенная процедура выводит на экран цифру 9 и
запускает третью процедуру с параметром 30.
7. Третья процедура выводит 0 и вызывает четвертый rever с 3 в
качестве параметра.
8. Четвертая процедура выводит 3 на экран и ничего больше не
вызывает, т.к. условие (3 div 10) <> 0 ложно.
9. Четвертая процедура завершается и передает управление третьей.
10. Третья процедура завершается и передает управление второй.
11. Вторая процедура завершается и передает управление первой.
12. Первая процедура завершается и передает управление в
основную ветку программы.
В итоге, процедура rever была вызвана четыре раза (глубина рекурсии
равна 4), хотя из основной программы к ней было единственное обращение.
Наличие условия в теле рекурсивной функции (или процедуры), при
котором она больше себя не будет вызывать, очень важно. В противном
случае, как и в ситуации с циклами, может произойти так называемое
зацикливание.
Возможна чуть более сложная схема: функция A вызывает функцию B,
а та в свою очередь вызывает A. Это называется сложной рекурсией. Ниже
представлен пример сложной рекурсии.
procedure A(n: integer); {Полное описание процедуры A}
begin
7
writeln(n);
B(n-1);
end;
procedure B(n: integer); {Полное описание процедуры B}
begin
writeln(n);
if n<10 then
A(n+2);
end;

1.2 Применение рекурсивных алгоритмов

Рекурсивные алгоритмы имею очень широкое применение. Одним из


примеров применения рекурсии, является методы для работы с деревьями.
Дерево – это структура данных, представляющая собой совокупность
элементов и отношений, образующих иерархическую структуру этих
элементов. Каждый элемент дерева называется вершиной (узлом) дерева.
Вершины дерева соединены направленными дугами, которые называют
ветвями дерева. Начальный узел дерева называют корнем дерева, ему
соответствует нулевой уровень. Листьями дерева называют вершины, в
которые входит одна ветвь и не выходит ни одной ветви.[2] На рисунке 1.1
приведен пример бинарного дерева.

8
Рисунок 1.1 - Бинарное дерево

В свою очередь деревья различных типов применяются:


 для ускорения выборки данных из базы данных, за счет
построения индексов;
 разбора арифметических выражений;
 построения иерархии папок в операционной системе;
 и т.д.

На рисунке 1.2 представлен пример обхода дерева тремя различными


структурами рекурсивной процедуры.
 форма с выполнением действий до рекурсивного вызова (на
рекурсивном спуске);
 форма с выполнением действий после рекурсивного вызова (на
рекурсивном возврате);
 форма с выполнением действий как до, так и после рекурсивного
вызова (с выполнением действий как на рекурсивном спуске, так и на
рекурсивном возврате).

9
Рисунок 1.2 - Результат обхода дерева тремя различными структурами
рекурсивной процедуры
Еще один пример рекурсии - это фракталы. Фракталами называют
геометрические объекты, линии, поверхности, пространственные тела,
имеющие сильно изрезанную форму и обладающие свойством самоподобия
(«fractus» - делить, ломать).
Ниже приведены примеры фракталов.

10
Рисунок 1.3 - Кривая коха

Рисунок 1.4 Дерево Пифагора

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


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

12
2 Итерации

2.1 Понятие итерационных алгоритмов

Итерация (лат. iteratio — повторяю) в широком смысле слова —


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

2.1.1 Цикл с известным числом повторений (for)

Если число повторений тела цикла заранее известно, то используется


оператор цикла for, который также часто называют оператором цикла с
параметром.
Оператор for состоит из двух частей: тела цикла и заголовка, который
предназначен для описания начального и конечного значений параметра
цикла, а также варианта его изменения.
В зависимости от направления изменения параметра цикла
(возрастание - to или убывание - downto) в языке Паскаль оператор цикла for
может быть записан в одной из двух форм:
for параметр := нач_знач to кон_знач do

13
оператор;
или
for параметр := нач_знач downto кон_знач do
оператор;

Рисунок 2.1 - Блок-схема цикла for

Переменная-параметр цикла может принимать любой порядковый тип.


При этом начальное и конечное значения должны иметь тип совместимый с
типом переменной-параметром.
Рассмотрим работу цикла for.
Перед началом выполнения оператора цикла вычисляются начальное
значение, присваиваемое переменной-параметру, и конечное значение. Затем,
циклически выполняются следующие операции:
1. Сравнивается текущее значение параметра с конечным
значением.
2. Если условие параметр <= кон_знач истинно, то выполняется
тело цикла, в противном случае оператор for завершает работу и управление
передается оператору, следующему за циклом.

14
2.1.2 Цикл с предусловием. Оператор while

Оператор цикла while применяется в тех случаях, когда число


повторений цикла заранее неизвестно и, действия, описанные в цикле, могут
вообще не выполняться.
Оператор цикла while в Паскаль имеет следующий формат записи:
while выражение do
оператор;

Рисунок 2.2 - Блок-схема цикла while

Рассмотрим работу оператора цикла while.


1. Вычисляется значение выражения (т. е. условие, стоящее после
ключевого слова while), которое должно быть логическим выражением.
15
2. Если результат вычисления выражения равен true (истина), то
выполняется тело цикла (простой или составной оператор, расположенный
после ключевого слова do). Затем, снова проверяется условие и т. д.
3. Если результат равен false (ложь), то происходит выход из цикла
и управление передается на первый оператор, следующий за циклом.

2.1.3 Цикл с постусловием. Оператор repeat

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


в заголовке сразу вернуло false. Однако такая ситуация не всегда может быть
приемлемой. Бывает, что тело цикла должно выполниться хотя бы один раз,
не зависимо оттого, что вернет логическое выражение. В таком случае
используется цикл repeat – цикл с постусловием.

В цикле repeat логическое выражение стоит после тела цикла. Причем,


в отличие от цикла while, здесь всё наоборот: в случае true происходит выход
из цикла, в случае false – его повторение.
Оператор repeat, который имеет следующий формат записи:
repeat
тело цикла
until выражение

16
Рисунок 2.3 - Блок-схема цикла repeat

Работа оператора цикла repeat происходит следующим образом:


1. Выполняется последовательность операторов, заключенная
между ключевыми словами repeat и until (поэтому тело цикла выполнится
хотя бы один раз).
2. Производится проверка продолжения цикла: если значение
выражения, записанного после ключевого слова until, равно false (ложь), то
тело цикла выполняется снова.
3. Если значение выражения равно true (истина), то происходит
выход из цикла.

17
2.1.4 Вложенные циклы

Основная идея использования вложенных циклов состоит в том, что


даже когда некий процесс, требует цикла -- повтора действий (т.н. "внешний
цикл"), то даже внутри отдельного действия можно запустить свой цикл (т.н.
"внутренний" или "вложенный цикл") для решения какой-то "местной"
задачи. То есть: внутри витка внешнего цикла, можно запустить цикл
внутренний, тогда на один виток внешнего цикла, внутренний цикл будет
каждый раз выполнять все свои витки.
Рассмотрим классический пример задачи с выводом таблицы
умножения.
var i, j: integer;
begin
for i := 1 to 9 do // цикл по строкам таблицы, счетчик как левый
множитель
begin
for j := 1 to 9 do // выводим равенства очередной строки, счётчик
как правый множитель
write(i, '*', j, '=', i*j:2, #9);
writeln(); // переносим строку
end;
end.
Пример работы программы представлен на рисунке 2.4.

Рисунок 2.4 - Результат работы программы вывода таблицы умножения


18
2.2 Применение итерационных алгоритмов

Огромное количество различного рода задач решается с применением


алгоритмов циклической структуры:
 сортировка данных;
 приближенное вычисление интегралов;
 уточнение корней уравнения;
 решение систем уравнений;
 задачи оптимизации (задачи линейного программирования);
 и многое другое.
Рассмотрим более подробно один из возможных практических
вариантов применения алгоритмов практических задач - сортировку данных -
на примере алгоритма сортировки методом пузырька.
Данная задача решается с использованием вложенных циклов.
Алгоритм:
1) Последовательно бежим по массиву и сравниваем два соседних
элемента, если порядок не верный то меняем их местами.
2) Повторять проходы на один меньше, чем кол-во элементов в массиве
или пока не окажеться, что за проход не произведено ни одной замены.
Просмотрим алгоритм на примере:
Отсортируем массив: 524613 где каждая цифра это элемент массива.
Начало цикла, в массиве 524613 нет отсортированных чисел:
1) Первый проход:
524613->254613->245613->245613->245163->245136
Мы бежим по массиву и меняем элементы местами, которые находятся
рядом друг с другом и не соответствуют порядку сортировки. Первый шаг
это цифры 5 и 2, второй 5 и 4, третий 6 и 5, четвёртый 6 и 1, пятый 6 и 3.
2) Второй проход.
19
245136->245136->245136->241536->241356->241356
3) Третий проход.
241356->241356->214356->213456->213456->213456
4) Четвёртый проход.
213456->123456->123456->123456->123456->123456
После первого шага видно, что массив отсортирован, но условия
выхода из сортировки ещё нет сказано, что не должно проходить ни одного
изменения, или шаг должен быть на один меньше, чем кол-во элементов в
массиве.
5) Пятый проход финальный.
123456->123456->123456->123456->123456->123456
Сразу удовлетворилось два условия выхода, не было произведено ни
одного изменения и проход по счёту равен количеству элементов в массиве
минус 1.
Ниже представлена код процедуры реализующей данный алгоритм на
языке программирования Pascal.
Procedure Bubble_Sort (var a : array of real; LengthArray :Integer);
var i,i2:integer;
begin
for i:=1 to LengthArray-1 do
for i2:=1 to LengthArray-2 do
if a[i2+1]<a[i2] then
Swap(a[i2],a[i2+1]);
end;

Рассмотрим еще одну задачу решаемую с применением итерационных


алгоритмов - вычисление определенного интеграла. Рассмотрим на примере
метода трапеций. Блок-схема данного метода приведена на рисунке 2.5.

20
Рисунок 2.5 - Блок-схема алгоритма метода трапеций

Ниже приведен код программы на языке паскаль реализующий данный


алгоритм.
function f(x: real): real;
begin
f := sqr(x) - x - 1;
end;
var
a, b, S, h, integ: real;
i, n: integer;
begin
readln(a, b, n);
h := (b - a) / n;
for i := 1 to n - 1 do
S := s + f(a + h * i);
integ := h * ((f(a) + f(b)) / 2 + S);
writeln('S=', integ);
end.
21
3 Сравнение рекурсивных и итерационных алгоритмов

Для сравнения итерационных и рекурсивных алгоритмов рассмотрим


их на решении простых задач.

3.1 Вычисление N-го числа Фибоначчи

Числа Фибоначчи — элементы числовой последовательности в


которой первые два числа равны либо 1 и 1, либо 0 и 1, а каждое
последующее число равно сумме двух предыдущих чисел[3].
Ниже приведена программа на языке программирования Pascal
вычисляющая n-е число Фибоначчи.
program Fibonacci;
function Recursion (n: word): longint;
begin
if (n = 0) or (n = 1) then
Recursion:= 1
else Recursion := Recursion(n - 1) + Recursion(n - 2); {рекурсивный
вызов}
end;

function Iteration(n: word): longint;


var
x, y, t: longint;
k: integer;
begin
x := 1;
y := 1;

22
for k := 2 to n do
begin
t := y;
y := x + y;
x := t;
end;
Iteration := y; {итерация}
end;

Var
n:integer = 20;
begin
writeln('Рекурсивный алгоритм : F(', n, ')= ', Recursion(n));
writeln('Итерационный алгоритм : F(', n, ')= ', Iteration(n));
end.

Исходя из алгоритма можно сделать следующие выводы:


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

3.2 Вычисление факториала

Слово факториал произошло от латинского factor (делающий,


производящий). Факториал числа — это произведение натуральных чисел от
1 до самого числа (включая данное число).

23
Ниже приставлен код вычисления факториала на языке
программирования Pascal.
function FacRecursive(n: Integer): Real;
begin
if n = 0 then
FacRecursive := 1
else FacRecursive := n * FacRecursive(n - 1)
end;
function FacIteration(n: Integer): Real;
var
s, i: integer;
begin
s := 1;
for i := 1 to n do
S := S * i;
FacIteration:=s;
end;
var
n: integer;
begin
write('n: ');
readln(n);
writeln('Итерация: ',FacIteration(n));
writeln('Рекурсия: ',FacRecursive(n));
end.
Как и при вычислении числа Фибоначчи, рекурсивный код алгоритма
более компактный, но на него затрачивается большее количество памяти.
Важнейшим достоинством рекурсивного подхода к программированию
является краткость записи и прозрачность рекурсивных алгоритмов. Не
следует также забывать об ориентации рекурсивного программирования на
24
математическую форму записи, а следовательно, о возможности
доказательства правильности построенных алгоритмов.
Однако, если существует очевидное итеративное решение, не следует
заменять его на рекурсию, поскольку активация рекурсивной процедуры
потребует дополнительных затрат времени и памяти множество объектов,
т.е. множество переменных, констант, типов и процедур, которые
определены в ней. При каждом рекурсивном вызове процедуры порождается
новое множество локальных переменных. Хотя они имеют те же самые
имена, что и локальные переменные предыдущего поколения, их значения
могут быть различными, а конфликты по именам разрешаются с помощью
правил, определяющих область действия идентификаторов. Аналогичные
утверждения справедливы и для параметров рекурсивных процедур
(функций). При этом все предыдущие поколения локальных переменных
сохраняются в области памяти, называемой стеком вызова подпрограмм. В
данном случае его можно называть стеком рекурсивных вызовов. Кроме
локальных переменных, в стеке сохраняется текущее состояние вычислений,
т.е. адрес той команды, на которую нужно вернуться, чтобы закончить
вычисления, прерванные вызовом подпрограммы. Очевидно, что при
большой глубине рекурсии может возникнуть проблема нехватки памяти
(переполнение стека рекурсивных вызовов). В итеративных алгоритмах такие
проблемы, как правило, не возникают.

25
ЗАКЛЮЧЕНИЕ

В ходе работы били рассмотрены циклические и итерационные


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

26
Список литературы

27

Вам также может понравиться