पूंछ पुनरावृत्ति क्या है?


52

मैं पुनरावृत्ति की सामान्य अवधारणा को जानता हूं। मुझे क्विकशॉट एल्गोरिदम का अध्ययन करते हुए पूंछ पुनरावृत्ति की अवधारणा में आया था । 18:30 सेकंड पर एमआईटी से त्वरित सॉर्ट एल्गोरिथ्म के इस वीडियो में प्रोफेसर कहते हैं कि यह एक पूंछ पुनरावर्ती एल्गोरिदम है। यह मेरे लिए स्पष्ट नहीं है कि वास्तव में पूंछ की पुनरावृत्ति का क्या अर्थ है।

क्या कोई उचित उदाहरण के साथ अवधारणा की व्याख्या कर सकता है?

एसओ समुदाय द्वारा यहां दिए गए कुछ उत्तर ।


हमें उस संदर्भ के बारे में अधिक बताएं, जहां आपने टेल रीमर्शन शब्द का सामना किया है । संपर्क? प्रशस्ति पत्र?
शुकुल

@ A.Schulz मैंने संदर्भ का लिंक डाल दिया है।
गीक


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

1
आपको यह भी कहना चाहिए कि अस्पष्ट भाग क्या है या आप पिछले उत्तरों से संतुष्ट क्यों नहीं हैं, मुझे लगता है कि एसओ लोग अच्छे उत्तर प्रदान करते हैं लेकिन आपको इसे फिर से पूछने का क्या कारण है?

जवाबों:


52

टेल रिकर्सन रिकर्सन का एक विशेष मामला है जहां कॉलिंग फ़ंक्शन रिकर्सिव कॉल करने के बाद अधिक संगणना नहीं करता है। उदाहरण के लिए, फ़ंक्शन

int f (इंट x, int y) {
  अगर (y == 0) {
    वापसी x;
  }

  वापसी f (x * y, y-1);
}

पूंछ पुनरावर्ती है (चूंकि अंतिम निर्देश एक पुनरावर्ती कॉल है) जबकि यह कार्य पूंछ पुनरावर्ती नहीं है:

इंट जी (इंट एक्स) {
  अगर (x == 1) {
    वापसी 1;
  }

  int y = g (x-1);

  वापसी x * y;
}

चूंकि यह पुनरावर्ती कॉल वापस आने के बाद कुछ गणना करता है।

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


2
आपने लिखा है "इसका मतलब है कि हमें पुनरावर्ती कॉल के लिए सभी पर कॉल स्टैक की आवश्यकता नहीं है"। कॉल स्टैक हमेशा रहेगा, बस यह है कि रिटर्न एड्रेस को कॉल स्टैक में नहीं लिखा जाना चाहिए, है ना?
Geek

2
यह कुछ हद तक गणना के आपके मॉडल पर निर्भर करता है :) लेकिन हां, एक वास्तविक कंप्यूटर पर कॉल स्टैक अभी भी है, हम इसका उपयोग नहीं कर रहे हैं।
मैट लुईस

क्या होगा अगर यह अंतिम कॉल है, लेकिन लूप के लिए। तो आप ऊपर अपनी सभी def recurse(x): if x < 0 return 1; for i in range 100{ (do calculations) recurse(x)}
संगणनाएँ

13

सीधे शब्दों में कहा जाए तो टेल रिकर्सन एक रिकर्सन है, जहां कंपाइलर रिकॉर्सिव कॉल को "गोटो" कमांड से बदल सकता है, इसलिए संकलित संस्करण को स्टैक डेप्थ को बढ़ाना नहीं होगा।

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

उदाहरण के लिए, यह है नहीं एक पूंछ पुनरावर्ती समारोह:

int factorial(int x) {
    if (x > 0) {
        return x * factorial(x - 1);
    }
    return 1;
}

लेकिन यह एक पूंछ-पुनरावर्ती कार्य है:

int factorial(int x) {
    return tailfactorial(x, 1);
}

int tailfactorial(int x, int multiplier) {
    if (x > 0) {
        return tailfactorial(x - 1, x * multiplier);
    }
    return multiplier;
}

क्योंकि कंपाइलर एक गैर-पुनरावर्ती के लिए पुनरावर्ती कार्य को फिर से लिख सकता है, इस तरह से कुछ का उपयोग कर (एक छद्मकोड):

