क्या एक पुनरावर्ती कार्य में पुनरावृत्तियों / लूप हो सकते हैं?


12

मैं पुनरावर्ती कार्यों के बारे में अध्ययन कर रहा हूं, और जाहिर है, वे ऐसे कार्य हैं जो स्वयं को कॉल करते हैं, और पुनरावृत्तियों / छोरों का उपयोग नहीं करते हैं (अन्यथा यह पुनरावर्ती कार्य नहीं होगा)।

हालाँकि, उदाहरण के लिए वेब पर सर्फिंग करते समय (8-क्वीन्स-पुनरावर्ती समस्या), मुझे यह फ़ंक्शन मिला:

private boolean placeQueen(int rows, int queens, int n) {
    boolean result = false;
    if (row < n) {
        while ((queens[row] < n - 1) && !result) {
            queens[row]++;
            if (verify(row,queens,n)) {
                ok = placeQueen(row + 1,queens,n);
            }
        }
        if (!result) {
            queens[row] = -1;
        }
    }else{
        result = true;
    }
    return result;
}

इसमें एक whileलूप शामिल होता है।

... तो मैं अब थोड़ा खो गया हूं। क्या मैं छोरों का उपयोग कर सकता हूं या नहीं?


5
क्या यह संकलन करता है। हाँ। तो क्यों पूछें?
थॉमस ईडिंग

6
पुनरावृत्ति की पूरी परिभाषा यह है कि कुछ बिंदु पर, फ़ंक्शन को वापस आने से पहले अपने निष्पादन के हिस्से के रूप में फिर से बुलाया जा सकता है (चाहे वह स्वयं द्वारा फिर से बुलाया जाए या किसी अन्य फ़ंक्शन द्वारा कॉल किया जाए)। उस परिभाषा के बारे में कुछ भी लूपिंग की संभावना को बाहर नहीं करता है।
cHao

CHao की टिप्पणी के परिशिष्ट के रूप में, एक पुनरावर्ती कार्य स्वयं के आसान संस्करण पर फिर से कॉल किया जाएगा (अन्यथा, यह हमेशा के लिए लूप होगा)। परिक्रमा करने के लिए ( सादे अंग्रेजी से, पुनरावर्तन क्या है? ): "पुनरावर्ती प्रोग्रामिंग एक समस्या को उत्तरोत्तर कम करने की प्रक्रिया है जिससे स्वयं के संस्करणों को हल करने में आसानी होती है।" इस मामले में, का सबसे कठिन संस्करण placeQueen"जगह 8 रानी" है और इसका आसान संस्करण placeQueen"जगह 7 रानी" (तब जगह 6, आदि) है
ब्रायन

आप ओमेगा काम करने वाली किसी भी चीज़ का उपयोग कर सकते हैं। बहुत कम ही सॉफ़्टवेयर विनिर्देश निर्दिष्ट करते हैं कि प्रोग्रामिंग की किस शैली का उपयोग करना है - जब तक आप स्कूल में नहीं हैं और आपका असाइनमेंट ऐसा कहता है।
अपूर्व खुरसिया २ 27'१२ को २:३२

@ThomasEding: हाँ, यह संकलन और काम करता है। लेकिन मैं अभी इंजीनियरिंग का अध्ययन कर रहा हूं - इस समय मेरे लिए क्या मायने रखता है, यह सख्त अवधारणा / परिभाषा है न कि जिस तरह से आजकल प्रोग्रामर इसे नियोजित करते हैं। इसलिए मैं पूछ रहा हूं कि मेरे पास जो अवधारणा है वह सही है (जो नहीं है, ऐसा लगता है)।
ओमेगा

जवाबों:


41

आपने पुनरावृत्ति को गलत समझा: हालांकि इसका उपयोग पुनरावृत्ति को बदलने के लिए किया जा सकता है, पुनरावर्ती कार्य के लिए पुनरावृत्तियों को स्वयं के लिए आंतरिक नहीं होने की कोई आवश्यकता नहीं है।

किसी फ़ंक्शन को पुनरावर्ती माना जाने के लिए एकमात्र आवश्यकता एक कोड पथ का अस्तित्व है जिसके माध्यम से वह खुद को प्रत्यक्ष या अप्रत्यक्ष रूप से कहता है। सभी सही पुनरावर्ती कार्यों में भी किसी प्रकार की एक शर्त है, उन्हें "हमेशा के लिए पीछे हटने" से रोकना।

