संस्मरण क्या है और मैं इसे पायथन में कैसे उपयोग कर सकता हूं?


378

मैंने अभी पायथन शुरू किया है और मुझे पता नहीं है कि संस्मरण क्या है और इसका उपयोग कैसे करना है। इसके अलावा, क्या मेरे पास एक सरलीकृत उदाहरण हो सकता है?


215
जब प्रासंगिक विकिपीडिया लेख के दूसरे वाक्य में वाक्यांश "पारस्परिक रूप से पुनरावर्ती वंशज पार्सिंग [1] एक सामान्य टॉप-डाउन पार्सिंग एल्गोरिथ्म [2] [3] में शामिल है जो अस्पष्टता और बहुपद समय और स्थान में बाईं पुनरावृत्ति को समायोजित करता है," मुझे लगता है एसओ से यह पूछना पूरी तरह से उचित है कि क्या हो रहा है।
क्लूलेस

10
@ क्लूलेस: वह वाक्यांश "संस्मरण से पहले भी इस्तेमाल किया गया है" अन्य संदर्भों में भी इस्तेमाल किया गया है (और गति लाभ के अलावा अन्य उद्देश्यों के लिए), जैसे "। तो यह सिर्फ उदाहरणों की एक सूची है (और समझने की आवश्यकता नहीं है); यह संस्मरण के स्पष्टीकरण का हिस्सा नहीं है।
श्रीवत्सआर

1
@StefanGruenwald वह लिंक मर चुका है। क्या आप कृपया एक अद्यतन पा सकते हैं?
जेएस।

2
पीडीएफ फाइल का नया लिंक, चूंकि pycogsci.info नीचे है: People.ucsc.edu/~abrsvn/NLTK_parsing_demos.pdf
Stefan Gruenwald

4
@ क्लूलेस, लेख वास्तव में कहता है " साधारण पारस्परिक रूप से पुनरावर्ती वंशज पार्सिंग [1] एक सामान्य टॉप-डाउन पार्सिंग एल्गोरिथ्म में [2] [3] जो वंशानुगत समय और स्थान में अस्पष्टता और बाईं पुनरावृत्ति को समायोजित करता है"। आप सरल से चूक गए , जो स्पष्ट रूप से उस उदाहरण को अधिक स्पष्ट बनाता है :)।
स्टूजेक

जवाबों:


353

मेमोइज़ेशन प्रभावी रूप से याद करने के लिए संदर्भित करता है ("ज्ञापन" → "मेमोरंडम" → याद किया जाना) विधि इनपुट्स के आधार पर विधि कॉल के परिणाम और फिर परिणाम की गणना करने के बजाय याद किए गए परिणाम को वापस करना। आप इसे विधि परिणामों के लिए कैश के रूप में सोच सकते हैं। अधिक जानकारी के लिए, परिचय एल्गोरिथ्म (3e), कॉर्मेन एट अल में परिभाषा के लिए पृष्ठ 387 देखें ।

पायथन में संस्मरण का उपयोग करते हुए तथ्यात्मक गणना के लिए एक सरल उदाहरण कुछ इस तरह होगा:

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)

पायथन 2.4 में " डेकोरेटर्स " के रूप में जाना जाने वाला एक फीचर जोड़ा गया था, जो आपको बस उसी चीज को पूरा करने के लिए निम्नलिखित लिखने की अनुमति देता है:

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

अजगर डेकोरेटर लाइब्रेरी एक ऐसी ही डेकोरेटर कहा जाता है memoizedथोड़ा की तुलना में अधिक मजबूत है कि Memoizeवर्ग यहाँ दिखाया गया है।


2
इस सुझाव के लिए धन्यवाद। मेमोइज़ क्लास एक सुरुचिपूर्ण समाधान है जिसे आसानी से मौजूदा कोड पर लागू किया जा सकता है, जिसमें बहुत अधिक रिफैक्टिंग की आवश्यकता के बिना।
कप्तान लेप्टन

10
मेमोइज़ क्लास सॉल्यूशन छोटी गाड़ी है, यह उसी तरह काम नहीं करेगी factorial_memo, क्योंकि factorialअंदर def factorialअभी भी पुराने को अनम्मोइज़ कहा जाता है factorial
एडम्स्मिथ

