Студопедия

КАТЕГОРИИ:

АстрономияБиологияГеографияДругие языкиДругоеИнформатикаИсторияКультураЛитератураЛогикаМатематикаМедицинаМеханикаОбразованиеОхрана трудаПедагогикаПолитикаПравоПсихологияРиторикаСоциологияСпортСтроительствоТехнологияФизикаФилософияФинансыХимияЧерчениеЭкологияЭкономикаЭлектроника


Индукция. Рекурсия. Стек




Начну с классического примера о факториале. Факториалом целого положительного числа N называется произведение всех целых чисел от 1 до N. Например, факториал пяти равен 1*2*3*4*5, то есть 120. Факториал единицы считается равным 1.

Все понятно. Однако, существует еще один, совершенно ужасный способ объяснения, что такое факториал. Вот он:

“Факториал единицы равен 1. Факториал любого целого положительного числа N, большего единицы, равен числу N, умноженному на факториал числа N-1.”

Если вам уже все ясно, значит вы - математический талант. Для нормальных людей поясню. Чтобы последнее предложение было понятнее, возьмем какое-нибудь конкретное N, например, 100. Тогда это предложение будет звучать так: Факториал числа 100 равен числу 100, умноженному на факториал числа 99.

Ну и что? И как же отсюда узнать, чему равен какой-нибудь конкретный факториал, скажем, факториал трех? Будем рассуждать совершенно чудовищным образом:

 

Смотрю в определение: Факториал трех равен 3 умножить на факториал двух. Не знаю, сколько это. Спускаюсь на ступеньку ниже.

 

Смотрю в определение: Факториал двух равен 2 умножить на факториал единицы. Не знаю, сколько это. Спускаюсь еще на ступеньку.

 

Смотрю в определение: Факториал единицы равен 1. Вот - впервые конкретное число. Значит можно подниматься.

 

Поднимаюсь на одну ступеньку. Факториал двух равен 2 умножить на 1, то есть 2.

 

Поднимаюсь еще на ступеньку. Факториал трех равен 3 умножить на 2, то есть 6. Задача решена.

 

Рассуждая таким образом, можно вычислить факториал любого числа. Способ рассуждения называется рекурсивным, а способ объяснения называется индуктивным.

 

Какое отношение все это имеет к компьютерам? Дело в том, что рекурсивный способ рассуждений реализован во многих языках программирования, в том числе - и в Паскале. Значит, этим языкам должен быть понятен и индуктивный способ написания программ.

Обозначим кратко факториал числа N, как Factorial(N), и снова повторим наш индуктивный способ объяснения:

“Если N=1, то Factorial(N) = 1.

Если N>1, то Factorial(N) вычисляется умножением N на Factorial(N-1).”

 

В соответствии с этим объяснением напишем на Паскале функцию Factorial для вычисления факториала:

FUNCTION Factorial(N: Byte): LongInt;

BEGIN

if N=1 then Factorial :=1;

if N>1 then Factorial :=N* Factorial(N-1)

END;

BEGIN

WriteLn(Factorial(3))

END.

Обратите внимание, что в программе нигде не употребляется оператор цикла. Вся соль программы в том, что функция Factorial вместо этого включает в себя вызов самой себя - Factorial(N-1).

 

Что же происходит в компьютере во время выполнения программы? Механизм происходящего в точности соответствует нашему рассуждению по рекурсии.

 

Все начинается с того, что Паскаль пробует выполнить строку WriteLn(Factorial(3)). Для этого он вызывает функцию Factorial. Выполнение подпрограммы начинается с того, что в стеке отводится место для всех формальных параметров и локальных переменных, а значит и для нашего формального параметра N. Затем фактический параметр 3 подставляется на место формального параметра N, то есть в стек в ячейку N посылается 3. Затем выполняется тело функции. Так как 3>1, то Паскаль пытается выполнить умножение 3* Factorial(3-1) и сталкивается с необходимостью знать значение функции Factorial(2), для чего вызывает ее, то есть отправляется ее выполнять, недовыполнив Factorial(3), но предварительно запомнив, куда возвращаться.

 

Спускаюсь на ступеньку ниже. В соседнем месте стека отводится место для N. Это уже другое N, путать их нельзя. В эту ячейку N посылается 2. Затем выполняется тело функции. Пусть вас не смущает, что Паскаль второй раз выполняет тело функции, не закончив его выполнять в первый раз. Так как 2>1, то Паскаль пытается выполнить умножение 2* Factorial(2-1) и сталкивается с необходимостью знать значение функции Factorial(1), для чего вызывает ее.

 

Спускаюсь еще на ступеньку. В соседнем месте стека отводится место еще для одного N. В эту ячейку N посылается 1. Затем выполняется тело функции. Так как 1=1, то Паскаль вычисляет Factorial :=1. Вот - впервые конкретное число. Затем Паскаль пытается выполнить следующую строку if N>1 then Factorial :=N* Factorial(N-1). Поскольку нельзя сказать, что 1>1, то выполнение тела функции закончено. Значит можно подниматься.

 

Поднимаюсь на одну ступеньку. Паскаль возвращается внутрь тела функции (той, где N=2) и успешно выполняет умножение - Factorial :=2*1=2.

 

Поднимаюсь еще на ступеньку. Паскаль возвращается внутрь тела функции (той, где N=3) и успешно выполняет умножение - Factorial :=3*2=6. Задача решена.

 

После выхода из подпрограммы место в стеке освобождается.

 

Итак, рекурсией в программировании называется вызов подпрограммы из тела самой подпрограммы.

Теперь поговорим о переполнении стека. Размер стека в Паскале не превышает 64K. В нашем случае в стеке одновременно хранилось три копии формальных параметров и локальных переменных. Если бы мы вычисляли факториал десяти, то копий было бы десять. В более сложных, чем факториал, задачах стек может легко переполниться, о чем Паскаль сообщает, когда уже поздно.

Чем хорош рекурсивный стиль программирования? В нашей программе о факториале мы как бы и не программировали вовсе, а просто обяснили компьютеру, что такое факториал. Как бы перешли на новый уровень общения с компьютером: вместо программирования - постановка задачи.

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

 

Задание 124: Напишите рекурсивную функцию fib для вычисления чисел Фибоначчи.


Поделиться:

Дата добавления: 2015-09-14; просмотров: 60; Мы поможем в написании вашей работы!; Нарушение авторских прав





lektsii.com - Лекции.Ком - 2014-2024 год. (0.028 сек.) Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав
Главная страница Случайная страница Контакты