आपका पुनरावर्ती फ़ंक्शन बैकट्रैकिंग के साथ पुनरावर्ती खोज की संरचना का वर्णन करने के लिए आदर्श है। यह बाहर निकलने की स्थिति की जांच के साथ शुरू होता है row < n, और अपने स्तर पर पुनरावृत्ति के लिए खोज निर्णय लेने के लिए आगे बढ़ता है (यानी रानी संख्या के लिए एक संभावित स्थान उठाता है row)। प्रत्येक पुनरावृत्ति के बाद, कॉन्फ़िगरेशन पर निर्माण करने के लिए एक पुनरावर्ती कॉल किया जाता है जिसे फ़ंक्शन ने अब तक पाया है; अंततः, यह "बॉटम्स आउट" होता rowहै जब nपुनरावर्ती कॉल में पहुंचता है जो कि nगहरा स्तर होता है।


1
+1 के "सही" पुनरावर्ती कार्यों के लिए एक सशर्त, बहुत सारे गलत लोग हैं जो बाहर नहीं निकलते हैं
जिमी होफा

6
+1 "आवर्ती" हमेशा के लिए `कछुआ () {कछुआ ();}
Mr.Mindor

1
@ Mr.Mindor मुझे बहुत पसंद है "यह कछुए हर तरह से नीचे है" बोली :)
dasblinkenlight

इसने मुझे मुस्कुरा दिया :-)
Martijn Verburg

2
"सभी सही पुनरावर्ती कार्यों में भी किसी प्रकार की एक शर्त है, उन्हें" हमेशा के लिए पीछे हटने "से रोकना।" गैर-सख्त मूल्यांकन के साथ सच नहीं है।
पबबी

12

एक पुनरावर्ती कार्य की सामान्य संरचना कुछ इस प्रकार है:

myRecursiveFunction(inputValue)
begin
   if evaluateBaseCaseCondition(inputValue)=true then
       return baseCaseValue;
   else
       /*
       Recursive processing
       */
       recursiveResult = myRecursiveFunction(nextRecursiveValue); //nextRecursiveValue could be as simple as inputValue-1
       return recursiveResult;
   end if
end

मेरे द्वारा चिह्नित पाठ /*recursive processing*/कुछ भी हो सकता है। इसमें एक लूप शामिल हो सकता है , अगर हल की जा रही समस्या को इसकी आवश्यकता होती है, और इसमें पुनरावर्ती कॉल भी शामिल हो सकते हैं myRecursiveFunction


1
यह भ्रामक है, क्योंकि इसका तात्पर्य है कि केवल एक पुनरावर्ती कॉल है, और बहुत अधिक ऐसे मामलों को बाहर करता है जहां पुनरावर्ती कॉल स्वयं लूप के अंदर होता है (जैसे बी-ट्री ट्रैवर्सल)।
पीटर टेलर

@PeterTaylor: हां, मैं इसे सरल रखने की कोशिश कर रहा था।
FrustratedWithFormsDesigner

या एक लूप के बिना भी कई कॉल, जैसे कि एक सादे बाइनरी ट्री को ट्रेस करना, जहां आपको 2 बच्चों वाले प्रत्येक नोड के कारण 2 कॉल होंगे।
इजाकाता

6

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


1

पुनरावर्ती कॉल और लूप एक पुनरावृत्ति गणना को लागू करने के लिए सिर्फ दो तरीके / निर्माण हैं।

एक whileलूप एक पूंछ-पुनरावर्ती कॉल (उदाहरण के लिए यहां देखें ) से मेल खाता है , अर्थात एक पुनरावृत्ति जिसमें आपको दो पुनरावृत्तियों के बीच मध्यवर्ती परिणामों को बचाने की आवश्यकता नहीं है (अगले चक्र में प्रवेश करने पर एक चक्र के सभी परिणाम तैयार हैं)। यदि आपको मध्यवर्ती परिणामों को संग्रहीत करने की आवश्यकता होती है जिसे आप बाद में फिर से उपयोग कर सकते हैं तो आप whileस्टैक के साथ एक लूप का उपयोग कर सकते हैं ( यहां देखें ), या एक गैर पूंछ-पुनरावर्ती (यानी मनमाना) पुनरावर्ती कॉल।

कई भाषाएं आपको दोनों तंत्रों का उपयोग करने की अनुमति देती हैं और आप उनमें से एक का चयन कर सकते हैं जो आपको बेहतर सूट और यहां तक ​​कि उन्हें अपने कोड में एक साथ मिलाता है। सी, सी ++, जावा, आदि जैसे अनिवार्य भाषाओं में आप सामान्य रूप से एक का उपयोग करें whileया forजब आप एक ढेर की जरूरत नहीं है पाश, और आप पुनरावर्ती कॉल का उपयोग जब आप एक ढेर (आप परोक्ष रन-टाइम ढेर का उपयोग करें) की जरूरत है। हास्केल (एक कार्यात्मक भाषा) एक पुनरावृत्ति नियंत्रण संरचना की पेशकश नहीं करता है, इसलिए आप केवल पुनरावृत्ति करने के लिए पुनरावर्ती कॉल का उपयोग कर सकते हैं।

आपके उदाहरण में (मेरी टिप्पणियाँ देखें):

// queens should have type int [] , not int.
private boolean placeQueen(int row, int [] queens, int n)
{
    boolean result = false;
    if (row < n)
    {
        // Iterate with queens[row] = 1 to n - 1.
        // After each iteration, you either have a result
        // in queens, or you have to try the next column for
        // the current row: no intermediate result.
        while ((queens[row] < n - 1) && !result)
        {
            queens[row]++;
            if (verify(row,queens,n))
            {
                // I think you have 'result' here, not 'ok'.
                // This is another loop (iterate on row).
                // The loop is implemented as a recursive call
                // and the previous values of row are stored on
                // the stack so that we can resume with the previous
                // value if the current attempt finds no solution.
                result = placeQueen(row + 1,queens,n);
            }
        }
        if (!result) {
            queens[row] = -1;
        }
    }else{
        result = true;
    }
    return result;
}

1

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

आठ रानियों में, पुनरावर्ती भाग बैक ट्रैकिंग के लिए आवश्यक डेटा संग्रहीत करने से संबंधित है। जब आप पुनरावृत्ति के बारे में सोचते हैं, तो यह सोचना महत्वपूर्ण है कि स्टैक पर क्या धक्का दिया गया है। स्टैक में मान पैरामीटर और स्थानीय चर पास हो सकते हैं जो एल्गोरिथम में एक महत्वपूर्ण भूमिका निभाते हैं, या कभी-कभी सामान जो जाहिरा तौर पर प्रासंगिक नहीं है वापसी विवरण की तरह या इस मामले में, उपयोग की जाने वाली रानियों की संख्या के साथ एक पारित मूल्य लेकिन एल्गोरिथ्म द्वारा नहीं बदला गया।

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


0

"समस्या का एक छोटा संस्करण बनाएं" भाग में लूप हो सकते हैं। जब तक विधि स्वयं को पैरामीटर के रूप में समस्या के छोटे संस्करण के रूप में गुजरता है, तब तक विधि पुनरावर्ती है। बेशक एक निकास स्थिति, जब समस्या का सबसे छोटा संभव संस्करण हल हो जाता है और विधि एक मान लौटाती है, तो स्टैक ओवरफ्लो स्थिति से बचने के लिए प्रदान किया जाना चाहिए।

आपके प्रश्न में विधि पुनरावर्ती है।


0

रिकर्सियन मूल रूप से आपके फ़ंक्शन को फिर से कॉल करता है और रिकर्सन का मुख्य लाभ मेमोरी को बचाता है। पुनरावृत्ति में लूप हो सकते हैं वे कुछ अन्य ऑपरेशन करने के लिए उपयोग किए जाते हैं।


असहमत होना। कई एल्गोरिदम पुनरावर्ती या पुनरावृत्त हो सकते हैं और पुनरावर्ती समाधान अक्सर अधिक स्मृति गहन होते हैं यदि आप रिटर्न पते, पैरामीटर और स्थानीय चर की गणना करते हैं जिन्हें स्टैक पर धकेलना होगा। कुछ भाषाएं पूंछ की पुनरावृत्ति या टेल कॉल अनुकूलन के लिए अनुकूलन का पता लगाती हैं और मदद करती हैं, लेकिन यह कभी-कभी भाषा या कोड विशिष्ट होता है।
DeveloperDon
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.