Рекурсія h2>
Рекурсія - це такий спосіб організації допоміжного алгоритму
(підпрограми), при якому ця підпрограма (процедура або функція) в ході
виконання її операторів звертається сама до себе. Взагалі, рекурсивним називається
будь-який об'єкт, який частково визначається через себе. p>
Наприклад,
наведене нижче визначення двійкового коду є рекурсивним: p>
<двійковий код>:: = <двійкова
цифра> | <двійковий код> <двійкова цифра> p>
<двійкова цифра>:: = 0 | 1 p>
Тут для
опису поняття були використані, так звані, металінгвістіческій формули
Бекуса-Наура (мова БНФ); знак "::=" означає "за визначенням
є ", знак" | "-" або ". p>
Взагалі, в
рекурсивної визначенні повинно присутньо обмеження, граничне умова,
при виході на яке подальша ініціація рекурсивних звернень припиняється. p>
Наведемо інші
приклади рекурсивних визначень. p>
Приклад 1.
Класичний приклад, без якого не обходяться ні в одному оповіданні про рекурсії,
- Визначення факторіалу. З одного боку, факторіал визначається так:
n! = 1 * 2 * 3 *...* n. З іншого боку, Граничним
умовою в даному випадку є n <= 1. p>
Приклад 2.
Визначимо функцію K (n), яка повертає кількість цифр у заданому
натуральному числі n: p>
Завдання. За
аналогією визначте функцію S (n), яка обчислює суму цифр заданого натурального
числа. p>
Приклад 3. Функція
C (m, n), де 0 <= m <= n, для обчислення біноміального коефіцієнта по
такою формулою є
рекурсивної. p>
Нижче будуть
наведені програмні реалізації всіх цих (і не тільки) прикладів. p>
Звернення до
рекурсивної підпрограмі нічим не відрізняється від виклику будь-який інший
підпрограми. При цьому при кожному новому рекурсивної зверненні до пам'яті
створюється нова копія підпрограми з усіма локальними змінними. Такі копії
будуть породжуватися до виходу на граничне умова. Очевидно, у разі відсутності
граничного умови, необмежене зростання числа таких копій призведе до аварійного
завершення програми за рахунок переповнення стека. p>
Породження все
нових копій рекурсивної підпрограми до виходу на граничне умова називається
рекурсивним спуском. Максимальна кількість копій рекурсивної підпрограми,
яке одновренно може перебувати в пам'яті комп'ютера, називається глибиною
рекурсії. Завершення роботи рекурсивних підпрограм, аж до самої першої,
яка ініціювала рекурсивні виклики, називається рекурсивним підйомом. p>
Виконання
дій у рекурсивної підпрограмі може бути організовано одним з варіантів: p>
Begin Begin Begin p>
P; оператори; оператори; p>
оператори; P P; p>
End; End; оператори p>
End; p>
рекурсивний
підйом рекурсивний
спуск і рекурсивний узвіз, і
рекурсивний підйом p>
Тут P --
рекурсивна підпрограма. Як видно з малюнка, дії можуть виконуватися або
на одному з етапів рекурсивного звернення, або на обох відразу. Спосіб
організації дій диктується логікою розробляється алгоритму. p>
Реалізуємо
наведені вище рекурсивні визначення у вигляді функцій і процедур на мові
Pascal і у вигляді функцій на мові C. p>
Приклад 1. p>
(Функція на Pascal) p>
Function
Factorial (N: integer): Extended; p>
Begin p>
If N <= 1 p>
Then Factorial: = 1 p>
Else Factorial: = Factorial (N-1) * N p>
End; p>
(Процедура на Pascal) p>
Procedure Factorial (N: integer; Var
F: Extended); p>
Begin p>
If N <= 1 p>
Then F: = 1 p>
Else Begin Factorial (N-1, F); F: = F * N End p>
End; p>
/* Функція на C */ p>
double Factorial (int N) p>
( p>
double F; p>
if (N <= 1) F = 1.; else F = Factorial (N-1) * N; p>
return F; p>
) p>
Приклад 2. p>
(Функція на Pascal) p>
Function K (N: Longint): Byte; p>
Begin p>
If N <10 p>
Then K: = 1 p>
Else K: = K (N div 10) +1 p>
End; p>
(Процедура
на Pascal) p>
Procedure K (N: Longint; Var Kol: Byte) p>
Begin p>
If N <10 p>
Then Kol: = 1 p>
Else Begin K (N Div 10, Kol); Kol: = Kol 1 End; p>
End; p>
/*
Функція на C */ p>
int K (int N) p>
(int Kol; p>
if (N <10) Kol = 1; else Kol = K (N/10) 1; p>
return Kol; p>
) p>
Приклад 3. p>
(Функція на Pascal) p>
function C (m, n: Byte): Longint; p>
Begin p>
If (m = 0) or (m = n) p>
Then C: = 1 p>
Else C: = C (m, n-1) + C (m-1, n-1) p>
End; p>
(Процедура на Pascal) p>
Procedure C (m, n: Byte; Var R: Longint); p>
Var R1, R2: Longint; p>
Begin p>
If (m = 0) or (m = n) p>
Then R: = 1 p>
Else Begin p>
C (m, n-1,
R1); p>
C (m-1, n-1, R2); p>
R: = R1 + R2 p>
End; p>
End; p>
/* Функція на C */ p>
int C (int m, int n) p>
(int f; p>
if (m == 0 | | m == n) f = 1; else f = C (m, n-1) + C (m-1, n-1); p>
return f; p>
) p>
Приклад 4.
Обчислити суму елементів лінійного масиву. P>
При вирішенні
задачі використовуємо наступне міркування: сума дорівнює нулю, якщо кількість
елементів дорівнює нулю, і сумі всіх попередніх елементів плюс останній, якщо
кількість елементів не дорівнює нулю. p>
(Програма
на мові Pascal) p>
Program Rec2; p>
Type LinMas = Array [1 .. 100] Of Integer; p>
Var A: LinMas; p>
I, N: Byte; p>
(рекурсивні функції) p>
Function Summa (N: Byte; A: LinMas):
Integer; p>
Begin p>
If N = 0 Then Summa: = 0 Else Summa: = A [N] + Summa (N - 1, A) p>
End; p>
(Основна
програма) p>
Begin p>
Write ( 'Кількість елементів масиву?');
ReadLn (N); Randomize; p>
For I: = 1 To N Do p>
Begin p>
A [I]: = -10 + Random (21); Write (A [I]: 4) p>
End; p>
WriteLn; WriteLn ( 'Сума:',
Summa (N, A)) p>
End. p>
/* Програма на мові C */ p>
# include p>
# include p>
# include p>
# include p>
int summa (int N, int a [100 ]); p>
int i, n, a [100]; p>
void main () p>
( p>
clrscr (); p>
printf ( "nКолічество
елементів масиву? "); Scanf ("% d ", & n); p>
printf ( "nв
сформованому масиві% d чисел: n ", n); p>
randomize (); p>
for (i = 0; i
(a [i] = -10 + random (21); printf ( "% d", a [i ]);} p>
printf ( "Сума:
% d ", summa (n-1, a )); p>
) p>
int summa (int N, int a [100]) p>
( p>
if (N == 0) return a [0]; else return a [N] + summa (N-1, a); p>
) p>
Приклад 5.
Визначити, чи є задана рядок паліндромом, тобто читається однаково
зліва направо і справа наліво. p>
Ідея рішення
полягає в перегляді рядки одночасно зліва направо і справа наліво і
порівнянні відповідних символів. Якщо в якийсь момент символи не
збігаються, робиться висновок про те, що рядок не є паліндромом, якщо ж
вдається досягти середини рядки і при цьому всі відповідні символи збіглися,
то рядок є паліндромом. Граничне умова - рядок є
паліндромом, якщо вона порожня чи складається з одного символу. p>
(програма на мові Pascal) p>
Program
Palindrom; p>
(рекурсивна
функція) p>
Function Pal (S: String): Boolean; p>
Begin p>
If Length (S) <= 1 p>
Then Pal: = True p>
Else Pal: = (S [1] = S [Length (S)]) and Pal (Copy (S, 2, Length (S) - 2 )); p>
End; p>
Var S: String; p>
(Основна
програма) p>
Begin p>
Write ( 'Введіть рядок:'); ReadLn (S); p>
If Pal (S) Then WriteLn ( 'Рядок є
паліндромом ') p>
Else WriteLn ( 'Рядок не
є паліндромом ') p>
End. p>
/* програма на мові C */ p>
# include p>
# include p>
# include p>
char s [100]; p>
int pal (char s [100 ]); p>
void main () p>
(clrscr (); p>
printf ( "nВведіте рядок:");
gets (s); p>
if (pal (s))
printf ( "Рядок є паліндромом "); p>
else
printf ( "Рядок не є паліндромом "); p>
) p>
int pal (char s [100]) p>
(int l; char s1 [100]; p>
if (strlen (s) <= 1) return 1; p>
else (l = s [0] == s [strlen (s) -1]; p>
strncpy (s1, s 1, strlen (s) -2); p>
s1 [strlen (s) -2] = '