Лабораторная работа № 6. Рекурсия

Теоретические сведения

3 принципа рекурсии

Рекурсия - это способ решения задач с помощью разбиения их на всё меньшие и меньшие подзадачи до тех пор, пока результат не сможет быть найден тривиальным способом.

  • Рекурсивный алгоритм должен иметь базовый случай, когда алгоритм возвращает некое буквальное значение.
  • Рекурсивный алгоритм должен изменять своё состояние и двигаться по направлению к базовому случаю.
  • Рекурсивный алгоритм должен вызывать сам себя.

Пример трассировки рекурсивной функции

Проведём трассировку рекурсивной функции counter(n) для вывода на экран чисел от 1 до n. Блок-схема функции и основной программы показана на рисунке 1.

Начало
Начало
count(n) - вывод на
экран чисел от 1 до n.
co…
Конец
Конец
Да
Да
Нет
Нет
n > 0
n > 0
count(n - 1)
count(n - 1)
Вывод
n
Вывод n
Начало
Начало
count(3)
count(3)
Конец
Конец
Начинаем с этого блока после вызова функции и её добавления в стек.
Начинаем с этого блока после вызова функции и её добавления в стек.
Функция продолжит работу с этого блока после удаления предыдущей функции с вершины стека.
Функция продолжит работу с этого блока после удаления предыдущей функции с вершины стека.
После этого блока, функция удаляется с вершины стека.
После этого блока, функция удаляется с вершины стека.
Вызов функции с новым аргументом. Функция добавляется на вершину стека.
Вызов функции с новым аргументом. Функция добавляется на вершину стека.
Text is not SVG - cannot display
Рисунок 1

Изменение стека вызова функций показано на рисунке 2.

count(3)
n: 3
n > 0: True
count(3)…
count(3)
n: 3
n > 0: True
count(3)…
count(2)
n: 2
n > 0: True
count(2)…
count(3)
n: 3
n > 0: True
count(3)…
count(2)
n: 2
n > 0: True
count(2)…
count(1)
n: 1
n > 0: True
count(1)…
count(3)
n: 3
n > 0: True
count(3)…
count(2)
n: 2
n > 0: True
count(2)…
count(1)
n: 1
n > 0: True
count(1)…
count(0)
n: 0
n > 0:False
count(0)…
count(3)
n: 3
n > 0: True
count(3)…
count(2)
n: 2
n > 0: True
count(2)…
count(1)
n: 1
n > 0: True
count(1)…
count(3)
n: 3
n > 0: True
count(3)…
count(2)
n: 2
n > 0: True
count(2)…
count(3)
n: 3
n > 0: True
count(3)…
Вызов
функции
Вызов функции
Вызов
функции
Вызов функции
Вызов
функции
Вызов функции
Вызов
функции
Вызов функции
Вывод на экран “1”
Вывод на экран “1”
Вывод на экран “2”
Вывод на экран “2”
Вывод на экран “3”
Вывод на экран “3”
Text is not SVG - cannot display
Рисунок 2

Вызовем функцию с аргументом равным 3.

  1. После вызова функции с новым аргументом 🔶, она добавляется в стек вызова функций. В элементе стека сохраняется текущее значение параметра n.
  2. После добавления в стек, начинается выполнение инструкций в теле функции 🟢. Как только встретится вызов функции 🔶, работа функции на вершине стека приостанавливается и в стек добавляется вызванная функция с новым значением аргумента.
  3. Как только будет выполнена последняя инструкция в текущей функции 🟦, она удаляется с вершины стека.
  4. Управление возвращается к функции, которая теперь находится на вершине стека. Начнёт выполнятся инструкция расположенная непосредственно после рекурсивного вызова 🔺.

На Python функция будет реализована следующим образом:

Задания для самостоятельной работы

Задание 1. Трассировка рекурсивной функции

Проведите трассировку рекурсивной функции to_bin(x) предназначенной для перевода числа x из десятичной системы счисления в двоичную. Добавляйте в стек все вызовы функции, отмечайте значение параметра x и результат проверки условия для продолжения рекурсии x > 0 для каждого вызова функции. Записывайте значения, выводимые функцией на экран.

Начало
Начало
to_bin(x) - перевод
числа x из десятичной
в двоичную систему
счисления
to…
Конец
Конец
Да
Да
Нет
Нет
x > 0
x > 0
to_bin(x div 2)
to_bin(x div 2)
Вывод
x mod 2
Вывод x mod 2
Начало
Начало
to_bin(…)
to_bin(…)
Конец
Конец
Text is not SVG - cannot display
Рисунок 3

