Эффективность словарей на Python:

В рекурсивной функции ниже я применил некоторые методы мемоизации в python для сохранения предыдущих вычисленных значений в словаре, который теоретически должен иметь сохранение и извлечение O (1). Однако время выполнения примерно в три раза больше при использовании мемоизации, чем при простой рекурсии.

В двух частях приведенного ниже кода, какова возможная причина такого более длительного времени выполнения во втором коде? Я неправильно использую словари?

Простая рекурсивная функция:

import time

def deletion_distance(str1, str2):
  if str1 is "":
    return len(str2)
  elif str2 is "":
    return len(str1)
  else:
    if str1[len(str1) - 1] == str2[len(str2) - 1]:
      return deletion_distance(str1[:-1],str2[:-1])
    else:
      return 1 + min(deletion_distance(str1, str2[:-1]),deletion_distance(str1[:-1],str2))

str1 = "dragonified"
str2 = "infinitezimal"

start = time.time()
for i in range(0,2):
    deletion_distance(str1,str2)
end = time.time()

print((end - start) / 2)

Динамическое программирование с использованием словарей для мемоизации:

import time

def deletion_distance(str1, str2):
  global aux_dict

  if str1 is "":
    return len(str2)
  elif str2 is "":
    return len(str1)
  else:
    if str1[len(str1) - 1] == str2[len(str2) - 1]:
      if "1"+str1[:-1]+"2"+str2[:-1] in aux_dict:
        return aux_dict["1"+str1[:-1]+"2"+str2[:-1]]
      else:
        aux_dict["1"+str1[:-1]+"2"+str2[:-1]] = deletion_distance(str1[:-1],str2[:-1])
        return aux_dict["1"+str1[:-1]+"2"+str2[:-1]]
    else:

      return 1 + min(
        aux_dict["1"+str1+"2"+str2[:-1]] if "1"+str1+"2"+str2[:-1] in aux_dict else deletion_distance(str1, str2[:-1]),
        aux_dict["1"+str1[:-1]+"2"+str2] if "1"+str1[:-1]+"2"+str2 in aux_dict else deletion_distance(str1[:-1],str2))

aux_dict = {}      
str1 = "dragonified"
str2 = "infinitezimal"

start = time.time()
for i in range(0,2):
    deletion_distance(str1,str2)
end = time.time()

print((end - start) / 2)

Обе функции вычисляют расстояние удаления двух строк (вопрос PRAMP.COM), которое представляет собой просто минимальное количество символов обеих строк, которое необходимо удалить из двух строк, чтобы они стали одинаковыми.


person Community    schedule 08.11.2017    source источник
comment
Примечание: если количество промахов намного больше, чем количество совпадений, то есть вы в основном сохраняете в dict, а не читаете, то мемоизация может ухудшить, а не улучшить производительность.   -  person Moses Koledoye    schedule 08.11.2017
comment
Не используйте is для сравнения строк.   -  person Daniel    schedule 08.11.2017
comment
Да, вы ошибаетесь. aux_dict не должен быть местным.   -  person Robᵩ    schedule 08.11.2017
comment
Это правильно запоминать? Вызов ваших функций создает новый пустой dict каждый раз ... я что-то упускаю?   -  person juanpa.arrivillaga    schedule 08.11.2017
comment
Даниэль: Почему я должен использовать это для сравнения строк? (Что не должно быть проблемой, потому что я делаю это в обеих версиях) Роб / Хуанпа: ›Вы совершенно правы. Я закончил использовать его локально, а не глобально. Тем не менее, это не решает проблему, поскольку работает еще медленнее.   -  person    schedule 08.11.2017
comment
@ Nelsão из-за этого   -  person yinnonsanders    schedule 08.11.2017
comment
@ Nelsão, поскольку is сравнивает идентичность, и вы хотите равенства, вам следует использовать ==. Он не решает эту проблему, но это ошибка, ожидающая своего появления.   -  person juanpa.arrivillaga    schedule 08.11.2017
comment
Спасибо за объяснение. Похоже на противоположность java, в которой == - это место в памяти, а str.is () - это содержимое строки.   -  person    schedule 08.11.2017


Ответы (1)


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

aux_dict = {}

def deletion_distance(str1, str2):
    if (str1, str2) in aux_dict:
        return aux_dict[str1, str2]
    if not str1:
        return len(str2)
    elif not str2:
        return len(str1)
    elif str1[-1] == str2[-1]:
        result = deletion_distance(str1[:-1],str2[:-1])
    else:
        result = 1 + min(
            deletion_distance(str1, str2[:-1]),
            deletion_distance(str1[:-1], str2),
        )
    aux_dict[str1, str2] = result
    return result

что уменьшает количество вызовов для двух примеров строк с 1685178 до 268, и, следовательно, кешированная версия работает в 3000 раз быстрее.

РЕДАКТИРОВАТЬ: в обновленном вопросе вы по-прежнему неправильно используете словарь. Только в том случае, если последний символ обеих строк равен, используется словарь.

person Daniel    schedule 08.11.2017
comment
Правда. Я исправил это в своем коде, но вторая версия все еще медленнее. Исправляем в вопросе. - person ; 08.11.2017
comment
После внесения изменений он немного улучшился, но все еще примерно в 2 раза медленнее, чем обычная рекурсия (использовался цикл 100000 для каждой версии). - person ; 08.11.2017