Статья Автор: Деникина Н.В., Деникин А.В.

Вопрос 16-1. Рекурсия (Python). Декоратор @lru_cache

Ускоряем рекурсию

Когда мы используем рекурсию для вычисления значений функции, иногда мы можем столкнуться с ситуацией, когда одни и те же значения вычисляются несколько раз. 

Возьмем рекуррентное соотношние для вычисления чисел Фибоначчи: F(n) = F(n-1) + F(n-2)
На рисунке ниже изображено дерево, которое показывает какие значения вычисляются для того, чтобы вычислить шестое число Фибоначчи.

Как каждый раз, когда функция вызывается, она начинает вычисления сначала, не имея доступа к результатам предыдущих вычислений. В результате функция может многократно вычислять значения для одних и тех же значениях параметра (на рисунке такие значения отмечены одним и тем же цветом). Это приводит к избыточным вычислениям и замедлению работы программы.

В рамках решения задач ЕГЭ в таких случаях можно использовать метод динамического программирования одним из двух способов.
1)  Использовать мемоизацию. Это означает сохранение уже вычисленных значений в памяти и их повторное использование при последующих вызовах функции с теми же аргументами. 
Для этого можно использовать словарь или  список. Кроме этого, в Python есть специальный декоратор @lru_cache() модуля functools, который запоминает возвращаемый функцией результат и использует его при повторных вызовах. Декоратор пишется непосредственно перед описанием функции.
2) Использовать итеративный подход. То есть уйти от реализации функции и написать вычисления всех значений по шагам с использованием цикла. При этом также запоминая вычисленные значения, например в списке, и использовать их при вычислении следующих значений.

Первый способ достаточно прост, достаточно перед описанием функции подписать декоратор. Но своить второй способ также необходимо, в связи с тем, что глубина рекурсии ограничена размером стека и при больших значениях параметра отведенного размера стека может не хватить. 
 

Пример 2. Запоминаем вычисленные значения, используя декоратор

Алгоритм вычисления функции F(n) задан следующими соотношениями:

F(n) = n при n ≤ 3
F(n) = F(n–3) + F(n/3) + n, если n > 3 и  кратно трём,
F(n) = F(n–1) + F(n–2) + F(n-3), если n > 3 и не кратно трём.


Чему равно значение функции F(50) + F(65)
Знак / обозначает операцию целочисленного деления.

В данном примере вычисление значения функции при n не кратном трём будет очень много вычислений значений функции при одних и тех же значениях параметра n. Это помешает нам вычислить ответ в приемлемое время. Поэтому  здесь не обойтись без декоратора @lru_cache, который позволит выполнить вычисления значительно быстрее. 

 


Попробуйте удалить из кода четвертую строку и вы увидите, что программа не успеет выполнится в отведенное на выполнение кода время.
Пропустить Навигационные Ссылки.
Чтобы оставить комментарий нужна авторизация
Печать