Рекурсія 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>
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>
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>
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>
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>
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>
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>
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>
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] = '