2-ऑप्ट: Difference between revisions
No edit summary |
No edit summary |
||
Line 43: | Line 43: | ||
} | } | ||
नोट: यदि आप किसी विशेष नोड या डिपो पर शुरू / समाप्त करते हैं, तो आपको इसे स्वैपिंग के लिए योग्य आवेदक के रूप में परिवर्तन से निकालना होगा, क्रम को व्युत्क्रम से अमान्य पथ हो जाएगा। | |||
उदाहरण के लिए, A स्थित डिपो के साथ: | उदाहरण के लिए, A स्थित डिपो के साथ: | ||
Line 56: | Line 56: | ||
== कुशल कार्यान्वयन == | == कुशल कार्यान्वयन == | ||
नए रूट का निर्माण करना और नए रूट की दूरी की गणना करना एक बहुत अधिक संचालन हो सकता है, आमतौर पर <math>O(n)</math> जहां {{mvar|n}} रूट में शीर्षों की संख्या है। इसे <math>O(1)</math> संचालन निष्पादित करके सममित मामले में (जहां दो नोड्स के बीच की दूरी प्रत्येक विपरीत दिशा में समान है) छोड़ा जा सकता है। चूंकि 2-ऑप्ट संचालन में 2 किनारों को हटाना और 2 अलग-अलग किनारों को जोड़ना शामिल है, इसलिए हम केवल उन किनारों की दूरियां घटा और जोड़ सकते हैं। | |||
lengthDelta = - dist(route[v1], route[v1+1]) - dist(route[v2], route[v2+1]) + dist(route[v1+1], route[v2+1]) + dist(route[v1], route[v2]) | |||
यदि <code>lengthDelta</code>ऋणात्मक है तो इसका मतलब यह होगा कि स्वैप के बाद नई दूरी छोटी होगी। एक बार जब यह पता चल जाता है कि <code>lengthDelta</code> ऋणात्मक है, तो हम 2-ऑप्ट स्वैप करते हैं। इससे हमें बहुत सारी संगणना करने से बचाया जा सकता है। | |||
साथ ही वहां वर्ग दूरी का उपयोग करने से वर्गमूल फ़ंक्शन कॉल को छोड़कर गणना को कम करने में सहायता मिलती है। चूँकि हम केवल दो दूरियों की तुलना करने की परवाह करते हैं, सटीक दूरी की नहीं, इससे चीज़ों की गति बढ़ाने में मदद मिलेगी। यह बहुत ज़्यादा नहीं है, लेकिन यह लाखों शीर्षों वाले बड़े डेटासेट के साथ सहायता करता है। | |||
=== सी++ कोड === | === सी++ कोड === | ||
< | <syntaxhighlight lang="c++"> | ||
#< | #include <algorithm> | ||
# | #include <random> | ||
# | #include <stdio.h> | ||
# | #include <vector> | ||
using namespace std; | |||
class Point { | |||
public: | |||
int x, y; | |||
Point(int x, int y) { | |||
this->x = x; | |||
this->y = y; | |||
} | } | ||
Point() { | |||
this->x = 0; | |||
this->y = 0; | |||
} | } | ||
// | // Distance between two points squared | ||
inline int dist2(const Point &other) const { | |||
return (x - other.x) * (x - other.x) + (y - other.y) * (y - other.y); | |||
} | } | ||
}; | }; | ||
// | // Calculate the distance of the whole path (Squared Distances between points) | ||
int pathLengthSq( | int pathLengthSq(vector<Point> &path) { | ||
int length = 0; | |||
for (int i = 0; i < path.size(); i++) { | for (int i = 0; i < path.size(); i++) { | ||
length += path[i].dist2(path[(i + 1) % path.size()]); | |||
} | } | ||
return length; | |||
} | } | ||
// 2- | // Perform a 2-opt swap | ||
void do2Opt(vector<Point> &path, int i, int j) { | |||
reverse(begin(path) + i + 1, begin(path) + j + 1); | |||
} | } | ||
// | // Print the path. | ||
void printPath(string pathName, vector<Point> &path) { | |||
printf("%s = [", pathName.c_str()); | |||
for (int i = 0; i < path.size(); i++) { | for (int i = 0; i < path.size(); i++) { | ||
if (i % 10 == 0) { | |||
printf("\n "); | |||
} | } | ||
if (i < path.size() - 1) { | |||
printf("[ %3d, %3d], ", path[i].x, path[i].y); | |||
} | } | ||
else { | |||
printf("[ %3d, %3d]", path[i].x, path[i].y); | |||
} | } | ||
} | } | ||
printf("\n];\n"); | |||
} | } | ||
// 0 | // Create a path of length n with random points between 0 and 1000 | ||
vector<Point> createRandomPath(int n) { | |||
vector<Point> path; | |||
for (int i = 0; i < n; i++) { | |||
path.push_back( | path.push_back(Point(rand() % 1000, rand() % 1000)); | ||
} | } | ||
return path; | |||
} | } | ||
int main() { | |||
vector<Point> path = createRandomPath(100); | |||
printPath("path1", path); | |||
int curLength = pathLengthSq( | int curLength = pathLengthSq(path); | ||
int n = path.size(); | int n = path.size(); | ||
bool foundImprovement = true; | |||
while (foundImprovement) { | |||
foundImprovement = false; | |||
for (int i = 0; i <= n - 2; i++) { | |||
for (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 [ | int lengthDelta = -path[i].dist2(path[(i + 1) % n]) - path[j].dist2(path[(j + 1) % n]) + path[i].dist2(path[j]) + path[(i + 1) % n].dist2(path[(j + 1) % n]); | ||
// | // If the length of the path is reduced, do a 2-opt swap | ||
if (lengthDelta < 0) { | |||
do2Opt( | do2Opt(path, i, j); | ||
curLength += lengthDelta; | |||
foundImprovement = true; | |||
} | } | ||
} | } | ||
} | } | ||
} | } | ||
printPath("path2", path); | |||
return 0; | |||
} | } | ||
</syntaxhighlight> | |||
</ | |||
=== आउटपुट === | === आउटपुट === | ||
Line 189: | Line 188: | ||
]; | ]; | ||
</syntaxhighlight> | </syntaxhighlight> | ||
=== दृश्य-चित्रण === | |||
[[File:2-opt Swap Path Visualization.gif|300x300px|alt=2-ऑप्ट स्वैप पथ विज़ुअलाइज़ेशन|2-ऑप्ट स्वैप पथ विज़ुअलाइज़ेशन|left]] | |||
==यह भी देखें== | ==यह भी देखें== | ||
*[[3-ऑप्ट]] | *[[3-ऑप्ट]] | ||
*[[स्थानीय खोज (अनुकूलन)]] | *[[स्थानीय खोज (अनुकूलन)|लोकल सर्च (अनुकूलन)]] | ||
*लिन-कर्निघन अनुमानी | *लिन-कर्निघन अनुमानी | ||
Line 204: | Line 210: | ||
* {{cite book|author = G. A. CROES | year = 1958 | title = A method for solving traveling salesman problems | publisher = Operations Res. 6 (1958), pp., 791-812.}} | * {{cite book|author = G. A. CROES | year = 1958 | title = A method for solving traveling salesman problems | publisher = Operations Res. 6 (1958), pp., 791-812.}} | ||
* {{cite book|author = M. M. FLOOD | year = 1956 | title = The traveling-salesman problem | publisher = Operations Res. 4 (1956), pp., 61-75.}} | * {{cite book|author = M. M. FLOOD | year = 1956 | title = The traveling-salesman problem | publisher = Operations Res. 4 (1956), pp., 61-75.}} | ||
==बाहरी संबंध== | ==बाहरी संबंध== | ||
*[https://www.cs.ubc.ca/~hutter/previous-earg/EmpAlgReadingGroup/TSP-JohMcg97.pdf The Traveling Salesman Problem: A Case Study in Local Optimization] | *[https://www.cs.ubc.ca/~hutter/previous-earg/EmpAlgReadingGroup/TSP-JohMcg97.pdf The Traveling Salesman Problem: A Case Study in Local Optimization] |
Revision as of 11:10, 5 July 2023
अनुकूलन में, ट्रैवलिंग सेल्समैन की समस्या को हल करने के लिए 2-ऑप्ट एक सरल स्थानीय खोज एल्गोरिदम है। 2-ऑप्ट एल्गोरिदम पहली बार 1958 में क्रोज़ द्वारा प्रस्तावित किया गया था,[1] हालांकि मूल चाल का सुझाव फ्लड द्वारा पहले ही दिया जा चुका था।[2] इसके पीछे मुख्य विचार यह है कि एक ऐसा रूट अपनाया जाए जो स्वयं को पार करे और उसे पुन: व्यवस्थित किया जाए ताकि वह स्वयं पार न हो। एक संपूर्ण 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 मानते हुए)
- नया_रूट की विषय वस्तु:
- (A → B)
- A → B → (C → D → E)
- 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 डिपो नहीं छोड़ता है)।
कुशल कार्यान्वयन
नए रूट का निर्माण करना और नए रूट की दूरी की गणना करना एक बहुत अधिक संचालन हो सकता है, आमतौर पर जहां n रूट में शीर्षों की संख्या है। इसे संचालन निष्पादित करके सममित मामले में (जहां दो नोड्स के बीच की दूरी प्रत्येक विपरीत दिशा में समान है) छोड़ा जा सकता है। चूंकि 2-ऑप्ट संचालन में 2 किनारों को हटाना और 2 अलग-अलग किनारों को जोड़ना शामिल है, इसलिए हम केवल उन किनारों की दूरियां घटा और जोड़ सकते हैं।
lengthDelta = - dist(route[v1], route[v1+1]) - dist(route[v2], route[v2+1]) + dist(route[v1+1], route[v2+1]) + dist(route[v1], route[v2])
यदि lengthDelta
ऋणात्मक है तो इसका मतलब यह होगा कि स्वैप के बाद नई दूरी छोटी होगी। एक बार जब यह पता चल जाता है कि lengthDelta
ऋणात्मक है, तो हम 2-ऑप्ट स्वैप करते हैं। इससे हमें बहुत सारी संगणना करने से बचाया जा सकता है।
साथ ही वहां वर्ग दूरी का उपयोग करने से वर्गमूल फ़ंक्शन कॉल को छोड़कर गणना को कम करने में सहायता मिलती है। चूँकि हम केवल दो दूरियों की तुलना करने की परवाह करते हैं, सटीक दूरी की नहीं, इससे चीज़ों की गति बढ़ाने में मदद मिलेगी। यह बहुत ज़्यादा नहीं है, लेकिन यह लाखों शीर्षों वाले बड़े डेटासेट के साथ सहायता करता है।
सी++ कोड
#include <algorithm>
#include <random>
#include <stdio.h>
#include <vector>
using namespace std;
class Point {
public:
int x, y;
Point(int x, int y) {
this->x = x;
this->y = y;
}
Point() {
this->x = 0;
this->y = 0;
}
// Distance between two points squared
inline int dist2(const Point &other) const {
return (x - other.x) * (x - other.x) + (y - other.y) * (y - other.y);
}
};
// Calculate the distance of the whole path (Squared Distances between points)
int pathLengthSq(vector<Point> &path) {
int length = 0;
for (int i = 0; i < path.size(); i++) {
length += path[i].dist2(path[(i + 1) % path.size()]);
}
return length;
}
// Perform a 2-opt swap
void do2Opt(vector<Point> &path, int i, int j) {
reverse(begin(path) + i + 1, begin(path) + j + 1);
}
// Print the path.
void printPath(string pathName, vector<Point> &path) {
printf("%s = [", pathName.c_str());
for (int i = 0; i < path.size(); i++) {
if (i % 10 == 0) {
printf("\n ");
}
if (i < path.size() - 1) {
printf("[ %3d, %3d], ", path[i].x, path[i].y);
}
else {
printf("[ %3d, %3d]", path[i].x, path[i].y);
}
}
printf("\n];\n");
}
// Create a path of length n with random points between 0 and 1000
vector<Point> createRandomPath(int n) {
vector<Point> path;
for (int i = 0; i < n; i++) {
path.push_back(Point(rand() % 1000, rand() % 1000));
}
return path;
}
int main() {
vector<Point> path = createRandomPath(100);
printPath("path1", path);
int curLength = pathLengthSq(path);
int n = path.size();
bool foundImprovement = true;
while (foundImprovement) {
foundImprovement = false;
for (int i = 0; i <= n - 2; i++) {
for (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[j]) + path[(i + 1) % n].dist2(path[(j + 1) % n]);
// If the length of the path is reduced, do a 2-opt swap
if (lengthDelta < 0) {
do2Opt(path, i, j);
curLength += lengthDelta;
foundImprovement = true;
}
}
}
}
printPath("path2", path);
return 0;
}
आउटपुट
path1 = [
[ 743, 933], [ 529, 262], [ 508, 700], [ 256, 752], [ 119, 256], [ 351, 711], [ 705, 843], [ 393, 108], [ 366, 330], [ 932, 169],
[ 847, 917], [ 868, 972], [ 223, 980], [ 592, 549], [ 169, 164], [ 427, 551], [ 624, 190], [ 920, 635], [ 310, 944], [ 484, 862],
[ 301, 363], [ 236, 710], [ 431, 876], [ 397, 929], [ 491, 675], [ 344, 190], [ 425, 134], [ 30, 629], [ 126, 727], [ 334, 743],
[ 760, 104], [ 620, 749], [ 932, 256], [ 613, 572], [ 509, 490], [ 405, 119], [ 49, 695], [ 719, 327], [ 824, 497], [ 649, 596],
[ 184, 356], [ 245, 93], [ 306, 7], [ 754, 509], [ 665, 352], [ 738, 783], [ 690, 801], [ 337, 330], [ 656, 195], [ 11, 963],
[ 42, 427], [ 968, 106], [ 1, 212], [ 480, 510], [ 571, 658], [ 814, 331], [ 564, 847], [ 625, 197], [ 931, 438], [ 487, 18],
[ 187, 151], [ 179, 913], [ 750, 995], [ 913, 750], [ 134, 562], [ 547, 273], [ 830, 521], [ 557, 140], [ 726, 678], [ 597, 503],
[ 893, 408], [ 238, 988], [ 93, 85], [ 720, 188], [ 746, 211], [ 710, 387], [ 887, 209], [ 103, 668], [ 900, 473], [ 105, 674],
[ 952, 183], [ 787, 370], [ 410, 302], [ 110, 905], [ 996, 400], [ 585, 142], [ 47, 860], [ 731, 924], [ 386, 158], [ 400, 219],
[ 55, 415], [ 874, 682], [ 6, 61], [ 268, 602], [ 470, 365], [ 723, 518], [ 106, 89], [ 130, 319], [ 732, 655], [ 974, 993]
];
path2 = [
[ 743, 933], [ 750, 995], [ 847, 917], [ 868, 972], [ 974, 993], [ 913, 750], [ 920, 635], [ 874, 682], [ 726, 678], [ 732, 655],
[ 830, 521], [ 900, 473], [ 893, 408], [ 931, 438], [ 996, 400], [ 932, 256], [ 952, 183], [ 968, 106], [ 932, 169], [ 887, 209],
[ 760, 104], [ 746, 211], [ 720, 188], [ 656, 195], [ 625, 197], [ 624, 190], [ 585, 142], [ 557, 140], [ 487, 18], [ 306, 7],
[ 245, 93], [ 187, 151], [ 169, 164], [ 106, 89], [ 93, 85], [ 6, 61], [ 1, 212], [ 119, 256], [ 130, 319], [ 184, 356],
[ 301, 363], [ 337, 330], [ 366, 330], [ 410, 302], [ 344, 190], [ 393, 108], [ 405, 119], [ 425, 134], [ 386, 158], [ 400, 219],
[ 529, 262], [ 547, 273], [ 470, 365], [ 509, 490], [ 597, 503], [ 710, 387], [ 665, 352], [ 719, 327], [ 814, 331], [ 787, 370],
[ 824, 497], [ 754, 509], [ 723, 518], [ 649, 596], [ 571, 658], [ 613, 572], [ 592, 549], [ 480, 510], [ 427, 551], [ 268, 602],
[ 134, 562], [ 55, 415], [ 42, 427], [ 30, 629], [ 49, 695], [ 103, 668], [ 105, 674], [ 126, 727], [ 47, 860], [ 11, 963],
[ 110, 905], [ 179, 913], [ 223, 980], [ 238, 988], [ 310, 944], [ 256, 752], [ 236, 710], [ 334, 743], [ 351, 711], [ 491, 675],
[ 508, 700], [ 431, 876], [ 397, 929], [ 484, 862], [ 564, 847], [ 620, 749], [ 690, 801], [ 738, 783], [ 705, 843], [ 731, 924]
];
दृश्य-चित्रण
यह भी देखें
- 3-ऑप्ट
- लोकल सर्च (अनुकूलन)
- लिन-कर्निघन अनुमानी
संदर्भ
- G. A. CROES (1958). A method for solving traveling salesman problems. Operations Res. 6 (1958), pp., 791-812.
- M. M. FLOOD (1956). The traveling-salesman problem. Operations Res. 4 (1956), pp., 61-75.