Rev4: उपयोगकर्ता Sammaron द्वारा एक बहुत ही स्पष्ट टिप्पणी ने उल्लेख किया है कि, शायद, यह उत्तर पहले ऊपर-नीचे और नीचे-ऊपर उलझा हुआ है। हालांकि मूल रूप से यह उत्तर (रेव 3) और अन्य उत्तरों में कहा गया है कि "बॉटम-अप मेमोइज़ेशन है" ("उपप्रोब्लेम्स मानें"), यह प्रतिलोम हो सकता है (अर्थात "टॉप-डाउन" "उपप्रोब्लेम्स और" हो सकता है) नीचे-ऊपर "" उपप्रकारों की रचना हो सकती है ")। पहले, मैंने संस्मरण पर पढ़ा है कि गतिशील प्रोग्रामिंग के एक उपप्रकार के विपरीत एक अलग तरह की गतिशील प्रोग्रामिंग है। मैं उस दृष्टिकोण को उद्धृत कर रहा था, इसके लिए सदस्यता नहीं लेने के बावजूद। मैंने इस उत्तर को शब्दावली के अज्ञेय के रूप में फिर से लिखा है जब तक कि साहित्य में उचित संदर्भ नहीं मिल सकते। मैंने इस उत्तर को एक समुदाय विकी में भी बदल दिया है। कृपया शैक्षणिक स्रोतों को प्राथमिकता दें। संदर्भ की सूची:} {साहित्य: ५ }
संक्षिप्त
डायनेमिक प्रोग्रामिंग एक तरह से आपकी गणनाओं को क्रमबद्ध करने के बारे में है जो डुप्लिकेट कार्य को पुन: करने से बचा जाता है। आपको एक मुख्य समस्या है (आपके उपप्रकारों के पेड़ की जड़), और उपप्रोलेज (उपप्रकार)। उपप्रोब्लेम्स आमतौर पर दोहराए जाते हैं और ओवरलैप होते हैं ।
उदाहरण के लिए, Fibonnaci के अपने पसंदीदा उदाहरण पर विचार करें। यदि हम एक भोली पुनरावर्ती कॉल करते हैं, तो यह उपप्रकारों का पूर्ण वृक्ष है:
TOP of the tree
fib(4)
fib(3)...................... + fib(2)
fib(2)......... + fib(1) fib(1)........... + fib(0)
fib(1) + fib(0) fib(1) fib(1) fib(0)
fib(1) fib(0)
BOTTOM of the tree
(कुछ अन्य दुर्लभ समस्याओं में, यह पेड़ कुछ शाखाओं में अनंत हो सकता है, गैर-समाप्ति का प्रतिनिधित्व करता है, और इस प्रकार पेड़ का तल असीम रूप से बड़ा हो सकता है। इसके अलावा, कुछ समस्याओं में आप यह नहीं जान सकते हैं कि पूरा पेड़ आगे से कैसा दिखता है। समय। इस प्रकार, आपको यह तय करने के लिए एक रणनीति / एल्गोरिथ्म की आवश्यकता हो सकती है कि किस उपप्रोब्लेम्स को प्रकट करना है।)
संस्मरण, सारणीकरण
गतिशील प्रोग्रामिंग की कम से कम दो मुख्य तकनीकें हैं जो परस्पर अनन्य नहीं हैं:
संस्मरण - यह एक लाईसेज़-फैयर दृष्टिकोण है: आप मानते हैं कि आप पहले से ही सभी उपप्रोमास की गणना कर चुके हैं और आपको पता नहीं है कि इष्टतम मूल्यांकन क्रम क्या है। आमतौर पर, आप रूट से एक पुनरावर्ती कॉल (या कुछ पुनरावृत्त समतुल्य) का प्रदर्शन करेंगे, और या तो आशा करते हैं कि आप इष्टतम मूल्यांकन आदेश के करीब पहुंच जाएंगे, या एक प्रमाण प्राप्त करेंगे कि आप इष्टतम मूल्यांकन क्रम पर पहुंचने में मदद करेंगे। आप यह सुनिश्चित करेंगे कि पुनरावर्ती कॉल कभी भी एक उपप्रोग्राम को पुन: प्रदर्शित नहीं करता है क्योंकि आप परिणामों को कैश करते हैं, और इस प्रकार डुप्लिकेट उप-पेड़ पुन: विवादित नहीं होते हैं।
- उदाहरण: यदि आप फाइबोनैचि अनुक्रम की गणना कर रहे हैं
fib(100)
, तो आप इसे कॉल करेंगे, और यह कॉल करेगा fib(100)=fib(99)+fib(98)
, जो कॉल करेगा fib(99)=fib(98)+fib(97)
, ... आदि ..., जो कॉल करेगा fib(2)=fib(1)+fib(0)=1+0=1
। तब यह अंततः हल हो जाएगा fib(3)=fib(2)+fib(1)
, लेकिन इसे पुनर्गणना करने की आवश्यकता नहीं है fib(2)
, क्योंकि हमने इसे कैश किया था।
- यह पेड़ के शीर्ष पर शुरू होता है और पत्तियों से उपप्रकारों का मूल्यांकन करता है / जड़ की ओर वापस ऊपर की ओर बढ़ता है।
सारणीकरण - आप एक "टेबल-फिलिंग" एल्गोरिथम के रूप में गतिशील प्रोग्रामिंग के बारे में भी सोच सकते हैं (हालांकि आमतौर पर बहुआयामी, इस 'तालिका' में बहुत दुर्लभ मामलों में गैर-यूक्लिडियन ज्यामिति हो सकती है)। यह संस्मरण की तरह है, लेकिन अधिक सक्रिय है, और इसमें एक अतिरिक्त चरण शामिल है: आपको समय से पहले, सटीक क्रम चुनना होगा जिसमें आप अपनी गणना करेंगे। इसका मतलब यह नहीं होना चाहिए कि आदेश स्थिर होना चाहिए, लेकिन यह कि आपके पास संस्मरण की तुलना में अधिक लचीलापन है।
- उदाहरण: आप फिबोनैकी प्रदर्शन कर रहे हैं, तो आप इस क्रम में संख्या की गणना करने के लिए चुन सकता है:
fib(2)
, fib(3)
, fib(4)
... हर मूल्य कैशिंग, ताकि आपको अगली वाले और अधिक आसानी से गणना कर सकता है। आप इसे एक टेबल भरने (कैशिंग का दूसरा रूप) के रूप में भी सोच सकते हैं।
- मैं व्यक्तिगत रूप से 'सारणीकरण' शब्द को बहुत अधिक नहीं सुनता, लेकिन यह एक बहुत ही अच्छा शब्द है। कुछ लोग इसे "गतिशील प्रोग्रामिंग" मानते हैं।
- एल्गोरिथ्म को चलाने से पहले, प्रोग्रामर पूरे पेड़ पर विचार करता है, फिर एक विशेष क्रम में रूट के लिए उपप्रोम्बल्स का मूल्यांकन करने के लिए एक एल्गोरिथ्म लिखता है, आम तौर पर एक तालिका में भर जाता है।
- * फुटनोट: कभी-कभी 'टेबल' ग्रिड जैसी कनेक्टिविटी के साथ आयताकार तालिका नहीं होती है। बल्कि, इसमें एक अधिक जटिल संरचना हो सकती है, जैसे कि एक पेड़, या समस्या डोमेन के लिए विशिष्ट संरचना (उदाहरण के लिए नक्शे पर उड़ान दूरी के भीतर शहर), या यहां तक कि एक ट्रेली आरेख, जो ग्रिड की तरह है, जिसमें नहीं है एक ऊपर-नीचे-दाएं कनेक्टिविटी संरचना, आदि। उदाहरण के लिए, user3290797 ने एक पेड़ में अधिकतम स्वतंत्र सेट खोजने का एक गतिशील प्रोग्रामिंग उदाहरण जोड़ा , जो एक पेड़ में रिक्त स्थान को भरने से मेल खाता है।
(यह "सामान्य प्रोग्रामिंग" प्रतिमान में सबसे सामान्य है, मैं कहूंगा कि प्रोग्रामर पूरे पेड़ को मानता है, फिरएक एल्गोरिथ्म लिखता है जो उपप्रोमा का मूल्यांकन करने के लिए एक रणनीति को लागू करता है जो कि आप जो भी गुण चाहते हैं उसका अनुकूलन कर सकते हैं (आमतौर पर समय-जटिलता और अंतरिक्ष-जटिलता का संयोजन)। आपकी रणनीति कहीं न कहीं से शुरू होनी चाहिए, कुछ विशेष उपप्रोग्राम के साथ, और शायद उन मूल्यांकन के परिणामों के आधार पर खुद को अनुकूलित कर सकते हैं। "डायनेमिक प्रोग्रामिंग" के सामान्य अर्थ में, आप इन उपप्रकारों को कैश करने का प्रयास कर सकते हैं, और अधिक सामान्यतः, सूक्ष्म अंतर के साथ उपप्रोलेमीम को फिर से भेजने से बचने का प्रयास करें, शायद विभिन्न डेटा संरचनाओं में ग्राफ़ का मामला है। बहुत बार, ये डेटा संरचनाएं एरे या टेबलों की तरह उनके मूल में होती हैं। यदि हमें उनकी आवश्यकता नहीं है, तो उपप्रोलेम्स के समाधानों को फेंक दिया जा सकता है।
[पहले, इस जवाब ने टॉप-डाउन बनाम बॉटम-अप शब्दावली के बारे में एक बयान दिया; मेमोइज़ेशन और टेबुलेशन नामक स्पष्ट रूप से दो मुख्य दृष्टिकोण हैं जो उन शर्तों के साथ पूर्वाग्रह में हो सकते हैं (हालांकि पूरी तरह से नहीं)। ज्यादातर लोग जो सामान्य शब्द का उपयोग करते हैं वह अभी भी "डायनेमिक प्रोग्रामिंग" है और कुछ लोग "डायनामिक प्रोग्रामिंग" के उस विशेष उपप्रकार को संदर्भित करने के लिए "मेमोइज़ेशन" कहते हैं। यह उत्तर यह कहने की गिरावट करता है कि कौन सा टॉप-डाउन और बॉटम-अप है जब तक कि समुदाय अकादमिक पेपर में उचित संदर्भ नहीं पा सकता है। अंततः, शब्दावली के बजाय अंतर को समझना महत्वपूर्ण है।]
फायदा और नुकसान
कोडिंग में आसानी
मेमोइज़ेशन को कोड करना बहुत आसान है (आप आम तौर पर "मेमोइज़र" एनोटेशन या रैपर फ़ंक्शन लिख सकते हैं जो स्वचालित रूप से आपके लिए करता है), और आपके दृष्टिकोण की पहली पंक्ति होनी चाहिए। सारणीकरण का नकारात्मक पक्ष यह है कि आपको एक आदेश के साथ आना होगा।
* (यह वास्तव में केवल आसान है यदि आप फ़ंक्शन को स्वयं लिख रहे हैं, और / या एक अशुद्ध / गैर-कार्यात्मक प्रोग्रामिंग भाषा में कोडिंग कर रहे हैं ... उदाहरण के लिए यदि कोई पहले से ही एक fib
फ़ंक्शन लिखता है , तो यह आवश्यक रूप से स्वयं को पुनरावर्ती कॉल करता है, और आप उन पुनरावर्ती कॉलों को सुनिश्चित करने के बिना फ़ंक्शन को याद नहीं कर सकते हैं जो आपके नए ज्ञापन फ़ंक्शन को कॉल करते हैं (और मूल असम्बद्ध फ़ंक्शन को नहीं))
Recursiveness
ध्यान दें कि ऊपर-नीचे और नीचे-ऊपर दोनों को पुनरावृत्ति या पुनरावृत्ति तालिका-भरने के साथ लागू किया जा सकता है, हालांकि यह स्वाभाविक नहीं हो सकता है।
व्यावहारिक चिंता
संस्मरण के साथ, यदि पेड़ बहुत गहरा है (जैसे fib(10^6)
), तो आप स्टैक स्पेस से बाहर निकल जाएंगे, क्योंकि प्रत्येक विलंबित गणना को स्टैक पर रखा जाना चाहिए, और आपके पास उनमें से 10 ^ 6 होंगे।
optimality
या तो दृष्टिकोण समय-इष्टतम नहीं हो सकता है यदि आप जो आदेश देते हैं (या कोशिश करें) उपप्रोब्लेम्स पर जाएं, वह इष्टतम नहीं है, खासकर अगर सबप्रोब्लेम की गणना करने का एक से अधिक तरीका है (सामान्य रूप से कैशिंग इसका समाधान करेगा, लेकिन यह सैद्धांतिक रूप से संभव है) कैशिंग कुछ विदेशी मामलों में नहीं)। संस्मरण आमतौर पर आपके समय-जटिलता को आपके अंतरिक्ष-परिसर में जोड़ देगा (जैसे सारणीकरण के साथ आपके पास गणनाओं को फेंकने के लिए अधिक स्वतंत्रता है, जैसे कि फ़िब के साथ सारणीकरण का उपयोग करने से आपको O (1) स्थान का उपयोग करने की अनुमति मिलती है, लेकिन Fo के साथ संस्मरण O (N) का उपयोग करता है स्टैक स्पेस)।
उन्नत अनुकूलन
यदि आप भी बहुत जटिल समस्याएँ कर रहे हैं, तो आपके पास सारणीकरण करने के लिए (या कम से कम एक अधिक सक्रिय भूमिका लेने के लिए जहां आप इसे जाना चाहते हैं) के अलावा कोई विकल्प नहीं हो सकता है। इसके अलावा, यदि आप ऐसी स्थिति में हैं जहां अनुकूलन बिल्कुल महत्वपूर्ण है और आपको अनुकूलन करना होगा, तो सारणीकरण आपको अनुकूलन करने की अनुमति देगा, जो कि मेमोनाइजेशन अन्यथा आपको एक समझदार तरीके से नहीं करने देगा। मेरी विनम्र राय में, सामान्य सॉफ्टवेयर इंजीनियरिंग में, इन दोनों मामलों में से कोई भी कभी सामने नहीं आता है, इसलिए मैं सिर्फ संस्मरण ("एक फ़ंक्शन जो इसके जवाबों को कैश करता है") का उपयोग करेगा जब तक कि कुछ (जैसे स्टैक स्पेस) सारणीकरण को आवश्यक नहीं बनाता ... हालांकि तकनीकी रूप से स्टैक ब्लोआउट से बचने के लिए आप 1) भाषाओं में स्टैक के आकार की सीमा को बढ़ा सकते हैं जो इसे अनुमति देते हैं, या 2) अपने स्टैक (ick) को वर्चुअलाइज करने के लिए अतिरिक्त काम का एक स्थिर कारक खाते हैं,
अधिक जटिल उदाहरण
यहां हम विशेष रुचि के उदाहरणों को सूचीबद्ध करते हैं, जो न केवल सामान्य डीपी समस्याएं हैं, बल्कि दिलचस्प रूप से संस्मरण और सारणीकरण को अलग करती हैं। उदाहरण के लिए, एक सूत्रीकरण दूसरे की तुलना में बहुत आसान हो सकता है, या एक अनुकूलन हो सकता है जिसके लिए मूल रूप से सारणीकरण की आवश्यकता होती है:
- दो-आयामी तालिका-भरने वाले एल्गोरिथ्म के गैर-तुच्छ उदाहरण के रूप में, संपादित-दूरी [ 4 ] की गणना करने के लिए एल्गोरिथ्म