lab2

advertisement
Лабораторная работа № 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. Достоинства и недостатки рекурсивных программ.
Download