//Perform a 2-opt optimization on the given route and return the new route private static Route TwoOpt(Route orig, ref double costSavings) { /*for empty trucks, we cannot find a better path*/ if (orig.ids.Count == 0) { return null; } else { bool foundBetter = false; double newLen = 0; int[] betterPath = TwoOpt(orig.ids.ToArray(), ref foundBetter, ref newLen, false); if (foundBetter) { Console.WriteLine("** Truck Id {0} : New best Route len : {1:N} Savings = {2:N}", orig.truckId, newLen, newLen - orig.totalDist); costSavings = orig.totalDist - newLen; return new Route() { ids = betterPath.ToList(), totalDist = newLen, truckId = orig.truckId, unusedDemand = orig.unusedDemand }; } else { costSavings = 0; return null; } } }
private static Solution LS2Step(Solution basicSoln, ref double costSavings, ref int trucki, ref int truckj, ref int nodei, ref int nodej, bool exploreAll) { Solution betterSol = new Solution(); costSavings = 0; bool foundBetter = false; /*try to swap nodes pi and pj between truck_i and truck_j if the capccity constraints are satisfied*/ Route iRoute, jRoute; for (int i = 0; i < numTrucks - 1; i++) { iRoute = basicSoln.routes[i]; int freeCapacityIni = iRoute.unusedDemand; for (int j = i + 1; j < numTrucks; j++) { jRoute = basicSoln.routes[j]; int freeCapacityInj = jRoute.unusedDemand; if (iRoute.ids.Count == 0 || jRoute.ids.Count == 0) continue; double origCost = iRoute.totalDist + jRoute.totalDist; for (int piIdx = 0; piIdx < iRoute.ids.Count; piIdx++) { int pi = iRoute.ids[piIdx]; if (pi == 0) continue; for (int pjIdx = 0; pjIdx < jRoute.ids.Count; pjIdx++) { int pj = jRoute.ids[pjIdx]; if (pj == 0) continue; int demi = demands[pi]; int demj = demands[pj]; if ((freeCapacityIni + demi - demj >= 0) && (freeCapacityInj + demj - demi >= 0)) { List<int> newiPath = new List<int>(iRoute.ids); newiPath.Remove(pi); newiPath.Add(pj); List<int> newjPath = new List<int>(jRoute.ids); newjPath.Remove(pj); newjPath.Add(pi); bool foundBetteri = false, foundBetterj = false; double newLeni = 0, newLenj = 0; int[] betteriRoute = TwoOpt(newiPath.ToArray(), ref foundBetteri, ref newLeni, false); int[] betterjRoute = TwoOpt(newjPath.ToArray(), ref foundBetterj, ref newLenj, false); double newCost = newLeni + newLenj; double thisCostSavings = origCost - newCost; if (thisCostSavings > 0.000001) { if (thisCostSavings > costSavings) { costSavings = thisCostSavings; trucki = i; truckj = j; nodei = pi; nodej = pj; foundBetter = true; Route newiRoute = new Route(); if (foundBetteri && betteriRoute != null) { newiRoute.ids = betteriRoute.ToList(); } else { newiRoute.ids = newiPath; } newiRoute.totalDist = newLeni; newiRoute.truckId = iRoute.truckId; newiRoute.unusedDemand = iRoute.unusedDemand + demi - demj; Route newjRoute = new Route(); if (foundBetterj && betterjRoute != null) { newjRoute.ids = betterjRoute.ToList(); } else { newjRoute.ids = newjPath; } newjRoute.totalDist = newLenj; newjRoute.truckId = jRoute.truckId; newjRoute.unusedDemand = jRoute.unusedDemand + demj - demi; betterSol = new Solution(); //create the new solution with modified i and j routes for (int t = 0; t < basicSoln.routes.Count; t++) { if (t == i) { betterSol.routes.Add(newiRoute); } else if (t == j) { betterSol.routes.Add(newjRoute); } else { betterSol.routes.Add(basicSoln.routes[t]); } } if (!exploreAll) { goto end; } } } } } } } } end: if (foundBetter) { return betterSol; } else { return null; } }
private static Solution GetInitialFeasibleSolution(NodeInfo[] nodes) { Solution soln = new Solution(); int currTruck = 1; bool allDemandSatisfied = false; HashSet<int> assignedNodes = new HashSet<int>(); while (!allDemandSatisfied && currTruck <= numTrucks) { int availableCap = truckCapacity; //iterate over each of the nodes and if any node has not been assigned yet //to any truck and the capacity constraints of the truck are satisfied, then //assign this node to this truck. Route thisRoute = new Route(); thisRoute.ids.Add(0); thisRoute.truckId = currTruck; thisRoute.unusedDemand = availableCap; for (int i = 0; i < nodes.Length; i++) { if (nodes[i].id != 0) { int thisNodeId = nodes[i].id; int thisNodeDemand = demands[thisNodeId]; if (!assignedNodes.Contains(thisNodeId) && availableCap >= thisNodeDemand) { availableCap -= thisNodeDemand; assignedNodes.Add(thisNodeId); thisRoute.ids.Add(thisNodeId); thisRoute.unusedDemand = availableCap; if (assignedNodes.Count == nodes.Length - 1) { allDemandSatisfied = true; break; } } } } //thisRoute.ids.Add(0); /*to keep it consistent with tsp routines*/ CalculateRouteDistance(thisRoute); soln.routes.Add(thisRoute); currTruck++; } if (!allDemandSatisfied) { return null; } else { //fill up additional trucks with path : 0-0 int usedTrucks = soln.routes.Count; for (int i = 0; i < numTrucks - usedTrucks; i++) { soln.routes.Add(new Route() { ids = new List<int>() {0}, totalDist = 0, unusedDemand = truckCapacity, truckId = currTruck++ }); } return soln; } }
private static void CalculateRouteDistance(Route route) { double dist = 0; /*from origin till last node*/ for (int i = 0; i < route.ids.Count - 1; i++) { dist += distMatrix[route.ids[i], route.ids[i + 1]]; } dist += distMatrix[route.ids[route.ids.Count - 1], route.ids[0]]; /*add last node to origin*/ route.totalDist = dist; }
private static Solution LS1Step(Solution basicSoln, ref double costSavings, ref int trucki, ref int truckj) { Solution betterSol = new Solution(); bool foundBetter = false; /*try to move a node from truck_i to truck_j if the capccity constraints are satisfied*/ Route iRoute, jRoute; for (int i = 0; i < numTrucks; i++) { iRoute = basicSoln.routes[i]; for (int j = 0; j < numTrucks; j++) { jRoute = basicSoln.routes[j]; int freeCapacityInj = jRoute.unusedDemand; if (i == j) continue; if (freeCapacityInj == 0) continue; if (iRoute.ids.Count == 0) continue; List<int> idsToProbe = iRoute.ids.Where(x => x != 0 && demands[x] <= freeCapacityInj).ToList(); for (int k = 0; k < idsToProbe.Count; k++) { if (idsToProbe[k] != 0) { /*we cannot move the origin from i to j*/ int demandOfThisPointIni = demands[idsToProbe[k]]; if (freeCapacityInj >= demandOfThisPointIni) { /*capccity constraints are satisfied*/ /*move ids[k] from truck_i to truck_j*/ List<int> newiPath = new List<int>(iRoute.ids); newiPath.Remove(idsToProbe[k]); ; List<int> newjPath = new List<int>(jRoute.ids); newjPath.Add(idsToProbe[k]); double origCost = iRoute.totalDist + jRoute.totalDist; bool foundBetteri = false, foundBetterj =false; double newLeni = 0, newLenj = 0; int[] betteriRoute = TwoOpt(newiPath.ToArray(), ref foundBetteri, ref newLeni, false); int[] betterjRoute = TwoOpt(newjPath.ToArray(), ref foundBetterj, ref newLenj, false); double newCost = newLeni + newLenj; costSavings = origCost-newCost; if (costSavings > 0.000001) { trucki = i; truckj = j; foundBetter = true; Route newiRoute = new Route(); if (foundBetteri && betteriRoute != null) { newiRoute.ids = betteriRoute.ToList(); } else { newiRoute.ids = iRoute.ids; } newiRoute.totalDist = newLeni; newiRoute.truckId = iRoute.truckId; newiRoute.unusedDemand = iRoute.unusedDemand + demandOfThisPointIni; Route newjRoute = new Route(); if (foundBetterj && betterjRoute != null) { newjRoute.ids = betterjRoute.ToList(); } else { newjRoute.ids = jRoute.ids; } newjRoute.totalDist = newLenj; newjRoute.truckId = jRoute.truckId; newjRoute.unusedDemand = jRoute.unusedDemand - demandOfThisPointIni; //create the new solution with modified i and j routes for (int t = 0; t < basicSoln.routes.Count; t++) { if (t == i) { betterSol.routes.Add(newiRoute); } else if (t == j) { betterSol.routes.Add(newjRoute); } else { betterSol.routes.Add(basicSoln.routes[t]); } } goto end; } } } } } } end: if (foundBetter) { return betterSol; } else { return null; } }