Рекурсия « Я оглянулся посмотреть, не оглянулась ли она, чтоб посмотреть, не оглянулся ли я...» М. Леонидов Примеры рекурсий Матрешка Зеркало в зеркале Телевизор в телевизоре Задача о Ханойских башнях Лингвистические рекурсии: 1. У попа была собака… 2. Дом, который построил Джек… (Р. Бернс) Рекурсивной называют процедуру или функцию, внутри которой происходит обращение самой к себе, но с другими параметрами. Это прямая рекурсия. Косвенной называется рекурсия, когда две или более процедуры или функции вызывают друг друга. Пример косвенного вызова процедуры или функции: процедура A вызывает процедуру B, а процедура B вызывает процедуру A Структура описания рекурсивных процедур (функций) имеет следующий вид: <действия на входе в рекурсию>; if <условие> then <действия выхода из рекурсии> else <вызов процедуры (функции) с другими параметрами> Рекурсия должна иметь условие завершения, в ней должна быть не рекурсивная ветвь. В качестве <условие> выступают граничные случаи параметров, при которых результат работы рекурсии известен. Это условие завершения процесса вхождения в рекурсию. Механизм работы рекурсии 1.Со входом в рекурсию осуществляется вызов процедур (функций), а для выхода необходимо помнить, откуда пришли, т.е помнить точки возврата (адреса). 2. Место хранения точек возврата называется стеком вызова и для него отводится определенная область оперативной памяти. Механизм работы рекурсии 3. В стеке запоминаются также значения всех локальных переменных, т.е. создается копия параметров процедур (функций). 4. Стек ограничен! Возможно его переполнение – это главный недостаток рекурсии! Вычисление факториала N! 0!=1!=1 2!=2=1!*2=1*2 3!=2!*3=1!*2*3=1*2*3 /……………………….. N!= 1*2*3*4*….*n function fact(n:byte):longint; begin If (n=0)or (n=1) then fact:=1 else fact:=fact(n-1)*n; end; Работа рекурсивной функции для n=3 1. n=3 Fuction fact(3); begin fact:=3*fact(2); end; 2. n=2 Fuction fact(2); begin fact:=2*fact(1); end; 3. n=1 Fuction fact(1); begin fact:=1; end; Процедура ввода с клавиатуры последовательности чисел (окончание ввода - 0) и вывода ее на экран в обратном порядке. procedure solve; var n:integer; begin readln(n); if n<>0 then solve; write (n:5); end; Работа рекурсивной функции для n=3 1. n=3 procedure solve; var n:integer; begin readln(3); if 3<>0 then solve; write (n:5); end; 2. n=2 procedure solve; var n:integer; begin readln(2); if 2<>0 then solve; write (n:5); end; 3. n=0 procedure solve; var n:integer; begin readln(0); {if 0<>0 then solve;} write (n:5); end; Поиск n-ного числа Фибоначи (1,1,2,3,5,8,….) Function fib (n:integer):integer; begin If n<=2 then fib:=1 else fib:=fib(n-1)+fib(n-2); End; Работа рекурсивной функции для n=3 1. n=3 Function fib (3); begin If 3<=2 then fib:=1 else fib:=fib(2)+fib(1); End; 2. n=2 Function fib (2); begin If 2<=2 then fib:=1; End; Найти сумму n членов арифметической прогрессии(первый член – а, разность – d) Function sa (n,a:integer):integer; begin If n>0 then sa:=a+sa(n-1,a+d) else sa:=0; End; Работа рекурсивной функции для n=3 1. n=3 Function sa(3,a); begin If 3>0 then sa:=a+sa(2,a+d); 2. n=2 Function sa (2,a+d); begin If 2>0 then sa:=(a+d)+sa(1,a+2*d); End; End; 4. n=0 Function sa (0,a+d); begin sa:=0; End; 3. n=1 Function sa (1,a+2*d); begin If 1>0 then sa:=(a+2*d)+sa(0,a+d); End; Процедура, определяющая минимум среди элементов массива а a- глобальная переменная; n – размерность массива; если n>=2, то минимум среди a[n] и минимума из первых n-1 элементов массива если n=1, то минимум равен a[1] Procedure a_min(n:integer; var x:integer); begin if n=1 then x:=a[1] else begin a_min(n-1,x); if a[n]<x then x:=a[n]; end; end; Работа процедуры для массива из 4-х чисел с элементами 3,1,-2,4 Прямой порядок a_min(4,x); n<>1 a_min(3,x); n<>1 a_min(2,x); n<>1 a_min(1,x); n=1; x:=a[1]=3; Обратный порядок n=2; a[2]<x; x:=a[2]=1; n=3; a[3]<x; x:=a[3]=-2; n=4; a[4]<x; x:=a[3]=-2; Домашнее задание: Для приведенных примеров прорисуйте схемы работы процедур (функций) для конкретных входных данных (не очень больших!, иначе глубина рекурсии будет очень большой) Найти максимальный элемент в глобальном одномерном массиве А. Функция подсчета в строке суммы цифр function f(s:string):integer; var x,c,k:integer; begin n:=length(s); if n=0 then f:=0 else begin val(s[n],x,c); k:=0; if c=0 then k:=x; s:=copy(s,1,n-1); f:=f(s)+k end; end; Процедура удаления из строки всех точек procedure f(s:string); begin if length(s)>0 then begin s:=copy(s,1,length(s)-1); f(s) end; if s[length(s)]<>'.' then write(s[length(s)]); end; Фракталом называется множество, части которого являются повторением образа самого множества. В данном рисунке 2 уровня, на каждом из которых большая окружность окружена четырьмя окружностями поменьше, каждая из меньших в свою очередь окружена еще меньшими окружностями и т.д. Алгоритм построения будет заключаться в следующем: • Написать процедуру изображения одной окружности с четырьмя окружностями поменьше • Использовать эту процедуру с другими параметрами для построения окружностей следующего уровня Для построения каждого уровня необходимо знать расстояние от центра большой окружности до малой и радиус малой окружности. rm, rб, ro - радиус малой окружности, радиус большой окружности и радиус орбиты (т.е. расстояние от центра большой окружности до малой) соответственно. k1= rm / rб, k2= ro / rб. Задав значения k1, k2 можно на каждом уровне вычислять расстояние от центра большой окружности до малой и радиус малой окружности. Формальными параметрами процедуры являются: х, у - координаты центра большой окружности данного уровня, г - радиус большой окружности данного уровня, г1 - радиус орбиты, n - номер уровня. При каждом вызове процедуры номер уровня уменьшается на 1 и выход из рекурсии осуществляется при n=0. uses graph; var x, y, n, r, r1, gd, gm: integer; k1,k2:real; procedure picture2(x,y,r,r1,n:integer); var x1,y1, i: integer; begin if n>0 then begin circle(x,y,r); r1:=trunc(r*k2); for i:=1 to 4 do begin x1 :=trunc(x+r1 *cos(pi/2*i)); y1 :=trunc(y+r1 *sin(pi/2*i)); picture2(x1,y1, trunc(r*k1),r1,n-1); end; end; end; begin x:=300; y:=200; k1:=0.3; k2:=2; readln(n); readln(r); Gd :=detect; InitGraph(Gd, Gm,’ ‘); picture2(x,y,r,r1,n); Readln; closegraph; end. Написать программу построения следующего изображения: В треугольнике проведены три средние линии. В результате он разбивается на 4 новых треугольника. В каждом треугольнике, кроме центрального, опять проводятся средние линии и т.д. uses graph; var xa, xb, xc, ya, yb, yc, n, gd, gm,r: integer; procedure triangle (xa,ya,xb,yb,xc,yc,n: integer); var xp, xq, xr, yp, yq, yr: integer; begin if n>0 then begin {высчитываются координаты средних линий треугольников} xp:=(xb+xc) div 2; yp:=(yb+yc) div 2; xq:=(xa+xc) div 2; yq:=(ya+yc) div 2; xr:=(xb+xa) div 2; yr:=(yb+ya) div 2; {рисуем средние линии треугольника} line(xp,yp,xq,yq); line(xq,yq,xr,yr); line(xp,yp,xr,yr); {рекурсивно вызываем алгоритм для новых треугольников} triangle(xa,ya,xr,yr,xq,yq,n-1); triangle(xb,yb,xp,yp,xr,yr,n-1); triangle(xc,yc,xq,yq,xp,yp,n-1); end; end; begin xc;=300; yc:=0; xb:=600; yb:=400; xa:=0; ya:=400; readln(n); Gd := Detect; lnitGraph(Gd, Grn, ‘’); line(xa,ya,xb,yb); line(xb,yb,xc,yc); line(xa,ya,xc,yc); triangle(xa,ya,xb,yb,xc,yc,n); readln; closegraph; end. Дан линейный массив чисел, записать его в обратном порядке. var A:array [1..10] of integer; procedure invertItem(N1, M1:integer); begin t := A[N1]; A[N1] := A[M1] A[M1] := t inc(N1); dec(M1); if N1 < M1 then invertItem(N1, M1) end; invertItem(1, 10); Дан массив, напишите рекурсивную программу для вычисления суммы 1/a[i] function summa(n: integer): real; var S: real; begin if n = 0 then summa:=0 else begin S:=summa(n-1); summa:=S+1/a[n]; end; end; Или function s(r:integer):integer; begin inc(i); if i<=10 then begin v:=v+r; s:=s(a[i]); end; end; алгоритм вычисления суммы элементов массива function Summa(k:byte;x:Mas):integer; begin if k=0 then Summa:=0 else Summa:=x[k]+Summa(k-1,x) {если массив пуст, сумма=0, иначе к предыдущей сумме добавляем значение текущего элемента} end;