Работа над созданием разделов фиксированного размера в подход динамического программирования,
Я пишу этот кусок кода:
def cache(f):
memory={}
def g(*args):
if args not in memory:
memory[args]=f(*args)
return memory[args]
return g
#@cache remove # for cached version
def sum_to_n_list(n, size):
if size>1 :
l=[]
for i in range(n//size,n):
for tail in sum_to_n_list(n - i, size - 1):
if i>=tail[0] : l.append([i]+tail)
return l
else : return [[n]]
Например, разбиение 6 на 3 целых числа имеет три решения: 6 = 2+2+2 = 3+2+1 = 4+1+1:
In [9]: sum_to_n_list(6,3)
Out[9]: [[2, 2, 2], [3, 2, 1], [4, 1, 1]]
Как и ожидалось, кешированная версия работает быстрее:
In [10]: %timeit a=sum_to_n_list(20,10)
1 loops, best of 3: 1.25 s per loop
In [11]: %timeit a=sum_to_n_list(20,10) # cached version
100 loops, best of 3: 2.38 ms per loop
Я хочу иметь такое же улучшение с генератором:
def sum_to_n_gen(n, size):
if size>1 :
for i in range(n//size,n):
for tail in sum_to_n_gen(n - i, size - 1):
if i>=tail[0] : yield [i]+tail
else : yield [n]
Это работает, но я не могу получить кешированную версию этой функции. Как это сделать ?
In [12]: %timeit a=list(sum_to_n_gen(20,10))
1 loops, best of 3: 1.91 s per loop
РЕДАКТИРОВАТЬ
Спасибо @user2357112 и @thomas-k за полезные замечания и советы.
Моя цель здесь — использовать генератор для ясности, потому что он скрывает бесполезные манипуляции со списками в программе; не как экономия памяти.
Итак, я наконец нахожу это решение:
def gencache(f):
memory={}
def g(*args):
if args not in memory:
memory[args]=list(f(*args))
return iter(memory[args])
return g
@gencache
def sum_to_n_gen(n, size):
if size>1 :
for i in range(n//size,n):
for tail in sum_to_n_gen(n - i, size - 1):
if i>=tail[0] : yield [i]+tail
else : yield [n]
Это улучшает скорость с тем же фактором:
In [23]: %timeit a=sum_to_n_gen(20,10) #cached version
100 loops, best of 3: 2.61 ms per loop
Мой вопрос сейчас:
- Эта мемоизация работает, потому что все маленькие подзадачи решаются только один раз, а новый итератор перезаряжается при каждом вызове. но преимущество генератора (вычисление каждого следующего члена по запросу) исчезает и для внешнего вызова. У кого-нибудь есть идея сохранить эту функцию?
- Является ли уточнение кода признанным качеством генераторов?
Спасибо за весь вклад.