python memoization

¿Qué es la memoización y cómo puedo usarla en Python?



memoization (13)

Acabo de comenzar con Python y no tengo idea de qué es la memoization y cómo usarla. Además, ¿puedo tener un ejemplo simplificado?


Aquí hay una solución que funcionará con los argumentos de tipo list o dict sin quejarse:

def memoize(fn): """returns a memoized version of any function that can be called with the same list of arguments. Usage: foo = memoize(foo)""" def handle_item(x): if isinstance(x, dict): return make_tuple(sorted(x.items())) elif hasattr(x, ''__iter__''): return make_tuple(x) else: return x def make_tuple(L): return tuple(handle_item(x) for x in L) def foo(*args, **kwargs): items_cache = make_tuple(sorted(kwargs.items())) args_cache = make_tuple(args) if (args_cache, items_cache) not in foo.past_calls: foo.past_calls[(args_cache, items_cache)] = fn(*args,**kwargs) return foo.past_calls[(args_cache, items_cache)] foo.past_calls = {} foo.__name__ = ''memoized_'' + fn.__name__ return foo

Tenga en cuenta que este enfoque puede extenderse naturalmente a cualquier objeto implementando su propia función hash como un caso especial en handle_item. Por ejemplo, para hacer que este enfoque funcione para una función que toma un conjunto como un argumento de entrada, podría agregarlo a handle_item:

if is_instance(x, set): return make_tuple(sorted(list(x)))


Bueno, debería responder la primera parte primero: ¿qué es la memoria?

Es solo un método para intercambiar memoria por tiempo. Piensa en la tabla de multiplicación .

El uso de objetos mutables como valor predeterminado en Python generalmente se considera malo. Pero si lo usa con prudencia, en realidad puede ser útil para implementar una memoization .

Aquí hay un ejemplo adaptado de http://docs.python.org/2/faq/design.html#why-are-default-values-shared-between-objects

Al usar un dict mutable en la definición de la función, los resultados calculados intermedios se pueden almacenar en caché (por ejemplo, al calcular factorial(10) después de calcular factorial(9) , podemos reutilizar todos los resultados intermedios)

def factorial(n, _cache={1:1}): try: return _cache[n] except IndexError: _cache[n] = factorial(n-1)*n return _cache[n]


He encontrado esto extremadamente útil

def memoize(function): from functools import wraps memo = {} @wraps(function) def wrapper(*args): if args in memo: return memo[args] else: rv = function(*args) memo[args] = rv return rv return wrapper @memoize def fibonacci(n): if n < 2: return n return fibonacci(n - 1) + fibonacci(n - 2) fibonacci(25)


La memoización es la conversión de funciones en estructuras de datos. Por lo general, uno quiere que la conversión se realice de forma incremental y perezosa (a petición de un elemento de dominio determinado, o "clave"). En los lenguajes funcionales perezosos, esta conversión perezosa puede suceder automáticamente y, por lo tanto, la memoria puede implementarse sin efectos secundarios (explícitos).


La memorización consiste básicamente en guardar los resultados de operaciones anteriores realizadas con algoritmos recursivos para reducir la necesidad de atravesar el árbol de recursiones si se requiere el mismo cálculo en una etapa posterior.

vea http://scriptbucket.wordpress.com/2012/12/11/introduction-to-memoization/

Ejemplo de memorización de Fibonacci en Python:

fibcache = {} def fib(num): if num in fibcache: return fibcache[num] else: fibcache[num] = num if num < 2 else fib(num-1) + fib(num-2) return fibcache[num]


La memorización es mantener los resultados de cálculos costosos y devolver el resultado almacenado en caché en lugar de recalcularlo continuamente.

Aquí hay un ejemplo:

def doSomeExpensiveCalculation(self, input): if input not in self.cache: <do expensive calculation> self.cache[input] = result return self.cache[input]

Se puede encontrar una descripción más completa en la memoization .


