Лабораторная работа № 2 Рекурсии 1 Цель работы: Изучение методов программирования рекурсивных алгоритмов в языке Си. 2 Основные сведения В языке Си функции могут вызывать сами себя, т.е. обладать свойством рекурсивности. Рекурсивная функция обязательно должна содержать в себе условие окончания рекурсивности, чтобы не вызвать зацикливания программы. Рассмотрим пример использования рекурсии. Понятие факториала n!=1·2·3·…·(n-1) ·n можно задать рекурсивно: 0! = 1 (так условно принято). n! = n*(n-1)! Например, функцию вычисления факториала можно записать так: long fact( int n) { if (n <=0) return 1; else return n * fact ( n -1 ); // функция fact вызывает саму себя } При этом последовательно вызываются функции fact(n), fact(n-1), fact(n-2)…fact(1). При использовании рекурсивных процедур и функций велика опасность, что вложенные вызовы будут продолжаться бесконечно (это похоже на зацикливание цикла while). Поэтому в таких функциях необходимо предусмотреть условие, которое проверяется на каждом шаге и заканчивает вложенные вызовы, когда перестает выполняться. Для функции вычисления факториала таким условием является n <= 0. При каждом новом рекурсивном вызове происходит следующее: - запоминается состояние переменных на данном этапе; - в стековой памяти создается новый набор локальных переменных. Поскольку при каждом вызове затрачивается новая память и расходуется время на вызов функции, при использовании рекурсии необходимо помнить, что глубина рекурсии (количество вложенных вызовов) должна была достаточно мала. Программа, использующая рекурсию с большой глубиной, будет выполняться долго и может вызвать переполнение стека (нехватку стековой памяти). Поэтому, если задача может быть легко решена без использования рекурсии, рекурсию использовать нежелательно. В принципе, любая рекурсивная программа может быть написана без использования рекурсии, хотя такая реализация может оказаться очень сложной. Рассмотрим ещё один пример. Составить функцию для вычисления чисел Фибоначчи fi, которые задаются так: f0 = 0, f1 = 1. fi = fi-1 + fi-2 для i > 1. Используя рекурсию, получим функцию int Fib ( int n ) { if ( n == 0 ) return 0; if ( n == 1 ) return 1; return Fib(n-1) + Fib(n-2); } Заметим, что каждый рекурсивный вызов при n > 1 порождает еще 2 вызова функции. Функция получилась короткая, понятная, но по скорости работы не самая эффективная. Достоинством рекурсий является компактная запись, а недостатком – расход времени и памяти на повторные вызовы функции и передачу ей копий параметров. Реже используется более сложная конструкция, когда процедура вызывает сама себя не напрямую, а через другую процедуру (или функцию). Такой прием называется косвенная рекурсия. 3. Выполнение работы Составить программы с использованием рекурсии для следующих заданий: 1. Ввести в рекурсивной функции с клавиатуры последовательность чисел заканчивающихся нулем, и вывести на экран только положительные числа. Массив не использовать. 2. Дан массив ненулевых целых чисел из N элементов. Используя рекурсию, напечатать сначала все отрицательные, а потом – все положительные числа этой последовательности. Реализовать в одной функции, которая вызывается один раз. 3. Написать рекурсивную функцию для перевода числа из десятичной системы счисления в двоичную. 4. Контрольные вопросы 4.1. Что такое рекурсия? 4.2. Где хранятся локальные переменные при выполнении рекурсии? 4.3. Как меняются локальные переменные в рекурсиях? 4.4. Как закончить рекурсию? 4.5. Достоинства и недостатки рекурсивных программ.