QList<SStep::SCandidate> CTSPSolver::findCandidate(const TMatrix &matrix, int &nRow, int &nCol) const { nRow = -1; nCol = -1; QList<SStep::SCandidate> alts; SStep::SCandidate cand; double h = -1; double sum; for (int r = 0; r < nCities; r++) for (int c = 0; c < nCities; c++) if (matrix.at(r).at(c) == 0) { sum = findMinInRow(r,matrix,c) + findMinInCol(c,matrix,r); if (sum > h) { h = sum; nRow = r; nCol = c; alts.clear(); } else if ((sum == h) && !hasSubCycles(r,c)) { cand.nRow = r; cand.nCol = c; alts.append(cand); } } return alts; }
// Using the cost matrix provided configure nRow and nCol to locate the canidate that maximises // the difference between the inclusion and exclusion branch. We return a vector of alternative // canidate paths std::vector<SStep::SCandidate> CTSPSolver::findCandidate(const TMatrix &matrix, int &nRow, int &nCol) const { nRow = -1; nCol = -1; std::vector<SStep::SCandidate> alts; double bestMax = -1.0; #pragma omp parallel num_threads(numThreads) { // Our best difference so far double h = -1; double sum; int localRow = 0, localCol = 0; SStep::SCandidate cand; // For each row and col check for canidate paths. Because we have performed aligns to the // cost matrix we are only concerned with 0 values #pragma omp for for (int r = 0; r < nCities; r++) { for (int c = 0; c < nCities; c++) { if (matrix.at(r).at(c) == 0) { // Find the cost change for the exclusion branch by selecting this nRow, nCol sum = findMinInRow(r, matrix, c) + findMinInCol(c, matrix, r); // If the difference is greater then we want to choose this path. if (sum > h) { h = sum; localRow = r; localCol = c; // Alternativly we are finding paths of equal difference and will store these. The // optimum soulution could use one of these paths rather than the one we have selected. } else if ((sum == h) && !hasSubCycles(r ,c)) { cand.nRow = r; cand.nCol = c; } } } } #pragma omp critical { if(bestMax < h) { nRow = localRow; nCol = localCol; bestMax = h; } } } return alts; }
/*! * \brief Solves the given task. * \param numCities Number of cities in the task. * \param task The matrix of city-to-city travel costs. * \return Pointer to the root of the solution tree. * * \todo TODO: Comment the algorithm. */ SStep *CTSPSolver::solve(int numCities, const TMatrix &task) { if (numCities < 3) return NULL; QMutexLocker locker(&mutex); cleanup(); canceled = false; locker.unlock(); nCities = numCities; SStep *step = new SStep(); step->matrix = task; // We need to distinguish the values forbidden by the user // from the values forbidden by the algorithm. // So we replace user's infinities by the maximum available double value. normalize(step->matrix); #ifdef DEBUG qDebug() << step->matrix; #endif // DEBUG step->price = align(step->matrix); root = step; SStep *left, *right; int nRow, nCol; bool firstStep = true; double check = INFINITY; total = 0; while (route.size() < nCities) { step->alts = findCandidate(step->matrix,nRow,nCol); while (hasSubCycles(nRow,nCol)) { #ifdef DEBUG qDebug() << "Forbidden: (" << nRow << ";" << nCol << ")"; #endif // DEBUG step->matrix[nRow][nCol] = INFINITY; step->price += align(step->matrix); step->alts = findCandidate(step->matrix,nRow,nCol); } #ifdef DEBUG qDebug() /*<< step->matrix*/ << "Selected: (" << nRow << ";" << nCol << ")"; qDebug() << "Alternate:" << step->alts; qDebug() << "Step price:" << step->price << endl; #endif // DEBUG locker.relock(); if ((nRow == -1) || (nCol == -1) || canceled) { if (canceled && cc) cleanup(); return NULL; } locker.unlock(); // Route with (nRow,nCol) path right = new SStep(); right->pNode = step; right->matrix = step->matrix; for (int k = 0; k < nCities; k++) { if (k != nCol) right->matrix[nRow][k] = INFINITY; if (k != nRow) right->matrix[k][nCol] = INFINITY; } right->price = step->price + align(right->matrix); // Forbid the selected route to exclude its reuse in next steps. right->matrix[nCol][nRow] = INFINITY; right->matrix[nRow][nCol] = INFINITY; // Route without (nRow,nCol) path left = new SStep(); left->pNode = step; left->matrix = step->matrix; left->matrix[nRow][nCol] = INFINITY; left->price = step->price + align(left->matrix); step->candidate.nRow = nRow; step->candidate.nCol = nCol; step->plNode = left; step->prNode = right; // This matrix is not used anymore. Restoring infinities back. denormalize(step->matrix); if (right->price <= left->price) { // Route with (nRow,nCol) path is cheaper step->next = SStep::RightBranch; step = right; route[nRow] = nCol; emit routePartFound(route.size()); if (firstStep) { check = left->price; firstStep = false; } } else { // Route without (nRow,nCol) path is cheaper step->next = SStep::LeftBranch; step = left; QCoreApplication::processEvents(); if (firstStep) { check = right->price; firstStep = false; } } total++; } mayNotBeOptimal = (check < step->price); return root; }
/*! * \brief Solves the given task. * \param numCities Number of cities in the task. * \param task The matrix of city-to-city travel costs. * \return Pointer to the root of the solution tree. * * \todo TODO: Comment the algorithm. */ SStep* CTSPSolver::solve(int numCities, const TMatrix &task) { if (numCities < 3) { return NULL; } nCities = numCities; SStep *step = new SStep(); // Initial node for the solution step->matrix = task; // Copy the initial cost matrix // Align the matrix and set the price of the first step to a lower bound for the // entire algorithm. step->price = align(step->matrix); root = step; SStep *left, *right; int nRow, nCol; bool firstStep = true; double check = INFINITY; total = 0; while (route.size() < nCities) { // For this step setup the alternative paths while also setting the nRow nCol for the next transition step->alts = findCandidate(step->matrix, nRow, nCol); // Continually align the matrix while the path has any subcycles. We also // eliminate the currently considered path as we can determine this is not within // the solution. For each align we need to update the currenly lower bound. while (hasSubCycles(nRow,nCol)) { step->matrix[nRow][nCol] = INFINITY; // Eliminate path from consideration step->price += align(step->matrix); // Update lower bound step->alts = findCandidate(step->matrix,nRow,nCol); // Get new best path } // A path could not be generated without subcycles; our algorithm has failed if ((nRow == -1) || (nCol == -1)) { return NULL; } // Create the inclusion transition. This will compute a new cost matrix with the // the selected path as being within the soulution. right = new SStep(); right->pNode = step; right->matrix = step->matrix; // Remove the to and from paths for the respective cities as we are selecting a path. // This effectivly reduces the matrix from a NxN => (N-1)x(N-1) for (int k = 0; k < nCities; k++) { if (k != nCol) { right->matrix[nRow][k] = INFINITY; } if (k != nRow) { right->matrix[k][nCol] = INFINITY; } } // By considering the path compute the new lower bound for the matrix right->price = step->price + align(right->matrix); // Remove the path to and symetrical path from from the cost matrix right->matrix[nCol][nRow] = INFINITY; right->matrix[nRow][nCol] = INFINITY; // Create the left child for the current step and invalidate nRow and nCol left = new SStep(); left->pNode = step; left->matrix = step->matrix; // Exclude this path from the solution left->matrix[nRow][nCol] = INFINITY; // Update the lower bound for this cost matrix with the path excluded left->price = step->price + align(left->matrix); // Book-keeping for the old GUI program step->candidate.nRow = nRow; step->candidate.nCol = nCol; step->plNode = left; step->prNode = right; // If the right price is cheaper or equal then we will include the path into the solution // and add this transition to the route. Then we will repeat the algorithm from the right child. if (right->price <= left->price) { step->next = SStep::RightBranch; step = right; route[nRow] = nCol; if (firstStep) { check = left->price; firstStep = false; } // The exclusion path is cheaper and we will repeat the algorithm using the left child. // IMPORTANT: We do not modify the route } else { step->next = SStep::LeftBranch; step = left; if (firstStep) { check = right->price; firstStep = false; } } total++; } // On out first transition the step price is an estimated lower bound for the entire algorithm. // If our final steps price is greater or equal then we cannot guarantee the solution is optimum. mayNotBeOptimal = (check < step->price); totalCost = step->price; return root; }