5

Code Complete 2 से हवाला देते हुए,रिकर्सन रन-टाइम मेमोरी का अप्रत्याशित उपयोग कैसे करता है?

int Factorial(int number) { 
    if (number == 1) { 
     return 1; 
    } 
    else { 
     return number * Factorial(number - 1); 
    } 
} 

इसके अलावा धीमी[1] और रन-टाइम स्मृति का इस्तेमाल कर रही अप्रत्याशित[2], पुनरावर्ती संस्करण होने के इस दिनचर्या के को पुनरावृत्त संस्करण, से समझने के लिए कठिन है जो निम्नानुसार है:

int Factorial(int number) { 
    int intermediateResult = 1; 
    for (int factor = 2; factor <= number; factor++) { 
     intermediateResult = intermediateResult * factor; 
    } 
    return intermediateResult; 
} 

मुझे लगता है कि धीमा हिस्सा अनावश्यक फ़ंक्शन कॉल ओवरहेड्स की वजह से है।

लेकिन रिकर्सन रन-टाइम मेमोरी का उपयोग अप्रत्याशित कैसे करता है?

क्या हम हमेशा यह अनुमान नहीं लगा सकते कि कितनी मेमोरी की आवश्यकता होगी (जैसा कि हम जानते हैं कि रिकर्सन कब खत्म होना चाहिए)? मुझे लगता है कि यह पुनरावृत्ति के मामले के रूप में अप्रत्याशित होगा, लेकिन अब और नहीं।

+1

ध्यान दें कि पुनरावृत्ति निरंतर समय में चल सकती है अगर प्रक्रिया पूंछ-रिकर्सन का उपयोग करती है और यदि भाषा पूंछ-कॉल अनुकूलन का समर्थन करती है। यदि आप रुचि रखते हैं तो "पूंछ-रिकर्सन" देखें। – erjiang

+0

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

उत्तर

3

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

+1

जो मैंने सोचा था, लेकिन यह अप्रत्याशित क्यों होगा? – Lazer

+3

मुझे लगता है कि इसका मतलब है "रनटाइम पर एक मान पर निर्भर करता है"। पुनरावर्तक संस्करण जो मैं भविष्यवाणी कर सकता हूं वह हमेशा 8 बाइट स्टैक (या जो कुछ भी लेता है) लेता है, जबकि मैं रिकर्सिव "अनुमान" नहीं कर सकता, क्योंकि यह रनटाइम पर पारित 'संख्या' के मान पर निर्भर करता है। तो "अप्रत्याशित" का अर्थ है "संकलन समय पर निरंतर मूल्य की भविष्यवाणी करने में असमर्थ"। – Brian

+0

यदि आप नहीं जानते कि फ़ंक्शन कितनी बार कॉल करता है, तो यह अनुमानित नहीं होगा, है ना? यदि फ़ंक्शन स्वयं को कई बार कॉल करता है तो आपके पास 'stackoverflow' =) – gideon

0

यदि रिकर्सन स्तर बहुत गहरा हो जाता है, तो आप कॉल स्टैक को उड़ा देंगे और प्रक्रिया में बहुत सारी मेमोरी खाएंगे। ऐसा तब हो सकता है जब आपका number एक "बड़ा पर्याप्त" मान हो। क्या आप इससे भी बदतर कर सकते हैं? हां, यदि आपका फ़ंक्शन प्रत्येक रिकर्सन कॉल के साथ अधिक ऑब्जेक्ट आवंटित करता है।

1

क्या हम हमेशा यह अनुमान नहीं लगा सकते कि कितनी मेमोरी की आवश्यकता होगी (जैसा कि हम जानते हैं कि रिकर्सन कब खत्म होना चाहिए)? मुझे लगता है कि यह पुनरावृत्ति के मामले के रूप में अप्रत्याशित होगा, लेकिन अब और नहीं।

नहीं, सामान्य मामले में नहीं। अधिक पृष्ठभूमि के लिए halting problem के बारे में चर्चा देखें। अब, यहां पोस्ट की गई समस्याओं में से एक का एक पुनरावर्ती संस्करण है:

void u(int x) { 
    if (x != 1) { 
     u((x % 2 == 0) ? x/2 : 3*x+1); 
    } 
} 

यह पूंछ-पुनरावर्ती भी है। चूंकि आप भविष्यवाणी नहीं कर सकते हैं कि यह सामान्य रूप से समाप्त हो जाएगा, तो आप भविष्यवाणी कैसे कर सकते हैं कि कितनी मेमोरी की आवश्यकता है?

संबंधित मुद्दे