9
वैसे, आप लिख भी सकते हैं if k not in factorial_memo:, जो इससे बेहतर पढ़ता है if not k in factorial_memo:
श्रीवत्सआर

5
वास्तव में एक डेकोरेटर के रूप में ऐसा करना चाहिए।
एमिल ओ'रिगन

3
@ durden2.0 मुझे पता है कि यह एक पुरानी टिप्पणी है, लेकिन argsएक तुक है। def some_function(*args)बनाता है एक tuple args।
एडम स्मिथ

232

पायथन 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)और आपको लगभग दस सेकंड इंतजार करना होगा।

lru_cacheएनोटेशन जोड़ना यह सुनिश्चित करता है कि यदि फ़ंक्शन को हाल ही में किसी विशेष मान के लिए बुलाया गया है, तो वह उस मान को फिर से नहीं जमा करेगा, लेकिन पिछले परिणाम का उपयोग करें। इस मामले में, यह एक जबरदस्त गति सुधार की ओर जाता है, जबकि कोड को कैशिंग के विवरण के साथ नहीं जोड़ा गया है।


2
तंतुमय (1000) की कोशिश की गई, रिकर्सियन एरर: अधिकतम पुनरावृत्ति की गहराई तुलना में अधिक हो गई
एक्स

5
@Andyk डिफॉल्ट Py3 रिकर्सन लिमिट 1000 है। पहली बार fibकॉल करने पर, मेमोएशन होने से पहले बेस केस को रिकवर करना होगा। तो, आपका व्यवहार उम्मीद के मुताबिक है।
Quelklef

1
अगर मैं गलत नहीं हूं, तो यह तभी तक खत्म हो जाता है जब तक कि प्रक्रिया को मार नहीं दिया जाता है, है ना? या क्या यह इस बात की परवाह किए बिना कि यह कैश है या नहीं? जैसे, मैं कहता हूं कि मैं अपना सिस्टम फिर से शुरू करूंगा - क्या कैश्ड परिणाम अभी भी कैश होंगे?
क्रिस्टाडा 667

1
@ Kristada673 हां, यह 'मेमोरी में प्रोसेस होता है, डिस्क पर नहीं।
फ्लिम

2
ध्यान दें कि यह फ़ंक्शन के पहले रन को गति देता है, क्योंकि यह एक पुनरावर्ती फ़ंक्शन है और अपने स्वयं के मध्यवर्ती परिणामों को कैशिंग कर रहा है। एक गैर-पुनरावर्ती फ़ंक्शन का वर्णन करना अच्छा हो सकता है जो मेरे जैसे डमी को स्पष्ट करने के लिए स्वाभाविक रूप से धीमा है। : डी
एंडोलिथ

61

अन्य उत्तर कवर करते हैं कि यह काफी अच्छा है। मैं ऐसा नहीं दोहरा रहा हूं। बस कुछ बिंदु जो आपके लिए उपयोगी हो सकते हैं।

आमतौर पर, मेमॉज़ेशन एक ऑपरेशन है जिसे आप किसी भी फ़ंक्शन पर लागू कर सकते हैं जो कुछ (महंगी) गणना करता है और एक मूल्य देता है। इस वजह से, इसे अक्सर डेकोरेटर के रूप में लागू किया जाता है । कार्यान्वयन सीधा है और यह कुछ इस तरह होगा

memoised_function = memoise(actual_function)

या एक डेकोरेटर के रूप में व्यक्त किया गया

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

18

संस्मरण महंगा गणना के परिणामों को बनाए रख रहा है और लगातार पुनर्गणना करने के बजाय कैश्ड परिणाम लौटा रहा है।

यहाँ एक उदाहरण है:

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

ज्ञापन पर विकिपीडिया प्रविष्टि में अधिक पूर्ण विवरण पाया जा सकता है ।


हम्म, अब अगर वह सही पायथन था, तो वह हिल जाएगा, लेकिन ऐसा प्रतीत नहीं होता ... ठीक है, इसलिए "कैश" एक तानाशाही नहीं है? क्योंकि अगर यह है, तो यह होना चाहिए ( if input not in self.cache और तब से अप्रचलित है ... जल्दी 2.x श्रृंखला में, यदि 2.0 नहीं। कभी भी सही नहीं था। IIRC)self.cache[input]has_keyself.cache(index)
जुरगेन ए। एरहार्ड

