আমি কেবল পাইথন শুরু করেছি এবং স্মৃতিচারণ কী এবং এটি কীভাবে ব্যবহার করতে হয় সে সম্পর্কে আমার কোনও ধারণা নেই । এছাড়াও, আমি কি একটি সরল উদাহরণ দিতে পারি?
আমি কেবল পাইথন শুরু করেছি এবং স্মৃতিচারণ কী এবং এটি কীভাবে ব্যবহার করতে হয় সে সম্পর্কে আমার কোনও ধারণা নেই । এছাড়াও, আমি কি একটি সরল উদাহরণ দিতে পারি?
উত্তর:
মেমোয়েজেশন কার্যকরভাবে স্মরণকে বোঝায় ("স্মৃতিচারণ" → "স্মারকলিপি" remembered মনে রাখতে হবে) পদ্ধতি ইনপুটগুলির উপর ভিত্তি করে মেথড কলগুলির ফলাফল এবং তারপরে পুনরায় ফলাফল গণনা করার পরিবর্তে স্মরণ করা ফলাফল ফিরে আসে। আপনি এটিকে পদ্ধতির ফলাফলের ক্যাশে হিসাবে ভাবতে পারেন। আরও তথ্যের জন্য, আলগোরিদিম পরিচয় (3 ই) এর সংজ্ঞা সংজ্ঞাটির জন্য 387 পৃষ্ঠা দেখুন , Cormen এট আল।
পাইথনে মেমোয়েজেশন ব্যবহার করে ফ্যাক্টরিয়ালগুলি গণনা করার জন্য একটি সাধারণ উদাহরণ এটি হতে পারে:
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
এখনও পুরানো স্মরণকে কল করে না factorial
।
if k not in factorial_memo:
, যা পড়ার চেয়ে ভাল if not k in factorial_memo:
।
args
এটি একটি টিপল। def some_function(*args)
আরগসকে একটি টিপল করে তোলে।
পাইথন 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
টীকা নিশ্চিত করে যে যদি ফাংশন একটি নির্দিষ্ট মান জন্য সম্প্রতি বলা হয়েছে, এটা যে মান recompute করবে না, কিন্তু একটি ক্যাশে পূর্ববর্তী ফলাফলের ব্যবহার করুন। এই ক্ষেত্রে, এটি একটি দুর্দান্ত গতির উন্নতির দিকে পরিচালিত করে, যখন কোডটি ক্যাশিংয়ের বিশদ নিয়ে বিশৃঙ্খলাযুক্ত নয়।
fib
বলা হয়, স্মৃতিচারণ হওয়ার আগে এটি বেস কেসে পুনরাবৃত্তি করতে হবে। সুতরাং, আপনার আচরণটি প্রায় প্রত্যাশিত।
অন্যান্য উত্তরগুলি এটি বেশ ভাল কি কভার করে। আমি যে পুনরাবৃত্তি করছি না। আপনার পক্ষে কার্যকর হতে পারে এমন কয়েকটি পয়েন্ট।
সাধারণত, স্মৃতিচারণ একটি অপারেশন যা আপনি যে কোনও ফাংশনে প্রয়োগ করতে পারেন যা কোনও কিছুর (ব্যয়বহুল) গণনা করে এবং কোনও মান দেয়। এই কারণে, এটা প্রায়ই একটি হিসাবে প্রয়োগ করা হচ্ছে প্রসাধক । বাস্তবায়ন সোজা এবং এটি এরকম কিছু হবে
memoised_function = memoise(actual_function)
বা একটি সজ্জা হিসাবে প্রকাশ করা
@memoise
def actual_function(arg1, arg2):
#body
স্মৃতিচারণ ব্যয়বহুল গণনার ফলাফল রাখছে এবং ক্রমাগত পুনরায় গণনার চেয়ে ক্যাশেড ফলাফলটি ফিরিয়ে দিচ্ছে।
এখানে একটি উদাহরণ:
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
এবং self.cache[input]
( has_key
অপ্রচলিত যেহেতু ... ২.x সিরিজের শুরুর দিকে, ২.০ না হলে self.cache(index)
কখনওই সঠিক ছিল না। আইআইআরসি)
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]
আমি এটি অত্যন্ত দরকারী খুঁজে পেয়েছি
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)
functools.wraps
।
memo
মেমোরিটি যাতে মুক্ত হয় তাই আমাকে কী ম্যানুয়ালি সাফ করার দরকার আছে ?
স্মৃতিচারণ মূলত পুনরাবৃত্ত আলগোরিদিমগুলির সাথে সম্পন্ন অতীতের ক্রিয়াকলাপগুলির ফলাফলগুলি সংরক্ষণ করছে যাতে পরবর্তী পর্যায়ে যদি একই গণনা প্রয়োজন হয় তবে পুনরাবৃত্তি গাছটি অতিক্রম করার প্রয়োজনীয়তা হ্রাস করতে।
দেখতে 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]
মেমোয়েজেশন হ'ল ফাংশনগুলির ডেটা স্ট্রাকচারে রূপান্তর। সাধারণত কেউ চান যে রূপান্তরটি ক্রমবর্ধমান এবং অলসভাবে ঘটে (কোনও প্রদত্ত ডোমেন উপাদান - বা "কী" এর দাবিতে) ঘটে। অলস কার্যক্ষম ভাষাগুলিতে, এই অলস রূপান্তরটি স্বয়ংক্রিয়ভাবে ঘটতে পারে এবং এভাবে স্মৃতিচারণ (পারস্পরিক প্রতিক্রিয়া) ছাড়াই প্রয়োগ করা যেতে পারে।
ভাল আমার প্রথম অংশটি উত্তর দেওয়া উচিত: স্মৃতিচারণ কী?
সময়ের জন্য স্মৃতি বাণিজ্য করার এটি কেবল একটি পদ্ধতি। গুণন সারণীর কথা ভাবেন ।
পাইথনে ডিফল্ট মান হিসাবে পরিবর্তনীয় অবজেক্ট ব্যবহার করা সাধারণত খারাপ হিসাবে বিবেচিত হয়। তবে যদি এটি বুদ্ধিমানের সাথে ব্যবহার করে তবে এটি বাস্তবায়নের ক্ষেত্রে কার্যকর হতে পারে 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]
এখানে এমন একটি সমাধান রয়েছে যা শুকনো না করে তালিকা বা ডিক্ট ধরণের আর্গুমেন্টের সাথে কাজ করবে:
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
মনে রাখবেন যে হ্যান্ডেল_ইটিমে একটি বিশেষ কেস হিসাবে আপনার নিজের হ্যাশ ফাংশনটি প্রয়োগ করে এই পদ্ধতির কোনও অবজেক্টে স্বাভাবিকভাবে প্রসারিত করা যেতে পারে। উদাহরণস্বরূপ, কোনও ইনপুট আর্গুমেন্ট হিসাবে একটি সেট গ্রহণ করে এমন ক্রিয়াকলাপের জন্য এই পদ্ধতির কাজ করতে আপনি হ্যান্ডেল_াইটেম যুক্ত করতে পারেন:
if is_instance(x, set):
return make_tuple(sorted(list(x)))
list
যুক্তি [1, 2, 3]
ভুল করে set
মান হিসাবে একটি পৃথক যুক্তি হিসাবে একই হিসাবে বিবেচনা করা যেতে পারে {1, 2, 3}
। এছাড়াও, সেটগুলি অভিধানের মতো আনর্ডার্ড হয় তাই সেগুলিও হওয়া দরকার sorted()
। এছাড়াও মনে রাখবেন যে একটি পুনরাবৃত্ত তথ্য কাঠামো যুক্তি অসীম লুপের কারণ হতে পারে।
list
এস এবং set
এস একই জিনিসকে "টিপলাইজড" করা হয়েছে এবং তাই একে অপরের থেকে পৃথক হয়ে যায়। sets
আপনার সর্বশেষ আপডেটে বর্ণিত সমর্থনগুলির জন্য উদাহরণ কোডটি এড়াতে পারে না যে আমি ভীত। এটি সহজেই আলাদাভাবে পাস করার মাধ্যমে [1,2,3]
এবং {1,2,3}
"মেমোইজ" ডি টেস্ট ফাংশনের যুক্তিরূপে এবং এটি দু'বার বলা হয়েছে কিনা, এটি যেমন হওয়া উচিত, তা নয় কিনা তা সহজেই দেখা যায় ।
list
s এবং dict
গুলি কারণ এটা সম্ভব একটি জন্য list
এটা ঠিক একই জিনিস যে কলিং থেকে প্রসূত আছে make_tuple(sorted(x.items()))
একটি অভিধান জন্য। উভয় ক্ষেত্রেই একটি সহজ সমাধান type()
হ'ল উত্পন্ন টিউপলের মধ্যে মানটির অন্তর্ভুক্ত করা। আমি বিশেষত ওগুলি হ্যান্ডেল করার জন্য এমনকি আরও সহজ উপায় সম্পর্কে ভাবতে পারি set
, তবে এটি সাধারণ হয় না।
সমাধান যা কীওয়ার্ড আরগগুলি পাস করা হয়েছে ( ইন্সপেক্ট.জেটারগ্যাস্পেক ব্যবহার করে ) স্বাধীনভাবে ক্রমানুসারে অবস্থান এবং কীওয়ার্ড উভয় যুক্তির সাথে কাজ করে :
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
অনুরূপ প্রশ্ন: সমমানের ভারার্গস ফাংশন সনাক্তকরণকে পাইথনে স্মৃতিচারণের আহ্বান জানানো হয়
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]
if n not in cache
পরিবর্তে ব্যবহার করতে পারেন । ব্যবহার cache.keys
অজগর 2
ইতিমধ্যে সরবরাহ করা উত্তরগুলিতে কেবল যুক্ত করতে চেয়েছিলেন, পাইথন ডেকোরেটর লাইব্রেরিতে কিছু সাধারণ এখনও কার্যকর কার্যকর বাস্তবায়ন রয়েছে যা "অপ্রয়োজনীয় প্রকারগুলি" স্মরণও করতে পারে, বিপরীত functools.lru_cache
।