source: tspsg/src/tspsolver.cpp @ b2bf8e3b6b

0.1.3.145-beta1-symbian0.1.4.170-beta2-bb10appveyorimgbotreadme
Last change on this file since b2bf8e3b6b was 6beb157497, checked in by Oleksii Serdiuk, 15 years ago

+ Preliminary Symbian support.

  • Property mode set to 100644
File size: 8.2 KB
RevLine 
[67e53c96d7]1/*
[430bd7f7e9]2 *  TSPSG: TSP Solver and Generator
[1757eb594b]3 *  Copyright (C) 2007-2010 Lёppa <contacts[at]oleksii[dot]name>
[67e53c96d7]4 *
5 *  $Id$
6 *  $URL$
7 *
8 *  This file is part of TSPSG.
9 *
10 *  TSPSG is free software: you can redistribute it and/or modify
11 *  it under the terms of the GNU General Public License as published by
12 *  the Free Software Foundation, either version 3 of the License, or
13 *  (at your option) any later version.
14 *
15 *  TSPSG is distributed in the hope that it will be useful,
16 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 *  GNU General Public License for more details.
19 *
20 *  You should have received a copy of the GNU General Public License
21 *  along with TSPSG.  If not, see <http://www.gnu.org/licenses/>.
22 */
23
24#include "tspsolver.h"
25
[caef58b531]26//! Class constructor
[e664262f7d]27CTSPSolver::CTSPSolver()
[f0464480db]28        : nCities(0), root(NULL)
[e664262f7d]29{
30}
[67e53c96d7]31
[e0fcac5f2c]32/*!
33 * \brief Returns the sorted optimal path, starting from City 1.
34 * \return A string, containing sorted optimal path.
35 */
36QString CTSPSolver::getSortedPath() const
[430bd7f7e9]37{
[e0fcac5f2c]38        if (!root || route.isEmpty() || (route.size() != nCities))
39                return QString();
[430bd7f7e9]40
[e0fcac5f2c]41int i = 0; // We start from City 1
[1757eb594b]42QString path = tr("City %1").arg(1) + " -> ";
[e0fcac5f2c]43        while ((i = route[i]) != 0) {
[1757eb594b]44                path += tr("City %1").arg(i + 1) + " -> ";
[430bd7f7e9]45        }
[e0fcac5f2c]46        // And finish in City 1, too
[1757eb594b]47        path += tr("City %1").arg(1);
[e0fcac5f2c]48
49        return path;
[430bd7f7e9]50}
51
[e0fcac5f2c]52/*!
53 * \brief Returns CTSPSolver's version ID.
54 * \return A string: <b>\$Id$</b>.
55 */
56QString CTSPSolver::getVersionId()
[430bd7f7e9]57{
[e0fcac5f2c]58        return QString("$Id$");
[430bd7f7e9]59}
60
[e0fcac5f2c]61/*!
62 * \brief Returns whether or not the solution is definitely optimal.
63 * \return \c true if solution is definitely optimal, otherwise \c false.
64 *
65 *  The solution may need some further interations to determine whether it is optimal.
66 *  In such cases this function returns \c false.
67 */
68bool CTSPSolver::isOptimal() const
[430bd7f7e9]69{
[e0fcac5f2c]70        return !mayNotBeOptimal;
[430bd7f7e9]71}
72
[caef58b531]73/*!
74 * \brief Solves the given task.
75 * \param numCities Number of cities in the task.
76 * \param task The matrix of city-to-city travel costs.
77 * \param parent The parent widget for displaying messages and dialogs.
[85ad815b0b]78 * \return Pointer to the root of the solution tree.
[caef58b531]79 *
80 * \todo TODO: Comment the algorithm.
81 */
[f0464480db]82SStep *CTSPSolver::solve(int numCities, TMatrix task, QWidget *parent)
[67e53c96d7]83{
84        if (numCities <= 1)
85                return NULL;
[430bd7f7e9]86        cleanup();
[e664262f7d]87        nCities = numCities;
[430bd7f7e9]88QProgressDialog pd(parent);
89QProgressBar *pb = new QProgressBar(&pd);
90        pb->setAlignment(Qt::AlignCenter);
[1757eb594b]91        pb->setFormat(tr("%v of %m parts found"));
[430bd7f7e9]92        pd.setBar(pb);
93        pd.setMaximum(nCities);
94        pd.setMinimumDuration(1000);
[1757eb594b]95        pd.setLabelText(tr("Calculating optimal route..."));
96        pd.setWindowTitle(tr("Solution Progress"));
[430bd7f7e9]97        pd.setWindowModality(Qt::ApplicationModal);
98        pd.setValue(0);
99
[f0464480db]100SStep *step = new SStep();
[e664262f7d]101        step->matrix = task;
[9cf98b9bd6]102        step->price = align(step->matrix);
[e664262f7d]103        root = step;
[67e53c96d7]104
[f0464480db]105SStep *left, *right;
[430bd7f7e9]106int nRow, nCol;
[9cf98b9bd6]107bool firstStep = true;
[6beb157497]108double check = INFINITY;
[9cf98b9bd6]109        while (this->route.size() < nCities) {
[aaf2113307]110//              forbidden.clear();
[9cf98b9bd6]111                step->alts = findCandidate(step->matrix,nRow,nCol);
[430bd7f7e9]112                while (hasSubCycles(nRow,nCol)) {
[aaf2113307]113//                      forbidden[nRow] = nCol;
[430bd7f7e9]114                        step->matrix[nRow][nCol] = INFINITY;
115                        step->price += align(step->matrix);
[9cf98b9bd6]116                        step->alts = findCandidate(step->matrix,nRow,nCol);
[430bd7f7e9]117                }
118                if ((nRow == -1) || (nCol == -1) || pd.wasCanceled()) {
[f0464480db]119                        cleanup();
[430bd7f7e9]120                        break;
121                }
122
123                // Route with (nRow,nCol) path
[f0464480db]124                right = new SStep();
[3e46075789]125                right->pNode = step;
[430bd7f7e9]126                right->matrix = step->matrix;
127                for (int k = 0; k < nCities; k++) {
128                        if (k != nCol)
129                                right->matrix[nRow][k] = INFINITY;
130                        if (k != nRow)
131                                right->matrix[k][nCol] = INFINITY;
132                }
133                right->price = step->price + align(right->matrix);
134                // Forbid selected route to exclude its reuse in next steps.
135                right->matrix[nCol][nRow] = INFINITY;
136                right->matrix[nRow][nCol] = INFINITY;
137
138                // Route without (nRow,nCol) path
[f0464480db]139                left = new SStep();
[3e46075789]140                left->pNode = step;
[430bd7f7e9]141                left->matrix = step->matrix;
142                left->matrix[nRow][nCol] = INFINITY;
[6beb157497]143                left->price = step->price + align(left->matrix);
[430bd7f7e9]144
145                step->candidate.nRow = nRow;
146                step->candidate.nCol = nCol;
147                step->plNode = left;
148                step->prNode = right;
149
150                if (right->price <= left->price) {
151                        // Route with (nRow,nCol) path is cheaper
152                        step = right;
[9cf98b9bd6]153                        this->route[nRow] = nCol;
154                        pd.setValue(this->route.size());
155                        if (firstStep) {
156                                check = left->price;
157                                firstStep = false;
158                        }
[430bd7f7e9]159                } else {
160                        // Route without (nRow,nCol) path is cheaper
161                        step = left;
162                        qApp->processEvents();
[9cf98b9bd6]163                        if (firstStep) {
164                                check = right->price;
165                                firstStep = false;
166                        }
[430bd7f7e9]167                }
168        }
169
170        if (!root && !pd.wasCanceled()) {
[aaf2113307]171                pd.reset();
[1757eb594b]172                QMessageBox(QMessageBox::Warning,tr("Solution Result"),tr("Unable to find solution.\nMaybe, this task has no solutions."),QMessageBox::Ok,parent).exec();
[430bd7f7e9]173        }
174
[aaf2113307]175        qApp->processEvents();
176
[9cf98b9bd6]177        if (root) {
178                route = this->route;
179                mayNotBeOptimal = (check < step->price);
180        }
[430bd7f7e9]181        return root;
[67e53c96d7]182}
[9cf98b9bd6]183
[f0464480db]184CTSPSolver::~CTSPSolver()
185{
186        if (root != NULL)
[3e46075789]187                deleteTree(root);
[f0464480db]188}
189
[e0fcac5f2c]190/* Privates **********************************************************/
[9cf98b9bd6]191
[e4ae9e95f7]192double CTSPSolver::align(TMatrix &matrix)
[e0fcac5f2c]193{
[e4ae9e95f7]194double r = 0;
195double min;
[e0fcac5f2c]196        for (int k = 0; k < nCities; k++) {
197                min = findMinInRow(k,matrix);
198                if (min > 0) {
199                        r += min;
200                        subRow(matrix,k,min);
201                }
[9cf98b9bd6]202        }
[e0fcac5f2c]203        for (int k = 0; k < nCities; k++) {
204                min = findMinInCol(k,matrix);
205                if (min > 0) {
206                        r += min;
207                        subCol(matrix,k,min);
208                }
209        }
210        return r;
211}
[9cf98b9bd6]212
[e0fcac5f2c]213void CTSPSolver::cleanup()
214{
[1fbf016a09]215        QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
[e0fcac5f2c]216        route.clear();
217        mayNotBeOptimal = false;
[f0464480db]218        if (root != NULL)
[3e46075789]219                deleteTree(root);
[1fbf016a09]220        QApplication::restoreOverrideCursor();
[f0464480db]221}
222
[3e46075789]223void CTSPSolver::deleteTree(SStep *&root)
[f0464480db]224{
[3e46075789]225        if (root == NULL)
226                return;
227SStep *step = root;
228SStep *parent;
229        forever {
230                if (step->plNode != NULL) {
231                        // We have left child node - going inside it
232                        step = step->plNode;
233                        step->pNode->plNode = NULL;
234                        continue;
235                } else if (step->prNode != NULL) {
236                        // We have right child node - going inside it
237                        step = step->prNode;
238                        step->pNode->prNode = NULL;
239                        continue;
240                } else {
241                        // We have no child nodes. Deleting current one.
242                        parent = step->pNode;
243                        delete step;
244                        if (parent != NULL) {
245                                // Going back to the parent node.
246                                step = parent;
247                        } else {
248                                // We came back to the root node. Finishing.
249                                root = NULL;
250                                break;
251                        }
252                }
253        }
[9cf98b9bd6]254}
255
[fcaa74f7d7]256QList<SCandidate> CTSPSolver::findCandidate(const TMatrix &matrix, int &nRow, int &nCol) const
[55c4b858e9]257{
[e0fcac5f2c]258        nRow = -1;
259        nCol = -1;
[fcaa74f7d7]260QList<SCandidate> alts;
261SCandidate cand;
[e4ae9e95f7]262double h = -1;
263double sum;
[e0fcac5f2c]264        for (int r = 0; r < nCities; r++)
265                for (int c = 0; c < nCities; c++)
266//                      if ((matrix.at(r).at(c) == 0) && !forbidden.values(r).contains(c)) {
267                        if (matrix.at(r).at(c) == 0) {
268                                sum = findMinInRow(r,matrix,c) + findMinInCol(c,matrix,r);
269                                if (sum > h) {
270                                        h = sum;
271                                        nRow = r;
272                                        nCol = c;
[f0464480db]273                                        alts.clear();
274                                } else if ((sum == h) && !hasSubCycles(r,c)) {
275                                        cand.nRow = r;
276                                        cand.nCol = c;
277                                        alts.append(cand);
278                                }
[e0fcac5f2c]279                        }
280        return alts;
[55c4b858e9]281}
282
[e4ae9e95f7]283double CTSPSolver::findMinInCol(int nCol, const TMatrix &matrix, int exr) const
[9cf98b9bd6]284{
[e4ae9e95f7]285double min = INFINITY;
[e0fcac5f2c]286        for (int k = 0; k < nCities; k++)
287                if ((k != exr) && (min > matrix.at(k).at(nCol)))
288                        min = matrix.at(k).at(nCol);
289        return min == INFINITY ? 0 : min;
290}
291
[e4ae9e95f7]292double CTSPSolver::findMinInRow(int nRow, const TMatrix &matrix, int exc) const
[e0fcac5f2c]293{
[e4ae9e95f7]294double min = INFINITY;
[e0fcac5f2c]295        for (int k = 0; k < nCities; k++)
296                if (((k != exc)) && (min > matrix.at(nRow).at(k)))
297                        min = matrix.at(nRow).at(k);
298        return min == INFINITY ? 0 : min;
299}
300
[0ac9690913]301bool CTSPSolver::hasSubCycles(int nRow, int nCol) const
[e0fcac5f2c]302{
303        if ((nRow < 0) || (nCol < 0) || route.isEmpty() || !(route.size() < nCities - 1) || !route.contains(nCol))
304                return false;
305int i = nCol;
306        while (true) {
307                if ((i = route[i]) == nRow)
308                        return true;
309                if (!route.contains(i))
310                        return false;
311        }
312        return false;
313}
314
[e4ae9e95f7]315void CTSPSolver::subCol(TMatrix &matrix, int nCol, double val)
[e0fcac5f2c]316{
317        for (int k = 0; k < nCities; k++)
318                if (k != nCol)
319                        matrix[k][nCol] -= val;
320}
321
[e4ae9e95f7]322void CTSPSolver::subRow(TMatrix &matrix, int nRow, double val)
[e0fcac5f2c]323{
324        for (int k = 0; k < nCities; k++)
325                if (k != nRow)
326                        matrix[nRow][k] -= val;
[9cf98b9bd6]327}
Note: See TracBrowser for help on using the repository browser.