Compare commits
2 Commits
Author | SHA1 | Date | |
---|---|---|---|
decfc1c699 | |||
0b75e07b1d |
91
src/Tour.cpp
91
src/Tour.cpp
@ -116,69 +116,46 @@ void Tour::insertNearest(Point p) {
|
|||||||
nearest->next = newNode;
|
nearest->next = newNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class Orientation { COLLINEAR, CLOCKWISE, COUNTER_CLOCKWISE };
|
Node *findbestNodeTSP(Node *const start, Point &point) {
|
||||||
|
auto calcHeuristic = [&point](Node *const insertAt) -> double {
|
||||||
|
double distanceWithNewPoint = insertAt->point.distanceTo(point) +
|
||||||
|
point.distanceTo(insertAt->next->point);
|
||||||
|
double originalDistance = insertAt->point.distanceTo(insertAt->next->point);
|
||||||
|
|
||||||
// I'm not going to pretend that I came up with this on my own.
|
return distanceWithNewPoint - originalDistance;
|
||||||
// The description for the algorithms is found both in the
|
};
|
||||||
// linear algebra course, and on this wiki
|
|
||||||
// https://en.wikipedia.org/w/index.php?title=Line-line_intersection&oldid=1229564037
|
|
||||||
|
|
||||||
// Calculate the calcOrientation of the triplet (p, q, r)
|
double optimalH = Q_INFINITY;
|
||||||
inline Orientation calcOrientation(const Point &p, const Point &q, const Point &r) {
|
Node *optimalNode;
|
||||||
int val = (q.y - p.y) * (r.x - q.x) - (q.x - p.x) * (r.y - q.y);
|
|
||||||
|
|
||||||
if (val == 0)
|
Node *node = start;
|
||||||
return Orientation::COLLINEAR;
|
|
||||||
return (val > 0) ? Orientation::CLOCKWISE : Orientation::COUNTER_CLOCKWISE;
|
|
||||||
}
|
|
||||||
// True if q lies on the segment p-r
|
|
||||||
inline bool isOnSegment(const Point &p, const Point &q, const Point &r) {
|
|
||||||
return (q.x <= std::max(p.x, r.x) && q.x >= std::min(p.x, r.x) &&
|
|
||||||
q.y <= std::max(p.y, r.y) && q.y >= std::min(p.y, r.y));
|
|
||||||
}
|
|
||||||
// Check if the two line segments p1q1 and p2q2 intersect
|
|
||||||
bool doesPointsIntersect(const Point &p1, const Point &q1, const Point &p2,
|
|
||||||
const Point &q2) {
|
|
||||||
Orientation o1 = calcOrientation(p1, q1, p2);
|
|
||||||
Orientation o2 = calcOrientation(p1, q1, q2);
|
|
||||||
Orientation o3 = calcOrientation(p2, q2, p1);
|
|
||||||
Orientation o4 = calcOrientation(p2, q2, q1);
|
|
||||||
|
|
||||||
// General case: line segments intersect if they have different orientations
|
|
||||||
if (o1 != o2 && o3 != o4)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
// Special cases
|
|
||||||
// p1, q1, p2 are collinear and p2 lies on segment p1q1
|
|
||||||
if (o1 == Orientation::COLLINEAR && isOnSegment(p1, p2, q1))
|
|
||||||
return true;
|
|
||||||
// p1, q1, q2 are collinear and q2 lies on segment p1q1
|
|
||||||
if (o2 == Orientation::COLLINEAR && isOnSegment(p1, q2, q1))
|
|
||||||
return true;
|
|
||||||
// p2, q2, p1 are collinear and p1 lies on segment p2q2
|
|
||||||
if (o3 == Orientation::COLLINEAR && isOnSegment(p2, p1, q2))
|
|
||||||
return true;
|
|
||||||
// p2, q2, q1 are collinear and q1 lies on segment p2q2
|
|
||||||
if (o4 == Orientation::COLLINEAR && isOnSegment(p2, q1, q2))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Tour::pointsCross(Point &p1, Point &p2) {
|
|
||||||
if (m_startNode == nullptr || m_startNode->next == m_startNode)
|
|
||||||
return false;
|
|
||||||
Node *n1 = m_startNode, *n2 = m_startNode->next;
|
|
||||||
do {
|
do {
|
||||||
Point &q1 = n1->point, &q2 = n2->point;
|
double h = calcHeuristic(node);
|
||||||
if (doesPointsIntersect(p1, p2, q1, q2))
|
if (h < optimalH) {
|
||||||
return true;
|
optimalH = h;
|
||||||
|
optimalNode = node;
|
||||||
|
}
|
||||||
|
node = node->next;
|
||||||
|
} while (node != start);
|
||||||
|
|
||||||
n1 = n2;
|
return optimalNode;
|
||||||
n2 = n2->next;
|
}
|
||||||
} while (n1 != m_startNode);
|
|
||||||
|
|
||||||
return false;
|
void Tour::insertOptimalHeuristic(Point p) {
|
||||||
|
if (m_startNode == nullptr) {
|
||||||
|
m_startNode = new Node(p);
|
||||||
|
m_startNode->next = m_startNode; // Make it cirkular
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (m_startNode->next == nullptr) {
|
||||||
|
auto newNode = new Node(p, m_startNode);
|
||||||
|
m_startNode->next = newNode;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Node *bestNode = findbestNodeTSP(m_startNode, p);
|
||||||
|
auto *newNode = new Node(p, bestNode->next);
|
||||||
|
bestNode->next = newNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Tour::insertSmallest(Point p) {
|
void Tour::insertSmallest(Point p) {
|
||||||
|
@ -24,11 +24,10 @@ public:
|
|||||||
double distance() const;
|
double distance() const;
|
||||||
void insertNearest(Point p);
|
void insertNearest(Point p);
|
||||||
void insertSmallest(Point p);
|
void insertSmallest(Point p);
|
||||||
|
void insertOptimalHeuristic(Point p);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Node* m_startNode;
|
Node* m_startNode;
|
||||||
|
|
||||||
bool pointsCross(Point &p1, Point &p2);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // TOUR_H
|
#endif // TOUR_H
|
||||||
|
@ -49,7 +49,7 @@ int main(int argc, char *argv[]) {
|
|||||||
auto timeStart = std::chrono::high_resolution_clock::now();
|
auto timeStart = std::chrono::high_resolution_clock::now();
|
||||||
while (input >> x >> y) {
|
while (input >> x >> y) {
|
||||||
Point p(x, y);
|
Point p(x, y);
|
||||||
tour.insertNearest(p);
|
tour.insertOptimalHeuristic(p);
|
||||||
//uncomment the 4 lines below to animate
|
//uncomment the 4 lines below to animate
|
||||||
//tour.draw(scene);
|
//tour.draw(scene);
|
||||||
//std::chrono::milliseconds dura(50);
|
//std::chrono::milliseconds dura(50);
|
||||||
|
Reference in New Issue
Block a user