int tailfactorial(int x, int multiplier) {
    start:
    if (x > 0) {
        multiplier = x * multiplier;
        x--;
        goto start;
    }
    return multiplier;
}

संकलक के लिए नियम बहुत सरल है: जब आप " return thisfunction(newparameters);" पाते हैं , तो इसे " parameters = newparameters; goto start;" से बदल दें । लेकिन यह केवल तभी किया जा सकता है जब पुनरावर्ती कॉल द्वारा वापस किया गया मान सीधे लौटाया जाए।

यदि किसी फ़ंक्शन में सभी पुनरावर्ती कॉल को इस तरह बदला जा सकता है, तो यह एक पूंछ-पुनरावर्ती कार्य है।


13

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

दृष्टिकोण एक: रैखिक पुनरावर्ती प्रक्रिया

(define (factorial n)
 (if (= n 1)
  1
  (* n (factorial (- n 1)))))

दृष्टिकोण ए के लिए प्रक्रिया का आकार इस तरह दिखता है:

(factorial 5)
(* 5 (factorial 4))
(* 5 (* 4 (factorial 3)))
(* 5 (* 4 (* 3 (factorial 2))))
(* 5 (* 4 (* 3 (* 2 (factorial 1)))))
(* 5 (* 4 (* 3 (* 2 (* 1)))))
(* 5 (* 4 (* 3 (* 2))))
(* 5 (* 4 (* 6)))
(* 5 (* 24))
120

दृष्टिकोण बी: रैखिक Iterative प्रक्रिया

(define (factorial n)
 (fact-iter 1 1 n))

(define (fact-iter product counter max-count)
 (if (> counter max-count)
  product
  (fact-iter (* counter product)
             (+ counter 1)
             max-count)))

दृष्टिकोण बी के लिए प्रक्रिया का आकार इस प्रकार है:

(factorial 5)
(fact-iter 1 1 5)
(fact-iter 1 2 5)
(fact-iter 2 3 5)
(fact-iter 6 4 5)
(fact-iter 24 5 5)
(fact-iter 120 6 5)
120

रैखिक पुनरावृत्ति प्रक्रिया (दृष्टिकोण बी) निरंतर अंतरिक्ष में चलती है भले ही प्रक्रिया एक पुनरावर्ती प्रक्रिया हो। यह भी ध्यान दिया जाना चाहिए कि इस दृष्टिकोण में एक सेट चर किसी भी बिंदु पर प्रक्रिया की स्थिति को परिभाषित करता है। {product, counter, max-count}। यह भी एक तकनीक है जिसके द्वारा पूंछ पुनरावृत्ति संकलक अनुकूलन की अनुमति देता है।

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


5

टेल-रिकर्सन रिकर्सन का एक रूप है जिसमें रिकर्सिव कॉल फंक्शन में अंतिम निर्देश होते हैं (यानी टेल पार्ट कहां से आता है)। इसके अलावा, पुनरावर्ती कॉल को पिछले मान (फ़ंक्शन के मापदंडों के अलावा अन्य संदर्भ) को संग्रहीत करने वाली मेमोरी कोशिकाओं के संदर्भ में नहीं बनाया जाना चाहिए। इस तरह, हम पिछले मूल्यों के बारे में परवाह नहीं करते हैं और एक पुनरावर्ती कॉल के लिए एक स्टैक फ्रेम पर्याप्त है; पूंछ-पुनरावृत्ति पुनरावर्ती एल्गोरिदम को अनुकूलित करने का एक तरीका है। अन्य लाभ / अनुकूलन यह है कि पूंछ-पुनरावर्ती एल्गोरिदम को बराबर के रूप में बदलने का एक आसान तरीका है जो पुनरावृत्ति के बजाय पुनरावृत्ति का उपयोग करता है। तो हाँ, क्विकॉर्ट के लिए एल्गोरिथ्म वास्तव में पूंछ-पुनरावर्ती है।

QUICKSORT(A, p, r)
    if(p < r)
    then
        q = PARTITION(A, p, r)
        QUICKSORT(A, p, q–1)
        QUICKSORT(A, q+1, r)

यहाँ पुनरावृत्त संस्करण है:

QUICKSORT(A)
    p = 0, r = len(A) - 1
    while(p < r)
        q = PARTITION(A, p, r)
        r = q - 1

    p = 0, r = len(A) - 1
    while(p < r)
        q = PARTITION(A, p, r)
        p = q + 1
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.