source: tspsg-svn/trunk/src/tspsolver.cpp @ 115

Last change on this file since 115 was 110, checked in by laleppa, 15 years ago

+ Added ChangeLog?, Installation Guide and License pages to doxygen generated documentation.

  • Added city and separator parameters to CTSPSolver::getSortedPath() method to make path generation more flexible.
  • Fixed a bug when the solution graph wasn't drwan correctly in some situations.
  • Property svn:keywords set to Id URL
File size: 11.3 KB
RevLine 
[12]1/*
[42]2 *  TSPSG: TSP Solver and Generator
[87]3 *  Copyright (C) 2007-2010 Lёppa <contacts[at]oleksii[dot]name>
[12]4 *
5 *  $Id: tspsolver.cpp 110 2010-04-27 23:38:10Z laleppa $
6 *  $URL: https://tspsg.svn.sourceforge.net/svnroot/tspsg/trunk/src/tspsolver.cpp $
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
[99]26//! \internal \brief A short for maximum double, used internally in the solution algorithm.
27#define MAX_DOUBLE std::numeric_limits<double>::max()
28
[107]29namespace TSPSolver {
30
[99]31/*!
32 * \brief Returns CTSPSolver's version ID.
33 * \return A string: <b>\$Id: tspsolver.cpp 110 2010-04-27 23:38:10Z laleppa $</b>.
34 */
35QString CTSPSolver::getVersionId()
[13]36{
[99]37        return QString("$Id: tspsolver.cpp 110 2010-04-27 23:38:10Z laleppa $");
[13]38}
[12]39
[67]40/*!
[99]41 * \brief Constructs CTSPSolver object.
42 * \param parent A parent object.
43 */
44CTSPSolver::CTSPSolver(QObject *parent)
[107]45        : QObject(parent), nCities(0), total(0), root(NULL) {}
[99]46
47/*!
48 * \brief Cleans up the object and frees up memory used by the solution tree.
[107]49 * \param processEvents If set to \c true then \link QCoreApplication::processEvents() QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents)\endlink will be called from time to time while cleaning up.
[99]50 * \warning After call to this function a solution tree returned by the solve() function is no longer valid.
51 * \note It is not required to call this function manually. This function is always called by solve() at the beginning of the solution process.
52 *
53 * \sa solve()
54 */
55void CTSPSolver::cleanup(bool processEvents)
56{
[107]57#ifdef QAPPLICATION_H
[99]58        if (!processEvents)
59                QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
[107]60#endif
[99]61        route.clear();
62        mayNotBeOptimal = false;
63        if (root != NULL)
64                deleteTree(root, processEvents);
[107]65#ifdef QAPPLICATION_H
[99]66        if (!processEvents)
67                QApplication::restoreOverrideCursor();
[107]68#endif
[99]69}
70
71/*!
[67]72 * \brief Returns the sorted optimal path, starting from City 1.
[110]73 * \param city A string that represents city elements in the path.
74 * \param separator A string that represents separators between cities in the path.
[67]75 * \return A string, containing sorted optimal path.
[110]76 *
77 *  The resulting path will be in the form \a city+\a separator+\a city+...+\a separator+\a city.
78 *  \c \%1 in \a city will be replaced by the city number.
[67]79 */
[110]80QString CTSPSolver::getSortedPath(const QString &city, const QString &separator) const
[13]81{
[67]82        if (!root || route.isEmpty() || (route.size() != nCities))
83                return QString();
[42]84
[67]85int i = 0; // We start from City 1
[110]86QStringList path;
87        path << city.arg(1);
[67]88        while ((i = route[i]) != 0) {
[110]89                path << city.arg(i + 1);
[67]90        }
91        // And finish in City 1, too
[110]92        path << city.arg(1);
[12]93
[110]94        return path.join(separator);
[12]95}
96
[67]97/*!
[107]98 * \brief Returns a total number of steps in the current solution.
99 * \return A total number of steps or \c 0 if no solution.
100 * \note This is not always the same as the number of cities.
101 */
102int CTSPSolver::getTotalSteps() const
103{
104        return total;
105}
106
107/*!
[99]108 * \brief Indicates whether or not the solution is definitely optimal.
109 * \return \c true if the solution is definitely optimal, otherwise \c false.
[67]110 *
[99]111 *  The solution may need some further iterations to determine whether or not it is optimal.
[67]112 *  In such cases this function returns \c false.
113 */
114bool CTSPSolver::isOptimal() const
[42]115{
[67]116        return !mayNotBeOptimal;
[42]117}
118
[65]119/*!
120 * \brief Solves the given task.
121 * \param numCities Number of cities in the task.
122 * \param task The matrix of city-to-city travel costs.
[66]123 * \return Pointer to the root of the solution tree.
[65]124 *
125 * \todo TODO: Comment the algorithm.
126 */
[99]127SStep *CTSPSolver::solve(int numCities, const TMatrix &task)
[42]128{
[107]129        if (numCities < 3)
[12]130                return NULL;
[99]131
132QMutexLocker locker(&mutex);
[42]133        cleanup();
[99]134        canceled = false;
135        locker.unlock();
136
[13]137        nCities = numCities;
[42]138
[74]139SStep *step = new SStep();
[13]140        step->matrix = task;
[99]141        // We need to distinguish the values forbidden by the user
142        // from the values forbidden by the algorithm.
143        // So we replace user's infinities by the maximum available double value.
144        normalize(step->matrix);
145#ifdef DEBUG
146        qDebug() << step->matrix;
147#endif // DEBUG
[60]148        step->price = align(step->matrix);
[13]149        root = step;
[12]150
[74]151SStep *left, *right;
[42]152int nRow, nCol;
[60]153bool firstStep = true;
[93]154double check = INFINITY;
[107]155        total = 0;
156        while (route.size() < nCities) {
[60]157                step->alts = findCandidate(step->matrix,nRow,nCol);
[99]158
[42]159                while (hasSubCycles(nRow,nCol)) {
[99]160#ifdef DEBUG
161                        qDebug() << "Forbidden: (" << nRow << ";" << nCol << ")";
162#endif // DEBUG
[42]163                        step->matrix[nRow][nCol] = INFINITY;
164                        step->price += align(step->matrix);
[60]165                        step->alts = findCandidate(step->matrix,nRow,nCol);
[42]166                }
[99]167
168#ifdef DEBUG
169                qDebug() /*<< step->matrix*/ << "Selected: (" << nRow << ";" << nCol << ")";
170                qDebug() << "Alternate:" << step->alts;
171                qDebug() << "Step price:" << step->price << endl;
172#endif // DEBUG
173
174                locker.relock();
175                if ((nRow == -1) || (nCol == -1) || canceled) {
[74]176                        cleanup();
[99]177                        return NULL;
[42]178                }
[99]179                locker.unlock();
[42]180
181                // Route with (nRow,nCol) path
[74]182                right = new SStep();
[90]183                right->pNode = step;
[42]184                right->matrix = step->matrix;
185                for (int k = 0; k < nCities; k++) {
186                        if (k != nCol)
187                                right->matrix[nRow][k] = INFINITY;
188                        if (k != nRow)
189                                right->matrix[k][nCol] = INFINITY;
190                }
191                right->price = step->price + align(right->matrix);
[99]192                // Forbid the selected route to exclude its reuse in next steps.
[42]193                right->matrix[nCol][nRow] = INFINITY;
194                right->matrix[nRow][nCol] = INFINITY;
195
196                // Route without (nRow,nCol) path
[74]197                left = new SStep();
[90]198                left->pNode = step;
[42]199                left->matrix = step->matrix;
200                left->matrix[nRow][nCol] = INFINITY;
[93]201                left->price = step->price + align(left->matrix);
[42]202
203                step->candidate.nRow = nRow;
204                step->candidate.nCol = nCol;
205                step->plNode = left;
206                step->prNode = right;
207
[99]208                // This matrix is not used anymore. Restoring infinities back.
209                denormalize(step->matrix);
210
[42]211                if (right->price <= left->price) {
212                        // Route with (nRow,nCol) path is cheaper
[107]213                        step->next = SStep::RightBranch;
[42]214                        step = right;
[107]215                        route[nRow] = nCol;
216                        emit routePartFound(route.size());
[60]217                        if (firstStep) {
218                                check = left->price;
219                                firstStep = false;
220                        }
[42]221                } else {
222                        // Route without (nRow,nCol) path is cheaper
[107]223                        step->next = SStep::LeftBranch;
[42]224                        step = left;
[107]225                        QCoreApplication::processEvents();
[60]226                        if (firstStep) {
227                                check = right->price;
228                                firstStep = false;
229                        }
[42]230                }
[107]231                total++;
[42]232        }
233
[99]234        mayNotBeOptimal = (check < step->price);
[42]235
236        return root;
[12]237}
[60]238
[99]239/*!
240 * \brief Indicates whether or not the solution process was canceled.
241 * \return \c true if the solution process was canceled, otherwise \c false.
242 */
243bool CTSPSolver::wasCanceled() const
244{
245QMutexLocker locker(&mutex);
246        return canceled;
247}
248
249/*!
250 * \brief Cancels the solution process.
251 */
252void CTSPSolver::cancel()
253{
254QMutexLocker locker(&mutex);
255        canceled = true;
256}
257
[74]258CTSPSolver::~CTSPSolver()
259{
260        if (root != NULL)
[90]261                deleteTree(root);
[74]262}
263
[67]264/* Privates **********************************************************/
265
[89]266double CTSPSolver::align(TMatrix &matrix)
[60]267{
[89]268double r = 0;
269double min;
[67]270        for (int k = 0; k < nCities; k++) {
271                min = findMinInRow(k,matrix);
272                if (min > 0) {
273                        r += min;
[99]274                        if (min < MAX_DOUBLE)
275                                subRow(matrix,k,min);
[67]276                }
[60]277        }
[67]278        for (int k = 0; k < nCities; k++) {
279                min = findMinInCol(k,matrix);
280                if (min > 0) {
281                        r += min;
[99]282                        if (min < MAX_DOUBLE)
283                                subCol(matrix,k,min);
[67]284                }
285        }
[107]286        return (r != MAX_DOUBLE) ? r : INFINITY;
[67]287}
[60]288
[99]289void CTSPSolver::deleteTree(SStep *&root, bool processEvents)
[67]290{
[90]291        if (root == NULL)
292                return;
293SStep *step = root;
294SStep *parent;
295        forever {
[99]296                if (processEvents)
[107]297                        QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
[90]298                if (step->plNode != NULL) {
299                        // We have left child node - going inside it
300                        step = step->plNode;
301                        step->pNode->plNode = NULL;
302                        continue;
303                } else if (step->prNode != NULL) {
304                        // We have right child node - going inside it
305                        step = step->prNode;
306                        step->pNode->prNode = NULL;
307                        continue;
308                } else {
[107]309                        // We have no child nodes. Deleting the current one.
[90]310                        parent = step->pNode;
311                        delete step;
312                        if (parent != NULL) {
313                                // Going back to the parent node.
314                                step = parent;
315                        } else {
316                                // We came back to the root node. Finishing.
317                                root = NULL;
318                                break;
319                        }
320                }
321        }
[74]322}
323
[99]324void CTSPSolver::denormalize(TMatrix &matrix) const
325{
326        for (int r = 0; r < nCities; r++)
327                for (int c = 0; c < nCities; c++)
328                        if ((r != c) && (matrix.at(r).at(c) == MAX_DOUBLE))
329                                matrix[r][c] = INFINITY;
330}
331
[107]332QList<SStep::SCandidate> CTSPSolver::findCandidate(const TMatrix &matrix, int &nRow, int &nCol) const
[74]333{
[67]334        nRow = -1;
335        nCol = -1;
[107]336QList<SStep::SCandidate> alts;
337SStep::SCandidate cand;
[89]338double h = -1;
339double sum;
[67]340        for (int r = 0; r < nCities; r++)
341                for (int c = 0; c < nCities; c++)
342                        if (matrix.at(r).at(c) == 0) {
343                                sum = findMinInRow(r,matrix,c) + findMinInCol(c,matrix,r);
344                                if (sum > h) {
345                                        h = sum;
346                                        nRow = r;
347                                        nCol = c;
[74]348                                        alts.clear();
349                                } else if ((sum == h) && !hasSubCycles(r,c)) {
350                                        cand.nRow = r;
351                                        cand.nCol = c;
352                                        alts.append(cand);
353                                }
[67]354                        }
355        return alts;
[63]356}
357
[89]358double CTSPSolver::findMinInCol(int nCol, const TMatrix &matrix, int exr) const
[60]359{
[89]360double min = INFINITY;
[67]361        for (int k = 0; k < nCities; k++)
362                if ((k != exr) && (min > matrix.at(k).at(nCol)))
363                        min = matrix.at(k).at(nCol);
[99]364        return (min == INFINITY) ? 0 : min;
[60]365}
[67]366
[89]367double CTSPSolver::findMinInRow(int nRow, const TMatrix &matrix, int exc) const
[67]368{
[89]369double min = INFINITY;
[99]370        for (int k = 0; k < nCities; k++) {
[67]371                if (((k != exc)) && (min > matrix.at(nRow).at(k)))
372                        min = matrix.at(nRow).at(k);
[99]373        }
374        return (min == INFINITY) ? 0 : min;
[67]375}
376
[71]377bool CTSPSolver::hasSubCycles(int nRow, int nCol) const
[67]378{
379        if ((nRow < 0) || (nCol < 0) || route.isEmpty() || !(route.size() < nCities - 1) || !route.contains(nCol))
380                return false;
381int i = nCol;
[107]382        forever {
383                if ((i = route.value(i)) == nRow)
[67]384                        return true;
385                if (!route.contains(i))
386                        return false;
387        }
388        return false;
389}
390
[99]391void CTSPSolver::normalize(TMatrix &matrix) const
392{
393        for (int r = 0; r < nCities; r++)
394                for (int c = 0; c < nCities; c++)
395                        if ((r != c) && (matrix.at(r).at(c) == INFINITY))
396                                matrix[r][c] = MAX_DOUBLE;
397}
398
[89]399void CTSPSolver::subCol(TMatrix &matrix, int nCol, double val)
[67]400{
401        for (int k = 0; k < nCities; k++)
402                if (k != nCol)
403                        matrix[k][nCol] -= val;
404}
405
[89]406void CTSPSolver::subRow(TMatrix &matrix, int nRow, double val)
[67]407{
408        for (int k = 0; k < nCities; k++)
409                if (k != nRow)
410                        matrix[nRow][k] -= val;
411}
[98]412
[107]413}
414
[99]415#ifdef DEBUG
[107]416QDebug operator<<(QDebug dbg, const TSPSolver::TMatrix &matrix)
[98]417{
418        for (int r = 0; r < matrix.count(); r++) {
419                for (int c = 0; c < matrix.at(r).count(); c++)
[99]420                        dbg.space() << QString::number(matrix.at(r).at(c)).leftJustified(5);
[98]421                dbg << endl;
422        }
423        return dbg;
424}
[99]425
[107]426QDebug operator<<(QDebug dbg, const TSPSolver::SStep::SCandidate &cand)
[99]427{
428        dbg.nospace() << "(" << cand.nRow << ";" << cand.nCol << ")";
429        return dbg;
430}
431#endif // DEBUG
Note: See TracBrowser for help on using the repository browser.