15

hasattrजो हाथ से शिल्प करना चाहते हैं, उनके लिए अंतर्निहित फ़ंक्शन को नहीं भूलना चाहिए। इस तरह आप फ़ंक्शन परिभाषा के अंदर मेम कैश रख सकते हैं (जैसा कि एक वैश्विक विरोध)।

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]

यह बहुत महंगा विचार लगता है। प्रत्येक एन के लिए, यह न केवल एन के लिए परिणाम को कैश करता है, बल्कि 2 ... एन -1 के लिए भी।
कोडवर्डर

15

मैंने इसे बेहद उपयोगी पाया है

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)

क्यों एक का उपयोग करना चाहिए के लिए docs.python.org/3/library/functools.html#functools.wraps देखें functools.wraps
अनीशपेटेल

1
क्या मुझे मैन्युअल रूप से साफ़ करने की आवश्यकता है memoताकि मेमोरी फ़्री हो जाए?
ओपन स्कूल

संपूर्ण विचार यह है कि परिणाम एक सत्र के भीतर मेमो के अंदर संग्रहीत किए जाते हैं। यानी कुछ भी साफ़ नहीं किया जा रहा है क्योंकि यह है
mr.bjerre

6

संस्मरण मूल रूप से पुनरावर्ती एल्गोरिदम के साथ किए गए पिछले परिचालनों के परिणामों को सहेज रहा है ताकि यदि बाद में उसी गणना की आवश्यकता हो तो पुनरावर्ती वृक्ष को पार किया जा सके।

देख http://scriptbucket.wordpress.com/2012/12/11/introduction-to-memoization/

पायथन में फिबोनाची संस्मरण उदाहरण:

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]

2
अधिक प्रदर्शन के लिए पहले कुछ ज्ञात मूल्यों के साथ अपने फ़ाइबेक को पूर्व-बीज दें, फिर आप उन्हें कोड के 'हॉट पाथ' से बाहर निकालने के लिए अतिरिक्त तर्क ले सकते हैं।
शाम

5

संस्मरण डेटा संरचनाओं में कार्यों का रूपांतरण है। आमतौर पर कोई चाहता है कि रूपांतरण अचानक और आलसी (किसी दिए गए डोमेन तत्व की मांग पर - या "कुंजी") हो। आलसी कार्यात्मक भाषाओं में, यह आलसी रूपांतरण स्वचालित रूप से हो सकता है, और इस प्रकार संस्मरण बिना (स्पष्ट) दुष्प्रभावों के लागू किया जा सकता है।


5

वैसे मुझे पहले भाग का उत्तर पहले देना चाहिए: संस्मरण क्या है?

यह समय के लिए स्मृति का व्यापार करने की एक विधि है। गुणन तालिका के बारे में सोचो ।

पायथन में डिफॉल्ट वैल्यू के रूप में म्यूटेबल ऑब्जेक्ट का उपयोग करना आमतौर पर बुरा माना जाता है। लेकिन अगर इसका इस्तेमाल समझदारी से किया जाए, तो यह वास्तव में लागू करने के लिए उपयोगी हो सकता है memoization

यहां http://docs.python.org/2/faq/design.html#why-are-default-values-sared-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]

4

यहाँ एक समाधान है जो सूची या तानाशाही प्रकार के तर्कों के साथ काम करेगा बिना:

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)))

1
अच्छा प्रयास है। बिना रोके, गलती listसे एक तर्क [1, 2, 3]को एक अलग setतर्क के रूप में माना जा सकता है {1, 2, 3}। इसके अलावा, सेट डिक्शनरी की तरह अनियंत्रित होते हैं, इसलिए उन्हें भी होना चाहिए sorted()। यह भी ध्यान दें कि एक पुनरावर्ती डेटा संरचना तर्क एक अनंत लूप का कारण होगा।
मार्टिन

