पायथन: एक जनरेटर के रूप में एक पुनरावर्ती एल्गोरिथ्म का उपयोग करना


99

हाल ही में मैंने nontrivial बाधाओं के साथ कुछ दृश्यों को उत्पन्न करने के लिए एक फ़ंक्शन लिखा। समस्या एक प्राकृतिक पुनरावर्ती समाधान के साथ आई थी। अब ऐसा होता है कि, अपेक्षाकृत छोटे इनपुट के लिए भी, अनुक्रम कई हजारों होते हैं, इस प्रकार मैं अपने एल्गोरिथ्म को एक जनरेटर के रूप में उपयोग करना पसंद करूंगा बजाय सभी अनुक्रमों के साथ एक सूची को भरने के लिए।

यहाँ एक उदाहरण है। मान लीजिए कि हम एक पुनरावर्ती फ़ंक्शन के साथ एक स्ट्रिंग के सभी क्रमपरिवर्तन की गणना करना चाहते हैं। निम्नलिखित भोली एल्गोरिथ्म एक अतिरिक्त तर्क 'भंडारण' लेता है और जब भी यह एक पाता है, तो इसके लिए एक क्रमपरिवर्तन करता है:

def getPermutations(string, storage, prefix=""):
   if len(string) == 1:
      storage.append(prefix + string)   # <-----
   else:
      for i in range(len(string)):
         getPermutations(string[:i]+string[i+1:], storage, prefix+string[i])

storage = []
getPermutations("abcd", storage)
for permutation in storage: print permutation

(कृपया अक्षमता की परवाह न करें, यह केवल एक उदाहरण है।)

अब मैं अपने फंक्शन को जेनरेटर में बदलना चाहता हूं, यानी इसे स्टोरेज लिस्ट में अपीयर करने के बजाय परमिशन देना है:

def getPermutations(string, prefix=""):
   if len(string) == 1:
      yield prefix + string             # <-----
   else:
      for i in range(len(string)):
         getPermutations(string[:i]+string[i+1:], prefix+string[i])

for permutation in getPermutations("abcd"):
   print permutation

यह कोड काम नहीं करता है (फ़ंक्शन एक खाली जनरेटर की तरह व्यवहार करता है)।

क्या मैं कुछ भूल रहा हूँ? क्या पुनरावर्ती एल्गोरिदम को जनरेटर में बदलने के बिना इसे पुनरावृत्ति के साथ बदलने का एक तरीका है ?

जवाबों:


117
def getPermutations(string, prefix=""):
    if len(string) == 1:
        yield prefix + string
    else:
        for i in xrange(len(string)):
            for perm in getPermutations(string[:i] + string[i+1:], prefix+string[i]):
                yield perm

या एक संचायक के बिना:

def getPermutations(string):
    if len(string) == 1:
        yield string
    else:
        for i in xrange(len(string)):
            for perm in getPermutations(string[:i] + string[i+1:]):
                yield string[i] + perm

29
पायथन 3.4 में, आप अंतिम दो पंक्तियों को बदल सकते हैं yield from getPermutations(string[:i] + string[i+1:]), जो कई मायनों में अधिक कुशल है!
मैनुअल एबर्ट

1
आपको फिर भी किसी तरह से परिणाम तैयार करना होगा। उपयोग yield fromकरने के लिए आपको संचायक तर्क ( prefix) का उपयोग करने की आवश्यकता होगी ।
मार्कस जार्डेरोट

सुझाव: string[i],string[:i]+string[i+1:]जोड़े को लौटाने वाले एक और जनरेटर को परिभाषित करें । तो यह होगा:for letter,rest in first_letter_options(string): for perm in getPermuations(rest): yield letter+perm
थॉमस एंड्रयूज

29

यह len(string)-deep पुनरावृत्ति से बचा जाता है , और सामान्य रूप से जनरेटर-अंदर-जनरेटर को संभालने का एक अच्छा तरीका है:

from types import GeneratorType

def flatten(*stack):
    stack = list(stack)
    while stack:
        try: x = stack[0].next()
        except StopIteration:
            stack.pop(0)
            continue
        if isinstance(x, GeneratorType): stack.insert(0, x)
        else: yield x

def _getPermutations(string, prefix=""):
    if len(string) == 1: yield prefix + string
    else: yield (_getPermutations(string[:i]+string[i+1:], prefix+string[i])
            for i in range(len(string)))

def getPermutations(string): return flatten(_getPermutations(string))

for permutation in getPermutations("abcd"): print permutation

flattenहमें yieldइसके माध्यम से पुनरावृत्ति करने के बजाय, इसे केवल एक और जनरेटर में प्रगति जारी रखने की अनुमति देता है और yieldप्रत्येक आइटम को मैन्युअल रूप से सम्मिलित करता है।


पायथन 3.3 yield fromसिंटैक्स में जुड़ जाएगा , जो एक उप-जनरेटर के लिए प्राकृतिक प्रतिनिधिमंडल की अनुमति देता है:

def getPermutations(string, prefix=""):
    if len(string) == 1:
        yield prefix + string
    else:
        for i in range(len(string)):
            yield from getPermutations(string[:i]+string[i+1:], prefix+string[i])

20

GetPermutations के लिए आंतरिक कॉल - यह एक जनरेटर है, भी।

def getPermutations(string, prefix=""):
   if len(string) == 1:
      yield prefix + string            
   else:
      for i in range(len(string)):
         getPermutations(string[:i]+string[i+1:], prefix+string[i])  # <-----

आपको इसके लिए एक फ़ोर-लूप के माध्यम से पुनरावृति करने की आवश्यकता है (देखें @MizardX पोस्टिंग, जो मुझे सेकंड से बाहर निकाल देती है)!

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.