Цель лекции: изучить понятия рекурсии,
В окружающем нас мире часто можно встретить объекты, обладающие
(рис 3.1) Рекурсивные графические объектыПри первичном осмыслении понятие рекурсии достаточно просто и не требует специальных знаний. Иногда на рекурсию смотрят как на наличие в определении объекта ссылки на сам объект или проявление свойств самоповторения (при этом сколь угодно малая часть объекта подобна всему объекту в целом). Общий случай проявления рекурсивности может быть сформулирован как наличие циклических взаимных обращений в определении объекта, которые в итоге замыкаются на сам объект.
В технике
Рекурсивность в постановке задачи проявляется, если решение для общего случая сводится к аналогичным задачам для меньшего количества
Рекурсия в широком смысле – это определение объекта посредством ссылки на себя. Рекурсия в программировании – это пошаговое разбиение задачи на подзадачи, подобные исходной.
Рекурсивный алгоритм – это алгоритм, в определении которого содержится прямой или косвенный вызов этого же алгоритма.
В языках программирования процедурной
Функция называется рекурсивной, если в своем теле она содержит обращение к самой себе с измененным набором параметров. При этом количество обращений конечно, так как в итоге решение сводится к базовому случаю, когда ответ очевиден.
Пример 1. В арифметической прогрессии найдите an, если известны а1 = -2.5, d =0.4, не используя формулу n -го члена прогрессии.
По определению арифметической прогрессии, an=an-1+d, при этом
an-1=an-2+d, an-2=an-3+d,... a2=a1+d.
Таким образом, нахождение an для номера n сводится к решению аналогичной задачи, но только для номера n -1, что в свою очередь сводится к решению для номера n -2, и так далее, пока не будет достигнут номер 1 (значение а1 дано по условию задачи).
float arifm (int n, float a, float d) {
if (n<1) return 0; // для неположительных номеров
if (n==1) return a; // базовый случай: n=1
return arifm(n-1,a,d)+d; // общий случай
}
В рекурсивных функциях несколько раз используется return. В базовом случае возвращается конкретный результат (в примере – значение а ), а общий случай предусматривает вызов функцией себя же, но с меняющимися значениями отдельных параметров (в примере изменяется только номер члена последовательности, при этом не меняются разность и первый член прогрессии).
В программировании выделяют прямую и
Для решения задач
Целесообразность применения рекурсии в программировании обусловлена спецификой задач, в постановке которых явно или опосредовано указывается на возможность сведения задачи к подзадачам, аналогичным самой задаче. При этом эффективность рекурсивного или итерационного способов решения одной и той же задачи определяется в ходе анализа работоспособности программы на различных наборах данных. Таким образом, рекурсия не является универсальным способом в программировании. Ее следует рассматривать как альтернативный вариант при разработке алгоритмов решения задач.
Повысить эффективность рекурсивных алгоритмов часто представляется возможным за счет пересмотра этапов триады. Например, введение дополнительных параметров, не оговоренных в условии задачи, в реализации декомпозиции могут быть применены другие соотношения, а также можно организовать расширение базовых случаев с сохранением промежуточных результатов.
Область памяти, предназначенная для хранения всех промежуточных значений
Пример 2. Для целого неотрицательного числа n найдите его факториал.
Разработаем рекурсивную триаду.
n – неотрицательное целое число.
База рекурсии: для n =0 факториал равен 1.
n!=(n-1)!xn.
long factor (int n) {
if (n<0) return 0; // для отрицательных чисел
if (n==0) return 1; // базовый случай: n=0
return factor(n-1)*n; // общий случай (декомпозиция)
}
Рассмотрим задачи, для которых можно предложить рекурсивные алгоритмы решения, в то время как
Пример 3. Задача о коэффициентах Безу
Для любых натуральных чисел n и m найдите коэффициенты Безу, то есть такие целые a и b, что выполняется равенство: nod(n,m)=axn+bxm (где nod(n,m) – наибольший общий n и m ).
Параметризация.
m, n – данные
d – наибольший общий
bm, bn – коэффициенты Безу при n и m соответственно, эти параметры меняются при очередном рекурсивном вызове функции.
База рекурсии. Если при очередном d=mxbm–nxbn, то коэффициенты Безу найдены. Требуется вывести
Декомпозиция. Если равенство не выполняется, то инкрементно увеличиваем коэффициент при меньшем из чисел ( n или m ). Следующий вызов рекурсивной функции выполняется с измененным набором отдельных параметров. При этом снова проверяется база рекурсии, и рекурсивный алгоритм повторяется (либо достигается база и функция завершает работу, либо выполняется декомпозиционный переход).
//Коэффициенты Безу
#include "stdafx.h"
#include <iostream>
using namespace std;
int nod(int m, int n);
void bezu(int d, int m, int n, int bm, int bn);
int _tmain(int argc, _TCHAR* argv[]){
int x,y,del,buf;
printf("Задача нахождения коэффициентов Безу");
printf("\nВведите два натуральных числа:");
printf("\nX= ");
scanf("%d",x);
printf("Y= ");
scanf("%d",y);
if (x < y) {buf = x; x = y; y = buf;}
del=nod(x,y);
printf("\nЛинейная комбинация:\n");
bezu(del,x,y,1,1);
system("pause");
return 0;
}
//функция нахождения наибольшего общего делителя двух чисел
int nod(int m, int n){
if (m%n==0) return n;
return nod(n,m%n);
}
//функция нахождения и вывода на экран коэффициентов Безу
void bezu(int d, int m, int n, int bm, int bn){
int pm,pn;
pm = m * bm;
pn = n * bn;
//проверка базы рекурсии (выполнение линейной комбинации)
if (d == pm - pn)
printf ("%d = %d*%d - %d*%d", d, bm, m, bn, n);
//декомпозиция
else {
bn++;
pn=n*bn;
/*если произведение pm больше, чем pn, то порядок
параметров сохраняется*/
if (pm > pn) bezu(d, m, n, bm, bn);
/*если произведение pm меньше, чем pn, то порядок
параметров изменятеся*/
else bezu(d, n, m, bn, bm);
}
}
Пример 4. Задача о Ханойских башнях
Ханойская башня является одной из популярных головоломок XIX века. Даны три стержня, на один из которых нанизаны n колец, причем кольца отличаются размером и лежат меньшее на большем. Задача состоит в том, чтобы перенести n колец за наименьшее число ходов с одного стержня на другой. За один раз разрешается переносить только одно кольцо, причём нельзя класть большее кольцо на меньшее.
Существует древнеиндийская легенда, согласно которой в городе Бенаресе под куполом главного храма, в том месте, где находится центр Земли, на бронзовой площадке стоят три алмазных стержня. В день сотворения мира на один из этих стержней было надето 64 кольца. Бог поручил жрецам перенести кольца с одного стержня на другой, используя третий в качестве вспомогательного. Жрецы обязаны соблюдать условия:
Согласно легенде, когда, соблюдая все условия, жрецы перенесут все 64 кольца, наступит конец света. Для 64 колец это 18 446 744 073 709 551 615 перекладываний, и, если учесть скорость одно перекладывание в секунду, получится около 584 542 046 091 лет, то есть апокалипсис наступит нескоро.
На рисунке (рис 3.2) изображена ситуация, иллюстрирующая перекладывание 7 колец со стержня А на В через вспомогательный С.
(рис 3.2) Ситуация при переносе семи колецКольцо со стержня А можно перенести на стержень В или С, кольцо со стержня В можно перенести на стержень С, однако, нельзя перенести его на стержень А.
Задача состоит в том, чтобы определить последовательность минимальной длины переноса колец. Решением задачи будем считать последовательность допустимых переносов, каждый из которых имеет вид: A->B, A->C, B->A, B->C, C->A, C->B. Если кольцо всего одно, то задача решается за один перенос A->В. Для перемещения двух колец требуется выполнить три действия: A->C, A->В, C->B. Решение задачи для трех колец содержит семь действий, для четырех – 15.
Напишем
Параметризация. Функция имеет четыре параметра, первый параметр – число
База рекурсии. Перенос одного стержня.
).
(рис 3.3) Схема решения задачи о Ханойских башнях для четырех колецЧтобы перенести n колец со стержня A на стержень B, используя стержень C в качестве вспомогательного, можно поступить следующим образом:
n –1 кольцо со стержня A на C, используя стержень B в качестве вспомогательного стержня;A на стержень B ;n –1 кольцо со стержня C на B, используя стержень A в качестве вспомогательного стержня.При переносе n –1 кольца можно воспользоваться тем же алгоритмом, т.к. на нижнее кольцо с самым большим
//Ханойские башни
#include "stdafx.h"
#include <iostream>
using namespace std;
int hanoj(int n, char A, char B, char C);
//Объявление функции перемещения колец с A на C через B
int _tmain(int argc, _TCHAR* argv[]){
char x='A',y='B',z='C';
int k,h;
printf("Задача о Ханойских башнях");
printf("\nВведите количество колец: ");
scanf("%d",k);
h=hanoj(k,x,z,y);
printf("\nКоличество перекладываний равно %d",h);
system("pause");
return 0;
}
//Описание функции перемещения колец с A на C через B
int hanoj(int n, char A, char B, char C){
int num;
if (n == 1) {printf("\n %c -> %c", A, C); num = 1;}
else {
num=hanoj(n-1, A, C, B);
printf("\n %c -> %c", A, C);
num++;
num+=hanoj(n-1, B, A, C);
}
return num;
}
База рекурсии – это тривиальный случай, при котором решение задачи очевидно, то есть не требуется обращение функции к себе.
Декомпозиция – это выражение общего случая через более простые подзадачи с измененными параметрами.
Косвенная (взаимная) рекурсия – это последовательность взаимных вызовов нескольких функций, организованная в виде циклического
Параметризация – это выделение из постановки задачи параметров, которые используются для описания условия задачи и решения.
Прямая рекурсия – это непосредственное обращение рекурсивной функции к себе, но с иным набором
Рекурсивная триада – это этапы решения задач рекурсивным методом.
Рекурсивная функция – это функция, которая в своем теле содержит обращение к самой себе с измененным набором параметров.
Рекурсивный алгоритм – это алгоритм, в определении которого содержится прямой или косвенный вызов этого же алгоритма.
Рекурсивный стек – это область памяти, предназначенная для хранения всех промежуточных значений
Рекурсия в программировании – это пошаговое разбиение задачи на подзадачи, подобные исходной.
Рекурсия в широком смысле – это определение объекта посредством ссылки на себя.
Цель работы: изучить понятия рекурсии,
При выполнении лабораторной работы для каждого задания требуется написать программу на языке С++, которая получает на входе числовые данные, выполняет их обработку в соответствии с требованиями задания и выводит результат на экран. Ввод данных осуществляется с клавиатуры с учетом требований к
Теоретические сведения.
Ознакомьтесь с материалом лекции 3.
Задания к лабораторной работе.
Выполните приведенные ниже задания.
n -ый член последовательности: 1, 1, 2, 3, 5, 8, 13, ...an натуральной степени n вещественного числа a за наименьшее число операций.Указания к выполнению работы.
Каждое задание необходимо решить в соответствии с изученными
Следует реализовать каждое задание в соответствии с приведенными этапами:
Требования к отчету.
Отчет по лабораторной работе должен соответствовать следующей структуре.
Контрольные вопросы
Цель лекции: изучить понятия рекурсии,
В окружающем нас мире часто можно встретить объекты, обладающие
(рис 3.1) Рекурсивные графические объектыПри первичном осмыслении понятие рекурсии достаточно просто и не требует специальных знаний. Иногда на рекурсию смотрят как на наличие в определении объекта ссылки на сам объект или проявление свойств самоповторения (при этом сколь угодно малая часть объекта подобна всему объекту в целом). Общий случай проявления рекурсивности может быть сформулирован как наличие циклических взаимных обращений в определении объекта, которые в итоге замыкаются на сам объект.
В технике
Рекурсивность в постановке задачи проявляется, если решение для общего случая сводится к аналогичным задачам для меньшего количества
Рекурсия в широком смысле – это определение объекта посредством ссылки на себя. Рекурсия в программировании – это пошаговое разбиение задачи на подзадачи, подобные исходной.
Рекурсивный алгоритм – это алгоритм, в определении которого содержится прямой или косвенный вызов этого же алгоритма.
В языках программирования процедурной
Функция называется рекурсивной, если в своем теле она содержит обращение к самой себе с измененным набором параметров. При этом количество обращений конечно, так как в итоге решение сводится к базовому случаю, когда ответ очевиден.
Пример 1. В арифметической прогрессии найдите an, если известны а1 = -2.5, d =0.4, не используя формулу n -го члена прогрессии.
По определению арифметической прогрессии, an=an-1+d, при этом
an-1=an-2+d, an-2=an-3+d,... a2=a1+d.
Таким образом, нахождение an для номера n сводится к решению аналогичной задачи, но только для номера n -1, что в свою очередь сводится к решению для номера n -2, и так далее, пока не будет достигнут номер 1 (значение а1 дано по условию задачи).
float arifm (int n, float a, float d) {
if (n<1) return 0; // для неположительных номеров
if (n==1) return a; // базовый случай: n=1
return arifm(n-1,a,d)+d; // общий случай
}
В рекурсивных функциях несколько раз используется return. В базовом случае возвращается конкретный результат (в примере – значение а ), а общий случай предусматривает вызов функцией себя же, но с меняющимися значениями отдельных параметров (в примере изменяется только номер члена последовательности, при этом не меняются разность и первый член прогрессии).
В программировании выделяют прямую и
Для решения задач
Целесообразность применения рекурсии в программировании обусловлена спецификой задач, в постановке которых явно или опосредовано указывается на возможность сведения задачи к подзадачам, аналогичным самой задаче. При этом эффективность рекурсивного или итерационного способов решения одной и той же задачи определяется в ходе анализа работоспособности программы на различных наборах данных. Таким образом, рекурсия не является универсальным способом в программировании. Ее следует рассматривать как альтернативный вариант при разработке алгоритмов решения задач.
Повысить эффективность рекурсивных алгоритмов часто представляется возможным за счет пересмотра этапов триады. Например, введение дополнительных параметров, не оговоренных в условии задачи, в реализации декомпозиции могут быть применены другие соотношения, а также можно организовать расширение базовых случаев с сохранением промежуточных результатов.
Область памяти, предназначенная для хранения всех промежуточных значений
Пример 2. Для целого неотрицательного числа n найдите его факториал.
Разработаем рекурсивную триаду.
n – неотрицательное целое число.
База рекурсии: для n =0 факториал равен 1.
n!=(n-1)!xn.
long factor (int n) {
if (n<0) return 0; // для отрицательных чисел
if (n==0) return 1; // базовый случай: n=0
return factor(n-1)*n; // общий случай (декомпозиция)
}
Рассмотрим задачи, для которых можно предложить рекурсивные алгоритмы решения, в то время как
Пример 3. Задача о коэффициентах Безу
Для любых натуральных чисел n и m найдите коэффициенты Безу, то есть такие целые a и b, что выполняется равенство: nod(n,m)=axn+bxm (где nod(n,m) – наибольший общий n и m ).
Параметризация.
m, n – данные
d – наибольший общий
bm, bn – коэффициенты Безу при n и m соответственно, эти параметры меняются при очередном рекурсивном вызове функции.
База рекурсии. Если при очередном d=mxbm–nxbn, то коэффициенты Безу найдены. Требуется вывести
Декомпозиция. Если равенство не выполняется, то инкрементно увеличиваем коэффициент при меньшем из чисел ( n или m ). Следующий вызов рекурсивной функции выполняется с измененным набором отдельных параметров. При этом снова проверяется база рекурсии, и рекурсивный алгоритм повторяется (либо достигается база и функция завершает работу, либо выполняется декомпозиционный переход).
//Коэффициенты Безу
#include "stdafx.h"
#include <iostream>
using namespace std;
int nod(int m, int n);
void bezu(int d, int m, int n, int bm, int bn);
int _tmain(int argc, _TCHAR* argv[]){
int x,y,del,buf;
printf("Задача нахождения коэффициентов Безу");
printf("\nВведите два натуральных числа:");
printf("\nX= ");
scanf("%d",x);
printf("Y= ");
scanf("%d",y);
if (x < y) {buf = x; x = y; y = buf;}
del=nod(x,y);
printf("\nЛинейная комбинация:\n");
bezu(del,x,y,1,1);
system("pause");
return 0;
}
//функция нахождения наибольшего общего делителя двух чисел
int nod(int m, int n){
if (m%n==0) return n;
return nod(n,m%n);
}
//функция нахождения и вывода на экран коэффициентов Безу
void bezu(int d, int m, int n, int bm, int bn){
int pm,pn;
pm = m * bm;
pn = n * bn;
//проверка базы рекурсии (выполнение линейной комбинации)
if (d == pm - pn)
printf ("%d = %d*%d - %d*%d", d, bm, m, bn, n);
//декомпозиция
else {
bn++;
pn=n*bn;
/*если произведение pm больше, чем pn, то порядок
параметров сохраняется*/
if (pm > pn) bezu(d, m, n, bm, bn);
/*если произведение pm меньше, чем pn, то порядок
параметров изменятеся*/
else bezu(d, n, m, bn, bm);
}
}
Пример 4. Задача о Ханойских башнях
Ханойская башня является одной из популярных головоломок XIX века. Даны три стержня, на один из которых нанизаны n колец, причем кольца отличаются размером и лежат меньшее на большем. Задача состоит в том, чтобы перенести n колец за наименьшее число ходов с одного стержня на другой. За один раз разрешается переносить только одно кольцо, причём нельзя класть большее кольцо на меньшее.
Существует древнеиндийская легенда, согласно которой в городе Бенаресе под куполом главного храма, в том месте, где находится центр Земли, на бронзовой площадке стоят три алмазных стержня. В день сотворения мира на один из этих стержней было надето 64 кольца. Бог поручил жрецам перенести кольца с одного стержня на другой, используя третий в качестве вспомогательного. Жрецы обязаны соблюдать условия:
Согласно легенде, когда, соблюдая все условия, жрецы перенесут все 64 кольца, наступит конец света. Для 64 колец это 18 446 744 073 709 551 615 перекладываний, и, если учесть скорость одно перекладывание в секунду, получится около 584 542 046 091 лет, то есть апокалипсис наступит нескоро.
На рисунке (рис 3.2) изображена ситуация, иллюстрирующая перекладывание 7 колец со стержня А на В через вспомогательный С.
(рис 3.2) Ситуация при переносе семи колецКольцо со стержня А можно перенести на стержень В или С, кольцо со стержня В можно перенести на стержень С, однако, нельзя перенести его на стержень А.
Задача состоит в том, чтобы определить последовательность минимальной длины переноса колец. Решением задачи будем считать последовательность допустимых переносов, каждый из которых имеет вид: A->B, A->C, B->A, B->C, C->A, C->B. Если кольцо всего одно, то задача решается за один перенос A->В. Для перемещения двух колец требуется выполнить три действия: A->C, A->В, C->B. Решение задачи для трех колец содержит семь действий, для четырех – 15.
Напишем
Параметризация. Функция имеет четыре параметра, первый параметр – число
База рекурсии. Перенос одного стержня.
).
(рис 3.3) Схема решения задачи о Ханойских башнях для четырех колецЧтобы перенести n колец со стержня A на стержень B, используя стержень C в качестве вспомогательного, можно поступить следующим образом:
n –1 кольцо со стержня A на C, используя стержень B в качестве вспомогательного стержня;A на стержень B ;n –1 кольцо со стержня C на B, используя стержень A в качестве вспомогательного стержня.При переносе n –1 кольца можно воспользоваться тем же алгоритмом, т.к. на нижнее кольцо с самым большим
//Ханойские башни
#include "stdafx.h"
#include <iostream>
using namespace std;
int hanoj(int n, char A, char B, char C);
//Объявление функции перемещения колец с A на C через B
int _tmain(int argc, _TCHAR* argv[]){
char x='A',y='B',z='C';
int k,h;
printf("Задача о Ханойских башнях");
printf("\nВведите количество колец: ");
scanf("%d",k);
h=hanoj(k,x,z,y);
printf("\nКоличество перекладываний равно %d",h);
system("pause");
return 0;
}
//Описание функции перемещения колец с A на C через B
int hanoj(int n, char A, char B, char C){
int num;
if (n == 1) {printf("\n %c -> %c", A, C); num = 1;}
else {
num=hanoj(n-1, A, C, B);
printf("\n %c -> %c", A, C);
num++;
num+=hanoj(n-1, B, A, C);
}
return num;
}
База рекурсии – это тривиальный случай, при котором решение задачи очевидно, то есть не требуется обращение функции к себе.
Декомпозиция – это выражение общего случая через более простые подзадачи с измененными параметрами.
Косвенная (взаимная) рекурсия – это последовательность взаимных вызовов нескольких функций, организованная в виде циклического
Параметризация – это выделение из постановки задачи параметров, которые используются для описания условия задачи и решения.
Прямая рекурсия – это непосредственное обращение рекурсивной функции к себе, но с иным набором
Рекурсивная триада – это этапы решения задач рекурсивным методом.
Рекурсивная функция – это функция, которая в своем теле содержит обращение к самой себе с измененным набором параметров.
Рекурсивный алгоритм – это алгоритм, в определении которого содержится прямой или косвенный вызов этого же алгоритма.
Рекурсивный стек – это область памяти, предназначенная для хранения всех промежуточных значений
Рекурсия в программировании – это пошаговое разбиение задачи на подзадачи, подобные исходной.
Рекурсия в широком смысле – это определение объекта посредством ссылки на себя.
Цель работы: изучить понятия рекурсии,
При выполнении лабораторной работы для каждого задания требуется написать программу на языке С++, которая получает на входе числовые данные, выполняет их обработку в соответствии с требованиями задания и выводит результат на экран. Ввод данных осуществляется с клавиатуры с учетом требований к
Теоретические сведения.
Ознакомьтесь с материалом лекции 3.
Задания к лабораторной работе.
Выполните приведенные ниже задания.
n -ый член последовательности: 1, 1, 2, 3, 5, 8, 13, ...an натуральной степени n вещественного числа a за наименьшее число операций.Указания к выполнению работы.
Каждое задание необходимо решить в соответствии с изученными
Следует реализовать каждое задание в соответствии с приведенными этапами:
Требования к отчету.
Отчет по лабораторной работе должен соответствовать следующей структуре.
Контрольные вопросы
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.