Начальное значение аргумента функции зависит от вашего варианта.

to_bin(15)
to_bin(14)
to_bin(10)
to_bin(11)
to_bin(12)
to_bin(13)
to_bin(9)
to_bin(15)
to_bin(14)
to_bin(10)
to_bin(11)
to_bin(12)
to_bin(13)
to_bin(9)

Задание 2. Из десятичной в двоичную (sam02.py)

Реализуйте функциюto_bin(x) по блок-схеме на рисунке 3. Проверьте работу рекурсивной функции на числе из вашего варианта.

Задание 3. Сумма элементов списка (sam03.py)

Опишите функцию s_list(A, n) для поиска суммы элементов списка A. Реализация должна быть рекурсивной.

Рассмотрим работу функции на примере списка [5, 6, 2, 1]:

Пример вызова функции s_list

Пример вызова функции s_list

Чтобы найти сумму чисел во всём списке, нужно сложить последний элемент и сумму чисел в списке без этого элемента. Продолжая этот алгоритм мы дойдёт до случая в котором в списке останется один элемент. Тогда сумма будет равна значению этого элемента. Процесс вызова функции и обработки возвращаемых значений показан на рисунке.

Процесс работы функции s_list

Процесс работы функции s_list

Базовым случаем рекурсии будем считать ситуацию в которой список A состоит из одного элемента. В таком случае сумма будет равна x A[0] - первому элементу списка. Если n не равно 0, то нужно сложить элемент списка A[n] и сумму элементов до этого элемента. То есть нужно найти сумму элементов списка до индекса x n-1.


Используйте следующую блок-схему.

Начало
Начало
s_list(A, n) - поиск суммы
элементов списка A
n - индекс элемента
до которого происходит
поиск суммы
s_…
Конец
Конец
Да
Да
Нет
Нет
n == 0
n == 0
Вернуть
A[0]
Вернуть A[0]
Вернуть
A[n] + s
Вернуть A[n] + s
s = s_list(A, n - 1)
s = s_list(A, n - 1)
Text is not SVG - cannot display
Рисунок 4

Вызовите функцию для подсчёта суммы элементов списка вашего варианта.

lst = [4, 5, 9]
lst = [9, 5, -1, 9]
lst = [15, 3, 6]
lst = [3, 12, 11, 8]
lst = [1, 9, -4, 2, 6]
lst = [1, 9]
lst = [1, 9, -4, 2, 6]
lst = [4, 5, 9]
lst = [9, 5, -1, 9]
lst = [15, 3, 6]
lst = [3, 12, 11, 8]
lst = [1, 9, -4, 2, 6]
lst = [1, 9]
lst = [1, 9, -4, 2, 6]

Задание 4. Минимальный элемент списка (sam04.py)

Опишите метод min_el(A, n) для поиска минимального элемента списка A. Реализация должна быть рекурсивной.

Для этой задачи базовый случай - список из одного элемента. Он и будет считаться минимальным. В этом случае функция должна вернуть x A[0]. Если в списке больше одного элемента, то будем сравнивать элемент A[n] и минимальное значение среди первых x n-1 элементов.


Используйте следующую блок-схему.

Начало
Начало
min_el(A, n) - поиск минимального
элемента в списке A
n - индекс элемента на котором
останавливается поиск
mi…
Конец
Конец
Да
Да
Нет
Нет
n == 0
n == 0
Вернуть
A[0]
Вернуть A[0]
m = A[n]
m = A[n]
left = min_el(A, n - 1)
left = min_el(A, n - 1)
Да
Да
Нет
Нет
m < left
m < left
Вернуть
m
Вернуть m
Вернуть
left
Вернуть left
Text is not SVG - cannot display
Рисунок 5

Вызовите функцию для поиска минимального элемента в списке из предыдущего задания.

Задание 5 (sam05.py)

Решите задачу вашего варианта.

ПредупреждениеТребование к решениям

В решении задачи обязательно должна использоваться рекурсия.

Опишите функцию nod(x, y) для вычисления наибольшего общего делителя двух целых чисел a и b.

