Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Pascal.doc
Скачиваний:
32
Добавлен:
12.03.2016
Размер:
3.29 Mб
Скачать
      1. Индукция. Рекурсия. Стек

Начну с классического примера о факториале. Факториалом целого положительного числа 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 для вычисления факториала:

FUNCTIONFactorial(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для вычисления чисел Фибоначчи.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]