
14 Питання Рекурсия
Рекурсия — это такой способ организации вспомогательного алгоритма (подпрограммы), при котором эта подпрограмма (процедура или функция) в ходе выполнения ее операторов обращается сама к себе. Вообще, рекурсивным называется любой объект, который частично определяется через себя.
Например, приведенное ниже определение двоичного кода является рекурсивным:
<двоичный код> ::= <двоичная цифра> | <двоичный код><двоичная цифра>
<двоичная цифра> ::= 0 | 1
Здесь для описания понятия были использованы, так называемые, металингвистический формулы Бэкуса-Наура (язык БНФ); знак "::=" обозначает "по определению есть", знак "|" — "или".
Вообще, в рекурсивном определении должно присутствовать ограничение, граничное условие, при выходе на которое дальнейшая инициация рекурсивных обращений прекращается.
Приведём другие примеры рекурсивных определений.
Пример 1. Классический пример, без которого не обходятся ни в одном рассказе о рекурсии, — определение факториала. С одной стороны, факториал определяется так: n!=1*2*3*...*n. С другой стороны,
Граничным условием в данном случае является n<=1.
double Factorial(int N)
{
double F;
if (N<=1) F=1.; else F=Factorial(N-1)*N;
return F;
}
або
int factorial(int n) {
return !n ? 1 : n * factorial(n - 1);
}
15 Питання Рекурсия
Рекурсия — это такой способ организации вспомогательного алгоритма (подпрограммы), при котором эта подпрограмма (процедура или функция) в ходе выполнения ее операторов обращается сама к себе. Вообще, рекурсивным называется любой объект, который частично определяется через себя.
Например, приведенное ниже определение двоичного кода является рекурсивным:
<двоичный код> ::= <двоичная цифра> | <двоичный код><двоичная цифра>
<двоичная цифра> ::= 0 | 1
Здесь для описания понятия были использованы, так называемые, металингвистический формулы Бэкуса-Наура (язык БНФ); знак "::=" обозначает "по определению есть", знак "|" — "или".
Вообще, в рекурсивном определении должно присутствовать ограничение, граничное условие, при выходе на которое дальнейшая инициация рекурсивных обращений прекращается.
Приведём другие примеры рекурсивных определений.
Приклад: ханойські вежі
На дошці — три стрижні: 1, 2, 3. На першому розміщено вежу з n дис-
ків; нижній диск має найбільший діаметр, а діаметр кожного наступного
менший за діаметр попереднього. За один хід із будь-якого стрижня можна
взяти верхній диск і перемістити на інший стрижень, але дозволено класти
диск лише на дошку або на диск більшого діаметра. Треба перемістити
усю вежу зі стрижня 1 на стрижень 3.
Ця гра називається «Ханойські вежі». За легендою, цю гру почали по-
над тисячу років тому ченці в одному монастирі поблизу Ханоя у В’єтнамі.
У ченців було n = 64 диски. Коли вони закінчать гру, настане кінець Все-
світу. Розв’язком цієї гри-задачі є послідовність переносів дисків. Напи-
шемо програму виведення позначень цих переносів.
Щоб перенести вежу з n дисків зі стрижня 1 на стрижень 3, необхідно
перенести вежу з n-1 диска на стрижень 2, потім перенести нижній диск на100
стрижень 3, потім вежу з 2 на 3. При перенесенні вежі з 1 на 2 допоміжним
буде стрижень 3, а при перенесенні з 2 на 3 — 1, причому ніяка інша послі-
довність дій неможлива. Отже, розв’язання задачі для вежі висотою n опи-
сано за допомогою розв’язання для вежі висотою n-1, тобто рекурсивно.
Нехай diskMove(a,b) позначає перенос одного диска зі стрижня a
на стрижень b, а tower(h,a,b,c) — перенесення вежі висотою h з a на
b з використанням стрижня c як допоміжного. За h > 1 виконання
tower(h,a,b,c) буде таким.
tower(h-1,a,c,b);
diskMove(a,b);
tower(h-1,c,b,a)
За h = 1 переносять тільки один диск, тобто виконують
diskMove(a,b). Отже, очевидною є така програма.
#include <iostream>
using namespace std;
void diskMove(int diskFrom, int diskTo)
{ cout << diskFrom << "->" << diskTo << ' ';
return;
}
void tower(int height, int from,int to, int via)
//height - висота, from - з, to - на, via - через
{ if(height==1) diskMove(from,to);
else
{ tower(height-1,from,via,to);
diskMove(from,to);
tower(height-1,via,to,from);
}
}
int main()
{ int n;
cout << "Hanoi towers\n";
cout << "Enter a number of disks>";
cin >> n; if (n<1) n=1;
tower(n,1,3,2);
cout<<endl;
system("pause");return 0;
}
prog018.cpp
Визначимо кількість переносів дисків у вигляді функції s(n), де n — ви-
сота вежі. Очевидно, що s(1) = 1 і s(n) = 2⋅s(n-1)+1. Як бачимо, s(2) = 3,
s(3) = 7, s(4) = 15 тощо. Кожне наступне значення подвоюється з додаван-
ням 1, тому неважко переконатися, що s(n) = 2
n
-1. Значення s(64) приблиз-
но дорівнює 10
19
. Якщо припустити, що ченці переносять один диск щосе-
кунди, то для перенесення такої вежі потрібно більше 10
12
років! У ченців
ще багато роботи…
• Рекурсивна функція без жодного циклу може легко приховати величез-
ні обсяги обчислень. Як і будь-який потужний засіб, рекурсія вимагає
обережного використання