Начало
Начало
nod(a, b) - поиск
наибольшего общего
делителя чисел a и b
no…
Конец
Конец
Да
Да
Нет
Нет
b == 0
b == 0
Вернуть
a
Вернуть a
Вернуть
nod(b, a mod b)
Вернуть nod(b, a mod b)
Text is not SVG - cannot display
Рисунок 6
nod(45, 60) -> 15
nod(23, 17) -> 1
nod(22, 22) -> 22
nod(1, 1)   -> 1

Опишите функцию sum_dig(n) для нахождения суммы цифр числа n.

Начало
Начало
sum_dig(n) - поиск
суммы цифр числа n
su…
Конец
Конец
Да
Да
Нет
Нет
n < 10
n < 10
Вернуть
n
Вернуть n
Вернуть
cif + sum_dig(n div 10)
Вернуть cif + sum_dig(n div 10)
cif = n mod 10
cif = n mod 10
Text is not SVG - cannot display
Рисунок 7
sum_dig(7)      -> 7
sum_dig(387)    -> 18
sum_dig(0)      -> 0
sum_dig(100)    -> 1

Опишите функцию trib(n) для нахождения n-го числа последовательности трибоначчи: \[ t_0=0, t_1=0, t_2=1, t_n=t_{n-1}+t_{n-2}+t_{n-3} \]

Начало
Начало
trib(n) - вычисляет
n-ое число 
последовательности
трибоначчи
tr…
Конец
Конец
Да
Да
Нет
Нет
n < 2
n < 2
Вернуть
0
Вернуть 0
Вернуть
trib(n-1) + trib(n-2) + trib(n-3)
Вернутьtrib(n-1) + trib(n-2) + tri…
Да
Да
Нет
Нет
n == 2
n == 2
Вернуть
1
Вернуть 1
Text is not SVG - cannot display
Рисунок 8
trib(2)     -> 1
trib(4)     -> 2
trib(0)     -> 0
trib(10)    -> 81

Опишите функцию invert(s) для инвертирования строки s.

Начало
Начало
invert(s, n) -
инвертировать
строку s начиная с
символа под индексом n
in…
Конец
Конец
Да
Да
Нет
Нет
n == 0
n == 0
Вернуть
s[0]
Вернуть s[0]
Вернуть
s[n] + invert(s, n - 1)
Вернуть s[n] + invert(s, n - 1)
Text is not SVG - cannot display
Рисунок 9
s = 'наоборот'
invert(s, len(s) - 1)   -> 'торобоан'
s = 'шалаш'
invert(s, len(s) - 1)   -> 'шалаш'
s = 'а'
invert(s, len(s) - 1)   -> 'а'

Опишите функцию is_prime(x , d) с помощью которой можно определить, является ли x простым числом.

Начало
Начало
is_prime(x, d) - является ли
число x простым;
d - первый возможный делитель
is…
Конец
Конец
Да
Да
Нет
Нет
d == 1
d == 1
Вернуть
True
Вернуть True
Вернуть
is_prime(x, d - 1)
Вернуть is_prime(x, d - 1)
Да
Да
Нет
Нет
x mod d == 0
x mod d == 0
Вернуть
False
Вернуть False
Text is not SVG - cannot display
Рисунок 10
is_prime(30, 29)    -> False
is_prime(41, 40)    -> True

Опишите функцию prod(n) с помощью которой можно вычислить произведение целых чисел от 1 до n.

Начало
Начало
prod(n) - вычисляет
произведение целых
чисел от 1 до n
pr…
Конец
Конец
Да
Да
Нет
Нет
n < 2
n < 2
Вернуть
n
Вернуть n
Вернуть
n * prod(n - 1)
Вернуть n * prod(n - 1)
Text is not SVG - cannot display
Рисунок 11
prod(7)     -> 720
prod(1)     -> 1
prod(0)     -> 0
prod(12)    -> 479001600

Опишите функцию cannon(h) с помощью которой можно рассчитать количество шаров в пирамиде, состоящей из h уровней:

Пирамида из 4 уровней

Пирамида из 4 уровней
Начало
Начало
cannon(h) - считает,
сколько шаров будет
в пирамиде, состоящей
из h уровней
ca…
Конец
Конец
Да
Да
Нет
Нет
h == 1
h == 1
Вернуть
1
Вернуть 1
Вернуть
h * h + cannon(h - 1)
Вернуть h * h + cannon(h - 1)
Text is not SVG - cannot display
Рисунок 12
cannon(1)       -> 1
cannon(4)       -> 30
cannon(13)      -> 819
cannon(100)     -> 338350

Опишите функцию nod(x, y) для вычисления наибольшего общего делителя двух целых чисел a и b.

