void LKH::LKHAlg::Connect(Node * N1, int Max, int Sparse) { Node *N; Candidate *NN1; int d; N1->Next = 0; N1->NextCost = INT_MAX; if (!Sparse || N1->CandidateSet == 0 || N1->CandidateSet[0].To == 0 || N1->CandidateSet[1].To == 0) { /* Find the requested edge in a dense graph */ N = FirstNode; do { if (N == N1 || N == N1->Dad || N1 == N->Dad) continue; if (FixedOrCommon(N1, N)) { N1->NextCost = (this->*D)(N1, N); N1->Next = N; return; } if (!N1->FixedTo2 && !N->FixedTo2 && !Forbidden(N1, N) && (!c || (this->*c)(N1, N) < N1->NextCost) && (d = (this->*D)(N1, N)) < N1->NextCost) { N1->NextCost = d; if (d <= Max) return; N1->Next = N; } } while ((N = N->Suc) != FirstNode); } else { /* Find the requested edge in a sparse graph */ for (NN1 = N1->CandidateSet; (N = NN1->To); NN1++) { if (N == N1->Dad || N1 == N->Dad) continue; if (FixedOrCommon(N1, N)) { N1->NextCost = NN1->Cost + N1->Pi + N->Pi; N1->Next = N; return; } if (!N1->FixedTo2 && !N->FixedTo2 && (d = NN1->Cost + N1->Pi + N->Pi) < N1->NextCost) { N1->NextCost = d; if (d <= Max) return; N1->Next = N; } } } }
int IsPossibleCandidate(Node * From, Node * To) { Node *Na, *Nb, *Nc, *N; if (InInitialTour(From, To) || From->SubproblemSuc == To || To->SubproblemSuc == From || FixedOrCommon(From, To)) return 1; if (From->FixedTo2 || To->FixedTo2) return 0; if (MergeTourFiles < 2) return 1; if (!From->Head) { Nb = FirstNode; do { Na = Nb; Nb = Na->MergeSuc[0]; } while (Nb != FirstNode && FixedOrCommon(Na, Nb)); if (Nb != FirstNode) { N = Nb; do { Nc = Nb; do { Na = Nb; Na->Head = Nc; Nb = Na->MergeSuc[0]; } while (FixedOrCommon(Na, Nb)); do Nc->Tail = Na; while ((Nc = Nc->MergeSuc[0]) != Nb); } while (Nb != N); } else { do Nb->Head = Nb->Tail = FirstNode; while ((Nb = Nb->Suc) != FirstNode); } } if (From->Head == To->Head || (From->Head != From && From->Tail != From) || (To->Head != To && To->Tail != To)) return 0; return 1; }
static int FixedOrCommonCandidates(Node * N) { int Count = 0; Candidate *NN; if (N->FixedTo2) return 2; if (!N->FixedTo1 && MergeTourFiles == 0) return 0; for (NN = N->CandidateSet; NN->To; NN++) if (FixedOrCommon(N, NN->To)) Count++; return Count; }
int FixedOrCommonCandidates(Node * N) { int Count = 0; Candidate *NN; if (N->FixedTo2) return 2; if (!N->FixedTo1 && MergeTourFiles < 2) return 0; for (NN = N->CandidateSet; NN && NN->To; NN++) if (FixedOrCommon(N, NN->To)) Count++; if (Count > 2) eprintf("Node %d has more than two required candidate edges", N->Id); return Count; }
GainType LKH::LKHAlg::PatchCycles(int k, GainType Gain) { Node *s1, *s2, *sStart, *sStop; GainType NewGain; int M, i; if(!CurrentCycle.get()) { CurrentCycle.reset(new int(0)); Patchwork.reset(new int(0)); RecLevel.reset(new int(0)); } FindPermutation(k,this); M = Cycles(k); if (M == 1 && Gain > 0) { MakeKOptMove(k); return Gain; } if (M == 1 || M > PatchingC || k + M > NonsequentialMoveType) return 0; if (*RecLevel == 0) *Patchwork = 0; *CurrentCycle = ShortestCycle(M, k,this); for (i = 0; i < k; i++) { if ((*cycle.get())[(*p.get())[2 * i]] != *CurrentCycle) continue; sStart = (*t.get())[(*p.get())[2 * i]]; sStop = (*t.get())[(*p.get())[2 * i + 1]]; for (s1 = sStart; s1 != sStop; s1 = s2) { s2 = SUC(s1); if (FixedOrCommon(s1, s2)) continue; if (++(*Patchwork) > Dimension) return 0; (*t.get())[2 * k + 1] = s1; (*t.get())[2 * k + 2] = s2; MarkDeleted(s1, s2); /* Find a set of gainful alternating cycles */ NewGain = PatchCyclesRec(k, 2, M, Gain + (this->*C)(s1, s2),this); UnmarkDeleted(s1, s2); if (NewGain > 0) return NewGain; } } return 0; }
GainType SFCTour(int CurveType) { double XMin, XMax, YMin, YMax; Node *N, **Perm; int i; IndexFunction Index; GainType Cost; double EntryTime = GetTime(); if (CurveType == SIERPINSKI) { if (TraceLevel >= 1) printff("Sierpinski = "); Index = SierpinskiIndex; } else { if (TraceLevel >= 1) printff("Moore = "); Index = MooreIndex; } N = FirstNode; XMin = XMax = N->X; YMin = YMax = N->Y; N->V = 0; while ((N = N->Suc) != FirstNode) { if (N->X < XMin) XMin = N->X; else if (N->X > XMax) XMax = N->X; if (N->Y < YMin) YMin = N->Y; else if (N->Y > YMax) YMax = N->Y; } if (XMax == XMin) XMax = XMin + 1; if (YMax == YMin) YMax = YMin + 1; assert(Perm = (Node **) malloc(Dimension * sizeof(Node *))); for (i = 0, N = FirstNode; i < Dimension; i++, N = N->Suc) (Perm[i] = N)->V = Index((N->X - XMin) / (XMax - XMin), (N->Y - YMin) / (YMax - YMin)); qsort(Perm, Dimension, sizeof(Node *), compare); for (i = 1; i < Dimension; i++) Follow(Perm[i], Perm[i - 1]); free(Perm); /* Assure that all fixed or common edges belong to the tour */ N = FirstNode; do { N->LastV = 1; if (!FixedOrCommon(N, N->Suc) && N->CandidateSet) { Candidate *NN; for (NN = N->CandidateSet; NN->To; NN++) { if (!NN->To->LastV && FixedOrCommon(N, NN->To)) { Follow(NN->To, N); break; } } } } while ((N = N->Suc) != FirstNode); Cost = 0; N = FirstNode; do if (!Fixed(N, N->Suc)) Cost += Distance(N, N->Suc); while ((N = N->Suc) != FirstNode); if (TraceLevel >= 1) { printff(GainFormat, Cost); if (Optimum != MINUS_INFINITY && Optimum != 0) printff(", Gap = %0.1f%%", 100.0 * (Cost - Optimum) / Optimum); printff(", Time = %0.2f sec.\n", fabs(GetTime() - EntryTime)); } return Cost; }
void CreateQuadrantCandidateSet(int K) { Node *From, *To; Candidate *NFrom; int L, Q, CandPerQ, Added, Count, i; if (K <= 0) return; if (TraceLevel >= 2) printff("Creating quadrant candidate set ... "); KDTree = BuildKDTree(1); assert(XMin = (double *) malloc((1 + DimensionSaved) * sizeof(double))); assert(XMax = (double *) malloc((1 + DimensionSaved) * sizeof(double))); assert(YMin = (double *) malloc((1 + DimensionSaved) * sizeof(double))); assert(YMax = (double *) malloc((1 + DimensionSaved) * sizeof(double))); if (CoordType == THREED_COORDS) { assert(ZMin = (double *) malloc((1 + DimensionSaved) * sizeof(double))); assert(ZMax = (double *) malloc((1 + DimensionSaved) * sizeof(double))); } ComputeBounds(0, Dimension - 1); Contains = CoordType == THREED_COORDS ? Contains3D : Contains2D; BoxOverlaps = CoordType == THREED_COORDS ? BoxOverlaps3D : BoxOverlaps2D; L = CoordType == THREED_COORDS ? 8 : 4; CandPerQ = K / L; assert(CandidateSet = (Candidate *) malloc((K + 1) * sizeof(Candidate))); From = FirstNode; do { Count = 0; for (NFrom = From->CandidateSet; NFrom && NFrom->To; NFrom++) if (FixedOrCommon(From, NFrom->To) && ++Count == 2) break; if (Count == 2) continue; Added = 0; for (Q = 1; Q <= L; Q++) { NearestQuadrantNeighbors(From, Q, CandPerQ); for (i = 0; i < Candidates; i++) { To = CandidateSet[i].To; if (AddCandidate(From, To, D(From, To), 1)) Added++; } } if (K > Added) { NearestQuadrantNeighbors(From, 0, K - Added); for (i = 0; i < Candidates; i++) { To = CandidateSet[i].To; AddCandidate(From, To, D(From, To), 2); } } } while ((From = From->Suc) != FirstNode); free(CandidateSet); free(KDTree); free(XMin); free(XMax); free(YMin); free(YMax); if (CoordType == THREED_COORDS) { free(ZMin); free(ZMax); } if (Level == 0 && (WeightType == GEO || WeightType == GEOM || WeightType == GEO_MEEUS || WeightType == GEOM_MEEUS)) { Candidate **SavedCandidateSet; assert(SavedCandidateSet = (Candidate **) malloc((1 + DimensionSaved) * sizeof(Candidate *))); if (TraceLevel >= 2) printff("done\n"); From = FirstNode; while ((From = From->Suc) != FirstNode) if ((From->Y > 0) != (FirstNode->Y > 0)) break; if (From != FirstNode) { /* Transform longitude (180 and -180 map to 0) */ From = FirstNode; do { SavedCandidateSet[From->Id] = From->CandidateSet; From->CandidateSet = 0; From->Zc = From->Y; if (WeightType == GEO || WeightType == GEO_MEEUS) From->Y = (int) From->Y + 5.0 * (From->Y - (int) From->Y) / 3.0; From->Y += From->Y > 0 ? -180 : 180; if (WeightType == GEO || WeightType == GEO_MEEUS) From->Y = (int) From->Y + 3.0 * (From->Y - (int) From->Y) / 5.0; } while ((From = From->Suc) != FirstNode); Level++; CreateQuadrantCandidateSet(K); Level--; From = FirstNode; do From->Y = From->Zc; while ((From = From->Suc) != FirstNode); do { Candidate *QCandidateSet = From->CandidateSet; From->CandidateSet = SavedCandidateSet[From->Id]; for (NFrom = QCandidateSet; (To = NFrom->To); NFrom++) AddCandidate(From, To, NFrom->Cost, NFrom->Alpha); free(QCandidateSet); } while ((From = From->Suc) != FirstNode); free(SavedCandidateSet); } } if (Level == 0) { ResetCandidateSet(); AddTourCandidates(); if (CandidateSetSymmetric) SymmetrizeCandidateSet(); if (TraceLevel >= 2) printff("done\n"); } }
void LKH::LKHAlg::ChooseInitialTour() { Node *N, *NextN, *FirstAlternative, *Last; Candidate *NN; int Alternatives, Count = 0, i; if (KickType > 0 && Kicks > 0 && Trial > 1) { for (Last = FirstNode; (N = Last->BestSuc) != FirstNode; Last = N) Follow(N, Last); for (i = 1; i <= Kicks; i++) KSwapKick(KickType); return; } if (Trial == 1 && (!FirstNode->InitialSuc || InitialTourFraction < 1)) { if (InitialTourAlgorithm == BORUVKA || InitialTourAlgorithm == GREEDY || InitialTourAlgorithm == MOORE || InitialTourAlgorithm == NEAREST_NEIGHBOR || InitialTourAlgorithm == QUICK_BORUVKA || InitialTourAlgorithm == SIERPINSKI) { GainType Cost = InitialTourAlgorithm == MOORE || InitialTourAlgorithm == SIERPINSKI ? SFCTour(InitialTourAlgorithm) : GreedyTour(); if (MaxTrials == 0) { BetterCost = Cost; RecordBetterTour(); } if (!FirstNode->InitialSuc) return; } } Start: /* Mark all nodes as "not chosen" by setting their V field to zero */ N = FirstNode; do N->V = 0; while ((N = N->Suc) != FirstNode); /* Choose FirstNode without two incident fixed or common candidate edges */ do { if (FixedOrCommonCandidates(N,this) < 2) break; } while ((N = N->Suc) != FirstNode); FirstNode = N; /* Move nodes with two incident fixed or common candidate edges in front of FirstNode */ for (Last = FirstNode->Pred; N != Last; N = NextN) { NextN = N->Suc; if (FixedOrCommonCandidates(N,this) == 2) Follow(N, Last); } /* Mark FirstNode as chosen */ FirstNode->V = 1; N = FirstNode; /* Loop as long as not all nodes have been chosen */ while (N->Suc != FirstNode) { FirstAlternative = 0; Alternatives = 0; Count++; /* Case A */ for (NN = N->CandidateSet; (NextN = NN->To); NN++) { if (!NextN->V && FixedOrCommon(N, NextN)) { Alternatives++; NextN->Next = FirstAlternative; FirstAlternative = NextN; } } if (Alternatives == 0 && FirstNode->InitialSuc && Trial == 1 && Count <= InitialTourFraction * Dimension) { /* Case B */ for (NN = N->CandidateSet; (NextN = NN->To); NN++) { if (!NextN->V && InInitialTour(N, NextN)) { Alternatives++; NextN->Next = FirstAlternative; FirstAlternative = NextN; } } } if (Alternatives == 0 && Trial > 1 && ProblemType != HCP && ProblemType != HPP) { /* Case C */ for (NN = N->CandidateSet; (NextN = NN->To); NN++) { if (!NextN->V && FixedOrCommonCandidates(NextN,this) < 2 && NN->Alpha == 0 && (InBestTour(N, NextN) || InNextBestTour(N, NextN))) { Alternatives++; NextN->Next = FirstAlternative; FirstAlternative = NextN; } } } if (Alternatives == 0) { /* Case D */ for (NN = N->CandidateSet; (NextN = NN->To); NN++) { if (!NextN->V && FixedOrCommonCandidates(NextN,this) < 2) { Alternatives++; NextN->Next = FirstAlternative; FirstAlternative = NextN; } } } if (Alternatives == 0) { /* Case E (actually not really a random choice) */ NextN = N->Suc; while ((FixedOrCommonCandidates(NextN,this) == 2 || Forbidden(N, NextN)) && NextN->Suc != FirstNode) NextN = NextN->Suc; if (FixedOrCommonCandidates(NextN,this) == 2 || Forbidden(N, NextN)) { FirstNode = FirstNode->Suc; goto Start; } } else { NextN = FirstAlternative; if (Alternatives > 1) { /* Select NextN at random among the alternatives */ i = Random() % Alternatives; while (i--) NextN = NextN->Next; } } /* Include NextN as the successor of N */ Follow(NextN, N); N = NextN; N->V = 1; } if (Forbidden(N, N->Suc)) { FirstNode = FirstNode->Suc; goto Start; } if (MaxTrials == 0) { GainType Cost = 0; N = FirstNode; do Cost += (this->*C)(N, N->Suc) - N->Pi - N->Suc->Pi; while ((N = N->Suc) != FirstNode); Cost /= Precision; if (Cost < BetterCost) { BetterCost = Cost; RecordBetterTour(); } } }
static int DelaunayClustering(int MaxClusterSize) { int Count = 0, Count1, Count2 = 0, i, j = 0; Edge *EdgeSet, Key; Node *From, *To, *N, *F, *T; point *u, *v; edge *e_start, *e; delaunay(Dimension); for (i = 0; i < Dimension; i++) { u = &p_array[i]; e_start = e = u->entry_pt; do { v = Other_point(e, u); if (u->id < v->id) Count++; } while ((e = Next(e, u)) != e_start); } assert(EdgeSet = (Edge *) malloc(Count * sizeof(Edge))); for (i = 0; i < Dimension; i++) { u = &p_array[i]; e_start = e = u->entry_pt; do { v = Other_point(e, u); if (u->id < v->id) { EdgeSet[j].From = From = &NodeSet[u->id]; EdgeSet[j].To = To = &NodeSet[v->id]; EdgeSet[j++].Cost = FixedOrCommon(From, To) ? INT_MIN : Distance(From, To) * Precision + From->Pi + To->Pi; } } while ((e = Next(e, u)) != e_start); } free_memory(); if (WeightType == GEO || WeightType == GEOM || WeightType == GEO_MEEUS || WeightType == GEOM_MEEUS) { N = FirstNode; while ((N = N->Suc) != FirstNode) if ((N->Y > 0) != (FirstNode->Y > 0)) break; if (N != FirstNode) { N = FirstNode; do N->Zc = N->Y; while ((N = N->Suc) != FirstNode); /* Transform longitude (180 and -180 map to 0) */ From = FirstNode; do { From->Zc = From->Y; if (WeightType == GEO || WeightType == GEO_MEEUS) From->Y = (int) From->Y + 5.0 * (From->Y - (int) From->Y) / 3.0; From->Y += From->Y > 0 ? -180 : 180; if (WeightType == GEO || WeightType == GEO_MEEUS) From->Y = (int) From->Y + 3.0 * (From->Y - (int) From->Y) / 5.0; } while ((From = From->Suc) != FirstNode); delaunay(Dimension); do From->Y = From->Zc; while ((From = From->Suc) != FirstNode); qsort(EdgeSet, Count, sizeof(Edge), compareFromTo); for (i = 0; i < Dimension; i++) { u = &p_array[i]; e_start = e = u->entry_pt; do { v = Other_point(e, u); if (u->id < v->id) Count2++; } while ((e = Next(e, u)) != e_start); } Count1 = Count; assert(EdgeSet = (Edge *) realloc(EdgeSet, (Count1 + Count2) * sizeof(Edge))); for (i = 0; i < Dimension; i++) { u = &p_array[i]; e_start = e = u->entry_pt; do { v = Other_point(e, u); if (u->id > v->id) continue; Key.From = From = &NodeSet[u->id]; Key.To = To = &NodeSet[v->id]; if (!bsearch (&Key, EdgeSet, Count1, sizeof(Edge), compareFromTo)) { EdgeSet[Count].From = From; EdgeSet[Count].To = To; EdgeSet[Count].Cost = FixedOrCommon(From, To) ? INT_MIN : Distance(From, To) * Precision + From->Pi + To->Pi; Count++; } } while ((e = Next(e, u)) != e_start); } free_memory(); } } qsort(EdgeSet, Count, sizeof(Edge), compareCost); /* Union-Find with path compression */ N = FirstNode; do { N->Next = N; N->Size = 1; } while ((N = N->Suc) != FirstNode); for (i = 0; i < Count; i++) { for (F = EdgeSet[i].From; F != F->Next;) F = F->Next = F->Next->Next; for (T = EdgeSet[i].To; T != T->Next;) T = T->Next = T->Next->Next; if (F != T && F->Size + T->Size <= MaxClusterSize) { if (F->Size < T->Size) { F->Next = T; T->Size += F->Size; } else { T->Next = F; F->Size += T->Size; } } } free(EdgeSet); Count = 0; N = FirstNode; do { if (N->Next == N && N->Size > 3) N->Subproblem = ++Count; } while ((N = N->Suc) != FirstNode); do { for (T = N; T != T->Next;) T = T->Next = T->Next->Next; N->Subproblem = T->Subproblem; } while ((N = N->Suc) != FirstNode); return Count; }
Node *Best2OptMove(Node * t1, Node * t2, GainType * G0, GainType * Gain) { Node *t3, *t4, *T3 = 0, *T4 = 0; Candidate *Nt2; GainType G1, G2, BestG2 = MINUS_INFINITY; int Breadth2 = 0; if (SUC(t1) != t2) Reversed ^= 1; /* * Determine (T3,T4) = (t3,t4) * such that * * G4 = *G0 - C(t2,T3) + C(T3,T4) * * is maximum (= BestG2), and (T3,T4) has not previously been included. * If during this process a legal move with *Gain > 0 is found, then make * the move and exit Best2OptMove immediately */ /* Choose (t2,t3) as a candidate edge emanating from t2 */ for (Nt2 = t2->CandidateSet; (t3 = Nt2->To); Nt2++) { if (t3 == t2->Pred || t3 == t2->Suc || ((G1 = *G0 - Nt2->Cost) <= 0 && GainCriterionUsed && ProblemType != HCP && ProblemType != HPP)) continue; /* Choose t4 (only one choice gives a closed tour) */ t4 = PRED(t3); if (FixedOrCommon(t3, t4)) continue; G2 = G1 + C(t3, t4); if (!Forbidden(t4, t1) && (!c || G2 - c(t4, t1) > 0) && (*Gain = G2 - C(t4, t1)) > 0) { Swap1(t1, t2, t3); *G0 = G2; return 0; } if (++Breadth2 > MaxBreadth) break; if (GainCriterionUsed && G2 - Precision < t4->Cost) continue; if (!Backtracking || Swaps > 0) { if (G2 > BestG2 && Swaps < MaxSwaps && Excludable(t3, t4) && (!InInputTour(t3, t4) || !Near(t3, t4))) { T3 = t3; T4 = t4; BestG2 = G2; } } else if (MaxSwaps > 0) { GainType G = G2; Node *t = t4; Make2OptMove(t1, t2, t3, t4); Exclude(t1, t2); Exclude(t3, t4); while ((t = BestSubsequentMove(t1, t, &G, Gain))); if (*Gain > 0) return 0; RestoreTour(); if (t2 != SUC(t1)) Reversed ^= 1; } } *Gain = 0; if (T4) { /* Make the best 2-opt move */ Swap1(t1, t2, T3); Exclude(t1, t2); Exclude(T3, T4); *G0 = BestG2; } return T4; }
GainType LinKernighan() { Node *t1, *t2, *SUCt1; GainType Gain, G0, Cost; int X2, i, it = 0; Candidate *Nt1; Segment *S; SSegment *SS; double EntryTime = GetTime(); Reversed = 0; S = FirstSegment; i = 0; do { S->Size = 0; S->Rank = ++i; S->Reversed = 0; S->First = S->Last = 0; } while ((S = S->Suc) != FirstSegment); SS = FirstSSegment; i = 0; do { SS->Size = 0; SS->Rank = ++i; SS->Reversed = 0; SS->First = SS->Last = 0; } while ((SS = SS->Suc) != FirstSSegment); FirstActive = LastActive = 0; Swaps = 0; /* Compute the cost of the initial tour, Cost. Compute the corresponding hash value, Hash. Initialize the segment list. Make all nodes "active" (so that they can be used as t1). */ Cost = 0; Hash = 0; i = 0; t1 = FirstNode; do { t2 = t1->OldSuc = t1->Suc; t1->OldPred = t1->Pred; t1->Rank = ++i; Cost += (t1->SucCost = t2->PredCost = C(t1, t2)) - t1->Pi - t2->Pi; Hash ^= Rand[t1->Id] * Rand[t2->Id]; t1->Cost = INT_MAX; for (Nt1 = t1->CandidateSet; (t2 = Nt1->To); Nt1++) if (t2 != t1->Pred && t2 != t1->Suc && Nt1->Cost < t1->Cost) t1->Cost = Nt1->Cost; t1->Parent = S; S->Size++; if (S->Size == 1) S->First = t1; S->Last = t1; if (SS->Size == 0) SS->First = S; S->Parent = SS; SS->Last = S; if (S->Size == GroupSize) { S = S->Suc; SS->Size++; if (SS->Size == SGroupSize) SS = SS->Suc; } t1->OldPredExcluded = t1->OldSucExcluded = 0; t1->Next = 0; if (KickType == 0 || Kicks == 0 || !InBestTour(t1, t1->Pred) || !InBestTour(t1, t1->Suc)) Activate(t1); } while ((t1 = t1->Suc) != FirstNode); if (S->Size < GroupSize) SS->Size++; Cost /= Precision; if (TraceLevel >= 3 || (TraceLevel == 2 && Cost < BetterCost)) { printff("Cost = " GainFormat, Cost); if (Optimum != MINUS_INFINITY && Optimum != 0) printff(", Gap = %0.4f%%", 100.0 * (Cost - Optimum) / Optimum); printff(", Time = %0.1f sec. %s\n", fabs(GetTime() - EntryTime), Cost < Optimum ? "<" : Cost == Optimum ? "=" : ""); } PredSucCostAvailable = 1; /* Loop as long as improvements are found */ do { /* Choose t1 as the first "active" node */ while ((t1 = RemoveFirstActive())) { /* t1 is now "passive" */ SUCt1 = SUC(t1); if ((TraceLevel >= 3 || (TraceLevel == 2 && Trial == 1)) && ++it % (Dimension >= 100000 ? 10000 : Dimension >= 10000 ? 1000 : 100) == 0) printff("#%d: Time = %0.1f sec.\n", it, fabs(GetTime() - EntryTime)); /* Choose t2 as one of t1's two neighbors on the tour */ for (X2 = 1; X2 <= 2; X2++) { t2 = X2 == 1 ? PRED(t1) : SUCt1; if (FixedOrCommon(t1, t2) || (RestrictedSearch && Near(t1, t2) && (Trial == 1 || (Trial > BackboneTrials && (KickType == 0 || Kicks == 0))))) continue; G0 = C(t1, t2); /* Try to find a tour-improving chain of moves */ do t2 = Swaps == 0 ? BestMove(t1, t2, &G0, &Gain) : BestSubsequentMove(t1, t2, &G0, &Gain); while (t2); if (Gain > 0) { /* An improvement has been found */ assert(Gain % Precision == 0); Cost -= Gain / Precision; if (TraceLevel >= 3 || (TraceLevel == 2 && Cost < BetterCost)) { printff("Cost = " GainFormat, Cost); if (Optimum != MINUS_INFINITY && Optimum != 0) printff(", Gap = %0.4f%%", 100.0 * (Cost - Optimum) / Optimum); printff(", Time = %0.1f sec. %s\n", fabs(GetTime() - EntryTime), Cost < Optimum ? "<" : Cost == Optimum ? "=" : ""); } StoreTour(); if (HashSearch(HTable, Hash, Cost)) goto End_LinKernighan; /* Make t1 "active" again */ Activate(t1); break; } RestoreTour(); } } if (HashSearch(HTable, Hash, Cost)) goto End_LinKernighan; HashInsert(HTable, Hash, Cost); /* Try to find improvements using non-sequential 4/5-opt moves */ Gain = 0; if (Gain23Used && (Gain = Gain23()) > 0) { /* An improvement has been found */ assert(Gain % Precision == 0); Cost -= Gain / Precision; StoreTour(); if (TraceLevel >= 3 || (TraceLevel == 2 && Cost < BetterCost)) { printff("Cost = " GainFormat, Cost); if (Optimum != MINUS_INFINITY && Optimum != 0) printff(", Gap = %0.4f%%", 100.0 * (Cost - Optimum) / Optimum); printff(", Time = %0.1f sec. + %s\n", fabs(GetTime() - EntryTime), Cost < Optimum ? "<" : Cost == Optimum ? "=" : ""); } if (HashSearch(HTable, Hash, Cost)) goto End_LinKernighan; } } while (Gain > 0); End_LinKernighan: PredSucCostAvailable = 0; NormalizeNodeList(); NormalizeSegmentList(); return Cost; }
Node *Best3OptMove(Node * t1, Node * t2, GainType * G0, GainType * Gain) { Node *t3, *t4, *t5, *t6, *T3 = 0, *T4 = 0, *T5 = 0, *T6 = 0; Candidate *Nt2, *Nt4; GainType G1, G2, G3, G4, BestG4 = MINUS_INFINITY; int Case6, BestCase6 = 0, X4, X6; int Breadth2 = 0, Breadth4; if (SUC(t1) != t2) Reversed ^= 1; /* * Determine (T3,T4,T5,T6) = (t3,t4,t5,t6) * such that * * G4 = *G0 - C(t2,T3) + C(T3,T4) * - C(T4,T5) + C(T5,T6) * * is maximum (= BestG4), and (T5,T6) has not previously been included. * If during this process a legal move with *Gain > 0 is found, then make * the move and exit Best3OptMove immediately. */ /* Choose (t2,t3) as a candidate edge emanating from t2 */ for (Nt2 = t2->CandidateSet; (t3 = Nt2->To); Nt2++) { if (t3 == t2->Pred || t3 == t2->Suc || ((G1 = *G0 - Nt2->Cost) <= 0 && GainCriterionUsed && ProblemType != HCP && ProblemType != HPP)) continue; if (++Breadth2 > MaxBreadth) break; /* Choose t4 as one of t3's two neighbors on the tour */ for (X4 = 1; X4 <= 2; X4++) { t4 = X4 == 1 ? PRED(t3) : SUC(t3); if (FixedOrCommon(t3, t4)) continue; G2 = G1 + C(t3, t4); if (X4 == 1 && !Forbidden(t4, t1) && (!c || G2 - c(t4, t1) > 0) && (*Gain = G2 - C(t4, t1)) > 0) { Swap1(t1, t2, t3); return 0; } if (Backtracking && !Excludable(t3, t4)) continue; Breadth4 = 0; /* Choose (t4,t5) as a candidate edge emanating from t4 */ for (Nt4 = t4->CandidateSet; (t5 = Nt4->To); Nt4++) { if (t5 == t4->Pred || t5 == t4->Suc || ((G3 = G2 - Nt4->Cost) <= 0 && GainCriterionUsed) || (X4 == 2 && !BETWEEN(t2, t5, t3))) continue; if (++Breadth4 > MaxBreadth) break; /* Choose t6 as one of t5's two neighbors on the tour */ for (X6 = 1; X6 <= X4; X6++) { if (X4 == 1) { Case6 = 1 + !BETWEEN(t2, t5, t4); t6 = Case6 == 1 ? SUC(t5) : PRED(t5); } else { Case6 = 4 + X6; t6 = X6 == 1 ? SUC(t5) : PRED(t5); if (t6 == t1) continue; } if (FixedOrCommon(t5, t6)) continue; G4 = G3 + C(t5, t6); if (!Forbidden(t6, t1) && (!c || G4 - c(t6, t1) > 0) && (*Gain = G4 - C(t6, t1)) > 0) { Make3OptMove(t1, t2, t3, t4, t5, t6, Case6); return 0; } if (GainCriterionUsed && G4 - Precision < t6->Cost) continue; if (!Backtracking || Swaps > 0) { if (G4 > BestG4 && Swaps < MaxSwaps && Excludable(t5, t6) && (!InInputTour(t5, t6) || !Near(t5, t6))) { /* Ignore the move if the gain does not vary */ if (RestrictedSearch && ProblemType != HCP && ProblemType != HPP && G2 - t4->Pi == G4 - t6->Pi && G3 + t5->Pi == G1 + t3->Pi) continue; T3 = t3; T4 = t4; T5 = t5; T6 = t6; BestCase6 = Case6; BestG4 = G4; } } else if (MaxSwaps > 0) { GainType G = G4; Node *t = t6; Make3OptMove(t1, t2, t3, t4, t5, t6, Case6); Exclude(t1, t2); Exclude(t3, t4); Exclude(t5, t6); while ((t = BestSubsequentMove(t1, t, &G, Gain))); if (*Gain > 0) return 0; RestoreTour(); if (t2 != SUC(t1)) Reversed ^= 1; } } } } } *Gain = 0; if (T6) { /* Make the best 3-opt move */ Make3OptMove(t1, t2, T3, T4, T5, T6, BestCase6); Exclude(t1, t2); Exclude(T3, T4); Exclude(T5, T6); *G0 = BestG4; } return T6; }
LKH::LKHAlg::Node * LKH::LKHAlg::Best5OptMove(Node * t1, Node * t2, GainType * G0, GainType * Gain) { Node *t3, *t4, *t5, *t6 = 0, *t7, *t8 = 0, *t9, *t10 = 0; Node *T3 = 0, *T4 = 0, *T5 = 0, *T6 = 0, *T7 = 0, *T8 = 0, *T9 = 0, *T10 = 0; Candidate *Nt2, *Nt4, *Nt6, *Nt8; GainType G1, G2, G3, G4, G5, G6, G7, G8, BestG8 = MINUS_INFINITY; int Case6 = 0, Case8 = 0, Case10 = 0, BestCase10 = 0, X4, X6, X8, X10, BTW275 = 0, BTW674 = 0, BTW571 = 0, BTW376 = 0, BTW574 = 0, BTW671 = 0, BTW471 = 0, BTW673 = 0, BTW573 = 0, BTW273 = 0; int Breadth2 = 0, Breadth4, Breadth6, Breadth8; if (t2 != SUC(t1)) Reversed ^= 1; /* * Determine (T3,T4,T5,T6,T7,T8,T9,T10) = (t3,t4,t5,t6,t7,t8,t9,t10) * such that * * G8 = *G0 - C(t2,T3) + C(T3,T4) * - C(T4,T5) + C(T5,T6) * - C(T6,T7) + C(T7,T8) * - C(T8,T9) + C(T9,T10) * * is maximum (= BestG8), and (T9,T10) has not previously been included. * If during this process a legal move with *Gain > 0 is found, then make * the move and exit Best5OptMove immediately. */ /* Choose (t2,t3) as a candidate edge emanating from t2 */ for (Nt2 = t2->CandidateSet; (t3 = Nt2->To); Nt2++) { if (t3 == t2->Pred || t3 == t2->Suc || ((G1 = *G0 - Nt2->Cost) <= 0 && GainCriterionUsed && ProblemType != HCP && ProblemType != HPP)) continue; if (++Breadth2 > MaxBreadth) break; /* Choose t4 as one of t3's two neighbors on the tour */ for (X4 = 1; X4 <= 2; X4++) { t4 = X4 == 1 ? PRED(t3) : SUC(t3); if (FixedOrCommon(t3, t4)) continue; G2 = G1 + (this->*C)(t3, t4); if (X4 == 1 && !Forbidden(t4, t1) && (!c || G2 - (this->*c)(t4, t1) > 0) && (*Gain = G2 - (this->*C)(t4, t1)) > 0) { Make2OptMove(t1, t2, t3, t4); return 0; } if (Backtracking && !Excludable(t3, t4)) continue; Breadth4 = 0; /* Choose (t4,t5) as a candidate edge emanating from t4 */ for (Nt4 = t4->CandidateSet; (t5 = Nt4->To); Nt4++) { if (t5 == t4->Pred || t5 == t4->Suc || ((G3 = G2 - Nt4->Cost) <= 0 && GainCriterionUsed && ProblemType != HCP && ProblemType != HPP)) continue; if (++Breadth4 > MaxBreadth) break; /* Choose t6 as one of t5's two neighbors on the tour */ for (X6 = 1; X6 <= 2; X6++) { if (X4 == 1) { if (X6 == 1) { Case6 = 1 + !BETWEEN(t2, t5, t4); t6 = Case6 == 1 ? SUC(t5) : PRED(t5); } else { t6 = t6 == t5->Pred ? t5->Suc : t5->Pred; if ((t5 == t1 && t6 == t2) || (t5 == t2 && t6 == t1)) continue; Case6 += 2; } } else if (BETWEEN(t2, t5, t3)) { Case6 = 4 + X6; t6 = X6 == 1 ? SUC(t5) : PRED(t5); if (t6 == t1) continue; } else { Case6 = 6 + X6; t6 = X6 == 1 ? PRED(t5) : SUC(t5); if (t6 == t2) continue; } if (FixedOrCommon(t5, t6)) continue; G4 = G3 + (this->*C)(t5, t6); if ((Case6 <= 2 || Case6 == 5 || Case6 == 6) && !Forbidden(t6, t1) && (!c || G4 - (this->*c)(t6, t1) > 0) && (*Gain = G4 - (this->*C)(t6, t1)) > 0) { Make3OptMove(t1, t2, t3, t4, t5, t6, Case6); return 0; } if (Backtracking && !Excludable(t5, t6)) continue; Breadth6 = 0; /* Choose (t6,t7) as a candidate edge emanating from t6 */ for (Nt6 = t6->CandidateSet; (t7 = Nt6->To); Nt6++) { if (t7 == t6->Pred || t7 == t6->Suc || (t6 == t2 && t7 == t3) || (t6 == t3 && t7 == t2) || ((G5 = G4 - Nt6->Cost) <= 0 && GainCriterionUsed && ProblemType != HCP && ProblemType != HPP)) continue; if (++Breadth6 > MaxBreadth) break; /* Choose t8 as one of t7's two neighbors on the tour */ for (X8 = 1; X8 <= 2; X8++) { if (X8 == 1) { Case8 = Case6; switch (Case6) { case 1: if ((BTW275 = BETWEEN(t2, t7, t5))) t8 = SUC(t7); else { t8 = PRED(t7); BTW674 = BETWEEN(t6, t7, t4); } break; case 2: if ((BTW376 = BETWEEN(t3, t7, t6))) t8 = SUC(t7); else { t8 = PRED(t7); BTW571 = BETWEEN(t5, t7, t1); } break; case 3: t8 = SUC(t7); BTW574 = BETWEEN(t5, t7, t4); break; case 4: if ((BTW671 = BETWEEN(t6, t7, t1))) t8 = PRED(t7); else t8 = BETWEEN(t2, t7, t4) ? SUC(t7) : PRED(t7); break; case 5: t8 = PRED(t7); BTW471 = BETWEEN(t4, t7, t1); if (!BTW471) BTW673 = BETWEEN(t6, t7, t3); break; case 6: if ((BTW471 = BETWEEN(t4, t7, t1))) t8 = PRED(t7); else { t8 = SUC(t7); BTW573 = BETWEEN(t5, t7, t3); } break; case 7: case 8: t8 = SUC(t7); BTW273 = BETWEEN(t2, t7, t3); break; } } else { t8 = t8 == t7->Pred ? t7->Suc : t7->Pred; Case8 += 8; } if ((t7 == t1 && t8 == t2) || (t7 == t2 && t8 == t1) || (t7 == t3 && t8 == t4) || (t7 == t4 && t8 == t3)) continue; if (FixedOrCommon(t7, t8)) continue; if (Case6 == 3 && !BTW574 && (X8 == 1) == BETWEEN(t3, t7, t1)) continue; if (Case6 == 4 && BTW671 && X8 == 2) break; if (Case6 == 7 && !BTW273 && (X8 == 1) == BETWEEN(t5, t7, t1)) continue; if (Case6 == 8 && !BTW273 && !BETWEEN(t4, t7, t5)) break; G6 = G5 + (this->*C)(t7, t8); if (t8 != t1 && (Case6 == 3 ? BTW574 : Case6 == 4 ? !BTW671 : Case6 == 7 ? BTW273 : Case6 != 8 && X8 == 1) && !Forbidden(t8, t1) && (!c || G6 - (this->*c)(t8, t1) > 0) && (*Gain = G6 - (this->*C)(t8, t1)) > 0) { Make4OptMove(t1, t2, t3, t4, t5, t6, t7, t8, Case8); return 0; } if (Backtracking && !Excludable(t7, t8)) continue; Breadth8 = 0; /* Choose (t8,t9) as a candidate edge emanating from t8 */ for (Nt8 = t8->CandidateSet; (t9 = Nt8->To); Nt8++) { if (t9 == t8->Pred || t9 == t8->Suc || t9 == t1 || (t8 == t2 && t9 == t3) || (t8 == t3 && t9 == t2) || (t8 == t4 && t9 == t5) || (t8 == t5 && t9 == t4) || ((G7 = G6 - Nt8->Cost) <= 0 && GainCriterionUsed && ProblemType != HCP && ProblemType != HPP)) continue; if (++Breadth8 > MaxBreadth) break; /* Choose t10 as one of t9's two neighbors on the tour */ for (X10 = 1; X10 <= 2; X10++) { if (X10 == 1) { t10 = 0; switch (Case8) { case 1: t10 = (BTW275 ? BETWEEN(t8, t9, t5) || BETWEEN(t3, t9, t1) : BTW674 ? BETWEEN(t7, t9, t1) : BETWEEN(t7, t9, t5)) ? PRED(t9) : SUC(t9); Case10 = 22; break; case 2: t10 = (BTW376 ? BETWEEN(t8, t9, t4) : BTW571 ? BETWEEN(t7, t9, t1) || BETWEEN(t3, t9, t6) : BETWEEN(t7, t9, t1)) ? PRED(t9) : SUC(t9); Case10 = 23; break; case 3: if (BTW574) { t10 = BETWEEN(t5, t9, t1) ? PRED(t9) : SUC(t9); Case10 = 24; break; } if (!BETWEEN(t5, t9, t4)) break; t10 = SUC(t9); Case10 = 1; break; case 4: if (BTW671) { if (!BETWEEN(t2, t9, t5)) break; t10 = SUC(t9); Case10 = 2; break; } t10 = BETWEEN(t6, t9, t4) ? PRED(t9) : SUC(t9); Case10 = 25; break; case 5: t10 = (BTW471 ? BETWEEN(t7, t9, t1) : BTW673 ? BETWEEN(t7, t9, t5) : BETWEEN(t4, t9, t1) || BETWEEN(t7, t9, t5)) ? PRED(t9) : SUC(t9); Case10 = 26; break; case 6: t10 = (BTW471 ? BETWEEN(t7, t9, t3) : BTW573 ? BETWEEN(t8, t9, t6) : BETWEEN(t4, t9, t1) || BETWEEN(t8, t9, t6)) ? PRED(t9) : SUC(t9); Case10 = 27; break; case 7: if (BTW273) { t10 = BETWEEN(t5, t9, t3) ? PRED(t9) : SUC(t9); Case10 = 28; break; } if (!BETWEEN(t2, t9, t3)) break; t10 = SUC(t9); Case10 = 3; break; case 8: if (BTW273) { if (!BETWEEN(t4, t9, t5)) break; Case10 = 4; } else { if (!BETWEEN(t2, t9, t3)) break; Case10 = 5; } t10 = SUC(t9); break; case 9: if (BTW275) { if (!BETWEEN(t7, t9, t4)) break; t10 = SUC(t9); Case10 = 6; break; } if (!BTW674) { if (!BETWEEN(t2, t9, t7)) break; t10 = SUC(t9); Case10 = 7; break; } if (!BETWEEN(t6, t9, t7)) break; t10 = SUC(t9); Case10 = 8; break; case 10: if (BTW376) { if (!BETWEEN(t7, t9, t6)) break; t10 = SUC(t9); Case10 = 9; break; } if (BTW571) { if (!BETWEEN(t2, t9, t7)) break; t10 = SUC(t9); Case10 = 10; break; } if (!BETWEEN(t3, t9, t6) && !BETWEEN(t2, t9, t7)) break; t10 = SUC(t9); Case10 = 11; break; case 11: if (BTW574) { t10 = BETWEEN(t3, t9, t1) ? PRED(t9) : SUC(t9); Case10 = 29; break; } if (!BETWEEN(t5, t9, t4)) break; t10 = SUC(t9); Case10 = 12; break; case 12: t10 = BETWEEN(t3, t9, t1) ? PRED(t9) : SUC(t9); Case10 = 30; break; case 13: if (BTW471) { if (!BETWEEN(t2, t9, t7)) break; t10 = SUC(t9); Case10 = 13; break; } if (BTW673) { if (!BETWEEN(t6, t9, t7)) break; t10 = SUC(t9); Case10 = 14; break; } if (!BETWEEN(t6, t9, t3) && !BETWEEN(t2, t9, t7)) break; t10 = SUC(t9); Case10 = 15; break; case 14: if (BTW471) { if (!BETWEEN(t2, t9, t7)) break; t10 = SUC(t9); Case10 = 16; break; } if (BTW573) { if (!BETWEEN(t7, t9, t3) && !BETWEEN(t2, t9, t6)) break; t10 = SUC(t9); Case10 = 17; break; } if (!BETWEEN(t7, t9, t6)) break; t10 = SUC(t9); Case10 = 18; break; case 15: if (BTW273) { t10 = BETWEEN(t5, t9, t1) ? PRED(t9) : SUC(t9); Case10 = 31; break; } if (!BETWEEN(t2, t9, t3)) break; t10 = SUC(t9); Case10 = 19; break; case 16: if (BTW273) { if (!BETWEEN(t4, t9, t5)) break; Case10 = 20; } else { if (!BETWEEN(t2, t9, t3)) break; Case10 = 21; } t10 = SUC(t9); break; } if (!t10) break; } else { if (Case10 >= 22) continue; Case10 += 31; t10 = t10 == t9->Pred ? t9->Suc : t9->Pred; } if (t10 == t1 || (t9 == t3 && t10 == t4) || (t9 == t4 && t10 == t3) || (t9 == t5 && t10 == t6) || (t9 == t6 && t10 == t5)) continue; if (FixedOrCommon(t9, t10)) continue; G8 = G7 + (this->*C)(t9, t10); if (!Forbidden(t10, t1) && (!c || G8 - (this->*c)(t10, t1) > 0) && (*Gain = G8 - (this->*C)(t10, t1)) > 0) { Make5OptMove(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, Case10); return 0; } if (GainCriterionUsed && G8 - Precision < t10->Cost) continue; if (!Backtracking || Swaps > 0) { if ((G8 > BestG8 || (G8 == BestG8 && !Near(t9, t10) && Near(T9, T10))) && Swaps < MaxSwaps && Excludable(t9, t10) && !InInputTour(t9, t10)) { /* Ignore the move if the gain does not vary */ if (RestrictedSearch && ProblemType != HCP && ProblemType != HPP && G2 - t4->Pi == G4 - t6->Pi && G4 - t6->Pi == G6 - t8->Pi && G6 - t8->Pi == G8 - t10->Pi && G3 + t5->Pi == G1 + t3->Pi && G5 + t7->Pi == G3 + t5->Pi && G7 + t9->Pi == G5 + t7->Pi) continue; T3 = t3; T4 = t4; T5 = t5; T6 = t6; T7 = t7; T8 = t8; T9 = t9; T10 = t10; BestCase10 = Case10; BestG8 = G8; } } else if (MaxSwaps > 0) { GainType G = G8; Node *t = t10; Make5OptMove(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, Case10); Exclude(t1, t2); Exclude(t3, t4); Exclude(t5, t6); Exclude(t7, t8); Exclude(t9, t10); while ((t = (this->*BestSubsequentMove)(t1, t, &G, Gain))); if (*Gain > 0) return 0; RestoreTour(); if (t2 != SUC(t1)) Reversed ^= 1; } } } } } } } } } *Gain = 0; if (T10) { /* Make the best 5-opt move */ Make5OptMove(t1, t2, T3, T4, T5, T6, T7, T8, T9, T10, BestCase10); Exclude(t1, t2); Exclude(T3, T4); Exclude(T5, T6); Exclude(T7, T8); Exclude(T9, T10); *G0 = BestG8; } return T10; }
void LKH::LKHAlg::GenerateCandidates(int MaxCandidates, GainType MaxAlpha, int Symmetric) { Node *From, *To; Candidate *NFrom, *NN; int a, d, Count; if (TraceLevel >= 2) printff("Generating candidates ... "); if (MaxAlpha < 0 || MaxAlpha > INT_MAX) MaxAlpha = INT_MAX; /* Initialize CandidateSet for each node */ FreeCandidateSets(); From = FirstNode; do From->Mark = 0; while ((From = From->Suc) != FirstNode); if (MaxCandidates > 0) { do { assert(From->CandidateSet = (Candidate *) malloc((MaxCandidates + 1) * sizeof(Candidate))); From->CandidateSet[0].To = 0; } while ((From = From->Suc) != FirstNode); } else { AddTourCandidates(); do { if (!From->CandidateSet) eprintf("MAX_CANDIDATES = 0: No candidates"); } while ((From = From->Suc) != FirstNode); return; } /* Loop for each node, From */ do { NFrom = From->CandidateSet; if (From != FirstNode) { From->Beta = INT_MIN; for (To = From; To->Dad != 0; To = To->Dad) { To->Dad->Beta = !FixedOrCommon(To, To->Dad) ? Max(To->Beta, To->Cost) : To->Beta; To->Dad->Mark = From; } } Count = 0; /* Loop for each node, To */ To = FirstNode; do { if (To == From) continue; d = c && !FixedOrCommon(From, To) ? (this->*c)(From, To) : (this->*D)(From, To); if (From == FirstNode) a = To == From->Dad ? 0 : d - From->NextCost; else if (To == FirstNode) a = From == To->Dad ? 0 : d - To->NextCost; else { if (To->Mark != From) To->Beta = !FixedOrCommon(To, To->Dad) ? Max(To->Dad->Beta, To->Cost) : To->Dad->Beta; a = d - To->Beta; } if (FixedOrCommon(From, To)) a = INT_MIN; else { if (From->FixedTo2 || To->FixedTo2 || Forbidden(From, To)) continue; if (InInputTour(From, To)) { a = 0; if (c) d = (this->*D)(From, To); } else if (c) { if (a > MaxAlpha || (Count == MaxCandidates && (a > (NFrom - 1)->Alpha || (a == (NFrom - 1)->Alpha && d >= (NFrom - 1)->Cost)))) continue; if (To == From->Dad) { d = From->Cost; a = 0; } else if (From == To->Dad) { d = To->Cost; a = 0; } else { a -= d; a += (d = (this->*D)(From, To)); } } } if (a <= MaxAlpha && IsPossibleCandidate(From, To)) { /* Insert new candidate edge in From->CandidateSet */ NN = NFrom; while (--NN >= From->CandidateSet) { if (a > NN->Alpha || (a == NN->Alpha && d >= NN->Cost)) break; *(NN + 1) = *NN; } NN++; NN->To = To; NN->Cost = d; NN->Alpha = a; if (Count < MaxCandidates) { Count++; NFrom++; } NFrom->To = 0; } } while ((To = To->Suc) != FirstNode); } while ((From = From->Suc) != FirstNode); AddTourCandidates(); if (Symmetric) SymmetrizeCandidateSet(); if (TraceLevel >= 2) printff("done\n"); }
LKH::LKHAlg::Node * LKH::LKHAlg::Best4OptMove(Node * t1, Node * t2, GainType * G0, GainType * Gain) { Node *t3, *t4, *t5, *t6 = 0, *t7, *t8 = 0, *T3 = 0, *T4 = 0, *T5 = 0, *T6 = 0, *T7 = 0, *T8 = 0; Candidate *Nt2, *Nt4, *Nt6; GainType G1, G2, G3, G4, G5, G6, BestG6 = MINUS_INFINITY; int Case6 = 0, Case8 = 0, BestCase8 = 0, X4, X6, X8; int Breadth2 = 0, Breadth4, Breadth6; *Gain = 0; if (SUC(t1) != t2) Reversed ^= 1; /* * Determine (T3,T4,T5,T6,T7,T8) = (t3,t4,t5,t6,t7,t8) * such that * * G8 = *G0 - C(t2,T3) + C(T3,T4) * - C(T4,T5) + C(T5,T6) * - C(T6,T7) + C(T7,T8) * * is maximum (= BestG6), and (T7,T8) has not previously been included. * If during this process a legal move with *Gain > 0 is found, then make * the move and exit Best4OptMove immediately. */ /* Choose (t2,t3) as a candidate edge emanating from t2 */ for (Nt2 = t2->CandidateSet; (t3 = Nt2->To); Nt2++) { if (t3 == t2->Pred || t3 == t2->Suc || ((G1 = *G0 - Nt2->Cost) <= 0 && GainCriterionUsed && ProblemType != HCP && ProblemType != HPP)) continue; if (++Breadth2 > MaxBreadth) break; /* Choose t4 as one of t3's two neighbors on the tour */ for (X4 = 1; X4 <= 2; X4++) { t4 = X4 == 1 ? PRED(t3) : SUC(t3); if (FixedOrCommon(t3, t4)) continue; G2 = G1 + (this->*C)(t3, t4); if (X4 == 1 && !Forbidden(t4, t1) && (!c || G2 - (this->*c)(t4, t1) > 0) && (*Gain = G2 - (this->*C)(t4, t1)) > 0) { Swap1(t1, t2, t3); return 0; } if (Backtracking && !Excludable(t3, t4)) continue; Breadth4 = 0; /* Choose (t4,t5) as a candidate edge emanating from t4 */ for (Nt4 = t4->CandidateSet; (t5 = Nt4->To); Nt4++) { if (t5 == t4->Pred || t5 == t4->Suc || ((G3 = G2 - Nt4->Cost) <= 0 && GainCriterionUsed && ProblemType != HCP && ProblemType != HPP)) continue; if (++Breadth4 > MaxBreadth) break; /* Choose t6 as one of t5's two neighbors on the tour */ for (X6 = 1; X6 <= 2; X6++) { if (X4 == 1) { if (X6 == 1) { Case6 = 1 + !BETWEEN(t2, t5, t4); t6 = Case6 == 1 ? SUC(t5) : PRED(t5); } else { t6 = t6 == t5->Pred ? t5->Suc : t5->Pred; if ((t5 == t1 && t6 == t2) || (t5 == t2 && t6 == t1)) continue; Case6 += 2; } } else if (BETWEEN(t2, t5, t3)) { Case6 = 4 + X6; t6 = X6 == 1 ? SUC(t5) : PRED(t5); if (t6 == t1) continue; } else { if (X6 == 2) break; Case6 = 7; t6 = PRED(t5); if (t6 == t2) continue; } if (FixedOrCommon(t5, t6)) continue; G4 = G3 + (this->*C)(t5, t6); if ((Case6 <= 2 || Case6 == 5 || Case6 == 6) && !Forbidden(t6, t1) && (!c || G4 - (this->*c)(t6, t1) > 0) && (*Gain = G4 - (this->*C)(t6, t1)) > 0) { Make3OptMove(t1, t2, t3, t4, t5, t6, Case6); return 0; } if (Backtracking && !Excludable(t5, t6)) continue; Breadth6 = 0; /* Choose (t6,t7) as a candidate edge emanating from t6 */ for (Nt6 = t6->CandidateSet; (t7 = Nt6->To); Nt6++) { if (t7 == t6->Pred || t7 == t6->Suc || (t6 == t2 && t7 == t3) || (t6 == t3 && t7 == t2) || ((G5 = G4 - Nt6->Cost) <= 0 && GainCriterionUsed && ProblemType != HCP && ProblemType != HPP)) continue; if (++Breadth6 > MaxBreadth) break; /* Choose t8 as one of t7's two neighbors on the tour */ for (X8 = 1; X8 <= 2; X8++) { if (X8 == 1) { Case8 = Case6; t8 = 0; switch (Case6) { case 1: t8 = BETWEEN(t2, t7, t5) ? SUC(t7) : PRED(t7); break; case 2: t8 = BETWEEN(t3, t7, t6) ? SUC(t7) : PRED(t7); break; case 3: if (BETWEEN(t5, t7, t4)) t8 = SUC(t7); break; case 4: if (BETWEEN(t2, t7, t5)) t8 = BETWEEN(t2, t7, t4) ? SUC(t7) : PRED(t7); break; case 5: t8 = PRED(t7); break; case 6: t8 = BETWEEN(t2, t7, t3) ? SUC(t7) : PRED(t7); break; case 7: if (BETWEEN(t2, t7, t3)) t8 = SUC(t7); break; } if (t8 == 0) break; } else { if (Case6 != 3 && Case6 != 4 && Case6 != 7) break; t8 = t8 == t7->Pred ? t7->Suc : t7->Pred; Case8 += 8; } if (t8 == t1 || (t7 == t3 && t8 == t4) || (t7 == t4 && t8 == t3)) continue; if (FixedOrCommon(t7, t8)) continue; G6 = G5 + (this->*C)(t7, t8); if (t8 != t1 && !Forbidden(t8, t1) && (!c || G6 - (this->*c)(t8, t1) > 0) && (*Gain = G6 - (this->*C)(t8, t1)) > 0) { Make4OptMove(t1, t2, t3, t4, t5, t6, t7, t8, Case8); return 0; } if (GainCriterionUsed && G6 - Precision < t8->Cost) continue; if (!Backtracking || Swaps > 0) { if ((G6 > BestG6 || (G6 == BestG6 && !Near(t7, t8) && Near(T7, T8))) && Swaps < MaxSwaps && Excludable(t7, t8) && !InInputTour(t7, t8)) { /* Ignore the move if the gain does not vary */ if (RestrictedSearch && ProblemType != HCP && ProblemType != HPP && G2 - t4->Pi == G4 - t6->Pi && G4 - t6->Pi == G6 - t8->Pi && G3 + t5->Pi == G1 + t3->Pi && G5 + t7->Pi == G3 + t5->Pi) continue; T3 = t3; T4 = t4; T5 = t5; T6 = t6; T7 = t7; T8 = t8; BestCase8 = Case8; BestG6 = G6; } } else if (MaxSwaps > 0) { GainType G = G6; Node *t = t8; Make4OptMove(t1, t2, t3, t4, t5, t6, t7, t8, Case8); Exclude(t1, t2); Exclude(t3, t4); Exclude(t5, t6); Exclude(t7, t8); while ((t = (this->*BestSubsequentMove)(t1, t, &G, Gain))); if (*Gain > 0) return 0; RestoreTour(); if (t2 != SUC(t1)) Reversed ^= 1; } } } } } } } *Gain = 0; if (T8) { /* Make the best 4-opt move */ Make4OptMove(t1, t2, T3, T4, T5, T6, T7, T8, BestCase8); Exclude(t1, t2), Exclude(T3, T4); Exclude(T5, T6); Exclude(T7, T8); *G0 = BestG6; } return T8; }
int SolveSubproblem(int CurrentSubproblem, int Subproblems, GainType * GlobalBestCost) { Node *FirstNodeSaved = FirstNode, *N, *Next, *Last = 0; GainType OptimumSaved = Optimum, Cost, Improvement, GlobalCost; double LastTime, Time, ExcessSaved = Excess; int NewDimension = 0, OldDimension = 0, Number, i, InitialTourEdges = 0, AscentCandidatesSaved = AscentCandidates, InitialPeriodSaved = InitialPeriod, MaxTrialsSaved = MaxTrials; BestCost = PLUS_INFINITY; FirstNode = 0; N = FirstNodeSaved; do { if (N->Subproblem == CurrentSubproblem) { if (SubproblemsCompressed && (((N->SubproblemPred == N->SubBestPred || FixedOrCommon(N, N->SubproblemPred) || (N->SubBestPred && (N->FixedTo1Saved == N->SubBestPred || N->FixedTo2Saved == N->SubBestPred))) && (N->SubproblemSuc == N->SubBestSuc || FixedOrCommon(N, N->SubproblemSuc) || (N->SubBestSuc && (N->FixedTo1Saved == N->SubBestSuc || N->FixedTo2Saved == N->SubBestSuc)))) || ((N->SubproblemPred == N->SubBestSuc || FixedOrCommon(N, N->SubproblemPred) || (N->SubBestSuc && (N->FixedTo1Saved == N->SubBestSuc || N->FixedTo2Saved == N->SubBestSuc))) && (N->SubproblemSuc == N->SubBestPred || FixedOrCommon(N, N->SubproblemSuc) || (N->SubBestPred && (N->FixedTo1Saved == N->SubBestPred || N->FixedTo2Saved == N->SubBestPred)))))) N->Subproblem = -CurrentSubproblem; else { if (!FirstNode) FirstNode = N; NewDimension++; } N->Head = N->Tail = 0; if (N->SubBestSuc) OldDimension++; } N->SubBestPred = N->SubBestSuc = 0; N->FixedTo1Saved = N->FixedTo1; N->FixedTo2Saved = N->FixedTo2; } while ((N = N->SubproblemSuc) != FirstNodeSaved); if ((Number = CurrentSubproblem % Subproblems) == 0) Number = Subproblems; if (NewDimension <= 3 || NewDimension == OldDimension) { if (TraceLevel >= 1 && NewDimension <= 3) printff ("\nSubproblem %d of %d: Dimension = %d (too small)\n", Number, Subproblems, NewDimension); FirstNode = FirstNodeSaved; return 0; } if (AscentCandidates > NewDimension - 1) AscentCandidates = NewDimension - 1; if (InitialPeriod < 0) { InitialPeriod = NewDimension / 2; if (InitialPeriod < 100) InitialPeriod = 100; } if (Excess < 0) Excess = 1.0 / NewDimension; if (MaxTrials == -1) MaxTrials = NewDimension; N = FirstNode; do { Next = N->SubproblemSuc; if (N->Subproblem == CurrentSubproblem) { N->Pred = N->Suc = N; if (N != FirstNode) Follow(N, Last); Last = N; } else if (Next->Subproblem == CurrentSubproblem && !Fixed(Last, Next)) { if (!Last->FixedTo1 || Last->FixedTo1->Subproblem != CurrentSubproblem) Last->FixedTo1 = Next; else Last->FixedTo2 = Next; if (!Next->FixedTo1 || Next->FixedTo1->Subproblem != CurrentSubproblem) Next->FixedTo1 = Last; else Next->FixedTo2 = Last; if (C == C_EXPLICIT) { if (Last->Id > Next->Id) Last->C[Next->Id] = 0; else Next->C[Last->Id] = 0; } } } while ((N = Next) != FirstNode); Dimension = NewDimension; AllocateSegments(); InitializeStatistics(); if (CacheSig) for (i = 0; i <= CacheMask; i++) CacheSig[i] = 0; OptimumSaved = Optimum; Optimum = 0; N = FirstNode; do { if (N->SubproblemSuc == N->InitialSuc || N->SubproblemPred == N->InitialSuc) InitialTourEdges++; if (!Fixed(N, N->Suc)) Optimum += Distance(N, N->Suc); if (N->FixedTo1 && N->Subproblem != N->FixedTo1->Subproblem) eprintf("Illegal fixed edge (%d,%d)", N->Id, N->FixedTo1->Id); if (N->FixedTo2 && N->Subproblem != N->FixedTo2->Subproblem) eprintf("Illegal fixed edge (%d,%d)", N->Id, N->FixedTo2->Id); } while ((N = N->Suc) != FirstNode); if (TraceLevel >= 1) printff ("\nSubproblem %d of %d: Dimension = %d, Upper bound = " GainFormat "\n", Number, Subproblems, Dimension, Optimum); FreeCandidateSets(); CreateCandidateSet(); for (Run = 1; Run <= Runs; Run++) { LastTime = GetTime(); Cost = Norm != 0 ? FindTour() : Optimum; /* Merge with subproblem tour */ Last = 0; N = FirstNode; do { if (N->Subproblem == CurrentSubproblem) { if (Last) Last->Next = N; Last = N; } } while ((N = N->SubproblemSuc) != FirstNode); Last->Next = FirstNode; Cost = MergeWithTour(); if (MaxPopulationSize > 1) { /* Genetic algorithm */ for (i = 0; i < PopulationSize; i++) Cost = MergeTourWithIndividual(i); if (!HasFitness(Cost)) { if (PopulationSize < MaxPopulationSize) { AddToPopulation(Cost); if (TraceLevel >= 1) PrintPopulation(); } else if (Cost < Fitness[PopulationSize - 1]) { ReplaceIndividualWithTour(PopulationSize - 1, Cost); if (TraceLevel >= 1) PrintPopulation(); } } } if (Cost < BestCost) { N = FirstNode; do { N->SubBestPred = N->Pred; N->SubBestSuc = N->Suc; } while ((N = N->Suc) != FirstNode); BestCost = Cost; } if (Cost < Optimum || (Cost != Optimum && OutputTourFileName)) { Improvement = Optimum - Cost; if (Improvement > 0) { BestCost = GlobalCost = *GlobalBestCost -= Improvement; Optimum = Cost; } else GlobalCost = *GlobalBestCost - Improvement; N = FirstNode; do N->Mark = 0; while ((N = N->SubproblemSuc) != FirstNode); do { N->Mark = N; if (!N->SubproblemSuc->Mark && (N->Subproblem != CurrentSubproblem || N->SubproblemSuc->Subproblem != CurrentSubproblem)) N->BestSuc = N->SubproblemSuc; else if (!N->SubproblemPred->Mark && (N->Subproblem != CurrentSubproblem || N->SubproblemPred->Subproblem != CurrentSubproblem)) N->BestSuc = N->SubproblemPred; else if (!N->Suc->Mark) N->BestSuc = N->Suc; else if (!N->Pred->Mark) N->BestSuc = N->Pred; else N->BestSuc = FirstNode; } while ((N = N->BestSuc) != FirstNode); Dimension = DimensionSaved; i = 0; do { if (ProblemType != ATSP) BetterTour[++i] = N->Id; else if (N->Id <= Dimension / 2) { i++; if (N->BestSuc->Id != N->Id + Dimension / 2) BetterTour[i] = N->Id; else BetterTour[Dimension / 2 - i + 1] = N->Id; } } while ((N = N->BestSuc) != FirstNode); BetterTour[0] = BetterTour[ProblemType != ATSP ? Dimension : Dimension / 2]; WriteTour(OutputTourFileName, BetterTour, GlobalCost); if (Improvement > 0) { do if (N->Subproblem != CurrentSubproblem) break; while ((N = N->SubproblemPred) != FirstNode); if (N->SubproblemSuc == N->BestSuc) { N = FirstNode; do { N->BestSuc->SubproblemPred = N; N = N->SubproblemSuc = N->BestSuc; } while (N != FirstNode); } else { N = FirstNode; do (N->SubproblemPred = N->BestSuc)->SubproblemSuc = N; while ((N = N->BestSuc) != FirstNode); } RecordBestTour(); WriteTour(TourFileName, BestTour, GlobalCost); } Dimension = NewDimension; if (TraceLevel >= 1) { printff("*** %d: Cost = " GainFormat, Number, GlobalCost); if (OptimumSaved != MINUS_INFINITY && OptimumSaved != 0) printff(", Gap = %04f%%", 100.0 * (GlobalCost - OptimumSaved) / OptimumSaved); printff(", Time = %0.2f sec. %s\n", fabs(GetTime() - LastTime), GlobalCost < OptimumSaved ? "<" : GlobalCost == OptimumSaved ? "=" : ""); } } Time = fabs(GetTime() - LastTime); UpdateStatistics(Cost, Time); if (TraceLevel >= 1 && Cost != PLUS_INFINITY) printff("Run %d: Cost = " GainFormat ", Time = %0.2f sec.\n\n", Run, Cost, Time); if (PopulationSize >= 2 && (PopulationSize == MaxPopulationSize || Run >= 2 * MaxPopulationSize) && Run < Runs) { Node *N; int Parent1, Parent2; Parent1 = LinearSelection(PopulationSize, 1.25); do Parent2 = LinearSelection(PopulationSize, 1.25); while (Parent1 == Parent2); ApplyCrossover(Parent1, Parent2); N = FirstNode; do { int d = C(N, N->Suc); AddCandidate(N, N->Suc, d, INT_MAX); AddCandidate(N->Suc, N, d, INT_MAX); N = N->InitialSuc = N->Suc; } while (N != FirstNode); } SRandom(++Seed); if (Norm == 0) break; } if (TraceLevel >= 1) PrintStatistics(); if (C == C_EXPLICIT) { N = FirstNode; do { for (i = 1; i < N->Id; i++) { N->C[i] -= N->Pi + NodeSet[i].Pi; N->C[i] /= Precision; } if (N->FixedTo1 && N->FixedTo1 != N->FixedTo1Saved) { if (N->Id > N->FixedTo1->Id) N->C[N->FixedTo1->Id] = Distance(N, N->FixedTo1); else N->FixedTo1->C[N->Id] = Distance(N, N->FixedTo1); } if (N->FixedTo2 && N->FixedTo2 != N->FixedTo2Saved) { if (N->Id > N->FixedTo2->Id) N->C[N->FixedTo2->Id] = Distance(N, N->FixedTo2); else N->FixedTo2->C[N->Id] = Distance(N, N->FixedTo2); } } while ((N = N->Suc) != FirstNode); } FreeSegments(); FreeCandidateSets(); FreePopulation(); if (InitialTourEdges == Dimension) { do N->InitialSuc = N->SubproblemSuc; while ((N = N->SubproblemSuc) != FirstNode); } else { do N->InitialSuc = 0; while ((N = N->SubproblemSuc) != FirstNode); } Dimension = ProblemType != ATSP ? DimensionSaved : 2 * DimensionSaved; N = FirstNode = FirstNodeSaved; do { N->Suc = N->BestSuc = N->SubproblemSuc; N->Suc->Pred = N; Next = N->FixedTo1; N->FixedTo1 = N->FixedTo1Saved; N->FixedTo1Saved = Next; Next = N->FixedTo2; N->FixedTo2 = N->FixedTo2Saved; N->FixedTo2Saved = Next; } while ((N = N->Suc) != FirstNode); Optimum = OptimumSaved; Excess = ExcessSaved; AscentCandidates = AscentCandidatesSaved; InitialPeriod = InitialPeriodSaved; MaxTrials = MaxTrialsSaved; return 1; }
void LKH::LKHAlg::MinimumSpanningTree(int Sparse) { Node *Blue; /* Points to the last node included in the tree */ Node *NextBlue = 0; /* Points to the provisional next node to be included */ Node *N; Candidate *NBlue; int d; Blue = N = FirstNode; Blue->Dad = 0; /* The root of the tree has no father */ if (Sparse && Blue->CandidateSet) { /* The graph is sparse */ /* Insert all nodes in the heap */ Blue->Loc = 0; /* A blue node is not in the heap */ while ((N = N->Suc) != FirstNode) { N->Dad = Blue; N->Cost = N->Rank = INT_MAX; HeapLazyInsert(N,this); } /* Update all neighbors to the blue node */ for (NBlue = Blue->CandidateSet; (N = NBlue->To); NBlue++) { if (FixedOrCommon(Blue, N)) { N->Dad = Blue; N->Cost = NBlue->Cost + Blue->Pi + N->Pi; N->Rank = INT_MIN; HeapSiftUp(N,this); } else if (!Blue->FixedTo2 && !N->FixedTo2) { N->Dad = Blue; N->Cost = N->Rank = NBlue->Cost + Blue->Pi + N->Pi; HeapSiftUp(N,this); } } /* Loop as long as there are more nodes to include in the tree */ while ((NextBlue = HeapDeleteMin(this))) { Follow(NextBlue, Blue); Blue = NextBlue; /* Update all neighbors to the blue node */ for (NBlue = Blue->CandidateSet; (N = NBlue->To); NBlue++) { if (!N->Loc) continue; if (FixedOrCommon(Blue, N)) { N->Dad = Blue; N->Cost = NBlue->Cost + Blue->Pi + N->Pi; N->Rank = INT_MIN; HeapSiftUp(N,this); } else if (!Blue->FixedTo2 && !N->FixedTo2 && (d = NBlue->Cost + Blue->Pi + N->Pi) < N->Cost) { N->Dad = Blue; N->Cost = N->Rank = d; HeapSiftUp(N,this); } } } } else { /* The graph is dense */ while ((N = N->Suc) != FirstNode) N->Cost = INT_MAX; /* Loop as long as there a more nodes to include in the tree */ while ((N = Blue->Suc) != FirstNode) { int Min = INT_MAX; /* Update all non-blue nodes (the successors of Blue in the list) */ do { if (FixedOrCommon(Blue, N)) { N->Dad = Blue; N->Cost = (this->*D)(Blue, N); NextBlue = N; Min = INT_MIN; } else { if (!Blue->FixedTo2 && !N->FixedTo2 && !Forbidden(Blue, N) && (!c || (this->*c)(Blue, N) < N->Cost) && (d = (this->*D)(Blue, N)) < N->Cost) { N->Cost = d; N->Dad = Blue; } if (N->Cost < Min) { Min = N->Cost; NextBlue = N; } } } while ((N = N->Suc) != FirstNode); Follow(NextBlue, Blue); Blue = NextBlue; } } }