La memorización se refiere efectivamente a los resultados de las llamadas de método basadas en las entradas del método ("memoización" → "memorándum" → a recordar) y luego devuelve el resultado recordado en lugar de calcular nuevamente el resultado. Puedes considerarlo como un caché para los resultados del método. Para más detalles, consulte la página 387 para la definición en Introducción a los algoritmos (3e), Cormen et al.

Un ejemplo simple para calcular factoriales utilizando la memorización en Python sería algo como esto:

factorial_memo = {} def factorial(k): if k < 2: return 1 if k not in factorial_memo: factorial_memo[k] = k * factorial(k-1) return factorial_memo[k]

Puede obtener más complicado y encapsular el proceso de memorización en una clase:

class Memoize: def __init__(self, f): self.f = f self.memo = {} def __call__(self, *args): if not args in self.memo: self.memo[args] = self.f(*args) #Warning: You may wish to do a deepcopy here if returning objects return self.memo[args]

Entonces:

def factorial(k): if k < 2: return 1 return k * factorial(k - 1) factorial = Memoize(factorial)

Se agregó una característica conocida como " decorators " en Python 2.4 que le permite ahora simplemente escribir lo siguiente para lograr lo mismo:

@Memoize def factorial(k): if k < 2: return 1 return k * factorial(k - 1)

La biblioteca de Python Decorator tiene un decorador similar llamado memoized que es ligeramente más robusto que la clase Memoize muestra aquí.


Las otras respuestas cubren lo que está bastante bien. No estoy repitiendo eso. Sólo algunos puntos que pueden ser útiles para usted.

Por lo general, memoisation es una operación que puede aplicar en cualquier función que calcule algo (costoso) y devuelva un valor. Debido a esto, a menudo se implementa como un decorator . La implementación es sencilla y sería algo así.

memoised_function = memoise(actual_function)

o expresado como decorador

@memoise def actual_function(arg1, arg2): #body


No olvidemos la función hasattr incorporada, para aquellos que quieren hacer manualidades. De esa manera puede mantener la memoria caché de mem dentro de la definición de la función (a diferencia de una global).

def fact(n): if not hasattr(fact, ''mem''): fact.mem = {1: 1} if not n in fact.mem: fact.mem[n] = n * fact(n - 1) return fact.mem[n]


Nuevo en Python 3.2 es functools.lru_cache . De forma predeterminada, solo almacena en caché las 128 llamadas utilizadas más recientemente, pero puede establecer el maxsize en None para indicar que el caché nunca debe caducar:

import functools @functools.lru_cache(maxsize=None) def fib(num): if num < 2: return num else: return fib(num-1) + fib(num-2)

Esta función por sí misma es muy lenta, intente con fib(36) y tendrá que esperar unos diez segundos.

La lru_cache anotación lru_cache asegura que si la función se ha llamado recientemente para un valor particular, no volverá a calcular ese valor, sino que usará un resultado anterior en caché. En este caso, conduce a una tremenda mejora de la velocidad, mientras que el código no está saturado con los detalles del almacenamiento en caché.


Solo quería agregar a las respuestas ya proporcionadas, la memoized tiene algunas implementaciones simples pero útiles que también pueden memorizar "tipos no lavables", a diferencia de functools.lru_cache .


Solución que funciona con argumentos de palabras clave y posicionales independientemente del orden en que se pasaron los argumentos de palabras clave (utilizando inspect.getargspec ):

import inspect import functools def memoize(fn): cache = fn.cache = {} @functools.wraps(fn) def memoizer(*args, **kwargs): kwargs.update(dict(zip(inspect.getargspec(fn).args, args))) key = tuple(kwargs.get(k, None) for k in inspect.getargspec(fn).args) if key not in cache: cache[key] = fn(**kwargs) return cache[key] return memoizer

Pregunta similar: Identificar las funciones de varargs equivalentes para la memorización en Python


cache = {} def fib(n): if n <= 1: return n else: if n not in cache: cache[n] = fib(n-1) + fib(n-2) return cache[n]