Начало
Начало
nod(a, b) - поиск
наибольшего общего
делителя чисел a и b
no…
Конец
Конец
Да
Да
Нет
Нет
b == 0
b == 0
Вернуть
a
Вернуть a
Вернуть
nod(b, a mod b)
Вернуть nod(b, a mod b)
Text is not SVG - cannot display
Рисунок 13
nod(45, 60) -> 15
nod(23, 17) -> 1
nod(22, 22) -> 22
nod(1, 1)   -> 1

Опишите функцию sum_dig(n) для нахождения суммы цифр числа n.

Начало
Начало
sum_dig(n) - поиск
суммы цифр числа n
su…
Конец
Конец
Да
Да
Нет
Нет
n < 10
n < 10
Вернуть
n
Вернуть n
Вернуть
cif + sum_dig(n div 10)
Вернуть cif + sum_dig(n div 10)
cif = n mod 10
cif = n mod 10
Text is not SVG - cannot display
Рисунок 14
sum_dig(7)      -> 7
sum_dig(387)    -> 18
sum_dig(0)      -> 0
sum_dig(100)    -> 1

Опишите функцию trib(n) для нахождения n-го числа последовательности трибоначчи: \[ t_0=0, t_1=0, t_2=1, t_n=t_{n-1}+t_{n-2}+t_{n-3} \]

Начало
Начало
trib(n) - вычисляет
n-ое число 
последовательности
трибоначчи
tr…
Конец
Конец
Да
Да
Нет
Нет
n < 2
n < 2
Вернуть
0
Вернуть 0
Вернуть
trib(n-1) + trib(n-2) + trib(n-3)
Вернутьtrib(n-1) + trib(n-2) + tri…
Да
Да
Нет
Нет
n == 2
n == 2
Вернуть
1
Вернуть 1
Text is not SVG - cannot display
Рисунок 15
trib(2)     -> 1
trib(4)     -> 2
trib(0)     -> 0
trib(10)    -> 81

Опишите функцию invert(s) для инвертирования строки s.

Начало
Начало
invert(s, n) -
инвертировать
строку s начиная с
символа под индексом n
in…
Конец
Конец
Да
Да
Нет
Нет
n == 0
n == 0
Вернуть
s[0]
Вернуть s[0]
Вернуть
s[n] + invert(s, n - 1)
Вернуть s[n] + invert(s, n - 1)
Text is not SVG - cannot display
Рисунок 16
s = 'наоборот'
invert(s, len(s) - 1)   -> 'торобоан'
s = 'шалаш'
invert(s, len(s) - 1)   -> 'шалаш'
s = 'а'
invert(s, len(s) - 1)   -> 'а'

Опишите функцию is_prime(x , d) с помощью которой можно определить, является ли x простым числом.

Начало
Начало
is_prime(x, d) - является ли
число x простым;
d - первый возможный делитель
is…
Конец
Конец
Да
Да
Нет
Нет
d == 1
d == 1
Вернуть
True
Вернуть True
Вернуть
is_prime(x, d - 1)
Вернуть is_prime(x, d - 1)
Да
Да
Нет
Нет
x mod d == 0
x mod d == 0
Вернуть
False
Вернуть False
Text is not SVG - cannot display
Рисунок 17
is_prime(30, 29)    -> False
is_prime(41, 40)    -> True

Опишите функцию prod(n) с помощью которой можно вычислить произведение целых чисел от 1 до n.

Начало
Начало
prod(n) - вычисляет
произведение целых
чисел от 1 до n
pr…
Конец
Конец
Да
Да
Нет
Нет
n < 2
n < 2
Вернуть
n
Вернуть n
Вернуть
n * prod(n - 1)
Вернуть n * prod(n - 1)
Text is not SVG - cannot display
Рисунок 18
prod(7)     -> 720
prod(1)     -> 1
prod(0)     -> 0
prod(12)    -> 479001600

Опишите функцию cannon(h) с помощью которой можно рассчитать количество шаров в пирамиде, состоящей из h уровней:

Пирамида из 4 уровней

Пирамида из 4 уровней
Начало
Начало
cannon(h) - считает,
сколько шаров будет
в пирамиде, состоящей
из h уровней
ca…
Конец
Конец
Да
Да
Нет
Нет
h == 1
h == 1
Вернуть
1
Вернуть 1
Вернуть
h * h + cannon(h - 1)
Вернуть h * h + cannon(h - 1)
Text is not SVG - cannot display
Рисунок 19
cannon(1)       -> 1
cannon(4)       -> 30
cannon(13)      -> 819
cannon(100)     -> 338350