हाँ, सेटों को विशेष आवरण हैंडल_इटेम (एक्स) और सॉर्टिंग द्वारा नियंत्रित किया जाना चाहिए। मुझे यह नहीं कहना चाहिए कि यह कार्यान्वयन सेट करता है, क्योंकि यह नहीं है - लेकिन यह मुद्दा यह है कि इसे विशेष आवरण हैंड_टेम द्वारा आसानी से बढ़ाया जा सकता है, और जब तक कोई भी वर्ग या चलने योग्य वस्तु के लिए काम करेगा आप स्वयं हैश फ़ंक्शन लिखने के लिए तैयार हैं। मुश्किल हिस्सा - बहुआयामी सूचियों या शब्दकोशों से निपटना - यहाँ पहले से ही निपटा हुआ है, इसलिए मैंने पाया है कि यह मेमोइज़ फंक्शन सरल आधार के रूप में काम करने के लिए बहुत आसान है "मैं केवल वॉशेबल तर्कों को लेता हूं" प्रकार।
रसेलस्टार्ट

मैंने जो समस्या बताई है वह इस तथ्य के कारण है कि lists और sets एक ही चीज़ में "tupleized" हैं और इसलिए एक दूसरे से अप्रभेद्य बन जाते हैं। setsआपके नवीनतम अपडेट में वर्णित समर्थन को जोड़ने के लिए उदाहरण कोड से मुझे डर नहीं है। इसे आसानी से अलग-अलग पास करके [1,2,3]और {1,2,3}"मेमॉइज़" डी टेस्ट फ़ंक्शन के तर्क के रूप में देखा जा सकता है और यह देखने के लिए कि क्या इसे दो बार कहा जाता है, जैसा कि होना चाहिए, या नहीं।
मार्टीन्यू

हाँ, मैंने उस समस्या को पढ़ा, लेकिन मैंने इसे संबोधित नहीं किया क्योंकि मुझे लगता है कि यह आपके द्वारा उल्लेखित अन्य की तुलना में बहुत अधिक मामूली है। जब पिछली बार आपने एक ज्ञापन समारोह लिखा था जहां एक निश्चित तर्क या तो एक सूची या एक सेट हो सकता है, और दोनों के परिणामस्वरूप अलग-अलग आउटपुट थे? यदि आप इस तरह के दुर्लभ मामले में भाग लेना चाहते थे, तो आप फिर से हैंडल करने के लिए handle_item को फिर से लिखेंगे, एक 0 कहें यदि तत्व एक सेट है, या 1 यदि यह एक सूची है।
रसेलस्टैवर्ट

वास्तव में, वहाँ के साथ एक समान मुद्दा है listऔर dictक्योंकि यह संभव एक के लिए listउस में बिल्कुल एक ही बात यह है कि फोन करने से हुई है करने के लिए make_tuple(sorted(x.items()))एक शब्दकोश के लिए। दोनों मामलों के लिए एक सरल समाधान यह होगा कि type()उत्पन्न हुए मूल्य में मूल्य को शामिल किया जाए । मैं विशेष रूप से setएस को संभालने के लिए और भी सरल तरीके के बारे में सोच सकता हूं , लेकिन यह सामान्य नहीं करता है।
मार्टिन

3

समाधान है कि आदेश की स्वतंत्र रूप से दोनों स्थितीय और कीवर्ड तर्क, जिसमें कीवर्ड आर्ग (का उपयोग करते हुए पारित किए गए के साथ काम करता है 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

इसी तरह का सवाल: पायथन में संस्मरण के लिए समतुल्य वर्गास फ़ंक्शन कॉल की पहचान करना


2
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]

4
आप if n not in cacheइसके बजाय बस का उपयोग कर सकते हैं । उपयोग cache.keysकरने से अजगर 2
n611x007

2

बस पहले से उपलब्ध कराए गए उत्तरों को जोड़ना चाहता था, पायथन डेकोरेटर लाइब्रेरी में कुछ सरल अभी तक उपयोगी कार्यान्वयन हैं जो इसके विपरीत "अस्वास्थ्यकर प्रकार" को भी याद कर सकते हैं functools.lru_cache


1
यह डेकोरेटर "अस्वास्थ्यकर प्रकार" को याद नहीं करता है ! यह सिर्फ समारोह को याद करने के बिना कॉल करने के लिए वापस आ जाता है, स्पष्ट के खिलाफ जाना निहित हठधर्मिता से बेहतर है
ओस्ट्रोकैच
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.