एक लूप (जबकि / के लिए) को पुनरावृत्ति या एक लूप से पुनरावृत्ति से परिवर्तित करने का सामान्य तरीका?


23

यह समस्या मुख्य रूप से एल्गोरिथ्म पर ध्यान केंद्रित कर रही है, शायद कुछ सार और अधिक अकादमिक।

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

सामान्यतया, एक लूप को पुनरावर्ती में बदला जा सकता है।

उदाहरण के लिए:

for(int i=1;i<=100;++i){sum+=i;}

और इसका संबंधित पुनरावर्ती है:

int GetTotal(int number)
{
   if (number==1) return 1;   //The end number
   return number+GetTotal(number-1); //The inner recursive
}

और अंत में इसे सरल बनाने के लिए, एक पुनरावर्ती की आवश्यकता है:

int GetTotal (int number, int sum)
{
    if(number==1) return sum;
    return GetTotal(number-1,sum+number);
}

हालांकि, ज्यादातर मामलों में जवाब देना और विश्लेषण करना इतना आसान नहीं होता है। मैं जो जानना चाहता हूं वह है:

1) क्या हम एक पुनरावर्ती के लिए लूप (/ ………) में परिवर्तित करने के लिए "सामान्य सामान्य तरीका" प्राप्त कर सकते हैं? और रूपांतरण करते समय हमें किस तरह की चीजों पर ध्यान देना चाहिए? बेहतर होगा कि कुछ नमूनों और अपने प्रेरक सिद्धांतों के साथ-साथ रूपांतरण प्रक्रिया के साथ विस्तृत जानकारी लिखें।

2) "पुनरावर्ती" के दो रूप हैं: रेखीय पुनरावर्ती और पूंछ-पुनरावर्ती। तो कौन सा रूपांतरित करना बेहतर है? हमें किस "नियम" पर चलना चाहिए?

3) कभी-कभी हमें पुनरावर्ती के "इतिहास" को रखने की आवश्यकता होती है, यह आसानी से एक लूप स्टेटमेंट में किया जाता है:

उदाहरण के लिए:

List<string> history = new List<string>();
int sum=0;
for (int i=1;i<=100;++i)
{
   if(i==1) history.Add(i.ToString()+"'s result is:1.");
   else
   {
     StringBuilder sub = new StringBuilder();

      for(int j=1;j<=i;++j)
      {
          if(j==i) sbu.Append(j.ToString());
          else
          {
            sub.Append(j.ToString()+"+");
          }
      }
    sum +=i;
    sbu.Append("'s result is:"+sum+Environment.NewLine);
   }
}

नीचे परिणाम है:

1 का परिणाम 1 है।

1 + 2 का परिणाम 3 है।

1 + 2 + 3 का परिणाम 6 ……… है

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

और "पुनरावर्ती एल्गोरिथ्म से लूप तक" के बारे में क्या? क्या उन्हें एक दूसरे में परिवर्तित किया जा सकता है (मुझे लगता है कि यह सैद्धांतिक रूप से किया जाना चाहिए, लेकिन मैं अपने विचारों को साबित करने के लिए अधिक सटीक चीजें चाहता हूं)


"प्रेरक" का क्या अर्थ है?
gnat

जवाबों:


30

वास्तव में आपको पहले फ़ंक्शन को तोड़ देना चाहिए:

एक लूप के कुछ भाग होते हैं:

  1. हेडर, और लूप से पहले प्रसंस्करण । कुछ नए चर घोषित कर सकते हैं

  2. हालत, जब पाश को रोकने के लिए।

  3. वास्तविक पाश शरीर। यह हेडर के कुछ चरों और / या पास किए गए मापदंडों को बदलता है।

  4. पुंछ; पाश और वापसी परिणाम के बाद क्या होता है।

या इसे लिखने के लिए:

foo_iterative(params){
    header
    while(condition){
        loop_body
    }
    return tail
}

एक पुनरावर्ती कॉल करने के लिए इन ब्लॉकों का उपयोग करना बहुत सरल है:

foo_recursive(params){
    header
    return foo_recursion(params, header_vars)
}

foo_recursion(params, header_vars){
    if(!condition){
        return tail
    }

    loop_body
    return foo_recursion(params, modified_header_vars)
}

एट वॉयला; किसी भी लूप की एक पूंछ पुनरावर्ती संस्करण। लूप बॉडी में breaks और continues को अभी भी बदलना होगा return tailऔर foo_recursion(params, modified_header_vars)आवश्यकतानुसार वापस करना होगा लेकिन यह काफी सरल है।


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

हम उस पर काम करने के लिए एक स्विच का उपयोग कर सकते हैं:

bar_recurse(params){
    if(baseCase){
        finalize
        return
    }
    body1
    bar_recurse(mod_params)
    body2
    bar_recurse(mod_params)
    body3
}


bar_iterative(params){
    stack.push({init, params})

    while(!stack.empty){
        stackFrame = stack.pop()

        switch(stackFrame.resumPoint){
        case init:
            if(baseCase){
                finalize
                break;
            }
            body1
            stack.push({resum1, params, variables})
            stack.push({init, modified_params})
            break;
        case resum1:
            body2
            stack.push({resum2, params, variables})
            stack.push({init, modified_params})
            break;
        case resum2:
            body3
            break;
        }
    }
}

0

@Ratchet freak के उत्तर के बाद, मैंने इस उदाहरण को बनाया कि कैसे फाइबोनैचि फ़ंक्शन को जावा में थोड़ी देर के लूप में फिर से लिखा जा सकता है। ध्यान दें कि थोड़ी देर लूप के साथ फिबोनाची को फिर से लिखने के लिए बहुत सरल (और कुशल) तरीका है।

class CallContext { //this class is similar to the stack frame

    Object[] args;

    List<Object> vars = new LinkedList<>();

    int resumePoint = 0;

    public CallContext(Object[] args) {
        this.args = args;
    }

}


static int fibonacci(int fibNumber) {
    Deque<CallContext> callStack = new LinkedList<>();
    callStack.add(new CallContext(new Object[]{fibNumber}));
    Object lastReturn = null; //value of last object returned (when stack frame was dropped)
    while (!callStack.isEmpty()) {
        CallContext callContext = callStack.peekLast();
        Object[] args = callContext.args;
        //actual logic starts here
        int arg = (int) args[0];
        if (arg == 0 || arg == 1) {
            lastReturn = arg;
            callStack.removeLast();
        } else {
            switch (callContext.resumePoint) {
                case 0: //calculate fib(n-1)
                    callStack.add(new CallContext(new Object[]{arg - 1}));
                    callContext.resumePoint++;
                    break;
                case 1: //calculate fib(n-2)
                    callContext.vars.add(lastReturn); //fib1
                    callStack.add(new CallContext(new Object[]{arg - 2}));
                    callContext.resumePoint++;
                    break;
                case 2: // fib(n-1) + fib(n-2)
                    callContext.vars.add(lastReturn); //fib2
                    lastReturn = (int) callContext.vars.get(0) + (int) callContext.vars.get(1);
                    callStack.removeLast();
                    break;
            }
        }
    }
    return (int) lastReturn;
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.