2-ऑप्ट

अनुकूलन में, ट्रैवलिंग सेल्समैन की समस्या को हल करने के लिए 2-ऑप्ट एक सरल स्थानीय खोज एल्गोरिदम है। 2-ऑप्ट एल्गोरिदम पहली बार 1958 में क्रोज़ द्वारा प्रस्तावित किया गया था, हालांकि मूल चाल का सुझाव फ्लड द्वारा पहले ही दिया जा चुका था। इसके पीछे मुख्य विचार यह है कि एक ऐसा रूट अपनाया जाए जो स्वयं को पार करे और उसे पुन: व्यवस्थित किया जाए ताकि वह स्वयं पार न हो। एक संपूर्ण 2-ऑप्ट स्थानीय खोज स्वैपिंग तंत्र के हर संभव वैध संयोजन की तुलना करेगी। यह तकनीक ट्रैवलिंग सेल्समैन की समस्या के साथ-साथ कई संबंधित समस्याओं पर भी लागू की जा सकती है। इनमें वाहन रूटिंग समस्या (वीआरपी) के साथ-साथ कैपेसिटेटेड वीआरपी भी शामिल है, जिसमें एल्गोरिदम में मामूली संशोधन की आवश्यकता होती है।

स्यूडोकोड
दृष्टिगत रूप से, एक स्वैप इस तरह दिखता है:

- A  B -             - A - B - ×        ==>  - C   D -             - C - D -

स्यूडोकोड में, वह तंत्र जिसके द्वारा 2-ऑप्ट स्वैप किसी दिए गए रूट में हेरफेर करता है, इस प्रकार है। यहां v1 और v2 किनारों के पहले शीर्ष हैं जिन्हें आप रूट से गुजरते समय स्वैप करना चाहते हैं: procedure 2optSwap(route, v1, v2) { 1. take route[0] to route[v1] and add them in order to new_route 2. take route[v1+1] to route[v2] and add them in reverse order to new_route 3. take route[v2+1] to route[start] and add them in order to new_route return new_route; }

यहां यादृच्छिक माध्यम से इनपुट के साथ उपरोक्त का एक उदाहरण दिया गया है:


 * उदाहरण रूट: A → B → E → D → C → F → G → H → A
 * उदाहरण पैरामीटर: v1=1, v2=4 (प्रारंभिक सूचकांक 0 मानते हुए)
 * नया_रूट की विषय वस्तु:


 * 1) (A → B)
 * 2) A → B → (C → D → E)
 * 3) A → B → C → D → E → (F → G → H → A)

यह उपरोक्त तंत्र का उपयोग करते हुए पूर्ण 2-ऑप्ट स्वैप है:

repeat until no improvement is made { best_distance = calculateTotalDistance(existing_route) start_again: for (i = 0; i <= number of nodes eligible to be swapped - 1; i++) { for (j = i + 1; j <= number of nodes eligible to be swapped; j++) { new_route = 2optSwap(existing_route, i, j)            new_distance = calculateTotalDistance(new_route) if (new_distance < best_distance) { existing_route = new_route best_distance = new_distance goto start_again }        }     } }

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

उदाहरण के लिए, A स्थित डिपो के साथ: A → B → C → D → A

नोड[0] और नोड[2] का उपयोग करके स्वैप करने से परिणाम मिलेगा

C → B → A → D → A

जो वैध नहीं है (A डिपो नहीं छोड़ता है)।

कुशल कार्यान्वयन
नया रूट बनाना और नये रूट की दूरी की गणना करना आमतौर पर बहुत महंगा काम हो सकता है $$O(n)$$ कहाँ $n$ रूट में शीर्षों की संख्या है। इसे सममित स्थिति में (जहां दो नोड्स के बीच की दूरी प्रत्येक विपरीत दिशा में समान होती है) एक प्रदर्शन करके छोड़ा जा सकता है $$O(1)$$ कार्यवाही। चूँकि 2-ऑप्ट ऑपरेशन में 2 किनारों को हटाना और 2 अलग-अलग किनारों को जोड़ना शामिल है, हम केवल उन किनारों की दूरी को घटा और जोड़ सकते हैं।