Задание 6 (sam06.py)

Решите задачу вашего варианта.

ПредупреждениеТребование к решениям

В решении задачи обязательно должна использоваться рекурсия. Нельзя использовать циклы.

Напишите рекурсивную функцию palindrom(s) для определения, является ли строка s палиндромом.

palindrom('шалаш')      -> True
palindrom('палатка')    -> False
palindrom('1001')       -> True
palindrom('451')        -> False

Напишите рекурсивную функцию sum_even(n) для нахождения суммы всех положительных чётных чисел меньших или равных n.

sum_even(6)     -> 12
sum_even(2)     -> 2
sum_even(0)     -> 0
sum_even(50)    -> 650

Напишите рекурсивную функцию is_power(n) с помощью которой можно определить, является ли число n степенью двойки.

is_power(1)     -> True
is_power(8)     -> True
is_power(7)     -> False
is_power(10)    -> False

Напишите рекурсивную функцию count7(n) с помощью которой можно посчитать, сколько раз цифра 7 встречается в числе n.

count7(523)     -> 0
count7(0)       -> 0
count7(7777)    -> 4
count7(125797)  -> 2

Напишите рекурсивную функцию powerN(b, n) для возведения числа b в степень n.

powerN(2, 3)    -> 8
powerN(3, 1)    -> 3
powerN(6, 0)    -> 1
powerN(2, 8)    -> 256

Функция Аккермана определена следующим образом: \[ A(m, n) = \begin{cases} n + 1 & m = 0\\ A(m - 1, 1)& m > 0, n = 0\\ A(m - 1, A(m, n - 1))& m > 0, n > 0\\ \end{cases} \] Напишите рекурсивную функцию A(m, n) для вычисления значения функции для двух чисел m и n.

A(2, 2) -> 7
A(3, 2) -> 29
A(2, 1) -> 5

Напишите рекурсивную функцию coin_flip(s) которая принимает на вход строку s. Строка может состоять только из комбинации двух символов - “О” или “Р”. Функция должна посчитать, сколько раз в строке s встречается буква “Р”.

coin_flip("ОРРО")   -> 2
coin_flip("ООOOOO") -> 0
coin_flip("РРРРР")  -> 5
coin_flip("")       -> 0

Напишите рекурсивную функцию palindrom(s) для определения, является ли строка s палиндромом.

palindrom('шалаш')      -> True
palindrom('палатка')    -> False
palindrom('1001')       -> True
palindrom('451')        -> False

Напишите рекурсивную функцию sum_even(n) для нахождения суммы всех положительных чётных чисел меньших или равных n.

sum_even(6)     -> 12
sum_even(2)     -> 2
sum_even(0)     -> 0
sum_even(50)    -> 650

Напишите рекурсивную функцию is_power(n) с помощью которой можно определить, является ли число n степенью двойки.

is_power(1)     -> True
is_power(8)     -> True
is_power(7)     -> False
is_power(10)    -> False

Напишите рекурсивную функцию count7(n) с помощью которой можно посчитать, сколько раз цифра 7 встречается в числе n.

count7(523)     -> 0
count7(0)       -> 0
count7(7777)    -> 4
count7(125797)  -> 2

Напишите рекурсивную функцию powerN(b, n) для возведения числа b в степень n.

powerN(2, 3)    -> 8
powerN(3, 1)    -> 3
powerN(6, 0)    -> 1
powerN(2, 8)    -> 256

Функция Аккермана определена следующим образом: \[ A(m, n) = \begin{cases} n + 1 & m = 0\\ A(m - 1, 1)& m > 0, n = 0\\ A(m - 1, A(m, n - 1))& m > 0, n > 0\\ \end{cases} \] Напишите рекурсивную функцию A(m, n) для вычисления значения функции для двух чисел m и n.

A(2, 2) -> 7
A(3, 2) -> 29
A(2, 1) -> 5

Напишите рекурсивную функцию coin_flip(s) которая принимает на вход строку s. Строка может состоять только из комбинации двух символов - “О” или “Р”. Функция должна посчитать, сколько раз в строке s встречается буква “Р”.

coin_flip("ОРРО")   -> 2
coin_flip("ООOOOO") -> 0
coin_flip("РРРРР")  -> 5
coin_flip("")       -> 0