Бифуркационные рекурсивные вычисления с избыточными вычислениями

def T(n):
  if n <= 0:
    return 1
  else:
    return T(n-1) + (n-1) * T(n-2)
    
print T(4)

Мне нужен эффективный способ распечатать вывод функции T(n) генерируется с использованием приведенного выше рекуррентного отношения Есть ли способ сделать приведенный выше код более эффективным?

Пример вывода:

10

3 ответа
3

Если вы не можете использовать functools.cache или же functools.lru_cache и если вы не понимаете или не хотите использовать декоратор, как показано в другом ответе, здесь нетехнологичный подход к мемоизация. Обратите внимание, что functools подход проще, надежнее и т.д. Но если вы не беспокоитесь обо всем этом и просто хотите простой код, который радикально быстрее, чем ваш текущий подход, это сработает.

def T(n):
    if n not in T.cache:
        if n <= 0:
            T.cache[n] = 1
        else:
            T.cache[n] = T(n-1) + (n-1) * T(n-2)
    return T.cache[n]

# Initialize the cache.
T.cache = {}

  • 2

    Если вы измените T.cache = {0: 1} от if можно избавиться 🙂

    — Пейлонрайз

  • 1

    @Peilonrayz Ха, хорошая идея, хотя я думаю хотя бы {0: 1, -1: 1} необходим. И я не знаю, должна ли функция изящно обрабатывать отрицательные целые числа, поэтому я оставлю эту оптимизацию OP.

    — FMc

  • Есть ли способ синхронизировать код и проверить, насколько быстро каждый код дает результат?

    — Джастин


  • @Justin Да, это называется профилированием.

    — мачта

  • Ой, да, я ошибся!

    — Пейлонрайз


Еще один вариант — вообще не использовать рекурсию, а вместо этого вычислять последовательность, пока мы не получим желаемый член. Это не только позволяет избежать экспоненциальной сложности исходного алгоритма; это также позволяет избежать, по крайней мере, линейной (с точки зрения подсчета значений) сложности памяти при мемоизации. (Рекурсия / мемоизация также могут вызывать проблемы, связанные с реализацией, такие как ошибки глубины рекурсии или ошибки памяти, которые не связаны с приведенным ниже кодом.)

def T(n):
    if n<=0: return 1
    # Set a, b to T(-1), T(0)
    a, b = 1, 1
    for i in range(n):
        # Change a, b from T(i-1), T(i) to T(i), T(i+1)
        a, b = b, b+i*a
    # i=n-1 set b to T(n)
    return b

    Вы должны использовать if "__name__" == __main__: сторожить.

    Из Python 3.2 вы можете использовать functools.lru_cache а из Python 3.9 вы можете использовать functools.cache запоминать вызовы функций, только вызывать T $ O (п) $ раз. Это причудливое название для кеширования ввода и вывода функции. Это работает путем обертывания функции. Каждый раз, когда вызывается декорированная функция, мы сравниваем аргументы с ключами в кеше:

    • если есть совпадение, мы возвращаем значение, и
    • если совпадений нет, мы вызываем функцию, чтобы получить возвращаемое значение. Сохраните возвращаемое значение в кеше и верните возвращенное значение вызывающей стороне.

    Поскольку вы используете Python 2, мы можем сделать эту функцию сами. Мы можем использовать замыкание для создания функции-декоратора для хранения кеша.

    def cache(fn):
        def inner(*args):
            try:
                return CACHE[args]
            except KeyError:
                pass
            CACHE[args] = value = fn(*args)
            return value
        CACHE = {}
        return inner
    
    @cache
    def T(n):
      if n <= 0:
        return 1
      else:
        return T(n-1) + (n-1) * T(n-2)
    

    Спасибо за ответ! Но мне было интересно, есть ли у вас что-то для Python 2.7 … Я хотел, чтобы решение использовало только один рекурсивный вызов функции T. (Текущий вызывает функцию T дважды.) Я ищу что-то простое и Легко понять…

    — Джастин


    Добавить комментарий

    Ваш адрес email не будет опубликован. Обязательные поля помечены *