लंबाईडेल्टा = - जिला(रूट[v1], रूट[v1+1]) - जिला(रूट[v2], रूट[v2+1]) + जिला(रूट[v1+1], रूट[v2+1]) + जिला(रूट[v1], रूट[v2])

अगर  नकारात्मक है इसका मतलब यह होगा कि स्वैप के बाद नई दूरी छोटी होगी। एक बार ये तो पता चल ही जाता है   नकारात्मक है, तो हम 2-ऑप्ट स्वैप करते हैं। इससे हम बहुत सारी संगणना से बच जाते हैं।

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

सी++ कोड

 * 1) <एल्गोरिदम>शामिल करें
 * 2) शामिल <यादृच्छिक>
 * 3) शामिल 
 * 4) शामिल <वेक्टर>

नेमस्पेस एसटीडी का उपयोग करना;

क्लास पॉइंट { जनता: पूर्णांक एक्स, वाई;

बिंदु(int x, int y) { यह->x = x; यह->y = y; } बिंदु { यह->x = 0; यह->y = 0; }

// दो बिंदुओं के बीच की दूरी का वर्ग इनलाइन int dist2(const प्वाइंट और अन्य) const { वापसी (एक्स - अन्य.एक्स) * (एक्स - अन्य.एक्स) + (वाई - अन्य.वाई) * (वाई - अन्य.वाई); } };

// पूरे पथ की दूरी की गणना करें (बिंदुओं के बीच वर्ग दूरी) int pathLengthSq(वेक्टर<प्वाइंट> &पथ) { पूर्णांक लंबाई = 0; for (int i = 0; i < path.size; i++) { लंबाई += पथ[i].dist2(पथ[(i + 1) % पथ.आकार]); } वापसी की लंबाई; }

// 2-ऑप्ट स्वैप करें शून्य do2Opt(वेक्टर<प्वाइंट> &पथ, int i, int j) { रिवर्स (शुरू (पथ) + i + 1, आरंभ (पथ) + j + 1); }

// पथ प्रिंट करें। शून्य प्रिंटपाथ(स्ट्रिंग पथनाम, वेक्टर<प्वाइंट> और पथ) { प्रिंटफ( %s = [, pathName.c_str); for (int i = 0; i < path.size; i++) { यदि (i % 10 == 0) { प्रिंटफ( \n ); }

यदि (i  createRandomPath(int n) { वेक्टर<प्वाइंट> पथ; के लिए (int i = 0; i < n; i++) { path.push_back(प्वाइंट(रैंड % 1000, रैंड % 1000)); } वापसी का पथ; }

मुख्य प्रवेश बिंदु { वेक्टर<बिंदु> पथ = createRandomPath(100); प्रिंटपाथ(पथ1, पथ);

int curLength = pathLengthSq(पथ); int n = path.size; बूल पाया गया सुधार = सत्य; जबकि (सुधार मिला) { पाया गया सुधार = गलत; के लिए (int i = 0; i <= n - 2; i++) { के लिए (int j = i + 1; j <= n - 1; j++) { int lengthDelta = -path[i].dist2(path[(i + 1) % n]) - path[j].dist2(path[(j + 1) % n]) + path[i].dist2(path [जे]) + पथ[(i + 1) % n].dist2(पथ[(j + 1) % n]);

// यदि पथ की लंबाई कम हो गई है, तो 2-ऑप्ट स्वैप करें अगर (लंबाईडेल्टा < 0) { do2Opt(पथ, i, j); कर्ल लंबाई + = लंबाई डेल्टा; पाया गया सुधार = सत्य; } } } }

प्रिंटपाथ(पथ2, पथ); वापसी 0; }



यह भी देखें

 * 3-ऑप्ट
 * स्थानीय खोज (अनुकूलन)
 * लिन-कर्निघन अनुमानी

बाहरी संबंध

 * The Traveling Salesman Problem: A Case Study in Local Optimization
 * Improving Solutions: 2-opt Exchanges