source: tspsg/src/tspsolver.cpp @ caef58b531

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

More code documentation.

  • Property mode set to 100644
File size: 6.9 KB
RevLine 
[67e53c96d7]1/*
[430bd7f7e9]2 *  TSPSG: TSP Solver and Generator
[5354a01311]3 *  Copyright (C) 2007-2009 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()
[430bd7f7e9]28        : nCities(0)
[e664262f7d]29{
30}
[67e53c96d7]31
[430bd7f7e9]32void CTSPSolver::cleanup()
33{
34        route.clear();
[9cf98b9bd6]35        mayNotBeOptimal = false;
[430bd7f7e9]36}
37
38double CTSPSolver::findMinInRow(int nRow, tMatrix matrix, int exc)
[e664262f7d]39{
[2bc8e278b7]40double min = INFINITY;
[e664262f7d]41        for (int k = 0; k < nCities; k++)
[c10297cf73]42                if (((k != exc)) && (min > matrix.at(nRow).at(k)))
43                        min = matrix.at(nRow).at(k);
[2bc8e278b7]44        return min == INFINITY ? 0 : min;
[e664262f7d]45}
[67e53c96d7]46
[430bd7f7e9]47double CTSPSolver::findMinInCol(int nCol, tMatrix matrix, int exr)
[67e53c96d7]48{
[2bc8e278b7]49double min = INFINITY;
[e664262f7d]50        for (int k = 0; k < nCities; k++)
[c10297cf73]51                if ((k != exr) && (min > matrix.at(k).at(nCol)))
52                        min = matrix.at(k).at(nCol);
[2bc8e278b7]53        return min == INFINITY ? 0 : min;
[67e53c96d7]54}
55
[430bd7f7e9]56void CTSPSolver::subRow(tMatrix &matrix, int nRow, double val)
57{
58        for (int k = 0; k < nCities; k++)
59                if (k != nRow)
60                        matrix[nRow][k] -= val;
61}
62
63void CTSPSolver::subCol(tMatrix &matrix, int nCol, double val)
64{
65        for (int k = 0; k < nCities; k++)
66                if (k != nCol)
67                        matrix[k][nCol] -= val;
68}
69
70double CTSPSolver::align(tMatrix &matrix)
71{
72double r = 0;
73double min;
74        for (int k = 0; k < nCities; k++) {
75                min = findMinInRow(k,matrix);
76                if (min > 0) {
77                        r += min;
78                        subRow(matrix,k,min);
79                }
80        }
81        for (int k = 0; k < nCities; k++) {
82                min = findMinInCol(k,matrix);
83                if (min > 0) {
84                        r += min;
85                        subCol(matrix,k,min);
86                }
87        }
88        return r;
89}
90
[9cf98b9bd6]91bool CTSPSolver::findCandidate(tMatrix matrix, int &nRow, int &nCol)
[430bd7f7e9]92{
93        nRow = -1;
94        nCol = -1;
95bool alts = false;
[9cf98b9bd6]96double h = -1;
[430bd7f7e9]97double sum;
98        for (int r = 0; r < nCities; r++)
99                for (int c = 0; c < nCities; c++)
[6dfdef0c3e]100//                      if ((matrix.at(r).at(c) == 0) && !forbidden.values(r).contains(c)) {
[c10297cf73]101                        if (matrix.at(r).at(c) == 0) {
[430bd7f7e9]102                                sum = findMinInRow(r,matrix,c) + findMinInCol(c,matrix,r);
103                                if (sum > h) {
104                                        h = sum;
105                                        nRow = r;
106                                        nCol = c;
107                                        alts = false;
[6dfdef0c3e]108                                } else if ((sum == h) && !hasSubCycles(r,c))
[430bd7f7e9]109                                        alts = true;
110                        }
111        return alts;
112}
113
114bool CTSPSolver::hasSubCycles(int nRow, int nCol)
115{
116        if ((nRow < 0) || (nCol < 0) || route.isEmpty() || !(route.size() < nCities - 1) || !route.contains(nCol))
117                return false;
118int i = nCol;
119        while (true) {
120                if ((i = route[i]) == nRow)
121                        return true;
122                if (!route.contains(i))
123                        return false;
124        }
125        return false;
126}
127
[caef58b531]128/*!
129 * \brief Solves the given task.
130 * \param numCities Number of cities in the task.
131 * \param task The matrix of city-to-city travel costs.
132 * \param parent The parent widget for displaying messages and dialogs.
133 *
134 * \todo TODO: Comment the algorithm.
135 */
[430bd7f7e9]136sStep *CTSPSolver::solve(int numCities, tMatrix task, QWidget *parent)
[67e53c96d7]137{
138        if (numCities <= 1)
139                return NULL;
[430bd7f7e9]140        cleanup();
[e664262f7d]141        nCities = numCities;
[430bd7f7e9]142QProgressDialog pd(parent);
143QProgressBar *pb = new QProgressBar(&pd);
144        pb->setAlignment(Qt::AlignCenter);
145        pb->setFormat(trUtf8("%v of %m parts found"));
146        pd.setBar(pb);
147        pd.setMaximum(nCities);
148        pd.setMinimumDuration(1000);
149        pd.setLabelText(trUtf8("Calculating optimal route..."));
150        pd.setWindowTitle(trUtf8("Solution Progress"));
151        pd.setWindowModality(Qt::ApplicationModal);
152        pd.setValue(0);
153
[e664262f7d]154sStep *step = new sStep();
155        step->matrix = task;
[9cf98b9bd6]156        step->price = align(step->matrix);
[e664262f7d]157        root = step;
[67e53c96d7]158
[430bd7f7e9]159sStep *left, *right;
160int nRow, nCol;
[9cf98b9bd6]161bool firstStep = true;
162double check;
163        while (this->route.size() < nCities) {
[aaf2113307]164//              forbidden.clear();
[9cf98b9bd6]165                step->alts = findCandidate(step->matrix,nRow,nCol);
[430bd7f7e9]166                while (hasSubCycles(nRow,nCol)) {
[aaf2113307]167//                      forbidden[nRow] = nCol;
[430bd7f7e9]168                        step->matrix[nRow][nCol] = INFINITY;
169                        step->price += align(step->matrix);
[9cf98b9bd6]170                        step->alts = findCandidate(step->matrix,nRow,nCol);
[430bd7f7e9]171                }
172                if ((nRow == -1) || (nCol == -1) || pd.wasCanceled()) {
173                        root = NULL;
174                        break;
175                }
176
177                // Route with (nRow,nCol) path
178                right = new sStep();
179                right->matrix = step->matrix;
180                for (int k = 0; k < nCities; k++) {
181                        if (k != nCol)
182                                right->matrix[nRow][k] = INFINITY;
183                        if (k != nRow)
184                                right->matrix[k][nCol] = INFINITY;
185                }
186                right->price = step->price + align(right->matrix);
187                // Forbid selected route to exclude its reuse in next steps.
188                right->matrix[nCol][nRow] = INFINITY;
189                right->matrix[nRow][nCol] = INFINITY;
190
191                // Route without (nRow,nCol) path
192                left = new sStep();
193                left->matrix = step->matrix;
194                left->matrix[nRow][nCol] = INFINITY;
195                left->price = step->price + align(left->matrix);
196
197                step->candidate.nRow = nRow;
198                step->candidate.nCol = nCol;
199                step->plNode = left;
200                step->prNode = right;
201
202                if (right->price <= left->price) {
203                        // Route with (nRow,nCol) path is cheaper
204                        step = right;
[9cf98b9bd6]205                        this->route[nRow] = nCol;
206                        pd.setValue(this->route.size());
207                        if (firstStep) {
208                                check = left->price;
209                                firstStep = false;
210                        }
[430bd7f7e9]211                } else {
212                        // Route without (nRow,nCol) path is cheaper
213                        step = left;
214                        qApp->processEvents();
[9cf98b9bd6]215                        if (firstStep) {
216                                check = right->price;
217                                firstStep = false;
218                        }
[430bd7f7e9]219                }
220        }
221
222        if (!root && !pd.wasCanceled()) {
[aaf2113307]223                pd.reset();
224                QMessageBox(QMessageBox::Warning,trUtf8("Solution Result"),trUtf8("Unable to find solution.\nMaybe, this task has no solutions."),QMessageBox::Ok,parent).exec();
[430bd7f7e9]225        }
226
[aaf2113307]227        qApp->processEvents();
228
[9cf98b9bd6]229        if (root) {
230                route = this->route;
231                mayNotBeOptimal = (check < step->price);
232        }
[430bd7f7e9]233        return root;
[67e53c96d7]234}
[9cf98b9bd6]235
[caef58b531]236/*!
237 * \brief Returns the sorted optimal path, starting from City 1.
238 */
[9cf98b9bd6]239QString CTSPSolver::getSortedPath() const
240{
241        if (!root || route.isEmpty() || (route.size() != nCities))
242                return QString();
243
244int i = 0; // We start from City 1
245QString path = trUtf8("City %1").arg(1) + " -> ";
246        while ((i = route[i]) != 0) {
247                path += trUtf8("City %1").arg(i + 1) + " -> ";
248        }
249        // And finish in City 1, too
250        path += trUtf8("City %1").arg(1);
251
252        return path;
253}
254
[caef58b531]255/*!
256 * \brief Returns CTSPSolver's version ID.
257 *
258 *  Current version ID is <b>\$Id$</b>.
259 */
[55c4b858e9]260QString CTSPSolver::getVersionId()
261{
262        return QString("$Id$");
263}
264
[caef58b531]265/*!
266 * \brief Returns whether or not the solution is definitely optimal.
267 *
268 *  The solution may need some further interations to determine whether it is optimal.
269 *  In such cases this function returns \p false.
270 */
[9cf98b9bd6]271bool CTSPSolver::isOptimal() const
272{
273        return !mayNotBeOptimal;
274}
Note: See TracBrowser for help on using the repository browser.