/// <summary> /// Gets the minimum weight from customer1 -> customer3 while inserting customer2 and the best direction and turns to use. /// </summary> public static float CalculateCheapestInsert(this float[][] weights, float[] penalties, int directedId1, int id2, int directedId3, bool includeTurn1, bool includeTurn3, out int departureOffset1, out int arrivalOffset3, out int turn2) { // extract existing data. int id1, turn1, arrivalId1, departureId1, arrivalOffset1, temp; DirectedHelper.ExtractAll(directedId1, out arrivalId1, out departureId1, out id1, out turn1); DirectedHelper.ExtractOffset(turn1, out arrivalOffset1, out temp); int id3, turn3, arrivalId3, departureId3, departureOffset3; DirectedHelper.ExtractAll(directedId3, out arrivalId3, out departureId3, out id3, out turn3); DirectedHelper.ExtractOffset(turn3, out temp, out departureOffset3); // calculate current weight. var weightBefore = weights[departureId1][arrivalId3]; if (includeTurn1) { weightBefore += penalties[turn1]; } if (includeTurn3) { weightBefore += penalties[turn3]; } // evaluate all possibilities. var best = float.MaxValue; var base1 = id1 * 2; var base2 = id2 * 2; var base3 = id3 * 2; departureOffset1 = Constants.NOT_SET; arrivalOffset3 = Constants.NOT_SET; turn2 = Constants.NOT_SET; for (var do1 = 0; do1 < 2; do1++) { var d1 = base1 + do1; var turn1Weight = 0f; if (includeTurn1) { turn1 = DirectedHelper.BuildTurn(arrivalOffset1, do1); turn1Weight = penalties[turn1]; } for (var ao3 = 0; ao3 < 2; ao3++) { var a3 = base3 + ao3; var turn3Weight = 0f; if (includeTurn3) { turn3 = DirectedHelper.BuildTurn(departureOffset3, ao3); turn3Weight = penalties[turn3]; } for (var t = 0; t < 4; t++) { int ao2, do2; DirectedHelper.ExtractOffset(t, out ao2, out do2); var weight = weights[d1][base2 + ao2] + weights[base2 + do2][a3] + penalties[t] + turn1Weight + turn3Weight; if (weight < best) { best = weight; departureOffset1 = do1; arrivalOffset3 = ao3; turn2 = t; } } } } return best - weightBefore; }
/// <summary> /// Applies this operator. /// </summary> public bool Apply(SequenceDirectedProblem problem, SequenceDirectedObjective objective, Tour solution, out float delta) { var weights = problem.Weights; delta = objective.Zero; var fitnessBefore = objective.Calculate(problem, solution); var enumerator = solution.GetEnumerator(); while (enumerator.MoveNext()) { var previous = enumerator.Current; var previousDepartureId = DirectedHelper.ExtractDepartureId(previous); var current = solution.GetNeigbour(previous); if (current == Constants.NOT_SET) { // last of open. continue; } var next = solution.GetNeigbour(current); var nextArrivalId = Constants.NOT_SET; if (next != Constants.NOT_SET) { nextArrivalId = DirectedHelper.ExtractArrivalId(next); } int currentTurn, currentId, currentArrivalId, currentDepartureId; DirectedHelper.ExtractAll(current, out currentArrivalId, out currentDepartureId, out currentId, out currentTurn); var weightBefore = weights[previousDepartureId][currentArrivalId]; var weightAfter = float.MaxValue; var newDirectedId = Constants.NOT_SET; if (next == Constants.NOT_SET) { // there is no next, only one option here: // 0 or 1: arrival id offset = 0 OR // 2 or 3: arrival id offset = 1 if (currentArrivalId % 2 == 0) { // check turn = 2. weightAfter = weights[previousDepartureId][currentArrivalId + 1]; if (weightAfter < weightBefore) { // do the switch. newDirectedId = DirectedHelper.BuildDirectedId(currentId, 2); } } else { // check turn = 0 weightAfter = weights[previousDepartureId][currentArrivalId - 1]; if (weightAfter < weightBefore) { // do the switch. newDirectedId = DirectedHelper.BuildDirectedId(currentId, 0); } } } else { // three options left, excluding the current one of 4. weightBefore += weights[currentDepartureId][nextArrivalId]; weightBefore += problem.TurnPenalties[currentTurn]; currentArrivalId = currentArrivalId - (currentArrivalId % 2); currentDepartureId = currentDepartureId - (currentDepartureId % 2); for (var i = 0; i < 4; i++) { if (i == currentTurn) { continue; } int arrivalOffset, departureOffset; DirectedHelper.ExtractOffset(i, out arrivalOffset, out departureOffset); var newWeightAfter = weights[previousDepartureId][currentArrivalId + arrivalOffset] + weights[currentDepartureId + departureOffset][nextArrivalId] + problem.TurnPenalties[i]; if (newWeightAfter < weightAfter && newWeightAfter < weightBefore) { newDirectedId = DirectedHelper.BuildDirectedId(currentId, i); weightAfter = newWeightAfter; } } } // switch if a new directed if was found. if (newDirectedId != Constants.NOT_SET) { solution.Replace(current, newDirectedId); } } var fitnessAfter = objective.Calculate(problem, solution); delta = objective.Subtract(problem, fitnessBefore, fitnessAfter); return(objective.CompareTo(problem, delta, objective.Zero) > 0); }
/// <summary> /// Applies this operator. /// </summary> public bool Apply(TSProblem problem, TSPObjective objective, Tour solution, out float delta) { if (problem.Weights.Length <= 2) { delta = 0; return(false); } var weights = problem.Weights; delta = 0; // test switching directions in random order. if (_pool == null || solution.Count != _pool.Size) { // create a new pool. _pool = new RandomPool(solution.Count); } else { // just reset the existing one. _pool.Reset(); } while (_pool.MoveNext()) { var previous = solution.GetDirectedId(_pool.Current); var previousDepartureId = DirectedHelper.ExtractDepartureId(previous); var current = solution.GetNeigbour(previous); if (current == Constants.NOT_SET) { // last of open. continue; } var next = solution.GetNeigbour(current); var nextArrivalId = Constants.NOT_SET; if (next != Constants.NOT_SET) { nextArrivalId = DirectedHelper.ExtractArrivalId(next); } int currentTurn, currentId, currentArrivalId, currentDepartureId; DirectedHelper.ExtractAll(current, out currentArrivalId, out currentDepartureId, out currentId, out currentTurn); var weightBefore = weights[previousDepartureId][currentArrivalId]; var weightAfter = float.MaxValue; var newDirectedId = Constants.NOT_SET; if (next == Constants.NOT_SET) { // there is no next, only one option here: // 0 or 1: arrival id offset = 0 OR // 2 or 3: arrival id offset = 1 if (currentArrivalId % 2 == 0) { // check turn = 2. weightAfter = weights[previousDepartureId][currentArrivalId + 1]; if (weightAfter < weightBefore) { // do the switch. newDirectedId = DirectedHelper.BuildDirectedId(currentId, 2); } } else { // check turn = 0 weightAfter = weights[previousDepartureId][currentArrivalId - 1]; if (weightAfter < weightBefore) { // do the switch. newDirectedId = DirectedHelper.BuildDirectedId(currentId, 0); } } } else { // three options left, excluding the current one of 4. weightBefore += weights[currentDepartureId][nextArrivalId]; weightBefore += problem.TurnPenalties[currentTurn]; currentArrivalId = currentArrivalId - (currentArrivalId % 2); currentDepartureId = currentDepartureId - (currentDepartureId % 2); for (var i = 0; i < 4; i++) { if (i == currentTurn) { continue; } int arrivalOffset, departureOffset; DirectedHelper.ExtractOffset(i, out arrivalOffset, out departureOffset); var newWeightAfter = weights[previousDepartureId][currentArrivalId + arrivalOffset] + weights[currentDepartureId + departureOffset][nextArrivalId] + problem.TurnPenalties[i]; if (newWeightAfter < weightAfter && newWeightAfter < weightBefore) { newDirectedId = DirectedHelper.BuildDirectedId(currentId, i); weightAfter = newWeightAfter; } } } // switch if a new directed if was found. if (newDirectedId != Constants.NOT_SET) { delta = weightAfter - weightBefore; // negative when improvement. solution.Replace(current, newDirectedId); } } return(delta < 0); }