방금 파이썬을 시작했는데 메모 가 무엇인지 , 어떻게 사용 하는지 전혀 몰랐 습니다. 또한 간단한 예가 있습니까?
방금 파이썬을 시작했는데 메모 가 무엇인지 , 어떻게 사용 하는지 전혀 몰랐 습니다. 또한 간단한 예가 있습니까?
답변:
Memoization은 효과적으로 메소드 입력을 기반으로 메소드 호출의 결과를 기억 ( "memoization"→ "memorandum"→ 기억해야 함) 한 다음 결과를 다시 계산하지 않고 기억 된 결과를 리턴합니다. 메소드 결과의 캐시로 생각할 수 있습니다. 자세한 내용은 알고리즘 소개 (3e), Cormen et al.
파이썬에서 메모를 사용하여 계승을 계산하는 간단한 예는 다음과 같습니다.
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]
더 복잡해지고 메모 프로세스를 클래스로 캡슐화 할 수 있습니다.
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]
그때:
def factorial(k):
if k < 2: return 1
return k * factorial(k - 1)
factorial = Memoize(factorial)
파이썬 데코레이터 에는 " 데코레이터 " 라는 기능 이 추가되어 동일한 기능을 수행하기 위해 다음을 간단히 작성할 수 있습니다.
@Memoize
def factorial(k):
if k < 2: return 1
return k * factorial(k - 1)
파이썬 데코레이터 라이브러리 라는 유사한 장식이 memoized
약간 더 견고보다 Memoize
여기에 표시된 클래스를.
factorial_memo
때문에, factorial
내부는 def factorial
여전히 이전 unmemoize를 호출합니다 factorial
.
if k not in factorial_memo:
보다 쓸 수있는 글을 쓸 수도 있습니다 if not k in factorial_memo:
.
args
튜플 이라는 것을 알고있다 . def some_function(*args)
args를 튜플로 만듭니다.
Python 3.2의 새로운 기능은 functools.lru_cache
입니다. 기본적으로, 그것은 단지 128 개 가장 최근에 사용 된 통화를 캐시하지만 당신은을 설정할 수 있습니다 maxsize
위해 None
캐시가 만료되지 않도록해야 함을 나타냅니다 :
import functools
@functools.lru_cache(maxsize=None)
def fib(num):
if num < 2:
return num
else:
return fib(num-1) + fib(num-2)
이 기능 자체는 매우 느리므로 시도해보십시오 fib(36)
. 약 10 초 정도 기다려야합니다.
lru_cache
주석을 추가 하면 특정 값에 대해 함수가 최근에 호출 된 경우 해당 값을 다시 계산하지 않고 캐시 된 이전 결과를 사용합니다. 이 경우 코드 속도가 엄청나게 향상되는 반면 코드는 캐싱의 세부 사항으로 복잡하지 않습니다.
fib
호출 될 때 메모가 발생하기 전에 기본 사례로 재귀를 내려야합니다. 그래서 당신의 행동은 거의 예상됩니다.
메모 화는 값 비싼 계산 결과를 유지하고 캐시 된 결과를 지속적으로 다시 계산하지 않고 반환합니다.
예를 들면 다음과 같습니다.
def doSomeExpensiveCalculation(self, input):
if input not in self.cache:
<do expensive calculation>
self.cache[input] = result
return self.cache[input]
더 자세한 설명은 메모 에 대한 wikipedia 항목에서 찾을 수 있습니다 .
if input not in self.cache
하고 self.cache[input]
( has_key
이후 ... 초기 2.X 시리즈, 아닌 경우 2.0되지 않습니다. self.cache(index)
결코 정확했다 IIRC.)
hasattr
수공예를 원하는 사람들을 위해 내장 기능을 잊지 마십시오 . 이렇게하면 전역과 달리 함수 정의 내에 mem 캐시를 유지할 수 있습니다.
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]
나는 이것이 매우 유용하다는 것을 알았습니다.
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)
memo
메모리가 확보되도록 수동으로 지워야 합니까?
메모 화는 기본적으로 재귀 알고리즘으로 수행 한 과거 작업의 결과를 저장하여 나중에 동일한 계산이 필요한 경우 재귀 트리를 통과 할 필요성을 줄입니다.
참조 http://scriptbucket.wordpress.com/2012/12/11/introduction-to-memoization/를
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]
첫 번째 부분에 먼저 대답해야합니다. 메모 란 무엇입니까?
그것은 단지 시간 동안 메모리를 교환하는 방법 일뿐입니다. 생각 곱셈 표 .
파이썬에서 가변 객체를 기본값으로 사용하는 것은 일반적으로 나쁜 것으로 간주됩니다. 그러나 그것을 현명하게 사용한다면 실제로를 구현하는 것이 유용 할 수 있습니다 memoization
.
다음은 http://docs.python.org/2/faq/design.html#why-are-default-values-shared-between-objects 에서 수정 된 예입니다.
dict
함수 정의에서 변경 가능 을 사용 하여 중간 계산 결과를 캐시 할 수 있습니다 (예 : 계산 factorial(10)
후 계산할 때 factorial(9)
모든 중간 결과를 재사용 할 수 있음)
def factorial(n, _cache={1:1}):
try:
return _cache[n]
except IndexError:
_cache[n] = factorial(n-1)*n
return _cache[n]
다음은 목록 또는 dict 유형 인수로 작동하지 않는 솔루션입니다.
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
handle_item에서 특별한 경우로 고유 한 해시 함수를 구현하여이 방법을 모든 객체로 자연스럽게 확장 할 수 있습니다. 예를 들어, 입력 인수로 집합을 취하는 함수에이 접근 방식을 적용하려면 handle_item에 추가 할 수 있습니다.
if is_instance(x, set):
return make_tuple(sorted(list(x)))
list
인수 [1, 2, 3]
는 실수로 set
값이 다른 다른 인수와 동일한 것으로 간주 될 수 있습니다 {1, 2, 3}
. 또한 세트는 사전과 같이 정렬되지 않으므로이어야 sorted()
합니다. 또한 재귀 데이터 구조 인수는 무한 루프를 유발합니다.
list
s와 set
s가 같은 것으로 "튜플 링"되어 서로 구별 할 수 없기 때문에 발생합니다. sets
최신 업데이트 에 설명 된 지원을 추가하기위한 예제 코드는 두렵지 않습니다. 이것은 "memoize"d 테스트 함수에 대한 별도의 전달 [1,2,3]
및 {1,2,3}
인수로, 그리고 두 번 호출되는지 여부를 두 번 호출하는지 여부 를 쉽게 확인할 수 있습니다 .
list
s와 dict
이 때문에이야 가능 A가에 대한 list
호출의 결과 그것은 정확히 같은 것을 가지고 make_tuple(sorted(x.items()))
사전에 대한합니다. 두 경우 모두에 대한 간단한 해결책은 type()
생성 된 튜플에 값 을 포함시키는 것 입니다. 나는 set
s 를 처리하는 더 간단한 방법을 생각할 수 있지만 일반화하지는 않습니다.
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
이미 제공된 답변에 추가하고 싶었던 Python 데코레이터 라이브러리 에는 "해시 불가능한 유형"을 기억할 수있는 간단하면서도 유용한 구현이 있습니다 functools.lru_cache
.